desmost 0.9.1 → 0.10.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.
Files changed (97) hide show
  1. package/README.md +12 -3
  2. package/dist/compiler/compile.d.ts +1 -7
  3. package/dist/compiler/compile.d.ts.map +1 -1
  4. package/dist/compiler/compile.js +3 -94
  5. package/dist/compiler/compile.js.map +1 -1
  6. package/dist/compiler/compiler.d.ts +56 -0
  7. package/dist/compiler/compiler.d.ts.map +1 -0
  8. package/dist/compiler/compiler.js +209 -0
  9. package/dist/compiler/compiler.js.map +1 -0
  10. package/dist/compiler/format.d.ts +2 -2
  11. package/dist/compiler/format.d.ts.map +1 -1
  12. package/dist/compiler/format.js +18 -9
  13. package/dist/compiler/format.js.map +1 -1
  14. package/dist/decompiler/decompile.d.ts +1 -1
  15. package/dist/decompiler/decompile.d.ts.map +1 -1
  16. package/dist/decompiler/extract.d.ts.map +1 -1
  17. package/dist/decompiler/extract.js +7 -3
  18. package/dist/decompiler/extract.js.map +1 -1
  19. package/dist/errors.d.ts +35 -27
  20. package/dist/errors.d.ts.map +1 -1
  21. package/dist/errors.js +46 -48
  22. package/dist/errors.js.map +1 -1
  23. package/dist/index.internal.d.ts +1 -1
  24. package/dist/index.internal.d.ts.map +1 -1
  25. package/dist/magic/global/desmos.d.ts +2 -2
  26. package/dist/magic/global/desmos.d.ts.map +1 -1
  27. package/dist/magic/global/desmos.js +3 -2
  28. package/dist/magic/global/desmos.js.map +1 -1
  29. package/dist/magic/global/viewport.d.ts +2 -2
  30. package/dist/magic/global/viewport.d.ts.map +1 -1
  31. package/dist/magic/global/viewport.js +5 -5
  32. package/dist/magic/global/viewport.js.map +1 -1
  33. package/dist/magic/incantation.d.ts +4 -4
  34. package/dist/magic/incantation.d.ts.map +1 -1
  35. package/dist/magic/incantation.js +15 -8
  36. package/dist/magic/incantation.js.map +1 -1
  37. package/dist/magic/local/colour.d.ts +3 -1
  38. package/dist/magic/local/colour.d.ts.map +1 -1
  39. package/dist/magic/local/colour.js +15 -11
  40. package/dist/magic/local/colour.js.map +1 -1
  41. package/dist/magic/local/label.d.ts.map +1 -1
  42. package/dist/magic/local/label.js +7 -7
  43. package/dist/magic/local/label.js.map +1 -1
  44. package/dist/magic/local/line.d.ts +2 -2
  45. package/dist/magic/local/line.d.ts.map +1 -1
  46. package/dist/magic/local/line.js +0 -1
  47. package/dist/magic/local/line.js.map +1 -1
  48. package/dist/options.d.ts +57 -23
  49. package/dist/options.d.ts.map +1 -1
  50. package/dist/options.js +2 -1
  51. package/dist/options.js.map +1 -1
  52. package/dist/parser/ast.d.ts +3 -17
  53. package/dist/parser/ast.d.ts.map +1 -1
  54. package/dist/parser/ast.js +0 -2
  55. package/dist/parser/ast.js.map +1 -1
  56. package/dist/parser/desmost-parser.d.ts +33 -22
  57. package/dist/parser/desmost-parser.d.ts.map +1 -1
  58. package/dist/parser/desmost-parser.js +114 -100
  59. package/dist/parser/desmost-parser.js.map +1 -1
  60. package/dist/parser/generic-parser.d.ts +23 -14
  61. package/dist/parser/generic-parser.d.ts.map +1 -1
  62. package/dist/parser/generic-parser.js +67 -27
  63. package/dist/parser/generic-parser.js.map +1 -1
  64. package/dist/utils.d.ts +9 -2
  65. package/dist/utils.d.ts.map +1 -1
  66. package/dist/utils.js +13 -2
  67. package/dist/utils.js.map +1 -1
  68. package/package.json +5 -2
  69. package/src/compiler/compile.ts +7 -120
  70. package/src/compiler/compiler.ts +275 -0
  71. package/src/compiler/format.ts +19 -7
  72. package/src/decompiler/decompile.ts +1 -1
  73. package/src/decompiler/extract.ts +3 -1
  74. package/src/errors.ts +40 -24
  75. package/src/index.internal.ts +1 -1
  76. package/src/magic/.template.ts +2 -2
  77. package/src/magic/global/desmos.ts +7 -9
  78. package/src/magic/global/viewport.ts +11 -17
  79. package/src/magic/incantation.ts +21 -22
  80. package/src/magic/local/colour.ts +23 -13
  81. package/src/magic/local/label.ts +11 -17
  82. package/src/magic/local/line.ts +2 -2
  83. package/src/options.ts +81 -47
  84. package/src/parser/ast.ts +1 -21
  85. package/src/parser/desmost-parser.ts +139 -143
  86. package/src/parser/generic-parser.ts +82 -40
  87. package/src/utils.ts +25 -3
  88. package/dist/compiler/evaluate.d.ts +0 -18
  89. package/dist/compiler/evaluate.d.ts.map +0 -1
  90. package/dist/compiler/evaluate.js +0 -93
  91. package/dist/compiler/evaluate.js.map +0 -1
  92. package/dist/desmos.d.ts +0 -16
  93. package/dist/desmos.d.ts.map +0 -1
  94. package/dist/desmos.js +0 -19
  95. package/dist/desmos.js.map +0 -1
  96. package/src/compiler/evaluate.ts +0 -128
  97. package/src/desmos.ts +0 -22
@@ -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
+ }
@@ -1,11 +1,11 @@
1
1
  import type { DesmostOptions } from "../options";
2
2
 
3
- import { UnrecoverableError } from "../errors";
3
+ import { DesmostError } from "../errors";
4
4
  import { LINE } from "../utils";
5
5
 
6
6
 
7
7
  export function format_error(
8
- e: UnrecoverableError,
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.info != undefined && options.expand_errors) {
19
- display += `\n${LINE}`;
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
- if (e.info.hint != undefined) display += `\nHint: ${e.info.hint}`;
22
- if (e.info.note != undefined) display += `\n${LINE}\nNote: ${e.info.note}`;
23
- if (e.info.flagged_by != undefined) display += `\n${LINE}\nFlagged by: \`${e.info.flagged_by}\``;
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;
@@ -2,7 +2,7 @@ import { emit_global_incantation, emit_expression } from "./emit";
2
2
  import { extract_settings, extract_viewport, extract_expression } from "./extract";
3
3
 
4
4
  import { Ast } from "../parser";
5
- import type { DesmostOptions } from "../compiler";
5
+ import type { DesmostOptions } from "../options";
6
6
 
7
7
 
8
8
  export interface SemiStructuredAst
@@ -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 "../desmos";
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/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 UnrecoverableError extends Error
16
+ export class DesmostError extends Error
14
17
  {
15
- info?: UnrecoverableError.Info;
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 UnrecoverableError
26
+ export namespace DesmostError
19
27
  {
20
- export interface Info
28
+ export interface Data
21
29
  {
22
- hint?: string,
23
- note?: string,
24
- flagged_by?: string,
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 entire input source code. */
28
- export class UnexpectedEnd extends UnrecoverableError {
29
- constructor(msg: string, override info?: Info) { super(msg); this.name = "Unexpected End"; }
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 UnrecoverableError {
34
- constructor(msg: string, override info?: Info) { super(msg); this.name = "Missing Input"; }
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 UnrecoverableError {
39
- constructor(msg: string, override info?: Info) { super(msg); this.name = "Unexpected Input"; }
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 UnrecoverableError {
44
- constructor(msg: string, override info?: Info) { super(msg); this.name = "Excess Input"; }
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 UnrecoverableError {
49
- constructor(msg: string, override info?: Info) { super(msg); this.name = "Illegal Incantation"; }
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 couldn't be parsed. */
53
- export class InvalidArgument extends UnrecoverableError {
54
- constructor(msg: string, override info?: Info) { super(msg); this.name = "Invalid Argument"; }
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
- /** Indicates that a function may throw an `UnrecoverableError`. */
60
- export type Unrecoverable<Result> = Result | Result & { readonly __brand?: unique symbol };
73
+ /** Indicates that a function may throw a `DesmostError`. */
74
+ export type Fallible<Result>
75
+ = Result
76
+ | Result & { readonly __brand?: unique symbol };
@@ -1,5 +1,5 @@
1
1
  export { DEFAULT_OPTIONS } from "./options";
2
- export type { DesmostDebug } from "./compiler/compile";
2
+ export type { DesmostDebug } from "./compiler/compiler";
3
3
 
4
4
  export { Ast } from "./parser";
5
5
 
@@ -1,7 +1,7 @@
1
1
  // import { Incantation, ArgIncantation, type GLOBAL, type LOCAL, type EXPR } from "../incantation";
2
2
 
3
3
  // import type { DesmostOptions } from "../../options";
4
- // import { UnrecoverableError, type Unrecoverable } from "../../errors";
4
+ // import { DesmostError, type Fallible } from "../../errors";
5
5
 
6
6
 
7
7
  // export class ?Incantation extends ArgIncantation<?>
@@ -18,7 +18,7 @@
18
18
  // // TODO
19
19
  // }
20
20
 
21
- // override evaluate_arg(raw: ?, options: DesmostOptions): Unrecoverable<?>
21
+ // override evaluate_arg(raw: ?, options: DesmostOptions): Fallible<?>
22
22
  // {
23
23
  // let out = super.evaluate_arg(raw, options) as ?;
24
24
 
@@ -1,6 +1,6 @@
1
1
  import { Incantation, ArgIncantation, type GLOBAL } from "../incantation";
2
2
 
3
- import { UnrecoverableError, type Unrecoverable } from "../../errors";
3
+ import { DesmostError, type Fallible } from "../../errors";
4
4
  import type { DesmostOptions } from "../../options";
5
5
 
6
6
 
@@ -26,19 +26,17 @@ export class DesmosIncantation extends ArgIncantation<GLOBAL>
26
26
  }
27
27
  }
28
28
 
29
- override evaluate_arg(raw: string, options: DesmostOptions): Unrecoverable<DesmosSettings>
29
+ override evaluate_arg(raw: string, options: DesmostOptions): Fallible<DesmosSettings>
30
30
  {
31
31
  let out = super.evaluate_arg(raw, options) as DesmosSettings;
32
32
 
33
33
  if (options.check_args) {
34
34
  if (Object.keys(out).length === 0) {
35
- throw new UnrecoverableError.MissingInput(
36
- `/${this.identifier} received empty settings`,
37
- {
38
- hint: `Provide settings like \`/desmos{expressionsCollapsed: true}\``,
39
- note: `Use just /desmos (without \`{}\`) if you want it as an indicator`,
40
- }
41
- );
35
+ throw new DesmostError.MissingInput({
36
+ msg: `/desmos received empty settings`,
37
+ hint: `Provide settings like \`/desmos{expressionsCollapsed: true}\``,
38
+ note: `Use just /desmos (without \`{}\`) if you want it as an indicator`,
39
+ });
42
40
  }
43
41
  }
44
42
 
@@ -1,6 +1,6 @@
1
1
  import { Incantation, ArgIncantation, type GLOBAL } from "../incantation";
2
2
 
3
- import { UnrecoverableError, type Unrecoverable } from "../../errors";
3
+ import { DesmostError, type Fallible } from "../../errors";
4
4
  import type { DesmostOptions } from "../../options";
5
5
 
6
6
 
@@ -36,20 +36,17 @@ export class ViewportIncantation extends ArgIncantation<GLOBAL>
36
36
  target.setMathBounds({ left, right, bottom, top });
37
37
  }
38
38
 
39
- override evaluate_arg(raw: string, options: DesmostOptions): Unrecoverable<ViewportBounds>
39
+ override evaluate_arg(raw: string, options: DesmostOptions): Fallible<ViewportBounds>
40
40
  {
41
41
  let out = super.evaluate_arg(raw, options) as ViewportBounds;
42
42
 
43
43
  if (options.check_args) {
44
44
  if (Object.keys(out).length === 0) {
45
- throw new UnrecoverableError.InvalidArgument(
46
- `/${this.identifier} received empty viewport bounds`,
47
- {
48
- hint: `Provide bounds like \`/viewport{left: -8, right: 8}\``,
49
- note: `Missing bounds are kept as their defaults`,
50
- flagged_by: "check_args",
51
- }
52
- );
45
+ throw new DesmostError.InvalidArgument({
46
+ msg: `/${this.identifier} received empty viewport bounds`,
47
+ hint: `Provide bounds like \`/viewport{left: -8, right: 8}\``,
48
+ note: `Missing bounds are kept as their defaults`,
49
+ });
53
50
  }
54
51
 
55
52
  let invalid_keys = Object.keys(out).filter(key => !VALID_KEYS.includes(key));
@@ -57,13 +54,10 @@ export class ViewportIncantation extends ArgIncantation<GLOBAL>
57
54
  if (invalid_keys.length > 0) {
58
55
  let plural = invalid_keys.length > 1 ? "fields" : "field";
59
56
 
60
- throw new UnrecoverableError.InvalidArgument(
61
- `/${this.identifier} received invalid ${plural}: [${invalid_keys.join(", ")}]`,
62
- {
63
- hint: `Valid fields are: [${VALID_KEYS.join(", ")}]`,
64
- flagged_by: "check_args",
65
- }
66
- );
57
+ throw new DesmostError.InvalidArgument({
58
+ msg: `/${this.identifier} received invalid ${plural}: [${invalid_keys.join(", ")}]`,
59
+ hint: `Valid fields are: [${VALID_KEYS.join(", ")}]`,
60
+ });
67
61
  }
68
62
  }
69
63
 
@@ -2,7 +2,7 @@ import Json5 from "json5";
2
2
  import dedent from "dedent";
3
3
 
4
4
  import type { DesmostOptions } from "../options";
5
- import { UnrecoverableError, type Unrecoverable } from "../errors";
5
+ import { DesmostError, type Fallible } from "../errors";
6
6
 
7
7
 
8
8
  /**
@@ -46,9 +46,11 @@ export abstract class Incantation<
46
46
  {
47
47
  if ((actual ?? "expression") !== required)
48
48
  {
49
- throw new UnrecoverableError.IllegalIncantation(
50
- `/${this.identifier} can only applied to ${required === "expression" ? "latex" : required} blocks, but target block has type: ${actual}`
51
- );
49
+ let req = required === "expression" ? "latex" : required;
50
+
51
+ throw new DesmostError.IllegalIncantation({
52
+ msg: `/${this.identifier} can only applied to ${req} blocks, but target block has type: ${actual}`,
53
+ });
52
54
  }
53
55
  }
54
56
  }
@@ -80,7 +82,7 @@ export abstract class ArgIncantation<
80
82
  *
81
83
  * 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.
82
84
  */
83
- evaluate_arg(raw: string, options: DesmostOptions): Unrecoverable<unknown>
85
+ evaluate_arg(raw: string, options: DesmostOptions): Fallible<unknown>
84
86
  {
85
87
  switch (this.arg_type) {
86
88
  case Incantation.ArgType.LATEX: return raw;
@@ -95,7 +97,7 @@ export abstract class ArgIncantation<
95
97
  }
96
98
  catch (e) {
97
99
  // @ts-expect-error: fine
98
- throw new UnrecoverableError.InvalidArgument(e.message);
100
+ throw new DesmostError.InvalidArgument({ msg: e.message });
99
101
  }
100
102
  }
101
103
  }
@@ -103,34 +105,31 @@ export abstract class ArgIncantation<
103
105
 
104
106
  protected require_nonempty(
105
107
  arg: object,
106
- msg?: string,
107
- info?: UnrecoverableError.Info,
108
- ): Unrecoverable<void>
108
+ error_data?: DesmostError.Data,
109
+ ): Fallible<void>
109
110
  {
110
111
  if (Object.entries(arg).length === 0) {
111
- throw new UnrecoverableError.MissingInput(
112
- msg ?? `/${this.identifier} received an empty argument {}`,
113
- info,
114
- );
112
+ throw new DesmostError.MissingInput({
113
+ msg: `/${this.identifier} received an empty argument {}`,
114
+ ...error_data
115
+ });
115
116
  }
116
117
  }
117
118
 
118
119
  protected require_known(
119
120
  arg: object,
120
121
  valid_fields: string[],
121
- info?: UnrecoverableError.Info,
122
- ): Unrecoverable<void>
122
+ error_data?: DesmostError.Data,
123
+ ): Fallible<void>
123
124
  {
124
125
  // TODO accumulate
125
126
  for (let field of Object.keys(arg)) {
126
127
  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
- );
128
+ throw new DesmostError.InvalidArgument({
129
+ msg: `/${this.identifier} received invalid field: ${field}`,
130
+ hint: `Valid fields are [${valid_fields.join(", ")}]`,
131
+ ...error_data,
132
+ });
134
133
  }
135
134
  }
136
135
  }