code wiki / (root) / nx_mathml_gate.nx

nx_mathml_gate.nx source

↩ module page · 151 lines · 8651 B

1// nx_mathml_gate.nx -- IS THE SOVEREIGN LATEX->MATHML TYPESETTER CORRECT, AND CAN IT REFUSE? 2// 3// The teeth are structural facts about the emitted markup, not stored golden strings: a golden string 4// records that its author agreed with the code on the day they wrote it, whereas "a superscript produces 5// an msup element" is true of any correct implementation and false of any wrong one. 6// 7// THE REFUSALS ARE THE LOAD-BEARING HALF. A typesetter that silently drops what it cannot read produces a 8// formula that is wrong in a way the reader cannot see -- worse than no formula at all -- so an unknown 9// command, an unbalanced brace and a short buffer each have their own negative control, and each asserts 10// WHICH rule fired rather than merely that something was refused. 11// 12// license_tier: ORIGINAL expect_exit: 0 13import "syscalls.nx" 14import "nx_mathml_lib.nx" 15import "nx_gate_verdict.nx" 16 17const MG_CAP: i64 = 65536 18 19func mg_slen(s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { i = i + 1 } return i } 20 21func mg_has(buf: *u8, n: i64, pat: *u8) -> i64 { 22 let m: i64 = mg_slen(pat) 23 if m == 0 { return 1 } 24 var i: i64 = 0 25 while i + m <= n { 26 var j: i64 = 0 27 var ok: i64 = 1 28 while j < m { if buf[i+j] != pat[j] { ok = 0; j = m } else { j = j + 1 } } 29 if ok == 1 { return 1 } 30 i = i + 1 31 } 32 return 0 33} 34 35// Convert one formula into the shared buffer. Returns the byte count or the negative LM_ERR_*. 36func mg_conv(latex: *u8, out: *u8, cap: i64, bo: *i64, bl: *i64) -> i64 { 37 return lm_to_mathml(latex, mg_slen(latex), out, cap, 1, bo, bl) 38} 39 40func main() -> i64 { 41 let ctr: *i64 = gv_ctr() 42 gv_head("=== NX-MATHML-GATE -- LaTeX subset to MathML, emitted natively so no third-party typesetter is in the path ===" as *u8) 43 44 let out: *u8 = sys_mmap(MG_CAP) 45 let bo: *i64 = (sys_mmap(8)) as *i64 46 let bl: *i64 = (sys_mmap(8)) as *i64 47 48 // ---- FIXTURE REACHED THE CONDITION ------------------------------------------------------------- 49 let n1: i64 = mg_conv("E=mc^2" as *u8, out, MG_CAP, bo, bl) 50 gv_check("fixture-reached-the-condition: a real formula converts and returns a positive byte count" as *u8, 51 (n1 > 0) as i64, ctr) 52 53 // ---- STRUCTURE: each construct produces its own MathML element ---------------------------------- 54 gv_check("a-SUPERSCRIPT-produces-an-msup-element-so-the-browser-raises-it" as *u8, 55 mg_has(out, n1, "<msup>" as *u8), ctr) 56 gv_check("the-exponent-is-a-NUMBER-element-not-an-identifier" as *u8, 57 mg_has(out, n1, "<mn>2</mn>" as *u8), ctr) 58 // MY FIRST VERSION OF THIS TOOTH ASSERTED THE TWO IDENTIFIERS WERE ADJACENT IN "E=mc^2" AND THE GATE 59 // CORRECTLY FAILED IT: the c is the base of the superscript, so an msup element sits between them. 60 // The library was right and the assertion was wrong. The property actually worth testing is that 61 // adjacent letters become SEPARATE identifiers, so it is tested on a formula with no script on it. 62 let n1b: i64 = mg_conv("mc" as *u8, out, MG_CAP, bo, bl) 63 gv_check("a-single-letter-becomes-its-OWN-identifier-because-ab-is-a-times-b-not-a-variable-named-ab" as *u8, 64 mg_has(out, n1b, "<mi>m</mi><mi>c</mi>" as *u8), ctr) 65 let n1c: i64 = mg_conv("E=mc^2" as *u8, out, MG_CAP, bo, bl) 66 gv_check("the-SUPERSCRIPT-BASE-is-the-letter-immediately-before-it-not-the-whole-preceding-run" as *u8, 67 mg_has(out, n1c, "<msup><mi>c</mi>" as *u8), ctr) 68 gv_check("the-emitted-root-declares-the-MathML-namespace-so-a-browser-renders-it-natively" as *u8, 69 mg_has(out, n1, "www.w3.org/1998/Math/MathML" as *u8), ctr) 70 71 let n2: i64 = mg_conv("\\frac{a}{b}" as *u8, out, MG_CAP, bo, bl) 72 gv_check("a-FRACTION-produces-an-mfrac-with-two-argument-rows" as *u8, 73 ((mg_has(out, n2, "<mfrac>" as *u8)) & (mg_has(out, n2, "</mfrac>" as *u8))), ctr) 74 75 let n3: i64 = mg_conv("\\sqrt{x+1}" as *u8, out, MG_CAP, bo, bl) 76 gv_check("a-ROOT-produces-an-msqrt-around-its-whole-argument" as *u8, 77 ((mg_has(out, n3, "<msqrt>" as *u8)) & (mg_has(out, n3, "</msqrt>" as *u8))), ctr) 78 79 let n4: i64 = mg_conv("x_1" as *u8, out, MG_CAP, bo, bl) 80 gv_check("a-SUBSCRIPT-produces-an-msub-element" as *u8, mg_has(out, n4, "<msub>" as *u8), ctr) 81 82 let n5: i64 = mg_conv("\\alpha+\\beta" as *u8, out, MG_CAP, bo, bl) 83 gv_check("a-GREEK-command-emits-the-real-UTF-8-glyph-from-the-incumbent-symbol-library" as *u8, 84 mg_has(out, n5, "\xCE\xB1" as *u8), ctr) 85 gv_check("a-RELATION-or-operator-glyph-is-an-mo-so-the-browser-spaces-it-as-an-operator" as *u8, 86 mg_has(out, n5, "<mo>" as *u8), ctr) 87 88 let n6: i64 = mg_conv("a^{b^{c}}" as *u8, out, MG_CAP, bo, bl) 89 gv_check("NESTED-scripts-parse-so-the-grammar-is-genuinely-recursive-not-single-level" as *u8, 90 (n6 > 0) as i64, ctr) 91 92 let n7: i64 = mg_conv("\\sum_{i=1}^{n} i" as *u8, out, MG_CAP, bo, bl) 93 gv_check("a-SUM-with-both-limits-converts-which-is-the-shape-most-real-formulas-need" as *u8, 94 ((n7 > 0) as i64) & (mg_has(out, n7, "<msup>" as *u8)), ctr) 95 96 // ---- ESCAPE BY CONSTRUCTION -------------------------------------------------------------------- 97 // The one input an author fully controls is the formula text, so it is the one thing that must never 98 // reach the markup unescaped. This is the same property the unified document portal rests on. 99 let n8: i64 = mg_conv("a<b" as *u8, out, MG_CAP, bo, bl) 100 gv_check("a-MARKUP-character-in-the-formula-is-ESCAPED-into-a-reference" as *u8, 101 mg_has(out, n8, "&lt;" as *u8), ctr) 102 gv_check("neg-control-the-raw-markup-character-does-NOT-survive-into-element-content" as *u8, 103 (mg_has(out, n8, "<mo><</mo>" as *u8) == 0) as i64, ctr) 104 105 // ---- REFUSALS, EACH NAMING WHICH RULE FIRED ---------------------------------------------------- 106 let e1: i64 = mg_conv("\\foobar{x}" as *u8, out, MG_CAP, bo, bl) 107 gv_check_eq("neg-control-an-UNKNOWN-command-is-REFUSED-by-name-never-silently-dropped" as *u8, 108 e1, LM_ERR_UNKNOWN_CMD, ctr) 109 gv_check_eq("the-refusal-NAMES-the-offending-command-length-so-a-caller-can-point-at-it" as *u8, 110 bl[0], 6, ctr) 111 gv_check("a-REFUSED-conversion-leaves-the-output-EMPTY-rather-than-half-written" as *u8, 112 (out[0] == (0 as u8)) as i64, ctr) 113 114 let e2: i64 = mg_conv("\\frac{a" as *u8, out, MG_CAP, bo, bl) 115 gv_check_eq("neg-control-an-UNBALANCED-brace-is-REFUSED-as-its-own-named-rule" as *u8, 116 e2, LM_ERR_UNBALANCED, ctr) 117 118 let e3: i64 = lm_to_mathml("x+1" as *u8, 3, out, 8, 1, bo, bl) 119 gv_check_eq("neg-control-a-SHORT-buffer-is-a-NAMED-refusal-never-a-silent-truncation" as *u8, 120 e3, LM_ERR_SHORT, ctr) 121 122 let e4: i64 = lm_to_mathml("" as *u8, 0, out, MG_CAP, 1, bo, bl) 123 gv_check_eq("neg-control-EMPTY-input-is-refused-rather-than-answered-with-an-empty-formula" as *u8, 124 e4, LM_ERR_EMPTY, ctr) 125 126 // ---- THE PUBLISHED BUFFER CONTRACT MUST MATCH WHAT THE CONVERTER ACTUALLY DEMANDS --------------- 127 // A caller sizes its buffer from lm_out_cap_for. If that published figure and the internal check ever 128 // disagreed, every caller would size correctly and still be refused -- so the two are tested together. 129 let want: i64 = lm_out_cap_for(3) 130 let e5: i64 = lm_to_mathml("x+1" as *u8, 3, out, want, 1, bo, bl) 131 gv_check("the-PUBLISHED-buffer-size-is-exactly-sufficient-so-a-caller-that-obeys-it-is-never-refused" as *u8, 132 (e5 > 0) as i64, ctr) 133 let e6: i64 = lm_to_mathml("x+1" as *u8, 3, out, want - 1, 1, bo, bl) 134 gv_check_eq("neg-control-one-byte-BELOW-the-published-size-is-refused-so-the-figure-is-tight-not-padded" as *u8, 135 e6, LM_ERR_SHORT, ctr) 136 137 // ---- EMITTED VALUES ---------------------------------------------------------------------------- 138 gv_values_head() 139 gv_kv("bytes_for_E_equals_mc_squared" as *u8, n1) 140 gv_kv("bytes_for_a_fraction" as *u8, n2) 141 gv_kv("bytes_for_a_root" as *u8, n3) 142 gv_kv("bytes_for_a_sum_with_both_limits" as *u8, n7) 143 gv_kv("published_buffer_size_for_3_byte_input" as *u8, want) 144 gv_kv("unknown_command_error_code" as *u8, LM_ERR_UNKNOWN_CMD) 145 gv_kv("unbalanced_brace_error_code" as *u8, LM_ERR_UNBALANCED) 146 147 let rc: i64 = gv_verdict("mathml-gate" as *u8, ctr, 148 "a LaTeX subset converts to native MathML with every construct producing its own element, formula text escaped by construction, and four refusals each naming the rule that fired" as *u8) 149 sys_exit(rc) 150 return rc 151}