From e9a8c60d368716dfc5e71475d74f0cc138a84399 Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Wed, 31 Dec 2025 15:39:06 +0100 Subject: [PATCH 1/7] spec: support ":=" expr --- spec/expr.typ | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/expr.typ b/spec/expr.typ index 88d66d5d9..b0e1cffb6 100644 --- a/spec/expr.typ +++ b/spec/expr.typ @@ -55,7 +55,7 @@ "sub": 6, // - "idx": 7, // [] "cast": 8, // cast - "eq": 9, // = + "eq": 9, // = and := "MAX": 10, // ) @@ -100,6 +100,7 @@ rec(PREC.pow, e.at(1)) + `^` + rec(PREC.pow, e.at(2)) }, "=": (pp, rec, e) => rec(PREC.eq, e.at(1)) + ` = ` + rec(PREC.eq, e.at(2)), + ":=": (pp, rec, e) => rec(PREC.eq, e.at(1)) + ` := ` + rec(PREC.eq, e.at(2)), "-": (pp, rec, e) => { if e.len() == 2 { // Negation @@ -138,6 +139,7 @@ $#e.at(1)^#e.at(2)$ }, "=": (pp, rec, e) => $#rec(PREC.eq, e.at(1)) = #rec(PREC.eq, e.at(2))$, + ":=": (pp, rec, e) => $#rec(PREC.eq, e.at(1)) := #rec(PREC.eq, e.at(2))$, "-": (pp, rec, e) => { if e.len() == 2 { // Negation From d07f327f2db425426faf8beea39f2342f1953535 Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Wed, 31 Dec 2025 15:54:11 +0100 Subject: [PATCH 2/7] spec: refactor poly and polys rendering; supporting more complex definitions --- spec/chip.typ | 73 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/spec/chip.typ b/spec/chip.typ index 41bd44a29..0550d3196 100644 --- a/spec/chip.typ +++ b/spec/chip.typ @@ -26,27 +26,84 @@ /// Generates a table listing `chip`'s columns. #let render_chip_column_table(chip, config) = { + + // Render a definition's range + let render_def_range(idx, range) = { + if type(range) == array { + if range.len() == 1 { + [#raw(idx) `=` #range.at(0)] + } + if range.len() == 2 { + [#raw(idx) #sym.in `[`#range.at(0)`,`#range.at(1)`]`] + } else { + assert(false, message: "invalid range: " + repr(range)) + } + } else { + [#raw(idx) `=` #range] + } + } + + // Render definition `def` + let render_definition(def) = { + if type(def) == dictionary { + ( + [], + table.cell(align: right, emph[definition]), + expr_to_math((":=", ("idx", var.name, def.idx), def.poly)), + render_def_range(def.idx, def.range) + ) + } else { + ( + [], + table.cell(align: right, emph[definition]), + table.cell(colspan: 2, expr_to_math(def)) + ) + } + } + + // Render definition `defs` + // It is assumed that `defs` has several entries. + let render_indexed_definitions(var_name, defs) = { + ( + [], + table.cell(align: right, emph[definition]), + table.cell(colspan: 2, expr_to_math(("idx", var_name, defs.idx))) + ) + for (i, def) in defs.entries.enumerate() { + ( + [], + [], + [#expr_to_math((":=", " ", def.poly))], + [#render_def_range(defs.idx, def.range)], + ) + } + } + // Group variables by category show figure: set block(breakable: true) figure(table( - columns: (auto, auto, 1fr), + columns: (auto, auto, 1fr, auto), inset: 6pt, align: left + top, stroke: none, - table.header([*Label*], [*Type*], [*Description*]), + table.header([*Label*], [*Type*], table.cell(colspan: 2, [*Description*])), table.hline(stroke: stroke(thickness: 2pt)), ..for (cat, vars) in chip.variables.pairs() { - ([#emph(cat)], [], [], table.hline(stroke: .6pt)) + (table.cell(colspan: 4, emph(cat)), table.hline(stroke: .6pt)) for var in vars { - ([#raw(var.name)], [#type_to_code(var.type)], [#eval(var.desc, mode: "markup")]) - for (i, poly) in var.at("polys", default: ()).enumerate() { - (if i == 0 { emph[def] }, [], expr_to_math(("=", ("idx", var.name, i), poly))) + ( + [#raw(var.name)], + [#type_to_code(var.type)], + table.cell(colspan: 2, [#eval(var.desc, mode: "markup")]) + ) + if "polys" in var { + render_indexed_definitions(var.polys) } if "poly" in var { - (emph[def], [], expr_to_math(var.poly)) + render_definition(var.poly) } } - ([], [], []) + (table.cell(colspan: 4, [])) }, ), caption: [Column overview of #chip.name chip.]) } From c611e8ff89e0be7c7afc8a6e49cf5769b02b6c10 Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Wed, 31 Dec 2025 16:08:06 +0100 Subject: [PATCH 3/7] spec: enforce a var only has poly or polys (an not both) defined --- spec/chip.typ | 12 ++++++------ spec/src.typ | 11 ++++++++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/spec/chip.typ b/spec/chip.typ index 0550d3196..2b7c3fa8d 100644 --- a/spec/chip.typ +++ b/spec/chip.typ @@ -44,12 +44,12 @@ } // Render definition `def` - let render_definition(def) = { + let render_definition(def, var_name) = { if type(def) == dictionary { ( [], table.cell(align: right, emph[definition]), - expr_to_math((":=", ("idx", var.name, def.idx), def.poly)), + expr_to_math((":=", ("idx", var_name, def.idx), def.poly)), render_def_range(def.idx, def.range) ) } else { @@ -63,7 +63,7 @@ // Render definition `defs` // It is assumed that `defs` has several entries. - let render_indexed_definitions(var_name, defs) = { + let render_indexed_definitions(defs, var_name) = { ( [], table.cell(align: right, emph[definition]), @@ -97,13 +97,13 @@ table.cell(colspan: 2, [#eval(var.desc, mode: "markup")]) ) if "polys" in var { - render_indexed_definitions(var.polys) + render_indexed_definitions(var.polys, var.name) } if "poly" in var { - render_definition(var.poly) + render_definition(var.poly, var.name) } } - (table.cell(colspan: 4, [])) + (table.cell(colspan: 4, []), ) }, ), caption: [Column overview of #chip.name chip.]) } diff --git a/spec/src.typ b/spec/src.typ index 75a81a5f9..d4dfe2569 100644 --- a/spec/src.typ +++ b/spec/src.typ @@ -38,8 +38,17 @@ assert(category in config.variables.categories.all) } + // Check that either `poly` or `polys` is set, but not both + let all_vars = chip.variables.values().flatten() + for var in all_vars { + assert( + "poly" not in var or "polys" not in var, + message: "both 'poly' and 'polys' defined defined for " + repr(var.name) + ) + } + let all_labels = config.variables.types.map(type => type.label); - for var in chip.variables.values().flatten() { + for var in all_vars { let type_label = if type(var.type) == array { var.type.at(0) } else { From 7483b8720cdb5d8f1ca66aa105fd63e8471832bb Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Wed, 31 Dec 2025 16:29:47 +0100 Subject: [PATCH 4/7] spec: fix def rendering issues --- spec/chip.typ | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spec/chip.typ b/spec/chip.typ index 2b7c3fa8d..8111087a3 100644 --- a/spec/chip.typ +++ b/spec/chip.typ @@ -31,12 +31,11 @@ let render_def_range(idx, range) = { if type(range) == array { if range.len() == 1 { - [#raw(idx) `=` #range.at(0)] - } - if range.len() == 2 { + [#raw(idx) `=` #range.at(0)] + } else if range.len() == 2 { [#raw(idx) #sym.in `[`#range.at(0)`,`#range.at(1)`]`] } else { - assert(false, message: "invalid range: " + repr(range)) + assert(false, message: "invalid range: " + repr(range) + repr(range.len())) } } else { [#raw(idx) `=` #range] From 0ebdce456ec6a8e56919ac39b9b5ad9972d066c9 Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Fri, 2 Jan 2026 09:23:43 +0100 Subject: [PATCH 5/7] spec: rename `poly` and `polys` as `def` and `defs` --- spec/chip.typ | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/chip.typ b/spec/chip.typ index 8111087a3..f52e6b42c 100644 --- a/spec/chip.typ +++ b/spec/chip.typ @@ -68,7 +68,7 @@ table.cell(align: right, emph[definition]), table.cell(colspan: 2, expr_to_math(("idx", var_name, defs.idx))) ) - for (i, def) in defs.entries.enumerate() { + for (i, def) in defs.polys.enumerate() { ( [], [], @@ -95,11 +95,11 @@ [#type_to_code(var.type)], table.cell(colspan: 2, [#eval(var.desc, mode: "markup")]) ) - if "polys" in var { - render_indexed_definitions(var.polys, var.name) + if "defs" in var { + render_indexed_definitions(var.defs, var.name) } - if "poly" in var { - render_definition(var.poly, var.name) + if "def" in var { + render_definition(var.def, var.name) } } (table.cell(colspan: 4, []), ) From 275059755783ecbd1058f3816e36a65ad592cdf5 Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Fri, 2 Jan 2026 09:32:30 +0100 Subject: [PATCH 6/7] spec: refactor `def` format --- spec/chip.typ | 47 +++++++++++++++++++++++------------------------ spec/src.typ | 11 ++++++----- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/spec/chip.typ b/spec/chip.typ index f52e6b42c..527c0c32e 100644 --- a/spec/chip.typ +++ b/spec/chip.typ @@ -44,37 +44,39 @@ // Render definition `def` let render_definition(def, var_name) = { - if type(def) == dictionary { - ( + if type(def) in (array, str) { + return ( [], table.cell(align: right, emph[definition]), - expr_to_math((":=", ("idx", var_name, def.idx), def.poly)), - render_def_range(def.idx, def.range) + table.cell(colspan: 2, expr_to_math(def)) ) - } else { + } + + assert(type(def) == dictionary, message: "invalid definition: " + repr(def)) + + if "poly" in def { ( [], table.cell(align: right, emph[definition]), - table.cell(colspan: 2, expr_to_math(def)) + expr_to_math((":=", ("idx", var_name, def.idx), def.poly)), + render_def_range(def.idx, def.range) ) - } - } - - // Render definition `defs` - // It is assumed that `defs` has several entries. - let render_indexed_definitions(defs, var_name) = { - ( - [], - table.cell(align: right, emph[definition]), - table.cell(colspan: 2, expr_to_math(("idx", var_name, defs.idx))) - ) - for (i, def) in defs.polys.enumerate() { + } else if "polys" in def { ( [], - [], - [#expr_to_math((":=", " ", def.poly))], - [#render_def_range(defs.idx, def.range)], + table.cell(align: right, emph[definition]), + table.cell(colspan: 2, expr_to_math(("idx", var_name, def.idx))) ) + for (i, poly) in def.polys.enumerate() { + ( + [], + [], + expr_to_math((":=", " ", poly.poly)), + render_def_range(def.idx, poly.range), + ) + } + } else { + assert(false, message: "invalid definition: " + repr(def)) } } @@ -95,9 +97,6 @@ [#type_to_code(var.type)], table.cell(colspan: 2, [#eval(var.desc, mode: "markup")]) ) - if "defs" in var { - render_indexed_definitions(var.defs, var.name) - } if "def" in var { render_definition(var.def, var.name) } diff --git a/spec/src.typ b/spec/src.typ index d4dfe2569..7c9e68487 100644 --- a/spec/src.typ +++ b/spec/src.typ @@ -38,15 +38,16 @@ assert(category in config.variables.categories.all) } - // Check that either `poly` or `polys` is set, but not both - let all_vars = chip.variables.values().flatten() - for var in all_vars { + // Check that `def` is only contained in `virtual` variables + let non_virtual_vars = chip.variables.pairs().filter(x => x.first() != "virtual").map(x => x.last()).flatten(); + for var in non_virtual_vars { assert( - "poly" not in var or "polys" not in var, - message: "both 'poly' and 'polys' defined defined for " + repr(var.name) + "def" not in var, + message: "illegal `def` in non-virtual var: " + repr(var.name) ) } + let all_vars = chip.variables.values().flatten() let all_labels = config.variables.types.map(type => type.label); for var in all_vars { let type_label = if type(var.type) == array { From 6fe64c6189efb2be0dc93d6557cef69dd98efb8e Mon Sep 17 00:00:00 2001 From: Erik <159244975+erik-3milabs@users.noreply.github.com> Date: Fri, 2 Jan 2026 10:41:10 +0100 Subject: [PATCH 7/7] fix typo --- spec/expr.typ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/expr.typ b/spec/expr.typ index 0250e16e3..ae7bd0792 100644 --- a/spec/expr.typ +++ b/spec/expr.typ @@ -55,7 +55,7 @@ "add": 6, // + "sub": 7, // - "idx": 8, // [] - "eq": 9, // = and :- + "eq": 9, // = and := "MAX": 10, // )