symgroups.js (4728B)
1 /* eslint no-console:0 */ 2 "use strict"; 3 4 const fs = require("fs"); 5 const childProcess = require("child_process"); 6 7 const opts = require("nomnom") 8 .option("spacing", { 9 flag: true, 10 help: "Print mismatches involving spacing commands", 11 }) 12 .parse(); 13 14 const symbols = require("../src/symbols"); 15 const keys = Object.keys(symbols.math); 16 keys.sort(); 17 const types = [ 18 "mathord", "op", "bin", "rel", "open", "close", "punct", "inner", 19 "spacing", "accent", "textord", 20 ]; 21 22 process.nextTick(writeTexFile); 23 24 function writeTexFile() { 25 const tex = fs.createWriteStream("symgroups.tex"); 26 tex.on("finish", typeset); 27 tex.write("\\documentclass{article}\n" + 28 "\\usepackage{textcomp,amsmath,amssymb,gensymb}\n" + 29 "\\begin{document}\n" + 30 "\\showboxbreadth=\\maxdimen\\showboxdepth=\\maxdimen\n\n"); 31 keys.forEach(function(key, idx) { 32 const sym = symbols.math[key]; 33 const type = types.indexOf(sym.group) + 1; 34 tex.write("$" + idx + "+" + key + "+" + type + "\\showlists$\n\n"); 35 }); 36 tex.end("\\end{document}\n"); 37 } 38 39 function typeset() { 40 const proc = childProcess.spawn( 41 "pdflatex", ["--interaction=nonstopmode", "symgroups"], 42 {stdio: "ignore"}); 43 proc.on("exit", function(code, signal) { 44 if (signal) { 45 throw new Error("pdflatex terminated by signal " + signal); 46 } 47 fs.readFile("symgroups.log", "ascii", evaluate); 48 }).on("error", function(err) { 49 throw err; 50 }); 51 } 52 53 /* Consider the symbol "\sim" as an example. At the time of this 54 * writing, it has index 431 in our list, and is of group "rel" which 55 * is the fourth of the types listed above. So we construct an input line 56 * $431+\sim+4\showlists$ and receive corresponding output 57 * 58 * ### math mode entered at line 870 59 * \mathord 60 * .\fam0 4 61 * \mathord 62 * .\fam0 3 63 * \mathord 64 * .\fam0 2 65 * \mathbin 66 * .\fam0 + 67 * \mathrel 68 * .\fam2 ' 69 * \mathbin 70 * .\fam0 + 71 * \mathord 72 * .\fam0 4 73 * ### horizontal mode entered at line 870 74 * 75 * This is what we parse, using some regular expressions. 76 */ 77 78 // Extract individual blocks, from switch to math mode up to switch back. 79 const reMM = /^### math mode entered.*\n([^]*?)###/mg; 80 81 // Identify the parts separated by the plus signs 82 const reParts = /([^]*^\.\\fam0 \+\n)([^]+)(\\mathbin\n\.+\\fam0 \+[^]*)/m; 83 84 // Variation of the above in case we have nothing between the plus signs 85 const reEmpty = /^\.\\fam0 \+\n\\mathbin\n\.\\fam0 \+/m; 86 87 // Match any printed digit in the first or last of these parts 88 const reDigit = /^\.\\fam0 ([0-9])/mg; 89 90 // Match the atom type, i.e. "\mathrel" in the above example 91 const reAtom = /\\([a-z]+)/; 92 93 function evaluate(err, log) { 94 if (err) { 95 throw err; 96 } 97 98 let match; 99 let nextIndex = 0; 100 while ((match = reMM.exec(log)) !== null) { 101 const list = match[1]; 102 match = reParts.exec(list); 103 if (!match) { 104 match = reEmpty.exec(list); 105 if (match) { 106 console.log(keys[nextIndex] + " (index " + nextIndex + 107 ") in LaTeX apparently " + 108 "doesn't contribute to the output.\n"); 109 nextIndex++; 110 continue; 111 } 112 console.error("Can't split this into parts:"); 113 console.error(list); 114 process.exit(2); 115 } 116 const idx = extractDigits(match[1]); 117 const atom = match[2]; 118 const katexType = types[extractDigits(match[3]) - 1] || "???"; 119 match = reAtom.exec(atom); 120 if (!match) { 121 console.error("Failed to find atom type"); 122 console.error(atom); 123 console.error(list); 124 process.exit(3); 125 } 126 const latexType = match[1]; 127 if (katexType !== latexType && "math" + katexType !== latexType && 128 (katexType !== "textord" || latexType !== "mathord") && 129 (katexType !== "spacing" || opts.spacing)) { 130 console.log(keys[idx] + " (index " + idx + ") has '" + katexType + 131 "' in KaTeX, but LaTeX uses '" + latexType + "':"); 132 console.log(atom); 133 } 134 if (nextIndex !== idx) { 135 console.error("Index " + nextIndex + " not found in log"); 136 process.exit(4); 137 } 138 nextIndex = idx + 1; 139 } 140 if (nextIndex !== keys.length) { 141 console.error("Processed " + nextIndex + 142 " out of " + keys.length + " symbols"); 143 process.exit(4); 144 } 145 } 146 147 function extractDigits(str) { 148 let match; 149 let res = ""; 150 while ((match = reDigit.exec(str)) !== null) { 151 res += match[1]; 152 } 153 return +res; 154 }