|
| 1 | +#import "@preview/shiroa:0.3.1": * |
| 2 | +#import "/templates/page.typ": project |
| 3 | + |
| 4 | +#show: book |
| 5 | + |
| 6 | +#let meta = ( |
| 7 | + title: "Lambda VM specification", |
| 8 | + authors: ("3MI Labs", "Aligned"), |
| 9 | + version: "0.2", |
| 10 | + summary: ( |
| 11 | + ("PROOF SYSTEM", ( |
| 12 | + ("logup.typ", [`LogUp` argument], <logup>), |
| 13 | + ("memory.typ", [Memory argument], <memory>), |
| 14 | + )), |
| 15 | + ("OVERVIEW", ( |
| 16 | + ("variables.typ", [Variables], <vars>), |
| 17 | + ("signatures.typ", [Signatures], <signatures>), |
| 18 | + )), |
| 19 | + ("TEMPLATES", ( |
| 20 | + ("is_bit.typ", [`IS_BIT` template], <isbit>), |
| 21 | + ("sign.typ", [`SIGN` template], <sign>), |
| 22 | + ("add.typ", [`ADD`/`SUB` template], <add>), |
| 23 | + ("neg.typ", [`NEG` template], <neg>), |
| 24 | + )), |
| 25 | + ("MEMORY", ( |
| 26 | + ("memw.typ", [`MEMW` chip], <memw>), |
| 27 | + )), |
| 28 | + ("CPU", ( |
| 29 | + ("decode.typ", [`DECODE` table], <decode>), |
| 30 | + ("cpu.typ", [`CPU` chip], <cpu>), |
| 31 | + )), |
| 32 | + ("ALU", ( |
| 33 | + ("shift.typ", [`SHIFT` chip], <shift>), |
| 34 | + ("branch.typ", [`BRANCH` chip], <branch>), |
| 35 | + ("lt.typ", [`LT` chip], <lt>), |
| 36 | + ("mul.typ", [`MUL` chip], <mul>), |
| 37 | + ("dvrm.typ", [`DVRM` chip], <dvrm>), |
| 38 | + ("load.typ", [`LOAD` chip], <load>), |
| 39 | + ("bitwise.typ", [`BITWISE` chips], <bitwise>), |
| 40 | + )), |
| 41 | + ("ECALLS", ( |
| 42 | + ("about_ecalls.typ", [About `ECALL`], <ecall>), |
| 43 | + ("halt.typ", [`HALT` chip], <halt>), |
| 44 | + ("commit.typ", [`COMMIT` chip], <commit>), |
| 45 | + )) |
| 46 | + ) |
| 47 | +) |
| 48 | +#let meta_sections = meta.summary.map(m => m.at(1)).sum() |
| 49 | +#book-meta( |
| 50 | + title: meta.title, |
| 51 | + authors: meta.authors, |
| 52 | + summary: prefix-chapter("front.typ", meta.title) |
| 53 | + + meta.summary.map( |
| 54 | + ((title, sections)) => { |
| 55 | + heading(depth: 1, title) + sections.map(((ch, title, _ref)) => chapter(ch, title)).join() |
| 56 | + } |
| 57 | + ).join() |
| 58 | +) |
| 59 | + |
| 60 | +#let common-formatting(body) = { |
| 61 | + set footnote(numbering: "[1]") |
| 62 | + show raw.where(block: true): it => block(it, inset: 1em, width: 100%, radius: 5pt) |
| 63 | + body |
| 64 | +} |
| 65 | + |
| 66 | + |
| 67 | +#let todo(background: white, foreground: black, name: none, body) = block(fill: background, outset: 0.4em, radius: 20%, stroke: black)[ |
| 68 | + #set text(fill: foreground) |
| 69 | + *TODO #if name != none { [(#name)] }*: #body |
| 70 | +] |
| 71 | +#let rj = todo.with(background: teal, name: "Robin") |
| 72 | +#let et = todo.with(background: rgb("d4aa3a"), name: "Erik") |
| 73 | +#let cdsg = todo.with(background: olive, name: "Cyprien") |
| 74 | + |
| 75 | +#let aside(title, body) = context figure( |
| 76 | + block(inset: (left: 1em, right: 1em, bottom: 1em), stroke: luma(50%), breakable: false)[ |
| 77 | + #block(inset: (left: 1em, right: 1em, top: .75em, bottom: .75em), |
| 78 | + width: 100% + 2em, |
| 79 | + fill: rgb("55aaff"), |
| 80 | + stroke: luma(50%), |
| 81 | + align(center, strong(text(fill: black, title)))) |
| 82 | + #align(left, body) |
| 83 | +]) |
| 84 | + |
| 85 | + |
| 86 | +#let is-shiroa = "x-target" in sys.inputs |
| 87 | + |
| 88 | +// Strip styling to keep only "pure" content. |
| 89 | +// This is useful to avoid errors on the `set document(...)` in `project` |
| 90 | +// when invisibly including other chapters to resolve xrefs. |
| 91 | +#let strip-all(content) = { |
| 92 | + if repr(content.func()) == "sequence" { |
| 93 | + for c in content.children { |
| 94 | + strip-all(c) |
| 95 | + } |
| 96 | + } else if repr(content.func()) == "styled" { |
| 97 | + strip-all(content.child) |
| 98 | + } else { |
| 99 | + content |
| 100 | + } |
| 101 | +} |
| 102 | + |
| 103 | +#let _toplevel = state("_toplevel", none) |
| 104 | +#let _xref-included = state("_xref-included", (:)) |
| 105 | + |
| 106 | +// Invisibly include another chapter, so that its labels can be resolved |
| 107 | +#let xref-include(f) = { |
| 108 | + context { |
| 109 | + place(hide(box(width: auto, height: 0%, strip-all(include "/" + f)))) |
| 110 | + } |
| 111 | +} |
| 112 | + |
| 113 | +// Generate a cross-link for references to other chapters. |
| 114 | +// Leaves the ref untouched if it can't be resolved or points to the current chapter. |
| 115 | +#let xref(rf) = { |
| 116 | + assert(is-shiroa, message: "xref should only be used when compiling for shiroa") |
| 117 | + let lbl = rf.target |
| 118 | + let found = meta_sections.find(((_, _, tag)) => str(lbl).starts-with(str(tag))) |
| 119 | + context if found != none and found.at(0) != _toplevel.final() { |
| 120 | + let (ch, title, ref) = found |
| 121 | + if ref == lbl { |
| 122 | + cross-link("/" + ch, [Chapter #(meta_sections.position(x => x == found) + 1)]) |
| 123 | + } else { |
| 124 | + // Because shiroa does weird url escaping |
| 125 | + let shiroa-label = label(str(lbl).replace(":", "%3A")) |
| 126 | + context _xref-included.update(x => x + ((ch): true)) |
| 127 | + // The ideal would be to use `rf` directly as content argument to `cross-link`, |
| 128 | + // as that would inherit any/all formatting of the ref we want or need. |
| 129 | + // Unfortunately the ref link seems to take precedence over the cross-link hyperlink |
| 130 | + // when clicking. |
| 131 | + // There may still be some way around it by messing with some html output |
| 132 | + let link-content = context { |
| 133 | + let fig = query(lbl).first() |
| 134 | + let counter = if fig.has("counter") { |
| 135 | + fig.counter |
| 136 | + } else { |
| 137 | + counter(fig.func()) |
| 138 | + } |
| 139 | + |
| 140 | + let supplement = if rf.supplement == auto { |
| 141 | + fig.fields().at("supplement", default: none) |
| 142 | + } else { |
| 143 | + rf.supplement |
| 144 | + } |
| 145 | + [#supplement#numbering(fig.numbering, ..counter.at(lbl))] |
| 146 | + } |
| 147 | + cross-link("/" + ch, reference: shiroa-label, link-content) |
| 148 | + } |
| 149 | + } else { |
| 150 | + rf |
| 151 | + } |
| 152 | +} |
| 153 | + |
| 154 | +#let book-page(file, ..args) = { |
| 155 | + if not file.ends-with(".typ") { |
| 156 | + file = lower(file) + ".typ" |
| 157 | + } |
| 158 | + |
| 159 | + assert(meta_sections.find(s => s.at(0) == file) != none, message: "Couldn't resolve typst source file " + file) |
| 160 | + |
| 161 | + if is-shiroa { |
| 162 | + (body) => { |
| 163 | + show: common-formatting |
| 164 | + context _toplevel.update(s => { |
| 165 | + if s == none { |
| 166 | + file |
| 167 | + } else { |
| 168 | + s |
| 169 | + } |
| 170 | + }) |
| 171 | + let cond() = _toplevel.final() == file |
| 172 | + project.with(..args, title: context meta_sections.find(x => x.at(0) == _toplevel.final()).at(1), cond: cond)([ |
| 173 | + #show ref: it => context if _toplevel.final() == file { |
| 174 | + xref(it) |
| 175 | + } |
| 176 | + #context _xref-included.final().pairs().map(((key, value)) => context if value and cond() { |
| 177 | + xref-include(key) |
| 178 | + }).join() |
| 179 | + #body |
| 180 | + ]) |
| 181 | + } |
| 182 | + } else { |
| 183 | + body => body |
| 184 | + } |
| 185 | +} |
0 commit comments