symgroups.js 4.7 KB

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