From 1cf47ea3e2d0ff62e062c9dc1dd190775e706a46 Mon Sep 17 00:00:00 2001 From: ajet Date: Fri, 30 Jan 2026 10:09:18 -1000 Subject: [PATCH] ideate --- README.md | 236 +++++++++++++++++--- dual-mode.md | 381 ------------------------------- hiccup-prototype.md | 208 ----------------- plan.md | 84 ------- typst-mapping.md | 425 ----------------------------------- visualization.md | 531 -------------------------------------------- 6 files changed, 208 insertions(+), 1657 deletions(-) delete mode 100644 dual-mode.md delete mode 100644 hiccup-prototype.md delete mode 100644 plan.md delete mode 100644 typst-mapping.md delete mode 100644 visualization.md diff --git a/README.md b/README.md index e3843d7..9e0ff84 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,18 @@ Two modes coexist. Use whichever fits the moment. ### Mixing Freely ```clojure +(def title "My Document") +(def data [{:name "Alice" :value 100} + {:name "Bob" :value 200}]) + +(defn calculate-total [rows] + (reduce + (map :value rows))) + +(defn render-table [rows] + [:table {:columns 2} + (for [row rows] + (list (:name row) (:value row)))]) + [;; Hiccup for structured parts [:heading {:level 1} title] @@ -30,12 +42,10 @@ Two modes coexist. Use whichever fits the moment. " ;; Back to hiccup for programmatic stuff - [:table {:columns 2} - (for [row data] - (list (:name row) (:value row)))] + (render-table data) ;; Raw with interpolation - #t"The answer is ~(calculate-answer data)." + #t"The total is ~(calculate-total data)." ;; Nested: raw inside hiccup [:block {:inset "1em" :fill "gray.lighten(90%)"} @@ -46,12 +56,35 @@ Two modes coexist. Use whichever fits the moment. = Section Here's a dynamic table: - ~(render-table results) + ~(render-table data) And back to prose. "] ``` +```typst +#heading(level: 1)[My Document] + +This is just regular Typst content. I can write *bold* and _italic_ +without all the brackets. Much nicer for prose. + +- Bullet one +- Bullet two + +#table(columns: 2, [Alice], [100], [Bob], [200]) + +The total is 300. + +#block(inset: 1em, fill: gray.lighten(90%))[This is a _sidebar_ with easy formatting.] + += Section + +Here's a dynamic table: +#table(columns: 2, [Alice], [100], [Bob], [200]) + +And back to prose. +``` + ### Raw Escape Syntax ```clojure @@ -74,15 +107,38 @@ Using `~()` - the unquote concept from Clojure's syntax-quote: ```clojure ;; Simple expression -#t"The value is ~(expr)." +(def answer 42) +#t"The value is ~answer." +``` +```typst +The value is 42. +``` + +```clojure ;; Hiccup expression returns content +(def url "https://example.com") +(def label "here") #t"Click ~([:link {:src url} label]) to continue." +``` +```typst +Click #link("https://example.com")[here] to continue. +``` + +```clojure ;; Conditional -#t"Status: ~(if done? 'Complete' 'Pending')" +(def done true) +#t"Status: ~(if done 'Complete' 'Pending')" +``` +```typst +Status: Complete +``` + +```clojure ;; Loop +(def items [{:name "Apples"} {:name "Oranges"}]) #t" = Items ~(for [i items] @@ -90,6 +146,12 @@ Using `~()` - the unquote concept from Clojure's syntax-quote: " ``` +```typst += Items +- Apples +- Oranges +``` + ## Hiccup Syntax All elements follow standard Hiccup: @@ -352,54 +414,112 @@ It's a Lisp - of course we have macros. (list ~@body))) ;; Use it -(when-content show-abstract? +(def show-abstract true) +(def abstract-text "This paper presents...") + +(when-content show-abstract [:heading {:level 2} "Abstract"] abstract-text) ``` +```typst +== Abstract +This paper presents... +``` + ### DSL Macros ```clojure -;; Math DSL -(defmacro math-block [& exprs] - `[:math {:block true} - ~(compile-math-dsl exprs)]) - -(math-block - (sum :i 0 :n - (frac (pow x i) (factorial i)))) -;; => $ sum_(i=0)^n x^i / i! $ - -;; Shorthand for references +;; Shorthand macros for references (defmacro ref [id] `[:ref {:target ~id}]) (defmacro cite [& keys] `[:cite {:keys [~@keys]}]) -(ref :fig/arch) ; => [:ref {:target :fig/arch}] -(cite :smith2020 :jones2021) ; => [:cite {:keys [:smith2020 :jones2021]}] +(ref :fig/arch) +(cite :smith2020 :jones2021) +``` + +```typst +@fig:arch +@smith2020 @jones2021 +``` + +```clojure +;; Math helper functions +(defn sum [var from to body] + (str "sum_(" var "=" from ")^" to " " body)) + +(defn frac [num denom] + (str "(" num ") / (" denom ")")) + +(defn pow [base exp] + (str base "^" exp)) + +;; Math block macro +(defmacro math-block [& body] + `[:math {:block true} (str ~@body)]) + +(math-block (sum "i" 0 "n" (frac (pow "x" "i") "i!"))) +``` + +```typst +$ sum_(i=0)^n (x^i) / (i!) $ ``` ### Template Macros ```clojure +;; Helper functions for template rendering +(defn render-author [{:keys [name affil]}] + [:block {:align "center"} + [:strong name] [:linebreak] + [:emph affil]]) + +(defn render-authors [authors] + (map render-author authors)) + +(defn render-abstract [text] + [[:heading {:level 2} "Abstract"] + [:block {:inset "1em"} [:emph text]]]) + +;; Template macro - captures body and prepends document setup +(defmacro deftemplate [name params & preamble] + `(defmacro ~name [~@params & ~'body] + (concat ~@preamble ~'body))) + (deftemplate ieee-paper [title authors abstract] [[:set {:element :page :paper "us-letter" :columns 2}] - [:heading {:level 1 :align "center"} ~title] - (render-authors ~authors) - (render-abstract ~abstract) - ~'&body]) + [:set {:element :text :size "10pt"}] + [:heading {:level 1 :align "center"} title] + (render-authors authors) + (render-abstract abstract)]) +;; Use the template (ieee-paper "Neural Networks for Fun and Profit" [{:name "Alice" :affil "MIT"}] - "We present..." + "We present a novel approach..." [:heading {:level 2} "Introduction"] - "..." + "The problem of machine learning..." + [:heading {:level 2} "Methods"] - "...") + "Our method consists of...") +``` + +```typst +#set page(paper: "us-letter", columns: 2) +#set text(size: 10pt) +#heading(level: 1, align: center)[Neural Networks for Fun and Profit] +#block(align: center)[#strong[Alice] \ #emph[MIT]] +== Abstract +#block(inset: 1em)[#emph[We present a novel approach...]] +== Introduction +The problem of machine learning... +== Methods +Our method consists of... ``` ## Full Example @@ -482,6 +602,66 @@ It's a Lisp - of course we have macros. (compile-to-typst paper "paper.typ") ``` +```typst +#set page(paper: "a4") +#set text(font: "Linux Libertine") + +#heading(level: 1)[A Very Important Paper] + +#block(align: center)[ + #block(inset: 1em)[ + #strong[Alice Chen] \ + #emph[MIT] \ + #link("mailto:alice@mit.edu")[alice@mit.edu] + ] + #h(2em) + #block(inset: 1em)[ + #strong[Bob Smith] \ + #emph[Stanford] \ + #link("mailto:bob@stanford.edu")[bob@stanford.edu] + ] +] + += Abstract + +We present a novel approach to solving important problems. +Our method achieves *state-of-the-art* results on several +benchmarks while maintaining computational efficiency. + += Introduction + +The problem of _important things_ has long challenged researchers. +Previous approaches @citation1 @citation2 have made progress but +fundamental limitations remain. + +#figure( + image("architecture.png", width: 80%), + caption: [System architecture overview.] +) + +Our method works by first processing the input through +the encoder (see @fig:arch). + += Results + +#table( + columns: 2, + align: (left, right), + [#strong[Method]], [#strong[Accuracy]], + [Baseline], [72.0%], + [Ours], [94.0%] +) + +As shown in the table above, our method significantly +outperforms the baseline. + += Conclusion + +We have demonstrated that our approach is highly effective. + +#bibliography("refs.bib") +``` + ## Rules Summary 1. **`#t"..."`** - raw Typst content, returned as-is diff --git a/dual-mode.md b/dual-mode.md deleted file mode 100644 index 1925ba9..0000000 --- a/dual-mode.md +++ /dev/null @@ -1,381 +0,0 @@ -# Dual Mode: Hiccup + Raw Escapes - -Both systems coexist. Use whichever fits the moment. - -## The Two Modes - -```clojure -;; HICCUP - when you need programmatic structure -[:heading {:level 1} "Title"] - -;; RAW - when you just want to write -#t"= Title" -``` - -## Mixing Freely - -```clojure -[;; Hiccup for structured parts - [:heading {:level 1} title] - - ;; Raw escape for prose-heavy sections - #t" - This is just regular Typst content. I can write *bold* and _italic_ - without all the brackets. Much nicer for prose. - - - Bullet one - - Bullet two - " - - ;; Back to hiccup for programmatic stuff - [:table {:columns 2} - (for [row data] - (list (:name row) (:value row)))] - - ;; Raw with interpolation - #t"The answer is ~(calculate-answer data)." - - ;; Nested: raw inside hiccup (use :block for container) - [:block {:inset "1em" :fill "gray.lighten(90%)"} - #t"This is a _sidebar_ with easy formatting."] - - ;; Nested: hiccup inside raw - #t" - = Section - - Here's a dynamic table: - ~(render-table results) - - And back to prose. - "] -``` - -## Escape Syntax - -```clojure -;; Short string -#t"= Heading" - -;; Multi-line block -#t" -Multiple lines -of typst content -" - -;; Content with quotes (must escape) -#t"He said \"hello\" and *left*." -``` - -## Interpolation Inside Raw - -Using `~()` - the unquote concept from Clojure's syntax-quote: - -```clojure -;; Simple expression -#t"The value is ~(expr)." - -;; Hiccup expression returns content -#t"Click ~([:link {:src url} label]) to continue." - -;; Conditional -#t"Status: ~(if done? 'Complete' 'Pending')" - -;; Loop -#t" -= Items -~(for [i items] - (str '- ' (:name i) '\n')) -" - -;; Or loop returning hiccup (items are direct list children) -#t" -= Items -~[:list (for [i items] (:name i))] -" -``` - -Same mental model as macro unquote: -```clojure -`(let [x ~expr] ...) ; macro: escape to eval -#t"Value: ~(expr)" ; template: escape to eval -``` - -## Full Example - -```clojure -(ns my-paper.core - (:require [clojure-typst.core :refer :all])) - -(def authors - [{:name "Alice Chen" :affiliation "MIT" :email "alice@mit.edu"} - {:name "Bob Smith" :affiliation "Stanford" :email "bob@stanford.edu"}]) - -(def results - [{:method "Baseline" :accuracy 0.72} - {:method "Ours" :accuracy 0.94}]) - -(defn author-card [{:keys [name affiliation email]}] - [:block {:inset "1em"} - [:strong name] [:linebreak] - [:emph affiliation] [:linebreak] - [:link {:src (str "mailto:" email)} email]]) - -(defn results-table [data] - [:table {:columns 2 :align ["left" "right"]} - [:strong "Method"] [:strong "Accuracy"] - (for [{:keys [method accuracy]} data] - (list - method - (format "%.1f%%" (* 100 accuracy))))]) - -(def paper - [[:heading {:level 1} "A Very Important Paper"] - - ;; Programmatic author list - [:block {:align "center"} - (interpose [:h "2em"] - (map author-card authors))] - - ;; Prose in raw mode - much nicer to write - #t" - = Abstract - - We present a novel approach to solving important problems. - Our method achieves *state-of-the-art* results on several - benchmarks while maintaining computational efficiency. - - = Introduction - - The problem of _important things_ has long challenged researchers. - Previous approaches @citation1 @citation2 have made progress but - fundamental limitations remain. - - In this work, we propose a new framework that addresses these - limitations directly. Our key contributions are: - - + A novel architecture for processing data - + Theoretical analysis of convergence properties - + Extensive empirical evaluation - - = Methods - " - - ;; Back to hiccup for the technical diagram - [:figure {:caption "System architecture overview." :label :fig/arch} - [:image {:src "architecture.png" :width "80%"}]] - - #t" - Our method works by first processing the input through - the encoder (see @fig:arch). The encoded representation - is then passed to the decoder which produces the output. - - = Results - " - - ;; Programmatic table - (results-table results) - - #t" - As shown in the table above, our method significantly - outperforms the baseline. - - = Conclusion - - We have demonstrated that our approach is ~(if (> (:accuracy (last results)) 0.9) - "highly effective" - "somewhat effective"). - Future work will explore extensions to other domains. - " - - ;; Bibliography could be generated - [:bibliography {:src "refs.bib"}]]) - -;; Compile it -(compile-to-typst paper "paper.typ") -``` - -## Macros - -It's a Lisp - of course we have macros. - -### Standard Code Macros - -```clojure -;; Threading for cleaner data transforms -(defmacro -> [x & forms] - ...) - -(-> data - (filter :active) - (map :name) - (sort)) - -;; Custom control flow -(defmacro when-let [[binding expr] & body] - `(let [~binding ~expr] - (when ~binding ~@body))) -``` - -### Content Macros - -Macros that expand to hiccup/content: - -```clojure -;; Define a reusable content pattern -(defmacro defcomponent [name args & body] - `(defn ~name ~args - (list ~@body))) ; sequences flatten into parent content - -;; Conditional content sections -(defmacro when-content [test & body] - `(when ~test - (list ~@body))) - -;; Use it -(when-content show-abstract? - [:heading {:level 2} "Abstract"] - abstract-text) -``` - -### Document Structure Macros - -```clojure -;; Academic paper structure -(defmacro paper [& {:keys [title authors abstract] :as opts} & sections] - `[[:heading {:level 1} ~title] - (author-block ~authors) - (when ~abstract - (list - [:heading {:level 2} "Abstract"] - ~abstract)) - ~@sections]) - -;; Usage -(paper - :title "My Great Paper" - :authors ["Alice" "Bob"] - :abstract "We did cool stuff." - - [:heading {:level 2} "Introduction"] - "..." - - [:heading {:level 2} "Methods"] - "...") -``` - -### DSL Macros - -Build mini-languages for specific domains: - -```clojure -;; Math DSL that's easier to write than raw Typst math -(defmacro math-block [& exprs] - `[:math {:block true} - ~(compile-math-dsl exprs)]) - -(math-block - (sum :i 0 :n - (frac (pow x i) (factorial i)))) -;; => $ sum_(i=0)^n x^i / i! $ - -;; Table DSL -(defmacro deftable [name headers & row-specs] - ...) - -(deftable results-table - ["Method" "Precision" "Recall" "F1"] - :from results - :row (fn [{:keys [method p r f1]}] - [method (fmt p) (fmt r) (fmt f1)])) - -;; Figure DSL with auto-numbering -(defmacro figure [id & {:keys [src caption width]}] - `(do - (register-figure! ~id) - [:figure {:label ~id :caption ["Figure " (figure-num ~id) ": " ~caption]} - [:image {:src ~src :width ~(or width "100%")}]])) - -(figure :arch - :src "architecture.png" - :caption "System overview" - :width "80%") -``` - -### Syntax Extension Macros - -```clojure -;; Custom reader syntax for common patterns -;; (would require reader macro support) - -;; Shorthand for references -(defmacro ref [id] - `[:ref {:target ~id}]) - -;; Shorthand for citations -(defmacro cite [& keys] - `[:cite {:keys [~@keys]}]) - -;; Usage -(ref :fig/arch) ; => [:ref {:target :fig/arch}] -(cite :smith2020 :jones2021) ; => [:cite {:keys [:smith2020 :jones2021]}] -``` - -### Template Macros - -```clojure -;; Define document templates -(defmacro deftemplate [name args & structure] - `(defmacro ~name ~args - ~@structure)) - -(deftemplate ieee-paper [title authors abstract] - [[:set {:element :page :paper "us-letter" :columns 2}] - [:heading {:level 1 :align "center"} ~title] - (render-authors ~authors) - (render-abstract ~abstract) - ~'&body]) ; splice remaining content - -;; Use template -(ieee-paper - "Neural Networks for Fun and Profit" - [{:name "Alice" :affil "MIT"}] - "We present..." - - [:heading {:level 2} "Introduction"] - "..." - [:heading {:level 2} "Methods"] - "...") -``` - -### Why Macros Matter Here - -| Pattern | Without Macros | With Macros | -|---------|---------------|-------------| -| Repeated structure | Copy-paste hiccup | `defcomponent` | -| Conditional sections | Verbose `if` expressions | `when-content` | -| Domain languages | Manual compilation | DSL macros | -| Document templates | Function composition | Template macros | -| Boilerplate elimination | Nowhere to hide it | Macro it away | - -**The power:** Your document language grows to fit your domain. - -## Rules - -1. **`#t"..."`** - raw Typst content, returned as-is -2. **`~(...)`** inside raw - interpolate Clojure expression (like unquote) -3. **`[:tag ...]`** - hiccup, compiled to Typst function calls -4. **Strings in hiccup** - become Typst content directly -5. **Seqs in hiccup** - flattened (so `map`/`for` just work) -6. **Sequences flatten** - `(for ...)`, `(map ...)`, `(list ...)` results merge into parent - -## Why Both? - -| Situation | Use | -|-----------|-----| -| Prose, paragraphs, natural writing | Raw `#t"..."` | -| Tables, figures, structured layout | Hiccup | -| Loops, conditionals, data-driven | Hiccup or interpolated raw | -| Components, reusable parts | Hiccup functions | -| Quick one-off formatting | Either, your preference | - -**The principle:** Structure when you need it, prose when you don't. diff --git a/hiccup-prototype.md b/hiccup-prototype.md deleted file mode 100644 index 23688c9..0000000 --- a/hiccup-prototype.md +++ /dev/null @@ -1,208 +0,0 @@ -# Hiccup for Typst Content - Prototype - -## The Idea - -Hiccup represents markup as nested data structures: -```clojure -[:tag {attrs} & children] -``` - -## Syntax Mapping - -### Clojure-Typst Hiccup → Typst Output - -```clojure -;; Heading -[:heading {:level 2} "My Section"] -;; => #heading(level: 2)[My Section] - -;; Inline formatting (paragraphs are implicit in Typst) -["Hello " [:strong "world"] " and " [:emph "goodbye"]] -;; => Hello #strong[world] and #emph[goodbye] - -;; Lists (children are direct content, not :item tags) -[:list - "First thing" - "Second thing" - [[:strong "Important"] " third thing"]] -;; => #list( -;; [First thing], -;; [Second thing], -;; [#strong[Important] third thing] -;; ) - -;; Tables (cells are direct content, not :cell tags) -[:table {:columns 3} - "A" "B" "C" - "1" "2" "3"] -;; => #table(columns: 3, -;; [A], [B], [C], -;; [1], [2], [3] -;; ) - -;; Code blocks (use :raw for displayed code) -[:raw {:lang "python" :block true} "print('hello')"] -;; => #raw(lang: "python", block: true, "print('hello')") - -;; Math (inline by default) -[:math "x^2 + y^2 = z^2"] -;; => $x^2 + y^2 = z^2$ - -;; Math (block/display) -[:math {:block true} "sum_(i=0)^n i = (n(n+1))/2"] -;; => $ sum_(i=0)^n i = (n(n+1))/2 $ - -;; Images (src in attrs, no children) -[:image {:src "diagram.png" :width "50%"}] -;; => #image("diagram.png", width: 50%) - -;; Links (src in attrs, child is display text) -[:link {:src "https://typst.app"} "Typst"] -;; => #link("https://typst.app")[Typst] -``` - -## Full Document Example - -```clojure -(def my-doc - [[:heading {:level 1} "My Paper"] - - "This is the introduction. We discuss " - [:emph "important things"] "." - - [:parbreak] - - [:heading {:level 2} "Methods"] - - "We used the following approach:" - - [:list - "Step one" - "Step two" - "Step three"] - - [:heading {:level 2} "Results"] - - "The result is " [:math "x = 42"] "." - - [:table {:columns 2} - [:strong "Input"] [:strong "Output"] - "1" "1" - "2" "4" - "3" "9"]]) -``` - -**Compiles to:** - -```typst -#heading(level: 1)[My Paper] - -This is the introduction. We discuss #emph[important things]. - -#heading(level: 2)[Methods] - -We used the following approach: - -#list( - [Step one], - [Step two], - [Step three] -) - -#heading(level: 2)[Results] - -The result is $x = 42$. - -#table(columns: 2, - [#strong[Input]], [#strong[Output]], - [1], [1], - [2], [4], - [3], [9] -) -``` - -## Mixing Code and Content - -```clojure -;; Define a reusable component -(defn author-block [name institution] - [:block {:align "center"} - [:strong name] - [:linebreak] - [:emph institution]]) - -;; Use it with data -(def authors - [{:name "Alice" :inst "MIT"} - {:name "Bob" :inst "Stanford"}]) - -;; Generate content programmatically -[[:heading "Authors"] - (for [{:keys [name inst]} authors] - (author-block name inst))] -``` - -## Special Forms for Content - -```clojure -;; Conditional content (strings become paragraphs naturally) -(if peer-reviewed? - "This paper was peer reviewed." - "Preprint - not yet reviewed.") - -;; Loop over data (items are direct content in lists) -[:list - (for [item items] - (format-item item))] - -;; Let bindings for intermediate content -(let [title (get-title data) - abstract (get-abstract data)] - [[:heading title] - abstract]) -``` - -## The Alternative: Lisp for Code Only - -If hiccup feels too heavy, we could just use Lisp for logic and escape to raw Typst: - -```clojure -(defn factorial [n] - (if (<= n 1) - 1 - (* n (factorial (dec n))))) - -;; Escape to typst content -#t" -= Results - -The factorial of 5 is ~(factorial 5). - -Here's a table of factorials: -~(for [i (range 1 11)] - (str \"- \" i \"! = \" (factorial i) \"\\n\")) -" -``` - -## Questions - -1. **Implicit paragraphs?** Should adjacent strings auto-wrap in `[:p ...]`? -2. **Shorthand syntax?** Maybe `:#strong` instead of `[:strong ...]`? -3. **Component system?** How fancy do we get with reusable components? -4. **Escaping?** How to include literal `[` or other special chars? -5. **Whitespace handling?** Typst is whitespace-sensitive in content mode - -## Verdict? - -Hiccup gives us: -- ✅ Pure data representation of documents -- ✅ Full programmatic control -- ✅ Composable components -- ✅ Familiar to Clojure devs - -But: -- ❌ More verbose than raw Typst markup -- ❌ Loses Typst's nice content syntax -- ❌ Another layer to learn - -**Hybrid approach might be best:** Lisp for code/logic, with easy escape to Typst content syntax when you just want to write prose. diff --git a/plan.md b/plan.md deleted file mode 100644 index 016dae0..0000000 --- a/plan.md +++ /dev/null @@ -1,84 +0,0 @@ -# Clojure-Typst: A Clojure-style Lisp for Typst - -## The Problem - -Typst is a modern typesetting system with a nice programming language, but it's not a Lisp. We only like Lisps. - -## The Vision - -Create a Clojure-inspired Lisp that compiles to Typst, bringing: -- S-expression syntax -- Functional programming idioms -- Macros and metaprogramming -- Immutable-first data structures -- REPL-driven development (maybe?) - -## Open Questions - -### Syntax & Semantics -- [ ] How closely do we follow Clojure syntax? -- [ ] What Typst constructs map to Lisp naturally? -- [ ] How do we handle Typst's content mode vs code mode distinction? -- [ ] Do we support Clojure-style destructuring? -- [ ] What about namespaces/modules? - -### Data Structures -- [ ] Vectors `[]`, maps `{}`, sets `#{}`? -- [ ] How do these map to Typst arrays and dictionaries? -- [ ] Lazy sequences - possible in Typst? - -### Interop -- [ ] How do we call Typst functions from our Lisp? -- [ ] How do we emit Typst content/markup? -- [ ] Can we import Typst packages? - -### Implementation -- [ ] What language for the compiler? (Clojure? Rust? Typst itself?) -- [ ] AST representation -- [ ] Compilation strategy (source-to-source? interpreter?) -- [ ] Error messages and source maps - -## Typst Features to Leverage - -- Functions are first-class -- Has closures -- Pattern matching in function args -- Content as a first-class type -- Scripting mode `#` vs content mode - -## Example Syntax Ideas - -```clojure -; Define a function -(defn greet [name] - (str "Hello, " name "!")) - -; Emit typst content -(content - (heading "My Document") - (greet "World")) - -; Maybe something like hiccup for content? -[:heading {:level 1} "My Document"] -["Hello, " [:strong "World"] "!"] -``` - -## Next Steps - -1. Study Typst's language semantics more deeply -2. Identify the minimal viable feature set -3. Decide on implementation language -4. Build a basic parser for s-expressions -5. Implement core special forms (def, fn, let, if, do) -6. Add Typst content emission -7. Iterate - -## Resources - -- [Typst Documentation](https://typst.app/docs) -- [Clojure Reference](https://clojure.org/reference) -- [Make a Lisp](https://github.com/kanaka/mal) - -## Notes - -(Add brainstorming notes here) diff --git a/typst-mapping.md b/typst-mapping.md deleted file mode 100644 index 8792fda..0000000 --- a/typst-mapping.md +++ /dev/null @@ -1,425 +0,0 @@ -# Hiccup → Typst Mapping Reference - -## Hiccup Syntax - -All elements follow standard Hiccup: - -```clojure -[:tag {attrs} children...] -``` - -- **tag**: Element type (keyword) -- **attrs**: Optional map of attributes -- **children**: Content (strings, nested elements, or expressions) - -### Element Categories - -| Category | Examples | Children | -|----------|----------|----------| -| Content elements | `heading`, `strong`, `emph`, `block` | Display content | -| Source elements | `link`, `image`, `bibliography` | `link` has display text; others have none | -| List elements | `list`, `enum`, `table` | Each child = one item/cell | -| Container elements | `figure` | Single content child (compiled as function arg) | -| Math | `math` | Math expression string | -| References | `ref`, `cite` | None | -| Code | `raw` | Code string to display | -| Rules | `set` | None | -| Show rules | `show` | A `set` rule as child | - -### Reserved Attributes - -| Attr | Used by | Purpose | -|------|---------|---------| -| `:src` | `link`, `image`, `bibliography` | Source URL/path | -| `:label` | Any element | Makes element referenceable | -| `:caption` | `figure` | Figure caption text | -| `:target` | `ref` | Label to reference | -| `:keys` | `cite` | Citation key(s) | -| `:element` | `set`, `show` | Element type to configure | -| `:block` | `math`, `raw` | Display as block (default: inline) | -| `:lang` | `raw` | Code language for syntax highlighting | - -### Examples - -```clojure -;; Content element -[:heading {:level 2} "Section Title"] - -;; Source element with children -[:link {:src "https://example.com"} "Click here"] - -;; Source element without children -[:image {:src "diagram.png" :width "80%"}] - -;; List element -[:list "First" "Second" "Third"] - -;; Container with label -[:figure {:caption "Results" :label :fig/results} - [:image {:src "chart.png"}]] - -;; Math (inline and block) -[:math "E = m c^2"] -[:math {:block true} "sum_(i=0)^n i"] - -;; Reference and citation -[:ref {:target :fig/results}] -[:cite {:keys [:smith2020 :jones2021]}] - -;; Code display -[:raw {:lang "python" :block true} "print('hello')"] - -;; Set rule -[:set {:element :page :margin "2cm"}] - -;; Show rule (child is the set rule to apply) -[:show {:element :heading} - [:set {:element :text :fill "blue"}]] -``` - ---- - -## Audit of Current Tags - -### ✅ Valid - Direct Typst Functions - -| Hiccup | Typst | Notes | -|--------|-------|-------| -| `[:heading {:level 2} "Text"]` | `#heading(level: 2)[Text]` | ✅ Works | -| `[:strong "text"]` | `#strong[text]` | ✅ Works | -| `[:emph "text"]` | `#emph[text]` | ✅ Works | -| `[:link {:src "url"} "text"]` | `#link("url")[text]` | ✅ Works | -| `[:image {:src "path.png" :width "50%"}]` | `#image("path.png", width: 50%)` | ✅ Works | -| `[:block {:inset "1em"} ...]` | `#block(inset: 1em)[...]` | ✅ Works | -| `[:bibliography {:src "refs.bib"}]` | `#bibliography("refs.bib")` | ✅ Works | - -### ❌ Invalid - Don't Exist in Typst - -| Bad Hiccup | Problem | Fix | -|------------|---------|-----| -| `[:p "text"]` | No paragraph function | Just use strings, paragraphs are implicit | -| `[:item "text"]` | Not a function | Items are args to `list()` | -| `[:cell "text"]` | Not a function | Cells are args to `table()` | -| `[:div ...]` | HTML, not Typst | Use `[:block ...]` | -| `[:span ...]` | HTML, not Typst | Just inline content, or `[:box ...]` | -| `[:aside ...]` | Doesn't exist | Use `[:block ...]` with styling | -| `[:section ...]` | Doesn't exist | Just use headings | -| `[:doc ...]` | Doesn't exist | Top-level is just content | -| `[:table-row ...]` | Doesn't exist | Tables are flat | - -### ⚠️ Needs Adjustment - -| Current | Problem | Correct | -|---------|---------|---------| -| `[:linebreak]` | Function name | `[:linebreak]` → `#linebreak()` ✅ actually works | -| `[:h-space "2em"]` | Wrong name | `[:h "2em"]` → `#h(2em)` | -| `[:figure [:image ...] [:caption ...]]` | Caption isn't a child | Caption goes in attrs | -| `[:code {:lang "py"} "..."]` | Function is `raw` | `[:raw {:lang "python"} "..."]` | - -## Corrected Mappings - -### Lists - -Typst `list` takes content blocks as positional args, not `:item` children: - -```clojure -;; OLD (wrong) -[:list - [:item "First"] - [:item "Second"]] - -;; NEW (correct) -[:list "First" "Second"] -;; => #list([First], [Second]) - -;; With formatted items -[:list - [:strong "Important"] - "Regular item"] -;; => #list([#strong[Important]], [Regular item]) - -;; Nested -[:list - "Top level" - [:list "Nested 1" "Nested 2"]] -``` - -**Typst output:** -```typst -#list( - [First], - [Second], -) -``` - -### Tables - -Same pattern - cells are positional args: - -```clojure -;; OLD (wrong) -[:table {:columns 2} - [:cell "A"] [:cell "B"] - [:cell "1"] [:cell "2"]] - -;; NEW (correct) -[:table {:columns 2} - "A" "B" - "1" "2"] -;; => #table(columns: 2, [A], [B], [1], [2]) - -;; With formatting -[:table {:columns 2} - [:strong "Header 1"] [:strong "Header 2"] - "Data 1" "Data 2"] -``` - -**Typst output:** -```typst -#table( - columns: 2, - [#strong[Header 1]], [#strong[Header 2]], - [Data 1], [Data 2], -) -``` - -### Figures - -Caption is a named parameter, not a child element: - -```clojure -;; OLD (wrong) -[:figure - [:image "arch.png"] - [:caption "Architecture diagram"]] - -;; NEW (correct) -[:figure {:caption "Architecture diagram"} - [:image {:src "arch.png"}]] -;; => #figure(image("arch.png"), caption: [Architecture diagram]) - -;; With label for referencing -[:figure {:caption "Architecture" :label :fig/arch} - [:image {:src "arch.png" :width "80%"}]] -;; => #figure(image("arch.png", width: 80%), caption: [Architecture]) -``` - -**Typst output:** -```typst -#figure( - image("arch.png", width: 80%), - caption: [Architecture], -) -``` - -### Math - -```clojure -;; Inline math (default) -[:math "x^2 + y^2"] -;; => $x^2 + y^2$ - -;; Block/display math -[:math {:block true} "sum_(i=0)^n i"] -;; => $ sum_(i=0)^n i $ -``` - -### Code/Raw - -```clojure -;; Inline code -[:raw "let x = 1"] -;; => `let x = 1` - -;; Code block with language -[:raw {:lang "python" :block true} "print('hello')"] -;; => ```python -;; print('hello') -;; ``` -``` - -**Typst output:** -```typst -#raw("let x = 1") -#raw(lang: "python", block: true, "print('hello')") -``` - -### References and Citations - -```clojure -;; Reference a label -[:ref {:target :fig/arch}] -;; => @fig:arch - -;; Single citation -[:cite {:keys [:smith2020]}] -;; => @smith2020 - -;; Multiple citations -[:cite {:keys [:smith2020 :jones2021]}] -;; => @smith2020 @jones2021 -``` - -### Horizontal/Vertical Space - -```clojure -[:h "2em"] ;; => #h(2em) -[:v "1em"] ;; => #v(1em) -``` - -### Page/Document Settings - -`set` and `show` rules configure document styling. Use `:element` attr to specify the target: - -```clojure -;; Set rules (affects everything after) -[:set {:element :page :paper "a4" :margin "2cm"}] -;; => #set page(paper: "a4", margin: 2cm) - -[:set {:element :text :font "New Computer Modern" :size "11pt"}] -;; => #set text(font: "New Computer Modern", size: 11pt) - -;; Show rules (child is the set rule to apply) -[:show {:element :heading} - [:set {:element :text :fill "blue"}]] -;; => #show heading: set text(fill: blue) -``` - -### Paragraphs - -There is no paragraph function. Paragraphs are created by blank lines: - -```clojure -;; Just strings, separated by [:parbreak] or blank content -"First paragraph." -[:parbreak] -"Second paragraph." - -;; Or in raw mode, just write naturally -#t" -First paragraph. - -Second paragraph. -" -``` - -## Revised Full Example - -```clojure -(def my-doc - [;; Set rules at top - [:set {:element :page :paper "a4"}] - [:set {:element :text :font "Linux Libertine"}] - - [:heading {:level 1} "My Paper"] - - "This is the introduction. We discuss " - [:emph "important things"] "." - - [:parbreak] - - [:heading {:level 2} "Methods"] - - "We used the following approach:" - - [:list - "Step one" - "Step two" - [:strong "Step three (important)"]] - - [:heading {:level 2} "Results"] - - "See " [:ref {:target :fig/results}] " and " [:ref {:target :tab/data}] "." - - [:figure {:caption "Results visualization" :label :fig/results} - [:image {:src "results.png" :width "80%"}]] - - [:figure {:caption "Data table" :label :tab/data} - [:table {:columns 2} - [:strong "Input"] [:strong "Output"] - "1" "1" - "2" "4" - "3" "9"]] - - "The equation is " [:math "E = m c^2"] "." - - [:math {:block true} "integral_0^infinity e^(-x^2) dif x = sqrt(pi)/2"] - - [:bibliography {:src "refs.bib"}]]) -``` - -**Compiles to:** - -```typst -#set page(paper: "a4") -#set text(font: "Linux Libertine") - -#heading(level: 1)[My Paper] - -This is the introduction. We discuss #emph[important things]. - -#heading(level: 2)[Methods] - -We used the following approach: - -#list( - [Step one], - [Step two], - [#strong[Step three (important)]], -) - -#heading(level: 2)[Results] - -See @fig:results and @tab:data. - -#figure( - image("results.png", width: 80%), - caption: [Results visualization], -) - -#figure( - table( - columns: 2, - [#strong[Input]], [#strong[Output]], - [1], [1], - [2], [4], - [3], [9], - ), - caption: [Data table], -) - -The equation is $E = m c^2$. - -$ integral_0^infinity e^(-x^2) dif x = sqrt(pi)/2 $ - -#bibliography("refs.bib") -``` - -## Typst Gotchas - -Important Typst behaviors the compiler must handle: - -1. **Math variable spacing**: In math mode, adjacent letters like `mc` are parsed as a single variable name. To represent multiplication, add spaces: `$m c^2$` not `$mc^2$`. The compiler should insert spaces between single-letter variables. - -2. **Referenceable tables**: Tables cannot have labels directly attached. To make a table referenceable with `@label`, wrap it in a `#figure()`: - ```typst - #figure( - table(...), - caption: [...], - )