www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

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 }