Start writing latex[...] function
dkl9

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} &lt; ${r}`;
112
+                    case 10:
113
+                        return `${l} &#8614; ${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} &lt; 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}&#770;`;
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} &#120499; ${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]}&#8407;${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