desmost 0.8.2 → 0.9.1
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 +80 -13
- package/dist/compiler/compile.d.ts +7 -3
- package/dist/compiler/compile.d.ts.map +1 -1
- package/dist/compiler/compile.js +7 -3
- package/dist/compiler/compile.js.map +1 -1
- package/dist/compiler/evaluate.d.ts +5 -5
- package/dist/compiler/evaluate.d.ts.map +1 -1
- package/dist/compiler/evaluate.js +16 -8
- package/dist/compiler/evaluate.js.map +1 -1
- package/dist/compiler/format.d.ts +2 -2
- package/dist/compiler/format.d.ts.map +1 -1
- package/dist/compiler/format.js +14 -1
- package/dist/compiler/format.js.map +1 -1
- package/dist/compiler/index.d.ts +0 -1
- package/dist/compiler/index.d.ts.map +1 -1
- package/dist/decompiler/decompile.d.ts +12 -1
- package/dist/decompiler/decompile.d.ts.map +1 -1
- package/dist/decompiler/decompile.js +14 -2
- package/dist/decompiler/decompile.js.map +1 -1
- package/dist/decompiler/emit.d.ts.map +1 -1
- package/dist/decompiler/emit.js +13 -6
- package/dist/decompiler/emit.js.map +1 -1
- package/dist/decompiler/extract.d.ts.map +1 -1
- package/dist/decompiler/extract.js +3 -2
- package/dist/decompiler/extract.js.map +1 -1
- package/dist/decompiler/format.js +1 -1
- package/dist/decompiler/format.js.map +1 -1
- package/dist/desmos.d.ts +4 -0
- package/dist/desmos.d.ts.map +1 -1
- package/dist/desmos.js +6 -0
- package/dist/desmos.js.map +1 -1
- package/dist/errors.d.ts +18 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +37 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.internal.d.ts +1 -1
- package/dist/index.internal.d.ts.map +1 -1
- package/dist/index.internal.js +1 -1
- package/dist/index.internal.js.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/magic/expr/latex.d.ts +1 -2
- package/dist/magic/expr/latex.d.ts.map +1 -1
- package/dist/magic/expr/latex.js +1 -4
- package/dist/magic/expr/latex.js.map +1 -1
- package/dist/magic/expr/text.d.ts +1 -1
- package/dist/magic/expr/text.d.ts.map +1 -1
- package/dist/magic/expr/text.js +1 -1
- package/dist/magic/expr/text.js.map +1 -1
- package/dist/magic/global/dark-mode.d.ts.map +1 -1
- package/dist/magic/global/dark-mode.js +1 -3
- package/dist/magic/global/dark-mode.js.map +1 -1
- package/dist/magic/global/desmos.d.ts +4 -1
- package/dist/magic/global/desmos.d.ts.map +1 -1
- package/dist/magic/global/desmos.js +14 -1
- package/dist/magic/global/desmos.js.map +1 -1
- package/dist/magic/global/viewport.d.ts +3 -0
- package/dist/magic/global/viewport.d.ts.map +1 -1
- package/dist/magic/global/viewport.js +23 -0
- package/dist/magic/global/viewport.js.map +1 -1
- package/dist/magic/incantation.d.ts +13 -8
- package/dist/magic/incantation.d.ts.map +1 -1
- package/dist/magic/incantation.js +23 -3
- package/dist/magic/incantation.js.map +1 -1
- package/dist/magic/local/anim.d.ts.map +1 -1
- package/dist/magic/local/anim.js +0 -1
- package/dist/magic/local/anim.js.map +1 -1
- package/dist/magic/local/colour.d.ts +3 -2
- package/dist/magic/local/colour.d.ts.map +1 -1
- package/dist/magic/local/colour.js +3 -3
- package/dist/magic/local/colour.js.map +1 -1
- package/dist/magic/local/label.d.ts +4 -3
- package/dist/magic/local/label.d.ts.map +1 -1
- package/dist/magic/local/label.js +37 -16
- package/dist/magic/local/label.js.map +1 -1
- package/dist/magic/local/line.d.ts +3 -0
- package/dist/magic/local/line.d.ts.map +1 -1
- package/dist/magic/local/line.js +10 -0
- package/dist/magic/local/line.js.map +1 -1
- package/dist/magic/local/slider.d.ts +2 -2
- package/dist/magic/local/slider.d.ts.map +1 -1
- package/dist/magic/local/slider.js +0 -1
- package/dist/magic/local/slider.js.map +1 -1
- package/dist/{compiler/options.d.ts → options.d.ts} +49 -22
- package/dist/options.d.ts.map +1 -0
- package/dist/{compiler/options.js → options.js} +6 -1
- package/dist/options.js.map +1 -0
- package/dist/parser/desmost-parser.d.ts +1 -1
- package/dist/parser/desmost-parser.d.ts.map +1 -1
- package/dist/parser/desmost-parser.js +9 -5
- package/dist/parser/desmost-parser.js.map +1 -1
- package/dist/parser/generic-parser.d.ts +1 -5
- package/dist/parser/generic-parser.d.ts.map +1 -1
- package/dist/parser/generic-parser.js +7 -10
- package/dist/parser/generic-parser.js.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +1 -0
- package/dist/utils.js.map +1 -1
- package/package.json +9 -6
- package/src/compiler/compile.ts +8 -4
- package/src/compiler/evaluate.ts +22 -13
- package/src/compiler/format.ts +16 -3
- package/src/compiler/index.ts +0 -1
- package/src/decompiler/decompile.ts +18 -2
- package/src/decompiler/emit.ts +15 -6
- package/src/decompiler/extract.ts +3 -2
- package/src/decompiler/format.ts +1 -1
- package/src/decompiler/readme.md +9 -1
- package/src/desmos.ts +9 -0
- package/src/errors.ts +29 -8
- package/src/index.internal.ts +1 -1
- package/src/index.ts +2 -2
- package/src/magic/.template.ts +25 -16
- package/src/magic/expr/latex.ts +2 -7
- package/src/magic/expr/text.ts +2 -2
- package/src/magic/global/dark-mode.ts +1 -3
- package/src/magic/global/desmos.ts +23 -1
- package/src/magic/global/viewport.ts +40 -1
- package/src/magic/incantation.ts +59 -22
- package/src/magic/local/anim.ts +0 -1
- package/src/magic/local/colour.ts +4 -3
- package/src/magic/local/label.ts +52 -22
- package/src/magic/local/line.ts +20 -3
- package/src/magic/local/slider.ts +2 -3
- package/src/{compiler/options.ts → options.ts} +61 -14
- package/src/parser/desmost-parser.ts +12 -6
- package/src/parser/generic-parser.ts +12 -12
- package/src/parser/readme.md +2 -0
- package/src/utils.ts +3 -0
- package/svelte/desmost.svelte +5 -3
- package/dist/compiler/options.d.ts.map +0 -1
- package/dist/compiler/options.js.map +0 -1
package/src/magic/incantation.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import Json5 from "json5";
|
|
2
|
+
import dedent from "dedent";
|
|
2
3
|
|
|
4
|
+
import type { DesmostOptions } from "../options";
|
|
3
5
|
import { UnrecoverableError, type Unrecoverable } from "../errors";
|
|
4
6
|
|
|
5
7
|
|
|
@@ -21,17 +23,17 @@ export abstract class Incantation<
|
|
|
21
23
|
>
|
|
22
24
|
{
|
|
23
25
|
/** Short user-facing description of what the incantation does. */
|
|
24
|
-
|
|
26
|
+
abstract readonly description: string
|
|
25
27
|
|
|
26
28
|
/** The raw text sequence that matches this incantation, such as `viewport` or `hidden`. */
|
|
27
|
-
|
|
29
|
+
abstract readonly identifier: string
|
|
28
30
|
|
|
29
31
|
/** An alternative `.identifier`, strictly for localisation purposes only. */
|
|
30
|
-
|
|
32
|
+
readonly alias?: string
|
|
31
33
|
|
|
32
34
|
|
|
33
35
|
/** Apply this incantation's effect to `target`, using the provided `data` if required. */
|
|
34
|
-
|
|
36
|
+
abstract apply(
|
|
35
37
|
target: Effect extends GLOBAL ? Desmos.Calculator : Desmos.ExpressionState,
|
|
36
38
|
data?: unknown,
|
|
37
39
|
): void
|
|
@@ -45,7 +47,7 @@ export abstract class Incantation<
|
|
|
45
47
|
if ((actual ?? "expression") !== required)
|
|
46
48
|
{
|
|
47
49
|
throw new UnrecoverableError.IllegalIncantation(
|
|
48
|
-
`/${this.identifier} can only applied to ${required === "expression" ? "latex" : required}
|
|
50
|
+
`/${this.identifier} can only applied to ${required === "expression" ? "latex" : required} blocks, but target block has type: ${actual}`
|
|
49
51
|
);
|
|
50
52
|
}
|
|
51
53
|
}
|
|
@@ -61,10 +63,10 @@ export abstract class ArgIncantation<
|
|
|
61
63
|
extends Incantation<Effect>
|
|
62
64
|
{
|
|
63
65
|
/** Does this incantation always require an argument to be passed? */
|
|
64
|
-
|
|
66
|
+
readonly requires_arg: boolean = true
|
|
65
67
|
|
|
66
68
|
/** What type of argument does this incantation accept? */
|
|
67
|
-
|
|
69
|
+
abstract readonly arg_type: Incantation.ArgType
|
|
68
70
|
|
|
69
71
|
|
|
70
72
|
/**
|
|
@@ -75,14 +77,18 @@ export abstract class ArgIncantation<
|
|
|
75
77
|
* ## Notes
|
|
76
78
|
*
|
|
77
79
|
* Child incantation classes should override this method, though the defaults should cover most cases (except `ArgType.ENUM`).
|
|
80
|
+
*
|
|
81
|
+
* Validity checks for missing or invalid values, incorrect types, and unknown fields should be performed here, so that `.apply()` can assume the data it receives is guaranteed to be valid.
|
|
78
82
|
*/
|
|
79
|
-
|
|
83
|
+
evaluate_arg(raw: string, options: DesmostOptions): Unrecoverable<unknown>
|
|
80
84
|
{
|
|
81
85
|
switch (this.arg_type) {
|
|
82
|
-
case Incantation.ArgType.STRING: return raw;
|
|
83
86
|
case Incantation.ArgType.LATEX: return raw;
|
|
84
87
|
case Incantation.ArgType.ENUM: return raw;
|
|
85
88
|
|
|
89
|
+
case Incantation.ArgType.STRING:
|
|
90
|
+
return (options.dedent_text) ? dedent(raw): raw.trim();
|
|
91
|
+
|
|
86
92
|
case Incantation.ArgType.OBJECT: {
|
|
87
93
|
try {
|
|
88
94
|
return Json5.parse(`{${raw}}`);
|
|
@@ -94,9 +100,53 @@ export abstract class ArgIncantation<
|
|
|
94
100
|
}
|
|
95
101
|
}
|
|
96
102
|
}
|
|
103
|
+
|
|
104
|
+
protected require_nonempty(
|
|
105
|
+
arg: object,
|
|
106
|
+
msg?: string,
|
|
107
|
+
info?: UnrecoverableError.Info,
|
|
108
|
+
): Unrecoverable<void>
|
|
109
|
+
{
|
|
110
|
+
if (Object.entries(arg).length === 0) {
|
|
111
|
+
throw new UnrecoverableError.MissingInput(
|
|
112
|
+
msg ?? `/${this.identifier} received an empty argument {}`,
|
|
113
|
+
info,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
protected require_known(
|
|
119
|
+
arg: object,
|
|
120
|
+
valid_fields: string[],
|
|
121
|
+
info?: UnrecoverableError.Info,
|
|
122
|
+
): Unrecoverable<void>
|
|
123
|
+
{
|
|
124
|
+
// TODO accumulate
|
|
125
|
+
for (let field of Object.keys(arg)) {
|
|
126
|
+
if (!valid_fields.includes(field)) {
|
|
127
|
+
throw new UnrecoverableError.InvalidArgument(
|
|
128
|
+
`/${this.identifier} received invalid field: ${field}`,
|
|
129
|
+
{
|
|
130
|
+
hint: `Valid fields are [${valid_fields.join(", ")}]`,
|
|
131
|
+
...info,
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
97
137
|
}
|
|
98
138
|
|
|
99
139
|
|
|
140
|
+
/** An incantation that affects the entire Desmos calculator state, like `/desmos` or `/viewport`. */
|
|
141
|
+
export type GLOBAL = Incantation.Effect.GLOBAL;
|
|
142
|
+
|
|
143
|
+
/** An incantation that affects only the expression immediately following it, like `/hide` or `/slider`. */
|
|
144
|
+
export type LOCAL = Incantation.Effect.LOCAL;
|
|
145
|
+
|
|
146
|
+
/** An incantation that produces an expression, like `/latex` or `/text`. */
|
|
147
|
+
export type EXPR = Incantation.Effect.EXPR;
|
|
148
|
+
|
|
149
|
+
|
|
100
150
|
export namespace Incantation
|
|
101
151
|
{
|
|
102
152
|
/** The kind of effect an incantation produces - it either modifies only one block, or modifies the calculator as a whole. */
|
|
@@ -128,16 +178,3 @@ export namespace Incantation
|
|
|
128
178
|
OBJECT = "Object",
|
|
129
179
|
}
|
|
130
180
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// == ALIASES == //
|
|
134
|
-
|
|
135
|
-
/** An incantation that affects the entire Desmos calculator state, like `/desmos` or `/viewport`. */
|
|
136
|
-
export type GLOBAL = Incantation.Effect.GLOBAL;
|
|
137
|
-
|
|
138
|
-
/** An incantation that affects only the expression immediately following it, like `/hide` or `/slider`. */
|
|
139
|
-
export type LOCAL = Incantation.Effect.LOCAL;
|
|
140
|
-
|
|
141
|
-
/** An incantation that produces an expression, like `/latex` or `/text`. */
|
|
142
|
-
export type EXPR = Incantation.Effect.EXPR;
|
|
143
|
-
|
package/src/magic/local/anim.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Incantation, ArgIncantation, type LOCAL } from "../incantation";
|
|
2
|
-
import { UnrecoverableError } from "../../errors";
|
|
3
2
|
|
|
4
3
|
import { DesmosColour } from "../../desmos";
|
|
4
|
+
import { UnrecoverableError } from "../../errors";
|
|
5
|
+
import type { DesmostOptions } from "../../options";
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
export class ColourIncantation extends ArgIncantation<LOCAL>
|
|
8
9
|
{
|
|
9
10
|
override readonly description
|
|
10
|
-
= ""
|
|
11
|
+
= "Change the colour of a rendered expression, such as a line, region, polygon, etc."
|
|
11
12
|
|
|
12
13
|
override readonly identifier = "color"
|
|
13
14
|
override readonly alias = "colour"
|
|
@@ -20,7 +21,7 @@ export class ColourIncantation extends ArgIncantation<LOCAL>
|
|
|
20
21
|
target.color = data;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
override evaluate_arg(data: string): DesmosColour
|
|
24
|
+
override evaluate_arg(data: string, options: DesmostOptions): DesmosColour
|
|
24
25
|
{
|
|
25
26
|
switch (data.trim().toUpperCase()) {
|
|
26
27
|
case "RED": return DesmosColour.RED;
|
package/src/magic/local/label.ts
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
import { UnrecoverableError } from "../../errors";
|
|
2
1
|
import { Incantation, ArgIncantation, type LOCAL } from "../incantation";
|
|
3
2
|
|
|
3
|
+
import { UnrecoverableError } from "../../errors";
|
|
4
|
+
import type { DesmostOptions } from "../../options";
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
interface LabelOptions
|
|
6
8
|
{
|
|
7
9
|
text: string;
|
|
8
10
|
show?: boolean;
|
|
9
|
-
size?: number
|
|
10
|
-
pos?:
|
|
11
|
+
size?: number;
|
|
12
|
+
pos?: "ABOVE" | "BELOW" | "LEFT" | "RIGHT" | "ABOVE_LEFT" | "ABOVE_RIGHT" | "BELOW_LEFT" | "BELOW_RIGHT";
|
|
11
13
|
}
|
|
12
14
|
|
|
15
|
+
const VALID_FIELDS = ["text", "show", "size", "pos"];
|
|
16
|
+
|
|
17
|
+
const VALID_POSITIONS = [
|
|
18
|
+
"ABOVE", "BELOW", "LEFT", "RIGHT",
|
|
19
|
+
"ABOVE_LEFT", "ABOVE_RIGHT", "BELOW_LEFT", "BELOW_RIGHT",
|
|
20
|
+
];
|
|
21
|
+
|
|
13
22
|
|
|
14
23
|
export class LabelIncantation extends ArgIncantation<LOCAL>
|
|
15
24
|
{
|
|
@@ -25,36 +34,57 @@ export class LabelIncantation extends ArgIncantation<LOCAL>
|
|
|
25
34
|
super.require_expr_type(target.type, "expression");
|
|
26
35
|
target.label = data.text;
|
|
27
36
|
target.showLabel = data.show ?? true;
|
|
28
|
-
// @ts-expect-error: outdated types
|
|
37
|
+
// @ts-expect-error: outdated types
|
|
29
38
|
if (data.size != undefined) target.labelSize = data.size;
|
|
39
|
+
// @ts-expect-error: outdated types
|
|
30
40
|
if (data.pos != undefined) target.labelOrientation = data.pos;
|
|
31
41
|
}
|
|
32
42
|
|
|
33
|
-
override evaluate_arg(raw: string): LabelOptions
|
|
43
|
+
override evaluate_arg(raw: string, options: DesmostOptions): LabelOptions
|
|
34
44
|
{
|
|
35
|
-
let out = super.evaluate_arg(raw) as LabelOptions;
|
|
45
|
+
let out = super.evaluate_arg(raw, options) as LabelOptions;
|
|
46
|
+
|
|
47
|
+
if (options.check_args) {
|
|
48
|
+
super.require_nonempty(out, `/label received empty argument`, {
|
|
49
|
+
hint: `You can provide [${VALID_FIELDS.join(", ")}]`,
|
|
50
|
+
flagged_by: "check_args",
|
|
51
|
+
});
|
|
36
52
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
53
|
+
if (!("text" in out)) {
|
|
54
|
+
throw new UnrecoverableError.MissingInput(
|
|
55
|
+
`/label is missing label text`,
|
|
56
|
+
{
|
|
57
|
+
hint: `Provide text for the label: \`/label{text: "sup world!"}\``,
|
|
58
|
+
flagged_by: "check_args",
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
super.require_known(out, VALID_FIELDS);
|
|
41
64
|
}
|
|
42
65
|
|
|
43
66
|
if (typeof out.pos != "undefined") {
|
|
44
|
-
let pos = out.pos.trim().toUpperCase();
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
67
|
+
let pos = out.pos.trim().toUpperCase().replaceAll("-", "_");
|
|
68
|
+
|
|
69
|
+
if (VALID_POSITIONS.includes(pos)) {
|
|
70
|
+
// @ts-expect-error: validated
|
|
71
|
+
out.pos = pos;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// @ts-expect-error: indexing
|
|
75
|
+
let orientation = Desmos.LabelOrientations[pos];
|
|
76
|
+
|
|
77
|
+
if (options.check_args && orientation == undefined) {
|
|
55
78
|
throw new UnrecoverableError.InvalidArgument(
|
|
56
|
-
`/label
|
|
79
|
+
`/label received invalid label position: ${pos}`,
|
|
80
|
+
{
|
|
81
|
+
hint: `Valid label positions are ${VALID_POSITIONS.join(", ")}`,
|
|
82
|
+
flagged_by: "check_args"
|
|
83
|
+
}
|
|
57
84
|
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
out.pos = orientation;
|
|
58
88
|
}
|
|
59
89
|
}
|
|
60
90
|
|
package/src/magic/local/line.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { Incantation, ArgIncantation, type LOCAL } from "../incantation";
|
|
2
2
|
|
|
3
|
+
import type { DesmostOptions } from "../../options";
|
|
4
|
+
import { type Unrecoverable } from "../../errors";
|
|
5
|
+
|
|
3
6
|
|
|
4
7
|
interface LineOptions
|
|
5
8
|
{
|
|
6
|
-
style?: keyof typeof Desmos.Styles;
|
|
9
|
+
style?: keyof typeof Desmos.Styles;
|
|
7
10
|
width?: number;
|
|
8
11
|
opacity?: number;
|
|
9
12
|
}
|
|
10
13
|
|
|
14
|
+
const VALID_FIELDS = ["style", "width", "opacity"];
|
|
15
|
+
|
|
11
16
|
|
|
12
17
|
export class LineIncantation extends ArgIncantation<LOCAL>
|
|
13
18
|
{
|
|
@@ -22,8 +27,20 @@ export class LineIncantation extends ArgIncantation<LOCAL>
|
|
|
22
27
|
{
|
|
23
28
|
super.require_expr_type(target.type, "expression");
|
|
24
29
|
|
|
25
|
-
if (data.style
|
|
26
|
-
if (data.width
|
|
30
|
+
if (data.style != undefined) target.lineStyle = data.style;
|
|
31
|
+
if (data.width != undefined) target.lineWidth = data.width;
|
|
27
32
|
if (data.opacity != undefined) target.lineOpacity = data.opacity;
|
|
28
33
|
}
|
|
34
|
+
|
|
35
|
+
override evaluate_arg(raw: string, options: DesmostOptions): Unrecoverable<LineOptions>
|
|
36
|
+
{
|
|
37
|
+
let out = super.evaluate_arg(raw, options) as LineOptions;
|
|
38
|
+
|
|
39
|
+
if (options.check_args) {
|
|
40
|
+
super.require_nonempty(out);
|
|
41
|
+
super.require_known(out, VALID_FIELDS);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
29
46
|
}
|
|
@@ -3,8 +3,8 @@ import { Incantation, ArgIncantation, type LOCAL } from "../incantation";
|
|
|
3
3
|
|
|
4
4
|
interface SliderBounds
|
|
5
5
|
{
|
|
6
|
-
min
|
|
7
|
-
max
|
|
6
|
+
min: number | string;
|
|
7
|
+
max: number | string;
|
|
8
8
|
step?: number | string;
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -21,7 +21,6 @@ export class SliderIncantation extends ArgIncantation<LOCAL>
|
|
|
21
21
|
override apply(target: Desmos.ExpressionState, data: SliderBounds)
|
|
22
22
|
{
|
|
23
23
|
super.require_expr_type(target.type, "expression");
|
|
24
|
-
// @ts-expect-error: outdated types
|
|
25
24
|
target.sliderBounds = data;
|
|
26
25
|
}
|
|
27
26
|
}
|
|
@@ -1,11 +1,30 @@
|
|
|
1
|
+
import { LINE } from "./utils";
|
|
2
|
+
import { compile } from "./compiler/compile";
|
|
3
|
+
|
|
4
|
+
|
|
1
5
|
/**
|
|
2
6
|
* Options to customise Desmost compilation.
|
|
3
7
|
*
|
|
4
|
-
* To customise compilation, pass in a `DesmostOptions` object as the last argument to
|
|
8
|
+
* To customise compilation, pass in a `DesmostOptions` object as the last argument to {@linkcode compile()}:
|
|
5
9
|
*
|
|
6
10
|
* ```ts
|
|
7
11
|
* compile(calc, source, { errors: "crash" });
|
|
8
12
|
* ```
|
|
13
|
+
*
|
|
14
|
+
* ## Decompilation
|
|
15
|
+
*
|
|
16
|
+
* The same options can also be applied to decompilation:
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* decompile(calc, blank, { prettify: false });
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Here, this means “decompile into source code that would reproduce `calc`, *when compiled with these options*”. As an identity:
|
|
23
|
+
*
|
|
24
|
+
* ```ts
|
|
25
|
+
* let options = { ... };
|
|
26
|
+
* compile(_, decompile(calc, _ options), options) == calc // (value equality)
|
|
27
|
+
* ```
|
|
9
28
|
*/
|
|
10
29
|
export interface DesmostOptions
|
|
11
30
|
{
|
|
@@ -19,7 +38,7 @@ export interface DesmostOptions
|
|
|
19
38
|
*
|
|
20
39
|
* - `suppress`: Silently fail on the frontend (if you wish to give the illusion that everything is fine).
|
|
21
40
|
*/
|
|
22
|
-
errors
|
|
41
|
+
errors: "surface" | "crash" | "suppress"
|
|
23
42
|
|
|
24
43
|
/**
|
|
25
44
|
* Where should errors be placed?
|
|
@@ -28,7 +47,7 @@ export interface DesmostOptions
|
|
|
28
47
|
* - `end`: All aggregated at the end of the Desmos expressions list.
|
|
29
48
|
* - `start`: All aggregated at the start of the Desmos expressions list.
|
|
30
49
|
*/
|
|
31
|
-
place_errors
|
|
50
|
+
place_errors: "inline" | "end" | "start"
|
|
32
51
|
|
|
33
52
|
/**
|
|
34
53
|
* The prefix to prepend to error blocks.
|
|
@@ -37,42 +56,67 @@ export interface DesmostOptions
|
|
|
37
56
|
*
|
|
38
57
|
* Provide a `""` blank string if you wish for no prefix to be added.
|
|
39
58
|
*/
|
|
40
|
-
error_prefix
|
|
59
|
+
error_prefix: string
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Show all the available diagnostics for errors, including hints and debug information?
|
|
63
|
+
*
|
|
64
|
+
* Defaults to `true`, meaning all output is shown.
|
|
65
|
+
*/
|
|
66
|
+
expand_errors: boolean
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Error if an incantation receives a `{}` object argument with unknown fields, or no fields at all?
|
|
70
|
+
*
|
|
71
|
+
* For instance, `/label{position: LEFT}` is invalid; it should be `/label{pos: LEFT}`. With `check_args: false`, this silently no-ops.
|
|
72
|
+
*
|
|
73
|
+
* Desmost doesn’t remove unknown fields, it just passes them directly to the Desmos API. If you know what you’re doing and a field you know exists isn’t supported by Desmos, you can disable this to avoid erroring.
|
|
74
|
+
*
|
|
75
|
+
* Defaults to `true`, meaning arguments are checked. The Desmos API happily accepts and ignores invalid fields, so Desmost provides a safety net for you ;)
|
|
76
|
+
*/
|
|
77
|
+
check_args: boolean
|
|
41
78
|
|
|
42
79
|
/**
|
|
43
80
|
* Prettify LaTeX output so it renders nicely in the Desmos editor?
|
|
44
81
|
*
|
|
45
82
|
* Defaults to `true`, meaning LaTeX is prettified.
|
|
46
83
|
*/
|
|
47
|
-
prettify
|
|
84
|
+
prettify: boolean
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Strip common indentation from `/text{}` blocks?
|
|
88
|
+
*
|
|
89
|
+
* Defaults to `true`, meaning text is dedented.
|
|
90
|
+
*/
|
|
91
|
+
dedent_text: boolean
|
|
48
92
|
|
|
49
93
|
/**
|
|
50
94
|
* Should `%` LaTeX comments be ignored, instead of turned into text expressions (notes)?
|
|
51
95
|
*
|
|
52
96
|
* Defaults to `false`, meaning comments are kept.
|
|
53
97
|
*/
|
|
54
|
-
ignore_comments
|
|
98
|
+
ignore_comments: boolean
|
|
55
99
|
|
|
56
100
|
/**
|
|
57
101
|
* Should all line breaks be ignored, instead of kept as blank expressions?
|
|
58
102
|
*
|
|
59
103
|
* Defaults to `false`, meaning all blank lines are kept.
|
|
60
104
|
*/
|
|
61
|
-
ignore_all_blanks
|
|
105
|
+
ignore_all_blanks: boolean
|
|
62
106
|
|
|
63
107
|
/**
|
|
64
108
|
* Should trailing blank lines at the start of the source be kept as blank expressions?
|
|
65
109
|
*
|
|
66
110
|
* Defaults to `false`, meaning leading blank lines are ignored.
|
|
67
111
|
*/
|
|
68
|
-
keep_leading_blanks
|
|
112
|
+
keep_leading_blanks: boolean
|
|
69
113
|
|
|
70
114
|
/**
|
|
71
115
|
* Should trailing blank lines at the end of the source be kept as blank expressions?
|
|
72
116
|
*
|
|
73
117
|
* Defaults to `false`, meaning trailing blank lines are ignored.
|
|
74
118
|
*/
|
|
75
|
-
keep_trailing_blanks
|
|
119
|
+
keep_trailing_blanks: boolean
|
|
76
120
|
|
|
77
121
|
/**
|
|
78
122
|
* Return debug diagnostics?
|
|
@@ -81,16 +125,19 @@ export interface DesmostOptions
|
|
|
81
125
|
*
|
|
82
126
|
* Defaults to `false`, meaning `compile()` returns `void`.
|
|
83
127
|
*/
|
|
84
|
-
debug
|
|
128
|
+
debug: boolean
|
|
85
129
|
}
|
|
86
130
|
|
|
87
131
|
|
|
88
|
-
export const DEFAULT_OPTIONS =
|
|
132
|
+
export const DEFAULT_OPTIONS: DesmostOptions =
|
|
89
133
|
{
|
|
90
134
|
errors: "surface",
|
|
91
135
|
place_errors: "inline",
|
|
92
|
-
error_prefix:
|
|
136
|
+
error_prefix: `[DESMOST ERROR]\n${LINE}\n`,
|
|
137
|
+
expand_errors: true,
|
|
93
138
|
prettify: true,
|
|
139
|
+
dedent_text: true,
|
|
140
|
+
check_args: false,
|
|
94
141
|
ignore_comments: false,
|
|
95
142
|
ignore_all_blanks: false,
|
|
96
143
|
keep_leading_blanks: false,
|
|
@@ -102,10 +149,10 @@ export const DEFAULT_OPTIONS =
|
|
|
102
149
|
/**
|
|
103
150
|
* Fill in `options` with Desmost's defaults to produce a complete `DesmostOptions` config.
|
|
104
151
|
*/
|
|
105
|
-
export function fill_defaults(options: Partial<DesmostOptions> | undefined):
|
|
152
|
+
export function fill_defaults(options: Partial<DesmostOptions> | undefined): DesmostOptions
|
|
106
153
|
{
|
|
107
154
|
return {
|
|
108
155
|
...DEFAULT_OPTIONS,
|
|
109
156
|
...options,
|
|
110
|
-
}
|
|
157
|
+
};
|
|
111
158
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GenericParser } from "./generic-parser";
|
|
2
2
|
import { Ast } from "./ast";
|
|
3
3
|
|
|
4
|
-
import type { DesmostOptions } from "../
|
|
4
|
+
import type { DesmostOptions } from "../options";
|
|
5
5
|
import { NO_MATCH, UnrecoverableError } from "../errors";
|
|
6
6
|
import type { NoMatch, Unrecoverable } from "../errors";
|
|
7
7
|
import * as utils from "../utils";
|
|
@@ -468,20 +468,26 @@ export class DesmostParser extends GenericParser
|
|
|
468
468
|
}
|
|
469
469
|
|
|
470
470
|
/* NOTE: Cut in by 1 on both sides to exclude {} braces */
|
|
471
|
-
let
|
|
471
|
+
let raw = utils.chars(this.source, init + 1, this.i - 1);
|
|
472
472
|
|
|
473
473
|
for (let [i, j] of utils.reversing(utils.paired(indices_to_insert_quotes))) {
|
|
474
474
|
i -= init + 1;
|
|
475
475
|
j -= init + 1;
|
|
476
476
|
|
|
477
|
-
let value =
|
|
477
|
+
let value = raw.slice(i, j).join("");
|
|
478
478
|
if (["true", "false", "null", "undefined"].includes(value)) continue;
|
|
479
479
|
|
|
480
|
-
|
|
481
|
-
|
|
480
|
+
raw.splice(j, 0, `"`);
|
|
481
|
+
raw.splice(i, 0, `"`);
|
|
482
482
|
}
|
|
483
483
|
|
|
484
|
-
|
|
484
|
+
let out = raw.join("");
|
|
485
|
+
|
|
486
|
+
if (arg_type !== Incantation.ArgType.STRING) {
|
|
487
|
+
out = out.trim();
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return out;
|
|
485
491
|
}
|
|
486
492
|
}
|
|
487
493
|
|
|
@@ -48,14 +48,6 @@ export class GenericParser
|
|
|
48
48
|
return this.source.at(this.i);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
/**
|
|
52
|
-
* Get the character *after* the current one that the parser points at.
|
|
53
|
-
*/
|
|
54
|
-
protected next(): string | undefined
|
|
55
|
-
{
|
|
56
|
-
return this.source.at(this.i + 1);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
51
|
/**
|
|
60
52
|
* Peek a snippet of the upcoming source text.
|
|
61
53
|
*
|
|
@@ -64,7 +56,11 @@ export class GenericParser
|
|
|
64
56
|
public preview(idx?: number): string
|
|
65
57
|
{
|
|
66
58
|
idx ??= this.i;
|
|
67
|
-
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
this.source.slice(idx, idx + 20).replaceAll("\n", " ")
|
|
62
|
+
+ (idx + 20 >= this.length ? "⏎" : "...")
|
|
63
|
+
);
|
|
68
64
|
}
|
|
69
65
|
|
|
70
66
|
/**
|
|
@@ -140,7 +136,7 @@ export class GenericParser
|
|
|
140
136
|
|
|
141
137
|
while (this.current === raw[ii]) {
|
|
142
138
|
let r = this.try_advance();
|
|
143
|
-
if (r === NO_MATCH) return NO_MATCH;
|
|
139
|
+
if (r === NO_MATCH) return NO_MATCH; // FIXME?
|
|
144
140
|
|
|
145
141
|
ii++;
|
|
146
142
|
if (ii === raw.length) return;
|
|
@@ -167,14 +163,18 @@ export class GenericParser
|
|
|
167
163
|
/**
|
|
168
164
|
* Consume 0 or more whitespace characters.
|
|
169
165
|
*
|
|
170
|
-
* Same as `.consume_spaces()`, but allows newlines.
|
|
166
|
+
* Same as `.consume_spaces()`, but allows tabs and newlines.
|
|
171
167
|
*/
|
|
172
168
|
protected consume_whitespace(): void
|
|
173
169
|
{
|
|
174
170
|
/* NOTE: Callers should be able to assume this is safe to call even when at end of input, since it should just match 0 characters */
|
|
175
171
|
if (this.i >= this.length) return;
|
|
176
172
|
|
|
177
|
-
while (
|
|
173
|
+
while (
|
|
174
|
+
this.current === " "
|
|
175
|
+
|| this.current === "\n"
|
|
176
|
+
|| this.current === "\t"
|
|
177
|
+
) {
|
|
178
178
|
this.advance();
|
|
179
179
|
}
|
|
180
180
|
}
|
package/src/parser/readme.md
CHANGED
|
@@ -11,6 +11,8 @@ The parser is split into `DesmostParser` and `GenericParser`.
|
|
|
11
11
|
|
|
12
12
|
The parser is stateful, tracking its current position in the source text with its `.i` field.
|
|
13
13
|
|
|
14
|
+
`parse_next()` is the only method consumers need to be aware of, the rest is implementation detail. Would love to make them `private`, unfortunately we need them public for unit testing ;)
|
|
15
|
+
|
|
14
16
|
|
|
15
17
|
## Backtracking
|
|
16
18
|
|
package/src/utils.ts
CHANGED
package/svelte/desmost.svelte
CHANGED
|
@@ -141,9 +141,11 @@ onMount(() =>
|
|
|
141
141
|
inject(el_desmos, source.textContent);
|
|
142
142
|
}
|
|
143
143
|
else {
|
|
144
|
-
let observer = new IntersectionObserver(() => {
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
let observer = new IntersectionObserver(([entry]) => {
|
|
145
|
+
if (entry.isIntersecting) {
|
|
146
|
+
inject(el_desmos, source.textContent);
|
|
147
|
+
observer.disconnect();
|
|
148
|
+
}
|
|
147
149
|
});
|
|
148
150
|
observer.observe(el_desmos);
|
|
149
151
|
lazy_observers.push(observer);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/compiler/options.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAE9B;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,UAAU,CAAA;IAEzC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAA;IAEzC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IAErB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAElB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;IAEzB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAE3B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAE7B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAE9B;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CACf;AAGD,eAAO,MAAM,eAAe;;;;;;;;;;CAW3B,CAAC;AAGF;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,SAAS,GAAG,QAAQ,CAAC,cAAc,CAAC,CAMpG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/compiler/options.ts"],"names":[],"mappings":"AAuFA,MAAM,CAAC,MAAM,eAAe,GAC5B;IACG,MAAM,EAAE,SAAS;IACjB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,mBAAmB;IACjC,QAAQ,EAAE,IAAI;IACd,eAAe,EAAE,KAAK;IACtB,iBAAiB,EAAE,KAAK;IACxB,mBAAmB,EAAE,KAAK;IAC1B,oBAAoB,EAAE,KAAK;IAC3B,KAAK,EAAE,KAAK;CACd,CAAC;AAGF;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,OAA4C;IAEvE,OAAO;QACJ,GAAG,eAAe;QAClB,GAAG,OAAO;KACgB,CAAC;AACjC,CAAC"}
|