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, "<" 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}