desmost 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -11
- package/dist/compiler/compile.d.ts +1 -7
- package/dist/compiler/compile.d.ts.map +1 -1
- package/dist/compiler/compile.js +3 -94
- package/dist/compiler/compile.js.map +1 -1
- package/dist/compiler/compiler.d.ts +56 -0
- package/dist/compiler/compiler.d.ts.map +1 -0
- package/dist/compiler/compiler.js +209 -0
- package/dist/compiler/compiler.js.map +1 -0
- package/dist/compiler/format.d.ts +2 -2
- package/dist/compiler/format.d.ts.map +1 -1
- package/dist/compiler/format.js +18 -9
- package/dist/compiler/format.js.map +1 -1
- package/dist/decompiler/decompile.d.ts +1 -1
- package/dist/decompiler/decompile.d.ts.map +1 -1
- package/dist/decompiler/decompile.js +1 -3
- package/dist/decompiler/decompile.js.map +1 -1
- package/dist/decompiler/extract.d.ts.map +1 -1
- package/dist/decompiler/extract.js +7 -3
- package/dist/decompiler/extract.js.map +1 -1
- package/dist/decompiler/format.d.ts.map +1 -1
- package/dist/decompiler/format.js +0 -1
- package/dist/decompiler/format.js.map +1 -1
- package/dist/errors.d.ts +35 -27
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +46 -48
- package/dist/errors.js.map +1 -1
- package/dist/index.internal.d.ts +1 -1
- package/dist/index.internal.d.ts.map +1 -1
- package/dist/magic/global/desmos.d.ts +2 -2
- package/dist/magic/global/desmos.d.ts.map +1 -1
- package/dist/magic/global/desmos.js +3 -2
- package/dist/magic/global/desmos.js.map +1 -1
- package/dist/magic/global/viewport.d.ts +2 -2
- package/dist/magic/global/viewport.d.ts.map +1 -1
- package/dist/magic/global/viewport.js +5 -5
- package/dist/magic/global/viewport.js.map +1 -1
- package/dist/magic/incantation.d.ts +4 -4
- package/dist/magic/incantation.d.ts.map +1 -1
- package/dist/magic/incantation.js +15 -8
- package/dist/magic/incantation.js.map +1 -1
- package/dist/magic/local/colour.d.ts +3 -1
- package/dist/magic/local/colour.d.ts.map +1 -1
- package/dist/magic/local/colour.js +15 -11
- package/dist/magic/local/colour.js.map +1 -1
- package/dist/magic/local/label.d.ts +2 -2
- package/dist/magic/local/label.d.ts.map +1 -1
- package/dist/magic/local/label.js +8 -7
- package/dist/magic/local/label.js.map +1 -1
- package/dist/magic/local/line.d.ts +2 -2
- package/dist/magic/local/line.d.ts.map +1 -1
- package/dist/magic/local/line.js +0 -1
- package/dist/magic/local/line.js.map +1 -1
- package/dist/options.d.ts +59 -27
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +2 -1
- package/dist/options.js.map +1 -1
- package/dist/parser/ast.d.ts +3 -17
- package/dist/parser/ast.d.ts.map +1 -1
- package/dist/parser/ast.js +0 -2
- package/dist/parser/ast.js.map +1 -1
- package/dist/parser/desmost-parser.d.ts +33 -22
- package/dist/parser/desmost-parser.d.ts.map +1 -1
- package/dist/parser/desmost-parser.js +106 -92
- package/dist/parser/desmost-parser.js.map +1 -1
- package/dist/parser/generic-parser.d.ts +19 -10
- package/dist/parser/generic-parser.d.ts.map +1 -1
- package/dist/parser/generic-parser.js +56 -16
- package/dist/parser/generic-parser.js.map +1 -1
- package/dist/utils.d.ts +9 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +13 -2
- package/dist/utils.js.map +1 -1
- package/package.json +4 -3
- package/src/compiler/compile.ts +7 -120
- package/src/compiler/compiler.ts +275 -0
- package/src/compiler/format.ts +19 -7
- package/src/decompiler/decompile.ts +1 -7
- package/src/decompiler/extract.ts +3 -1
- package/src/decompiler/format.ts +0 -1
- package/src/errors.ts +40 -24
- package/src/index.internal.ts +1 -1
- package/src/magic/.template.ts +2 -2
- package/src/magic/global/desmos.ts +7 -9
- package/src/magic/global/viewport.ts +11 -17
- package/src/magic/incantation.ts +21 -22
- package/src/magic/local/colour.ts +23 -13
- package/src/magic/local/label.ts +14 -19
- package/src/magic/local/line.ts +2 -2
- package/src/options.ts +83 -51
- package/src/parser/ast.ts +1 -21
- package/src/parser/desmost-parser.ts +131 -135
- package/src/parser/generic-parser.ts +71 -29
- package/src/utils.ts +25 -3
- package/dist/compiler/evaluate.d.ts +0 -18
- package/dist/compiler/evaluate.d.ts.map +0 -1
- package/dist/compiler/evaluate.js +0 -93
- package/dist/compiler/evaluate.js.map +0 -1
- package/dist/desmos.d.ts +0 -16
- package/dist/desmos.d.ts.map +0 -1
- package/dist/desmos.js +0 -19
- package/dist/desmos.js.map +0 -1
- package/src/compiler/evaluate.ts +0 -128
- package/src/desmos.ts +0 -22
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,QAAiB,CAAC;AAGnC,wBAAiB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAK/D;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAGxE;AAED,wBAAiB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAkB9E;AAGD,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAGxE;AAGD,qBAAa,KAAK;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,QAAiB,CAAC;AAGnC;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,eAAe,GAAG,IAAI,IAAI,MAAM,CAAC,UAAU,CAGhF;AAGD,wBAAiB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAK/D;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAGxE;AAED,wBAAiB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAkB9E;AAGD,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAGxE;AAGD,qBAAa,KAAK;IAEjB,gDAAgD;IAChD,SAAgB,KAAK,EAAE,MAAM,CAAC;IAE9B,gDAAgD;IAChD,SAAgB,IAAI,EAAE,MAAM,CAAC;gBAG5B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM;IAOb,IAAI,MAAM,IAAI,MAAM,CAEnB;IAEA,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;CAMjD"}
|
package/dist/utils.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
export const LINE = '—'.repeat(10);
|
|
2
|
+
/**
|
|
3
|
+
* Is `expr` a Desmos LaTeX expression (as opposed to a note or table)?
|
|
4
|
+
*/
|
|
5
|
+
export function is_latex(expr) {
|
|
6
|
+
return (expr.type === undefined || expr.type === "expression");
|
|
7
|
+
}
|
|
2
8
|
export function* reversing(items) {
|
|
3
9
|
for (let i = items.length - 1; i >= 0; i--) {
|
|
4
10
|
yield items[i];
|
|
@@ -27,11 +33,16 @@ export function chars(s, start, stop) {
|
|
|
27
33
|
return new Range(start ?? 0, stop ?? s.length).map(i => s[i]).toArray();
|
|
28
34
|
}
|
|
29
35
|
export class Range {
|
|
36
|
+
/** The lower bound of the range (inclusive). */
|
|
30
37
|
start;
|
|
38
|
+
/** The upper bound of the range (exclusive). */
|
|
31
39
|
stop;
|
|
32
40
|
constructor(start, stop) {
|
|
33
|
-
this.start = start;
|
|
34
|
-
this.stop = stop;
|
|
41
|
+
this.start = Math.min(start, stop);
|
|
42
|
+
this.stop = Math.max(start, stop);
|
|
43
|
+
}
|
|
44
|
+
get length() {
|
|
45
|
+
return this.stop - this.start + 1;
|
|
35
46
|
}
|
|
36
47
|
*map(callback) {
|
|
37
48
|
for (let n = this.start; n < this.stop; n++) {
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAGnC,MAAM,SAAS,CAAC,CAAC,SAAS,CAAI,KAAmB;IAEhD,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5C,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;IAChB,CAAC;AACF,CAAC;AAED,MAAM,UAAU,MAAM,CAAI,KAAkB;IAE3C,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,SAAS,CAAC,CAAC,OAAO,CAAI,KAAkB;IAE7C,IAAI,QAAQ,GAAG,IAAI,CAAC;IAEpB,KAAK,IAAI,IAAI,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACvB,QAAQ,GAAG,IAAI,CAAC;YAChB,SAAS;QACV,CAAC;aACI,CAAC;YACL,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACvB,QAAQ,GAAG,IAAI,CAAC;QACjB,CAAC;IACF,CAAC;IAED,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,QAAQ,CAAC;IACjB,CAAC;AACF,CAAC;AAGD,MAAM,UAAU,KAAK,CAAC,CAAS,EAAE,KAAc,EAAE,IAAa;IAE7D,OAAO,IAAI,KAAK,CAAC,KAAK,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;AACzE,CAAC;AAGD,MAAM,OAAO,KAAK;
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAGnC;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,IAA4B;IAEpD,OAAO,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC;AAChE,CAAC;AAGD,MAAM,SAAS,CAAC,CAAC,SAAS,CAAI,KAAmB;IAEhD,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5C,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;IAChB,CAAC;AACF,CAAC;AAED,MAAM,UAAU,MAAM,CAAI,KAAkB;IAE3C,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,SAAS,CAAC,CAAC,OAAO,CAAI,KAAkB;IAE7C,IAAI,QAAQ,GAAG,IAAI,CAAC;IAEpB,KAAK,IAAI,IAAI,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACvB,QAAQ,GAAG,IAAI,CAAC;YAChB,SAAS;QACV,CAAC;aACI,CAAC;YACL,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACvB,QAAQ,GAAG,IAAI,CAAC;QACjB,CAAC;IACF,CAAC;IAED,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,QAAQ,CAAC;IACjB,CAAC;AACF,CAAC;AAGD,MAAM,UAAU,KAAK,CAAC,CAAS,EAAE,KAAc,EAAE,IAAa;IAE7D,OAAO,IAAI,KAAK,CAAC,KAAK,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;AACzE,CAAC;AAGD,MAAM,OAAO,KAAK;IAEjB,gDAAgD;IAChC,KAAK,CAAS;IAE9B,gDAAgD;IAChC,IAAI,CAAS;IAE7B,YACC,KAAa,EACb,IAAY;QAGZ,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,IAAI,GAAI,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,MAAM;QACT,OAAO,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;IACnC,CAAC;IAED,CAAC,GAAG,CAAI,QAA0B;QAEjC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7C,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC;IACF,CAAC;CACD"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "desmost",
|
|
3
3
|
"author": "Sup#2.0",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.10.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
|
|
7
7
|
"description": "A tiny DSL for compiling LaTeX to Desmos",
|
|
@@ -54,11 +54,12 @@
|
|
|
54
54
|
"test": "vitest run",
|
|
55
55
|
"testw": "vitest",
|
|
56
56
|
|
|
57
|
-
"check": "npx
|
|
57
|
+
"check": "npx tsc --noEmit -p tsconfig.build.json",
|
|
58
|
+
"lint": "npx oxlint src",
|
|
58
59
|
|
|
59
60
|
"doc": "cd ../.autodoc && rake",
|
|
60
61
|
|
|
61
|
-
"prepublishOnly": "
|
|
62
|
+
"prepublishOnly": "nu build.nu pre-publish"
|
|
62
63
|
},
|
|
63
64
|
|
|
64
65
|
"dependencies":
|
package/src/compiler/compile.ts
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DesmostCompiler, type DesmostDebug } from "./compiler";
|
|
2
2
|
|
|
3
3
|
import { type DesmostOptions, fill_defaults } from "../options";
|
|
4
|
-
import { DesmostParser, Ast } from "../parser";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
/** Debug diagnostics returned from `compile()`. */
|
|
8
|
-
export interface DesmostDebug
|
|
9
|
-
{
|
|
10
|
-
duration: number;
|
|
11
|
-
num_blocks: number;
|
|
12
|
-
ast: Ast[];
|
|
13
|
-
}
|
|
14
4
|
|
|
15
5
|
|
|
16
6
|
/**
|
|
@@ -55,114 +45,11 @@ export function compile(
|
|
|
55
45
|
return;
|
|
56
46
|
}
|
|
57
47
|
|
|
58
|
-
let
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
var ast: Ast[] = [];
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
let parser = new DesmostParser(source, opts);
|
|
67
|
-
|
|
68
|
-
let errors: string[] = [];
|
|
69
|
-
|
|
70
|
-
/* To aggregate errors at the start, we need a target to retroactively inject errors into */
|
|
71
|
-
if (opts.place_errors === "start") {
|
|
72
|
-
desmos.setExpression({ id: "deferred-start", latex: " " }); // FIXME check if ` ` needed
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/* Keep track of whether we're still evaluating leading blank expressions, which might be ignored. */
|
|
76
|
-
let seen_non_blank = false;
|
|
77
|
-
|
|
78
|
-
/** The number of pending blank expressions to add. */
|
|
79
|
-
let pending_blanks: number = 0;
|
|
80
|
-
|
|
81
|
-
/* The compiler is lazy, parsing and evaluating one block at a time (as opposed to first parsing the entire AST). We don't need the whole AST, so this saves memory. */
|
|
82
|
-
/* NOTE: It also doesn't sacrifice performance, since `.setExpressions()` internally just calls `.setExpression()` in a loop anyway. Benchmarks in the frontend produce similar times for both, so there's no performance improvement. */
|
|
83
|
-
try {
|
|
84
|
-
while (true) {
|
|
85
|
-
let r = parser.parse_next();
|
|
86
|
-
if (r === null) break;
|
|
87
|
-
|
|
88
|
-
if (opts.debug) {
|
|
89
|
-
num_blocks!++;
|
|
90
|
-
ast!.push(r);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** An error message to leave for aggregation later. */
|
|
94
|
-
let defer: string | void = undefined;
|
|
95
|
-
|
|
96
|
-
switch (r.kind) {
|
|
97
|
-
case Ast.Kind.INCANTATION_INVOCATION:
|
|
98
|
-
defer = evaluate_global_incantation(r, desmos, opts);
|
|
99
|
-
break;
|
|
100
|
-
|
|
101
|
-
case Ast.Kind.EXPRESSION:
|
|
102
|
-
// @ts-expect-error: check type-narrows
|
|
103
|
-
if (r.data.latex === " ") {
|
|
104
|
-
if (opts.ignore_all_blanks) break;
|
|
105
|
-
if (!seen_non_blank && !opts.keep_leading_blanks) break;
|
|
106
|
-
pending_blanks++;
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
flush_pending_blanks(desmos, pending_blanks);
|
|
111
|
-
pending_blanks = 0;
|
|
112
|
-
|
|
113
|
-
seen_non_blank = true;
|
|
114
|
-
defer = evaluate_expr(r, desmos, opts);
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
case Ast.Kind.INVALID_INCANTATION:
|
|
119
|
-
defer = evaluate_error(r.error, desmos, opts);
|
|
120
|
-
break;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (defer !== undefined) {
|
|
124
|
-
errors.push(defer);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
catch (e) {
|
|
129
|
-
errors.push((e as Error).message);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (opts.keep_trailing_blanks) {
|
|
133
|
-
flush_pending_blanks(desmos, pending_blanks);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (errors.length > 0) {
|
|
137
|
-
let expr: Desmos.ExpressionState = {
|
|
138
|
-
type: "text",
|
|
139
|
-
text: errors.join("\n\n"),
|
|
140
|
-
};
|
|
48
|
+
let compiler = new DesmostCompiler(
|
|
49
|
+
desmos,
|
|
50
|
+
source,
|
|
51
|
+
fill_defaults(options),
|
|
52
|
+
);
|
|
141
53
|
|
|
142
|
-
|
|
143
|
-
case "start":
|
|
144
|
-
desmos.setExpression({ ...expr, id: "deferred-start" });
|
|
145
|
-
break;
|
|
146
|
-
case "end":
|
|
147
|
-
case "inline":
|
|
148
|
-
desmos.setExpression(expr);
|
|
149
|
-
break;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (opts.debug) {
|
|
154
|
-
return {
|
|
155
|
-
duration: performance.now() - t_init!,
|
|
156
|
-
num_blocks: num_blocks!,
|
|
157
|
-
ast: ast!,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
function flush_pending_blanks(desmos: Desmos.Calculator, count: number)
|
|
164
|
-
{
|
|
165
|
-
for (let i = 0; i < count; i++) {
|
|
166
|
-
desmos.setExpression({ latex: ` ` });
|
|
167
|
-
}
|
|
54
|
+
return compiler.compile();
|
|
168
55
|
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import dedent from "dedent";
|
|
2
|
+
|
|
3
|
+
import { format_error, normalise_latex, prettify_latex } from "./format";
|
|
4
|
+
|
|
5
|
+
import type { DesmostOptions } from "../options";
|
|
6
|
+
import { DesmostError, type Fallible } from "../errors";
|
|
7
|
+
import { DesmostParser, Ast } from "../parser";
|
|
8
|
+
import { is_latex } from "../utils";
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/** Debug diagnostics returned from `compile()`. */
|
|
12
|
+
export interface DesmostDebug
|
|
13
|
+
{
|
|
14
|
+
duration: number;
|
|
15
|
+
num_blocks: number;
|
|
16
|
+
ast: Ast[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
export class DesmostCompiler
|
|
21
|
+
{
|
|
22
|
+
private desmos: Desmos.Calculator
|
|
23
|
+
|
|
24
|
+
/** The source code to compile. */
|
|
25
|
+
private source: string
|
|
26
|
+
|
|
27
|
+
private options: DesmostOptions
|
|
28
|
+
|
|
29
|
+
private parser: DesmostParser
|
|
30
|
+
|
|
31
|
+
/** Accumulated errors awaiting evaluation. */
|
|
32
|
+
private errors: DesmostError[] = []
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Have we only seen blank lines so far?
|
|
36
|
+
*
|
|
37
|
+
* This is used for excluding leading blanks.
|
|
38
|
+
*/
|
|
39
|
+
private seen_non_blank = false
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The number of pending blank expressions to add.
|
|
43
|
+
*
|
|
44
|
+
* This is used to excluding trailing blanks.
|
|
45
|
+
*/
|
|
46
|
+
private pending_blanks: number = 0
|
|
47
|
+
|
|
48
|
+
private debug: DesmostDebug | null = null
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
/** Create a compiler for compiling `source` into `desmos`. */
|
|
52
|
+
constructor(
|
|
53
|
+
desmos: Desmos.Calculator,
|
|
54
|
+
source: string,
|
|
55
|
+
options: DesmostOptions,
|
|
56
|
+
)
|
|
57
|
+
{
|
|
58
|
+
this.desmos = desmos;
|
|
59
|
+
this.source = source;
|
|
60
|
+
this.options = options;
|
|
61
|
+
this.parser = new DesmostParser(this.source, this.options);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Run the compiler to completion.
|
|
67
|
+
*/
|
|
68
|
+
public compile(): void | DesmostDebug
|
|
69
|
+
{
|
|
70
|
+
if (this.options.debug) {
|
|
71
|
+
this.debug = {
|
|
72
|
+
duration: performance.now(),
|
|
73
|
+
num_blocks: 0,
|
|
74
|
+
ast: [],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* To aggregate errors at the start, we need a target to retroactively inject errors into */
|
|
79
|
+
if (this.options.place_errors === "start") {
|
|
80
|
+
this.desmos.setExpression({ id: "deferred", latex: " " }); // FIXME check if ` ` needed
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* The compiler is lazy, parsing and evaluating one block at a time (as opposed to first parsing the entire AST). We don't need the whole AST, so this saves memory. */
|
|
84
|
+
/* NOTE: It also doesn't sacrifice performance, since `.setExpressions()` internally just calls `.setExpression()` in a loop anyway. Benchmarks in the frontend produce similar times for both, so there's no performance improvement. */
|
|
85
|
+
try {
|
|
86
|
+
let done = false;
|
|
87
|
+
|
|
88
|
+
while (!done) {
|
|
89
|
+
done = this.compile_next();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
this.errors.push(e as Error); // FIXME Should probably override as critical
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (this.options.keep_trailing_blanks) {
|
|
97
|
+
this.flush_pending_blanks();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* NOTE: No need to check `options.place_errors`!
|
|
101
|
+
|
|
102
|
+
- If it's `start`, `id: deferred` will replace the placeholder at the start as required.
|
|
103
|
+
- If it's `end`, `id: deferred` won't exist so this expression will be appended as required.
|
|
104
|
+
- If it's `inline`, we shouldn't have any accumulated errors at all, but if we do, then the end is where they should be anyway.
|
|
105
|
+
*/
|
|
106
|
+
if (this.errors.length > 0) {
|
|
107
|
+
this.desmos.setExpression({
|
|
108
|
+
id: "deferred",
|
|
109
|
+
type: "text",
|
|
110
|
+
text: this.errors.map(err => format_error(err, this.options)).join("\n\n"),
|
|
111
|
+
});
|
|
112
|
+
} else {
|
|
113
|
+
this.desmos.removeExpression({ id: "deferred" });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (this.options.debug) {
|
|
117
|
+
this.debug!.duration = performance.now() - this.debug!.duration;
|
|
118
|
+
|
|
119
|
+
return this.debug!;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Compile only the next block, returning `true` if compilation has finished.
|
|
125
|
+
*/
|
|
126
|
+
compile_next(): Fallible<boolean>
|
|
127
|
+
{
|
|
128
|
+
let result = this.parser.parse_next();
|
|
129
|
+
if (result === undefined) return true;
|
|
130
|
+
|
|
131
|
+
let { blocks, errors } = result;
|
|
132
|
+
|
|
133
|
+
for (let block of blocks) {
|
|
134
|
+
this.compile_block(block);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for (let error of errors) {
|
|
138
|
+
this.evaluate_error(error);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
compile_block(block: Ast): void
|
|
145
|
+
{
|
|
146
|
+
if (this.options.debug) {
|
|
147
|
+
this.debug!.num_blocks++;
|
|
148
|
+
this.debug!.ast.push(block);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
switch (block.kind)
|
|
152
|
+
{
|
|
153
|
+
case Ast.Kind.INCANTATION_INVOCATION:
|
|
154
|
+
this.evaluate_global_incantation(block);
|
|
155
|
+
break;
|
|
156
|
+
|
|
157
|
+
case Ast.Kind.EXPRESSION:
|
|
158
|
+
// @ts-expect-error: check type-narrows
|
|
159
|
+
if (block.data.latex === " ") {
|
|
160
|
+
if (this.options.ignore_all_blanks) break;
|
|
161
|
+
if (!this.seen_non_blank && !this.options.keep_leading_blanks) break;
|
|
162
|
+
|
|
163
|
+
this.pending_blanks++;
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
this.flush_pending_blanks();
|
|
168
|
+
this.pending_blanks = 0;
|
|
169
|
+
this.seen_non_blank = true;
|
|
170
|
+
|
|
171
|
+
this.evaluate_expr(block);
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
// == LOW-LEVEL == //
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Evaluate arguments (if any) to a global incantation `invocation`, then apply the incantation to `desmos`.
|
|
182
|
+
*/
|
|
183
|
+
evaluate_global_incantation(invocation: Ast.IncantationInvocation): Fallible<void>
|
|
184
|
+
{
|
|
185
|
+
let data = undefined;
|
|
186
|
+
|
|
187
|
+
if ("arg_raw" in invocation && invocation.arg_raw != undefined) {
|
|
188
|
+
try {
|
|
189
|
+
data = invocation.incantation.evaluate_arg(invocation.arg_raw, this.options);
|
|
190
|
+
invocation.incantation.apply(this.desmos, data);
|
|
191
|
+
}
|
|
192
|
+
catch (e) {
|
|
193
|
+
this.evaluate_error(e as Error);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Evaluate local incantation invocations on `expr`, then add `expr` to `desmos`.
|
|
201
|
+
*/
|
|
202
|
+
evaluate_expr(expr: Ast.Expression): Fallible<void>
|
|
203
|
+
{
|
|
204
|
+
for (let invocation of expr.incantations) {
|
|
205
|
+
let data = undefined;
|
|
206
|
+
|
|
207
|
+
if ("arg_raw" in invocation && invocation.arg_raw != undefined) {
|
|
208
|
+
try {
|
|
209
|
+
data = invocation.incantation.evaluate_arg(invocation.arg_raw, this.options);
|
|
210
|
+
}
|
|
211
|
+
catch (e) {
|
|
212
|
+
this.evaluate_error(e as Error);
|
|
213
|
+
// if it needs the arg, then we can't press on
|
|
214
|
+
if (invocation.incantation.requires_arg) continue;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
invocation.incantation.apply(expr.data, data);
|
|
220
|
+
}
|
|
221
|
+
catch (e) {
|
|
222
|
+
this.evaluate_error(e as Error);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (is_latex(expr.data)) {
|
|
227
|
+
expr.data.latex = normalise_latex(expr.data.latex!);
|
|
228
|
+
|
|
229
|
+
if (this.options.prettify) {
|
|
230
|
+
expr.data.latex = prettify_latex(expr.data.latex);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
else if (expr.data.type === "text" && this.options.dedent_text) {
|
|
234
|
+
expr.data.text = dedent(expr.data.text ?? "");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
this.desmos.setExpression(expr.data);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Handle a `DesmostError`, respecting the user's options.
|
|
242
|
+
*/
|
|
243
|
+
evaluate_error(error: DesmostError): Fallible<void | string>
|
|
244
|
+
{
|
|
245
|
+
switch (this.options.errors) {
|
|
246
|
+
case "crash":
|
|
247
|
+
throw error;
|
|
248
|
+
|
|
249
|
+
case "suppress":
|
|
250
|
+
console.error(format_error(error, this.options));
|
|
251
|
+
break;
|
|
252
|
+
|
|
253
|
+
default: switch (this.options.place_errors) {
|
|
254
|
+
case "end":
|
|
255
|
+
case "start":
|
|
256
|
+
this.errors.push(error);
|
|
257
|
+
break;
|
|
258
|
+
|
|
259
|
+
case "inline":
|
|
260
|
+
this.desmos.setExpression({
|
|
261
|
+
type: "text",
|
|
262
|
+
text: format_error(error, this.options),
|
|
263
|
+
});
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
flush_pending_blanks(): void
|
|
270
|
+
{
|
|
271
|
+
for (let i = 0; i < this.pending_blanks; i++) {
|
|
272
|
+
this.desmos.setExpression({ latex: ` ` });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
package/src/compiler/format.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { DesmostOptions } from "../options";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { DesmostError } from "../errors";
|
|
4
4
|
import { LINE } from "../utils";
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
export function format_error(
|
|
8
|
-
e:
|
|
8
|
+
e: DesmostError,
|
|
9
9
|
options: DesmostOptions,
|
|
10
10
|
): string
|
|
11
11
|
{
|
|
@@ -15,12 +15,24 @@ export function format_error(
|
|
|
15
15
|
// TODO maybe include stack? (configurable?)
|
|
16
16
|
let display = `${prefix}${sep}${e.name}: ${e.message}`;
|
|
17
17
|
|
|
18
|
-
if (e.
|
|
19
|
-
display
|
|
18
|
+
if (e.data == undefined) {
|
|
19
|
+
return display;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (e.data.show != undefined) {
|
|
23
|
+
display += `\n${LINE}\n`;
|
|
24
|
+
display += e.data.show.text;
|
|
25
|
+
|
|
26
|
+
if (e.data.show.span != undefined) {
|
|
27
|
+
display += `\n`;
|
|
28
|
+
display += ' '.repeat(e.data.show.span.start);
|
|
29
|
+
display += '^'.repeat(e.data.show.span.length);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
20
32
|
|
|
21
|
-
|
|
22
|
-
if (e.
|
|
23
|
-
if (e.
|
|
33
|
+
if (options.expand_errors) {
|
|
34
|
+
if (e.data.hint != undefined) display += `\n${LINE}\nHint: ${e.data.hint}`;
|
|
35
|
+
if (e.data.note != undefined) display += `\n${LINE}\nNote: ${e.data.note}`;
|
|
24
36
|
}
|
|
25
37
|
|
|
26
38
|
return display;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { emit_global_incantation, emit_expression } from "./emit";
|
|
2
2
|
import { extract_settings, extract_viewport, extract_expression } from "./extract";
|
|
3
3
|
|
|
4
|
-
import { fill_defaults } from "../options";
|
|
5
4
|
import { Ast } from "../parser";
|
|
6
5
|
import type { DesmostOptions } from "../options";
|
|
7
6
|
|
|
@@ -35,18 +34,13 @@ export function decompile(
|
|
|
35
34
|
options?: Partial<DesmostOptions>,
|
|
36
35
|
): string
|
|
37
36
|
{
|
|
38
|
-
let opts = fill_defaults(options);
|
|
39
37
|
return ast_to_source(desmos_to_ast(desmos, blank ?? desmos));
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
/**
|
|
43
41
|
* Decompile `desmos` into a semi-structured AST.
|
|
44
42
|
*/
|
|
45
|
-
export function desmos_to_ast(
|
|
46
|
-
desmos: Desmos.Calculator,
|
|
47
|
-
blank: Desmos.Calculator,
|
|
48
|
-
options: DesmostOptions,
|
|
49
|
-
): SemiStructuredAst
|
|
43
|
+
export function desmos_to_ast(desmos: Desmos.Calculator, blank: Desmos.Calculator): SemiStructuredAst
|
|
50
44
|
{
|
|
51
45
|
return {
|
|
52
46
|
globals: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { stringify_json5, prettify_source } from "./format";
|
|
2
2
|
|
|
3
3
|
import { Ast } from "../parser";
|
|
4
|
-
import { is_latex } from "../
|
|
4
|
+
import { is_latex } from "../utils";
|
|
5
5
|
|
|
6
6
|
import { DesmosIncantation, ViewportIncantation } from "../magic/global";
|
|
7
7
|
|
|
@@ -19,6 +19,7 @@ export function extract_settings(
|
|
|
19
19
|
let arg_raw = stringify_json5(
|
|
20
20
|
desmos.settings,
|
|
21
21
|
(k, v) => (
|
|
22
|
+
// @ts-expect-error: weird
|
|
22
23
|
v === defaults[k]
|
|
23
24
|
|| k === "__observers"
|
|
24
25
|
|| k === "guid"
|
|
@@ -50,6 +51,7 @@ export function extract_viewport(
|
|
|
50
51
|
|
|
51
52
|
let arg_raw = stringify_json5(
|
|
52
53
|
{ left, right, bottom, top },
|
|
54
|
+
/// @ts-expect-error: weird
|
|
53
55
|
(k, v) => v === defaults[k] ? undefined : v,
|
|
54
56
|
" ",
|
|
55
57
|
);
|
package/src/decompiler/format.ts
CHANGED
package/src/errors.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { Range } from "./utils";
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
/** The parser failed to parse something (usually during speculative parsing), which only results in internal backtracking. */
|
|
2
5
|
export const NO_MATCH = Symbol("no-match");
|
|
3
6
|
|
|
@@ -10,51 +13,64 @@ export type NoMatch = typeof NO_MATCH & { readonly __brand?: unique symbol };
|
|
|
10
13
|
*
|
|
11
14
|
* It's not *literally* unrecoverable in the sense that it will crash the compiler completely, but it means there are no further fallbacks to try.
|
|
12
15
|
*/
|
|
13
|
-
export class
|
|
16
|
+
export class DesmostError extends Error
|
|
14
17
|
{
|
|
15
|
-
|
|
18
|
+
data?: DesmostError.Data;
|
|
19
|
+
|
|
20
|
+
constructor(msg: string | undefined, name: string) {
|
|
21
|
+
super(msg);
|
|
22
|
+
this.name = name;
|
|
23
|
+
}
|
|
16
24
|
}
|
|
17
25
|
|
|
18
|
-
export namespace
|
|
26
|
+
export namespace DesmostError
|
|
19
27
|
{
|
|
20
|
-
export interface
|
|
28
|
+
export interface Data
|
|
21
29
|
{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
msg?: string
|
|
31
|
+
while?: string
|
|
32
|
+
hint?: string
|
|
33
|
+
note?: string
|
|
34
|
+
debug?: string
|
|
35
|
+
|
|
36
|
+
show?: {
|
|
37
|
+
text: string
|
|
38
|
+
span?: Range
|
|
39
|
+
}
|
|
25
40
|
}
|
|
26
41
|
|
|
27
|
-
/** The parser unexpectedly reached the end of its
|
|
28
|
-
export class UnexpectedEnd extends
|
|
29
|
-
constructor(
|
|
42
|
+
/** The parser unexpectedly reached the end of its source. */
|
|
43
|
+
export class UnexpectedEnd extends DesmostError {
|
|
44
|
+
constructor(override data: Data) { super(data.msg, "Unexpected End of Source"); }
|
|
30
45
|
}
|
|
31
46
|
|
|
32
47
|
/** The parser did not receive input that it expected. */
|
|
33
|
-
export class MissingInput extends
|
|
34
|
-
constructor(
|
|
48
|
+
export class MissingInput extends DesmostError {
|
|
49
|
+
constructor(override data: Data) { super(data.msg, "Missing Input"); }
|
|
35
50
|
}
|
|
36
51
|
|
|
37
52
|
/** The parser received input that did not match what it expected. */
|
|
38
|
-
export class UnexpectedInput extends
|
|
39
|
-
constructor(
|
|
53
|
+
export class UnexpectedInput extends DesmostError {
|
|
54
|
+
constructor(override data: Data) { super(data.msg, "Unexpected Input"); }
|
|
40
55
|
}
|
|
41
56
|
|
|
42
57
|
/** The parser received excess input that it did not expect. */
|
|
43
|
-
export class ExcessInput extends
|
|
44
|
-
constructor(
|
|
58
|
+
export class ExcessInput extends DesmostError {
|
|
59
|
+
constructor(override data: Data) { super(data.msg, "Excess Input"); }
|
|
45
60
|
}
|
|
46
61
|
|
|
47
62
|
/** An incantation can't be applied here. */
|
|
48
|
-
export class IllegalIncantation extends
|
|
49
|
-
constructor(
|
|
63
|
+
export class IllegalIncantation extends DesmostError {
|
|
64
|
+
constructor(override data: Data) { super(data.msg, "Illegal Incantation"); }
|
|
50
65
|
}
|
|
51
66
|
|
|
52
|
-
/** An incantation's argument
|
|
53
|
-
export class InvalidArgument extends
|
|
54
|
-
constructor(
|
|
67
|
+
/** An incantation's argument is invalid - could be a parsing, evaluation or validation failure. */
|
|
68
|
+
export class InvalidArgument extends DesmostError {
|
|
69
|
+
constructor(override data: Data) { super(data.msg, "Invalid Argument"); }
|
|
55
70
|
}
|
|
56
71
|
}
|
|
57
72
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
73
|
+
/** Indicates that a function may throw a `DesmostError`. */
|
|
74
|
+
export type Fallible<Result>
|
|
75
|
+
= Result
|
|
76
|
+
| Result & { readonly __brand?: unique symbol };
|