dkl9 commited on 2023-201 12:07:28
Showing 2 changed files, with 278 additions and 1 deletions.
| ... | ... |
@@ -5,11 +5,13 @@ import {Complex} from "./types/complex.js";
|
| 5 | 5 |
import {MathsFunc} from "./types/mathsfunc.js";
|
| 6 | 6 |
import {AST, vecOp, NumericType, anyNaN} from "./maths_ast.js";
|
| 7 | 7 |
import {Token} from "./maths_parser.js";
|
| 8 |
-import {RTensorRender} from "./rtensor_to_html.js"
|
|
| 8 |
+import {RTensorRender} from "./rtensor_to_html.js";
|
|
| 9 |
+import {} from "./rtensor_to_latex.js";
|
|
| 9 | 10 |
|
| 10 | 11 |
AST.prototype.shouldParenMul = RTensorRender.shouldParenMul; |
| 11 | 12 |
AST.prototype.shouldParenExp = RTensorRender.shouldParenExp; |
| 12 | 13 |
AST.prototype.toHTML = RTensorRender.toHTML; |
| 14 |
+AST.prototype.toLaTeX = RTensorRender.toLaTeX; |
|
| 13 | 15 |
|
| 14 | 16 |
export const IDENT_AST = new AST(1, new Token(1, "")); |
| 15 | 17 |
|
| ... | ... |
@@ -222,6 +224,18 @@ export const BUILTIN_FUNCS = {
|
| 222 | 224 |
} catch (_e) {
|
| 223 | 225 |
console.log(ah); |
| 224 | 226 |
} |
| 227 |
+ return 0; |
|
| 228 |
+ }]], {}),
|
|
| 229 |
+ "latex": new MathsFunc([[[IDENT_AST], function(args, de) {
|
|
| 230 |
+ const al = args["0"].toLaTeX(); |
|
| 231 |
+ try {
|
|
| 232 |
+ const oe = document.createElement("pre");
|
|
| 233 |
+ oe.innerText = al; |
|
| 234 |
+ iol.append(oe); |
|
| 235 |
+ } catch (_e) {
|
|
| 236 |
+ console.log(al); |
|
| 237 |
+ } |
|
| 238 |
+ return 0; |
|
| 225 | 239 |
}]], {}),
|
| 226 | 240 |
"lhs": new MathsFunc([[[IDENT_AST], function(args, de) {
|
| 227 | 241 |
const ce = args["0"]; |
| ... | ... |
@@ -0,0 +1,263 @@ |
| 1 |
+"use strict"; |
|
| 2 |
+ |
|
| 3 |
+import {AST} from "maths_ast.js";
|
|
| 4 |
+import {RTensorRender} from "rtensor_to_html.js";
|
|
| 5 |
+ |
|
| 6 |
+const LATEX_SYM = {
|
|
| 7 |
+ "del": "\\nabla", |
|
| 8 |
+ "complexes": "\\mathbb{C}",
|
|
| 9 |
+ "reals": "\\mathbb{R}",
|
|
| 10 |
+ "rationals": "\\mathbb{Q}",
|
|
| 11 |
+ "integers": "\\mathbb{Z}",
|
|
| 12 |
+ "naturals": "\\mathbb{N}",
|
|
| 13 |
+}; |
|
| 14 |
+[ |
|
| 15 |
+ "alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", |
|
| 16 |
+ "iota", "kappa", "lambda", "mu", "nu", "xi", "omicron", "pi", |
|
| 17 |
+ "rho", "sigma", "tau", "upsilon", "phi", "chi", "psi", "omega", |
|
| 18 |
+].map(x => [x, x[0].toUpperCase() + x.slice(1)]). |
|
| 19 |
+ forEach(([x, y]) => (LATEX_SYM[x] = "\\" + x) + (LATEX_SYM[y] = "\\" + y)); |
|
| 20 |
+[ |
|
| 21 |
+ "sin", "cos", "tan", "csc", "sec", "cot", "arcsin", "arccos", "arctan", |
|
| 22 |
+ "exp", "ln", "log", "partial", "pm", |
|
| 23 |
+].forEach(x => (LATEX_SYM[x] = "\\" + x)); |
|
| 24 |
+ |
|
| 25 |
+RTensorRender.toLaTeX = function() {
|
|
| 26 |
+ const lp = "\\left(";
|
|
| 27 |
+ const rp = "\\right)"; |
|
| 28 |
+ switch (this.ts) {
|
|
| 29 |
+ case 1: |
|
| 30 |
+ return this.v.split("_").map(s => LATEX_SYM[s] || s).join("_");
|
|
| 31 |
+ case 2: |
|
| 32 |
+ return `${this.v}`;
|
|
| 33 |
+ case 3: |
|
| 34 |
+ const l = this.l.toLaTeX(); |
|
| 35 |
+ const r = this.r.toLaTeX(); |
|
| 36 |
+ const pl = this.l.shouldParenMul() ? `${lp}${l}${rp}` : l;
|
|
| 37 |
+ const pr = this.r.shouldParenMul() ? `${lp}${r}${rp}` : r;
|
|
| 38 |
+ switch (this.m) {
|
|
| 39 |
+ default: |
|
| 40 |
+ return `${l} ? ${r}`;
|
|
| 41 |
+ } |
|
| 42 |
+ default: |
|
| 43 |
+ return "?"; |
|
| 44 |
+ } |
|
| 45 |
+}; |
|
| 46 |
+ |
|
| 47 |
+const thingy = {
|
|
| 48 |
+ // fancy LaTeX version of the AST |
|
| 49 |
+ toHTML() {
|
|
| 50 |
+ const lp = `<span class="big">(</span>`; |
|
| 51 |
+ const rp = `<span class="big">)</span>`; |
|
| 52 |
+ switch (this.ts) {
|
|
| 53 |
+ case 1: |
|
| 54 |
+ // underscores indicate subscripts |
|
| 55 |
+ let sbu = this.v.replace(/'/g, ucFromNcr("prime")).
|
|
| 56 |
+ split("_").map(s => RTensorRender.unicodify(s)).
|
|
| 57 |
+ map(([s, b]) => {
|
|
| 58 |
+ const s2 = new String(s); |
|
| 59 |
+ s2.greek = b; |
|
| 60 |
+ return s2; |
|
| 61 |
+ }); |
|
| 62 |
+ const cgify = s => s.greek ? `<span class="greek">${s}</span>` : s;
|
|
| 63 |
+ const it = sbu.length > 1 ? |
|
| 64 |
+ `${cgify(sbu[0])}<sub>${sbu.slice(1).map(cgify).join("_")}</sub>`:
|
|
| 65 |
+ cgify(sbu[0]); |
|
| 66 |
+ const spacify = sbu[0].replace(new RegExp(ucFromNcr("prime"), "g"), "").length >= 2 ? ` class="spaced"` : ``;
|
|
| 67 |
+ return (AST.BUILTIN_FUNCS[this.v] != null || EXTRA_BUILTINS.has(this.v)) ? |
|
| 68 |
+ `<var class="builtin">${it}</var>` :
|
|
| 69 |
+ `<var${spacify}>${it}</var>`;
|
|
| 70 |
+ break; |
|
| 71 |
+ case 2: |
|
| 72 |
+ return `${this.v}`;
|
|
| 73 |
+ case 3: |
|
| 74 |
+ const l = this.l.toHTML(); |
|
| 75 |
+ const r = this.r.toHTML(); |
|
| 76 |
+ const pl = this.l.shouldParenMul() ? `${lp}${l}${rp}` : l;
|
|
| 77 |
+ const pr = this.r.shouldParenMul() ? `${lp}${r}${rp}` : r;
|
|
| 78 |
+ switch (this.m) {
|
|
| 79 |
+ case 0: |
|
| 80 |
+ if ((this.r.ts == 7 || this.r.ts == 8) && this.r.l.v == "pm") {
|
|
| 81 |
+ return `${l} ${r}`;
|
|
| 82 |
+ } |
|
| 83 |
+ return `${l} + ${r}`;
|
|
| 84 |
+ case 1: |
|
| 85 |
+ return `${l} ${ucFromNcr("minus")} ${pr}`;
|
|
| 86 |
+ case 2: |
|
| 87 |
+ const dot = this.l.ts == 2 && this.r.ts == 2; |
|
| 88 |
+ return `${pl}${dot ? ` ${ucFromNcr("sdot")} ` : ""}${pr}`;
|
|
| 89 |
+ case 3: |
|
| 90 |
+ return `<table class="frac"> |
|
| 91 |
+ <tr class="numer"><td>${l}</td></tr>
|
|
| 92 |
+ <tr class="denom"><td>${r}</td></tr>
|
|
| 93 |
+ </table>`; |
|
| 94 |
+ case 4: |
|
| 95 |
+ const spe = this.l.shouldParenExp(); |
|
| 96 |
+ return `${spe ? lp : ""}${l}${spe ? rp : ""}<sup>${r}</sup>`;
|
|
| 97 |
+ case 5: |
|
| 98 |
+ return `log<sub>${l}</sub>${lp}${r}${rp}`;
|
|
| 99 |
+ case 6: |
|
| 100 |
+ case 11: |
|
| 101 |
+ return `${l} = ${r}`;
|
|
| 102 |
+ case 7: |
|
| 103 |
+ return `<table class="rboth"><tr> |
|
| 104 |
+ <td><sup><sup><sup class="rord">${l}</sup></sup></sup><span class="rcal">${ucFromNcr("radic")}</span></td>
|
|
| 105 |
+ <td><span class="rcand">${r}</span></td>
|
|
| 106 |
+ </tr></table>`; |
|
| 107 |
+ // TODO: case 8 not handled, because comma operator is confusing |
|
| 108 |
+ case 8: |
|
| 109 |
+ return `${l}, ${r}`;
|
|
| 110 |
+ case 9: |
|
| 111 |
+ return `${l} < ${r}`;
|
|
| 112 |
+ case 10: |
|
| 113 |
+ return `${l} ↦ ${r}`;
|
|
| 114 |
+ default: |
|
| 115 |
+ return `${this.l.toHTML()} ? ${this.r.toHTML()}`;
|
|
| 116 |
+ } |
|
| 117 |
+ break; |
|
| 118 |
+ case 4: |
|
| 119 |
+ return this.l ? `${this.m.toHTML()} = ${this.r.toHTML()}` : this.m.toHTML();
|
|
| 120 |
+ case 5: |
|
| 121 |
+ const m = this.m.toHTML(); |
|
| 122 |
+ const pm = this.m.shouldParenMul() ? `${lp}${m}${rp}` : m;
|
|
| 123 |
+ switch (this.l) {
|
|
| 124 |
+ case 0: |
|
| 125 |
+ return `+${pm}`;
|
|
| 126 |
+ case 1: |
|
| 127 |
+ return ucFromNcr("minus") + pm;
|
|
| 128 |
+ // case 2 not handled, because unary asterisk is not defined |
|
| 129 |
+ case 3: |
|
| 130 |
+ return `<table class="frac"> |
|
| 131 |
+ <tr class="numer"><td>1</td></tr> |
|
| 132 |
+ <tr class="denom"><td>${m}</td></tr>
|
|
| 133 |
+ </table>`; |
|
| 134 |
+ case 4: |
|
| 135 |
+ return `<var>e</var><sup>${m}</sup>`;
|
|
| 136 |
+ case 5: |
|
| 137 |
+ return `ln(${m})`;
|
|
| 138 |
+ case 6: |
|
| 139 |
+ return `${m} = 0`;
|
|
| 140 |
+ case 7: |
|
| 141 |
+ return `<table class="rboth"><tr> |
|
| 142 |
+ <td><span class="rcal">${ucFromNcr("radic")}</span></td>
|
|
| 143 |
+ <td><span class="rcand">${m}</span></td>
|
|
| 144 |
+ </tr></table>`; |
|
| 145 |
+ // likewise, no unary comma |
|
| 146 |
+ case 9: |
|
| 147 |
+ return `${m} < 0`;
|
|
| 148 |
+ // likewise, no unary maplet |
|
| 149 |
+ // likewise, no unary assignment |
|
| 150 |
+ default: |
|
| 151 |
+ return `? ${m}`;
|
|
| 152 |
+ } |
|
| 153 |
+ break; |
|
| 154 |
+ case 6: |
|
| 155 |
+ return `${this.l.toHTML()}[${this.m.toHTML()}]`;
|
|
| 156 |
+ case 7: |
|
| 157 |
+ case 8: |
|
| 158 |
+ const fa = this.m[0] ? this.m[0].toHTML() : "?"; |
|
| 159 |
+ const sa = this.m[1] ? this.m[1].toHTML() : "?"; |
|
| 160 |
+ const ta = this.m[2] ? this.m[2].toHTML() : "?"; |
|
| 161 |
+ const al = this.m.map(x => x.toHTML()); |
|
| 162 |
+ // special functions |
|
| 163 |
+ if (this.l.ts == 1) {
|
|
| 164 |
+ switch (this.l.v) {
|
|
| 165 |
+ case "abs": |
|
| 166 |
+ return `|${fa}|`;
|
|
| 167 |
+ case "all": |
|
| 168 |
+ return `${al.map(x => "(" + x + ")").join(` ${ucFromNcr("and")} `)}`;
|
|
| 169 |
+ case "any": |
|
| 170 |
+ return `${al.map(x => "(" + x + ")").join(` ${ucFromNcr("or")} `)}`;
|
|
| 171 |
+ case "approx": |
|
| 172 |
+ return `${fa} ${ucFromNcr("approx")} ${sa}`;
|
|
| 173 |
+ case "at": |
|
| 174 |
+ return `${lp}${fa}${rp}<span class="ebar">|</span><table class="ebounds">
|
|
| 175 |
+ <tr><td>${this.m[2] ? ta : ""}</td></tr><tr><td>${sa}</td></tr>
|
|
| 176 |
+ </table>`; |
|
| 177 |
+ case "brace": |
|
| 178 |
+ return `<span class="big">{</span>${fa}<span class="big">}</span>`;
|
|
| 179 |
+ case "bracket": |
|
| 180 |
+ return `<span class="big">[</span>${fa}<span class="big">]</span>`;
|
|
| 181 |
+ case "ceil": |
|
| 182 |
+ return ucFromNcr("lceil") + fa + ucFromNcr("rceil");
|
|
| 183 |
+ case "cross": |
|
| 184 |
+ return `${fa} ${ucFromNcr("Cross")} ${sa}`;
|
|
| 185 |
+ case "dot": |
|
| 186 |
+ return `${fa} ${ucFromNcr("sdot")} ${sa}`;
|
|
| 187 |
+ case "fact": |
|
| 188 |
+ const clp = this.m[0].shouldParenMul() ? lp : ""; |
|
| 189 |
+ const crp = this.m[0].shouldParenMul() ? rp : ""; |
|
| 190 |
+ return `${clp}${fa}${crp}!`;
|
|
| 191 |
+ case "filter": |
|
| 192 |
+ return `{<var>x</var> ${ucFromNcr("in")} ${fa} | ${sa}(<var>x</var>)}`;
|
|
| 193 |
+ case "floor": |
|
| 194 |
+ return ucFromNcr("lfloor") + fa + ucFromNcr("rfloor");
|
|
| 195 |
+ case "hat": |
|
| 196 |
+ return `${fa}̂`;
|
|
| 197 |
+ case "if": |
|
| 198 |
+ return `<span class="big">{</span><table class="pwf"><tr>
|
|
| 199 |
+ <td>${sa}</td>
|
|
| 200 |
+ <td class="cond">if ${fa}</td>
|
|
| 201 |
+ </tr><tr> |
|
| 202 |
+ <td>${ta}</td>
|
|
| 203 |
+ <td class="cond">otherwise</td> |
|
| 204 |
+ </tr></table>`; |
|
| 205 |
+ case "in": |
|
| 206 |
+ return `${fa} <span class="greek">${ucFromNcr("in")}</span> ${sa}`;
|
|
| 207 |
+ case "int": |
|
| 208 |
+ return this.m[1] ? |
|
| 209 |
+ `<span class="intsym">${ucFromNcr("int")}</span> <table class="intbs">
|
|
| 210 |
+ <tr class="intb"><td class="ub">${sa}</td></tr>
|
|
| 211 |
+ <tr class="intb"><td>${fa}</td></tr>
|
|
| 212 |
+ </table> ${ta}` :
|
|
| 213 |
+ `<span class="intsym">${ucFromNcr("int")}</span> ${fa}`;
|
|
| 214 |
+ case "intersection": |
|
| 215 |
+ return `${fa} <span class="greek">${ucFromNcr("Intersection")}</span> ${sa}`;
|
|
| 216 |
+ case "lim": |
|
| 217 |
+ return `<table class="limls"> |
|
| 218 |
+ <tr><td>${sa}</td></tr>
|
|
| 219 |
+ <tr><td>${true ? `<span class="big">${ucFromNcr("Lambda")}</span>` : "lim"}</td></tr>
|
|
| 220 |
+ <tr><td>${fa}</td></tr>
|
|
| 221 |
+ </table> ${ta}`;
|
|
| 222 |
+ case "map": |
|
| 223 |
+ return `${fa} 𝚳 ${sa}`;
|
|
| 224 |
+ case "matrix": |
|
| 225 |
+ const rc = parseInt(this.m[0]?.v || "2") || 2; |
|
| 226 |
+ const cc = parseInt(this.m[1]?.v || "2") || 2; |
|
| 227 |
+ return `<table class="mat"> |
|
| 228 |
+ <tr><td>${(new Array(rc)).fill(0).map((_x, i) =>
|
|
| 229 |
+ this.m.slice(2 + i * cc, 2 + (i + 1) * cc).map(el => el.toHTML()).join("</td><td>")).
|
|
| 230 |
+ join("</td></tr>\n<tr><td>")}</td></tr>
|
|
| 231 |
+ </table>`; |
|
| 232 |
+ case "paren": |
|
| 233 |
+ return `${lp}${fa}${rp}`;
|
|
| 234 |
+ case "pm": |
|
| 235 |
+ return `${ucFromNcr("plusmn")} ${this.m[0].shouldParenMul() ? lp + fa + rp : fa}`;
|
|
| 236 |
+ case "product": |
|
| 237 |
+ return `<table class="sum"> |
|
| 238 |
+ <tr><td>${sa}</td></tr>
|
|
| 239 |
+ <tr><td><span class="big">${ucFromNcr("Product")}</span></td></tr>
|
|
| 240 |
+ <tr><td>${fa}</td></tr>
|
|
| 241 |
+ </table> ${ta}`;
|
|
| 242 |
+ case "sum": |
|
| 243 |
+ return `<table class="sum"> |
|
| 244 |
+ <tr><td>${sa}</td></tr>
|
|
| 245 |
+ <tr><td><span class="big">${ucFromNcr("sum")}</span></td></tr>
|
|
| 246 |
+ <tr><td>${fa}</td></tr>
|
|
| 247 |
+ </table> ${ta}`;
|
|
| 248 |
+ case "union": |
|
| 249 |
+ return `${fa} <span class="greek">${ucFromNcr("Union")}</span> ${sa}`;
|
|
| 250 |
+ case "vec": |
|
| 251 |
+ // TODO: there's a better way (include notation in variable names) |
|
| 252 |
+ const m = fa.match(/(.*)(\<sub\>.*\<\/sub\>)?(.*?)/); |
|
| 253 |
+ //alert(`${m[1]} and ${m[2]} and ${m[3]}`);
|
|
| 254 |
+ return `${m[1]}⃗${m[2] || ""}${m[3] || ""}`;
|
|
| 255 |
+ } |
|
| 256 |
+ } |
|
| 257 |
+ // anything else |
|
| 258 |
+ return `${this.l.toHTML()}(${this.m.map(x => x.toHTML()).join(", ")})`;
|
|
| 259 |
+ default: |
|
| 260 |
+ return "?"; |
|
| 261 |
+ } |
|
| 262 |
+ }, |
|
| 263 |
+}; |
|
| 0 | 264 |