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.
Files changed (135) hide show
  1. package/README.md +80 -13
  2. package/dist/compiler/compile.d.ts +7 -3
  3. package/dist/compiler/compile.d.ts.map +1 -1
  4. package/dist/compiler/compile.js +7 -3
  5. package/dist/compiler/compile.js.map +1 -1
  6. package/dist/compiler/evaluate.d.ts +5 -5
  7. package/dist/compiler/evaluate.d.ts.map +1 -1
  8. package/dist/compiler/evaluate.js +16 -8
  9. package/dist/compiler/evaluate.js.map +1 -1
  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 +14 -1
  13. package/dist/compiler/format.js.map +1 -1
  14. package/dist/compiler/index.d.ts +0 -1
  15. package/dist/compiler/index.d.ts.map +1 -1
  16. package/dist/decompiler/decompile.d.ts +12 -1
  17. package/dist/decompiler/decompile.d.ts.map +1 -1
  18. package/dist/decompiler/decompile.js +14 -2
  19. package/dist/decompiler/decompile.js.map +1 -1
  20. package/dist/decompiler/emit.d.ts.map +1 -1
  21. package/dist/decompiler/emit.js +13 -6
  22. package/dist/decompiler/emit.js.map +1 -1
  23. package/dist/decompiler/extract.d.ts.map +1 -1
  24. package/dist/decompiler/extract.js +3 -2
  25. package/dist/decompiler/extract.js.map +1 -1
  26. package/dist/decompiler/format.js +1 -1
  27. package/dist/decompiler/format.js.map +1 -1
  28. package/dist/desmos.d.ts +4 -0
  29. package/dist/desmos.d.ts.map +1 -1
  30. package/dist/desmos.js +6 -0
  31. package/dist/desmos.js.map +1 -1
  32. package/dist/errors.d.ts +18 -0
  33. package/dist/errors.d.ts.map +1 -1
  34. package/dist/errors.js +37 -0
  35. package/dist/errors.js.map +1 -1
  36. package/dist/index.d.ts +2 -2
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.internal.d.ts +1 -1
  39. package/dist/index.internal.d.ts.map +1 -1
  40. package/dist/index.internal.js +1 -1
  41. package/dist/index.internal.js.map +1 -1
  42. package/dist/index.js +0 -1
  43. package/dist/index.js.map +1 -1
  44. package/dist/magic/expr/latex.d.ts +1 -2
  45. package/dist/magic/expr/latex.d.ts.map +1 -1
  46. package/dist/magic/expr/latex.js +1 -4
  47. package/dist/magic/expr/latex.js.map +1 -1
  48. package/dist/magic/expr/text.d.ts +1 -1
  49. package/dist/magic/expr/text.d.ts.map +1 -1
  50. package/dist/magic/expr/text.js +1 -1
  51. package/dist/magic/expr/text.js.map +1 -1
  52. package/dist/magic/global/dark-mode.d.ts.map +1 -1
  53. package/dist/magic/global/dark-mode.js +1 -3
  54. package/dist/magic/global/dark-mode.js.map +1 -1
  55. package/dist/magic/global/desmos.d.ts +4 -1
  56. package/dist/magic/global/desmos.d.ts.map +1 -1
  57. package/dist/magic/global/desmos.js +14 -1
  58. package/dist/magic/global/desmos.js.map +1 -1
  59. package/dist/magic/global/viewport.d.ts +3 -0
  60. package/dist/magic/global/viewport.d.ts.map +1 -1
  61. package/dist/magic/global/viewport.js +23 -0
  62. package/dist/magic/global/viewport.js.map +1 -1
  63. package/dist/magic/incantation.d.ts +13 -8
  64. package/dist/magic/incantation.d.ts.map +1 -1
  65. package/dist/magic/incantation.js +23 -3
  66. package/dist/magic/incantation.js.map +1 -1
  67. package/dist/magic/local/anim.d.ts.map +1 -1
  68. package/dist/magic/local/anim.js +0 -1
  69. package/dist/magic/local/anim.js.map +1 -1
  70. package/dist/magic/local/colour.d.ts +3 -2
  71. package/dist/magic/local/colour.d.ts.map +1 -1
  72. package/dist/magic/local/colour.js +3 -3
  73. package/dist/magic/local/colour.js.map +1 -1
  74. package/dist/magic/local/label.d.ts +4 -3
  75. package/dist/magic/local/label.d.ts.map +1 -1
  76. package/dist/magic/local/label.js +37 -16
  77. package/dist/magic/local/label.js.map +1 -1
  78. package/dist/magic/local/line.d.ts +3 -0
  79. package/dist/magic/local/line.d.ts.map +1 -1
  80. package/dist/magic/local/line.js +10 -0
  81. package/dist/magic/local/line.js.map +1 -1
  82. package/dist/magic/local/slider.d.ts +2 -2
  83. package/dist/magic/local/slider.d.ts.map +1 -1
  84. package/dist/magic/local/slider.js +0 -1
  85. package/dist/magic/local/slider.js.map +1 -1
  86. package/dist/{compiler/options.d.ts → options.d.ts} +49 -22
  87. package/dist/options.d.ts.map +1 -0
  88. package/dist/{compiler/options.js → options.js} +6 -1
  89. package/dist/options.js.map +1 -0
  90. package/dist/parser/desmost-parser.d.ts +1 -1
  91. package/dist/parser/desmost-parser.d.ts.map +1 -1
  92. package/dist/parser/desmost-parser.js +9 -5
  93. package/dist/parser/desmost-parser.js.map +1 -1
  94. package/dist/parser/generic-parser.d.ts +1 -5
  95. package/dist/parser/generic-parser.d.ts.map +1 -1
  96. package/dist/parser/generic-parser.js +7 -10
  97. package/dist/parser/generic-parser.js.map +1 -1
  98. package/dist/utils.d.ts +1 -0
  99. package/dist/utils.d.ts.map +1 -1
  100. package/dist/utils.js +1 -0
  101. package/dist/utils.js.map +1 -1
  102. package/package.json +9 -6
  103. package/src/compiler/compile.ts +8 -4
  104. package/src/compiler/evaluate.ts +22 -13
  105. package/src/compiler/format.ts +16 -3
  106. package/src/compiler/index.ts +0 -1
  107. package/src/decompiler/decompile.ts +18 -2
  108. package/src/decompiler/emit.ts +15 -6
  109. package/src/decompiler/extract.ts +3 -2
  110. package/src/decompiler/format.ts +1 -1
  111. package/src/decompiler/readme.md +9 -1
  112. package/src/desmos.ts +9 -0
  113. package/src/errors.ts +29 -8
  114. package/src/index.internal.ts +1 -1
  115. package/src/index.ts +2 -2
  116. package/src/magic/.template.ts +25 -16
  117. package/src/magic/expr/latex.ts +2 -7
  118. package/src/magic/expr/text.ts +2 -2
  119. package/src/magic/global/dark-mode.ts +1 -3
  120. package/src/magic/global/desmos.ts +23 -1
  121. package/src/magic/global/viewport.ts +40 -1
  122. package/src/magic/incantation.ts +59 -22
  123. package/src/magic/local/anim.ts +0 -1
  124. package/src/magic/local/colour.ts +4 -3
  125. package/src/magic/local/label.ts +52 -22
  126. package/src/magic/local/line.ts +20 -3
  127. package/src/magic/local/slider.ts +2 -3
  128. package/src/{compiler/options.ts → options.ts} +61 -14
  129. package/src/parser/desmost-parser.ts +12 -6
  130. package/src/parser/generic-parser.ts +12 -12
  131. package/src/parser/readme.md +2 -0
  132. package/src/utils.ts +3 -0
  133. package/svelte/desmost.svelte +5 -3
  134. package/dist/compiler/options.d.ts.map +0 -1
  135. package/dist/compiler/options.js.map +0 -1
@@ -1,6 +1,6 @@
1
- import { type DesmostOptions, fill_defaults } from "./options";
2
1
  import { evaluate_global_incantation, evaluate_expr, evaluate_error } from "./evaluate";
3
2
 
3
+ import { type DesmostOptions, fill_defaults } from "../options";
4
4
  import { DesmostParser, Ast } from "../parser";
5
5
 
6
6
 
@@ -37,8 +37,12 @@ export function compile(
37
37
  /** The Desmost source code to compile. */
38
38
  source: string,
39
39
 
40
- /** Compilation options. */
41
- options?: DesmostOptions,
40
+ /**
41
+ * Compilation options.
42
+ *
43
+ * See {@linkcode DesmostOptions} for the options available.
44
+ */
45
+ options?: Partial<DesmostOptions>,
42
46
  ): void | DesmostDebug
43
47
  {
44
48
  if (desmos == undefined) {
@@ -95,7 +99,7 @@ export function compile(
95
99
  break;
96
100
 
97
101
  case Ast.Kind.EXPRESSION:
98
- // @ts-expect-error: outdated types
102
+ // @ts-expect-error: check type-narrows
99
103
  if (r.data.latex === " ") {
100
104
  if (opts.ignore_all_blanks) break;
101
105
  if (!seen_non_blank && !opts.keep_leading_blanks) break;
@@ -1,8 +1,11 @@
1
+ import dedent from "dedent";
2
+
1
3
  import { format_error, normalise_latex, prettify_latex } from "./format";
2
- import type { DesmostOptions } from "./options";
4
+ import type { DesmostOptions } from "../options";
3
5
 
4
6
  import { Ast } from "../parser";
5
7
  import { UnrecoverableError, type Unrecoverable } from "../errors";
8
+ import { is_latex } from "../desmos";
6
9
 
7
10
 
8
11
  /**
@@ -11,14 +14,14 @@ import { UnrecoverableError, type Unrecoverable } from "../errors";
11
14
  export function evaluate_global_incantation(
12
15
  invocation: Ast.IncantationInvocation,
13
16
  desmos: Desmos.Calculator,
14
- options: Required<DesmostOptions>,
17
+ options: DesmostOptions,
15
18
  ): Unrecoverable<void | string>
16
19
  {
17
20
  let data = undefined;
18
21
 
19
22
  if ("arg_raw" in invocation && invocation.arg_raw != undefined) {
20
23
  try {
21
- data = invocation.incantation.evaluate_arg(invocation.arg_raw);
24
+ data = invocation.incantation.evaluate_arg(invocation.arg_raw, options);
22
25
  }
23
26
  catch (e) {
24
27
  let msg = evaluate_error(e as Error, desmos, options);
@@ -31,12 +34,12 @@ export function evaluate_global_incantation(
31
34
 
32
35
 
33
36
  /**
34
- * Evaluate arguments (if any) to local incantation invocations on `expr`, then add `expr` to `desmos`.
37
+ * Evaluate local incantation invocations on `expr`, then add `expr` to `desmos`.
35
38
  */
36
39
  export function evaluate_expr(
37
40
  expr: Ast.Expression,
38
41
  desmos: Desmos.Calculator,
39
- options: Required<DesmostOptions>,
42
+ options: DesmostOptions,
40
43
  ): Unrecoverable<void | string>
41
44
  {
42
45
  let errors: string[] = [];
@@ -54,31 +57,37 @@ export function evaluate_expr(
54
57
 
55
58
  if ("arg_raw" in invocation && invocation.arg_raw != undefined) {
56
59
  try {
57
- data = invocation.incantation.evaluate_arg(invocation.arg_raw);
60
+ data = invocation.incantation.evaluate_arg(invocation.arg_raw, options);
58
61
  }
59
62
  catch (e) {
60
63
  errors.push(format_error(e as Error, options));
64
+ // if it needs the arg, then we can't press on
61
65
  if (invocation.incantation.requires_arg) continue;
62
66
  }
63
67
  }
64
68
 
65
- invocation.incantation.apply(expr.data, data);
69
+ try {
70
+ invocation.incantation.apply(expr.data, data);
71
+ }
72
+ catch (e) {
73
+ errors.push(format_error(e as Error, options));
74
+ }
66
75
  }
67
76
 
68
77
  if (errors.length > 0) {
69
78
  desmos.setExpression({ type: "text", text: errors.join("\n\n") });
70
79
  }
71
80
 
72
- // @ts-expect-error: outdated types
73
- if (expr.data.latex != undefined) {
74
- // @ts-expect-error: outdated types
75
- expr.data.latex = normalise_latex(expr.data.latex);
81
+ if (is_latex(expr.data)) {
82
+ expr.data.latex = normalise_latex(expr.data.latex!);
76
83
 
77
84
  if (options.prettify) {
78
- // @ts-expect-error: outdated types
79
85
  expr.data.latex = prettify_latex(expr.data.latex);
80
86
  }
81
87
  }
88
+ else if (expr.data.type === "text" && options.dedent_text) {
89
+ expr.data.text = dedent(expr.data.text ?? "");
90
+ }
82
91
 
83
92
  desmos.setExpression(expr.data);
84
93
  }
@@ -92,7 +101,7 @@ export function evaluate_expr(
92
101
  export function evaluate_error(
93
102
  error: UnrecoverableError,
94
103
  desmos: Desmos.Calculator,
95
- options: Required<DesmostOptions>,
104
+ options: DesmostOptions,
96
105
  ): Unrecoverable<void | string>
97
106
  {
98
107
  switch (options.errors) {
@@ -1,18 +1,29 @@
1
- import type { DesmostOptions } from "./options";
1
+ import type { DesmostOptions } from "../options";
2
2
 
3
3
  import { UnrecoverableError } from "../errors";
4
+ import { LINE } from "../utils";
4
5
 
5
6
 
6
7
  export function format_error(
7
8
  e: UnrecoverableError,
8
- options: Required<DesmostOptions>,
9
+ options: DesmostOptions,
9
10
  ): string
10
11
  {
11
12
  let prefix = options.error_prefix;
12
13
  let sep = (prefix === "" || prefix.endsWith("\n")) ? "" : " ";
13
14
 
14
- return `${prefix}${sep}${e.name}: ${e.message}`;
15
15
  // TODO maybe include stack? (configurable?)
16
+ let display = `${prefix}${sep}${e.name}: ${e.message}`;
17
+
18
+ if (e.info != undefined && options.expand_errors) {
19
+ display += `\n${LINE}`;
20
+
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}\``;
24
+ }
25
+
26
+ return display;
16
27
  }
17
28
 
18
29
 
@@ -37,7 +48,9 @@ export function normalise_latex(latex: string): string
37
48
  export function prettify_latex(latex: string): string
38
49
  {
39
50
  latex = latex.replaceAll(/(?<!\\left)(\(|\[|\\\{)/g, "\\left$1");
51
+ latex = latex.replaceAll(/(?:\\left\s?)+/g, "\\left");
40
52
  latex = latex.replaceAll(/(?<!\\right)(\)|\]|\\\})/g, "\\right$1");
53
+ latex = latex.replaceAll(/(?:\\right\s?)+/g, "\\right");
41
54
  latex = latex.replaceAll(/(:|,)(?!\\ ) */g, "$1\\ ");
42
55
 
43
56
  latex = latex.replaceAll(
@@ -1,2 +1 @@
1
1
  export { compile } from "./compile";
2
- export type { DesmostOptions } from "./options";
@@ -2,6 +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
6
 
6
7
 
7
8
  export interface SemiStructuredAst
@@ -16,9 +17,24 @@ export interface SemiStructuredAst
16
17
  *
17
18
  * The caller must also supply a `blank` calculator instance. This is used as a reference for default field values, so that emitted incantations like `/desmos{}` don't get filled with a huge amount of noise.
18
19
  */
19
- export function decompile(desmos: Desmos.Calculator, blank: Desmos.Calculator): string
20
+ export function decompile(
21
+ /** The Desmos calculator instance to decompile. */
22
+ desmos: Desmos.Calculator,
23
+
24
+ /**
25
+ * The default blank Desmos calculator instance to use as a baseline.
26
+ *
27
+ * It is **highly recommended** to include this, otherwise your decompiled source will have a bunch of unnecessary incantations!
28
+ *
29
+ * This is used in deciding what settings are noise that can be omitted. For instance, most of your calculator settings are probably set to their default values, so there's no need to include *every single one of them* in `/desmos{}`.
30
+ */
31
+ blank?: Desmos.Calculator,
32
+
33
+ // TODO doc
34
+ options?: Partial<DesmostOptions>,
35
+ ): string
20
36
  {
21
- return ast_to_source(desmos_to_ast(desmos, blank));
37
+ return ast_to_source(desmos_to_ast(desmos, blank ?? desmos));
22
38
  }
23
39
 
24
40
  /**
@@ -31,15 +31,24 @@ export function emit_expression(expr?: Ast.Expression): string
31
31
 
32
32
  switch (expr.data.type) {
33
33
  case "text":
34
- content = `/text{ ${expr.data.text} }`;
34
+ expr.data.text ??= "";
35
+
36
+ if (expr.data.text.includes("\n")) {
37
+ content = `/text{\n${expr.data.text}\n}`;
38
+ } else {
39
+ content = `% ${expr.data.text}`;
40
+ }
35
41
  break;
42
+
43
+ case "table":
44
+ content = "[tables are currently unsupported]";
45
+ break;
46
+
36
47
  default:
37
- // @ts-expect-error: outdated types
38
- if (expr.data.latex?.trim() === "") {
39
- content = "";
48
+ if (expr.data.latex?.trim() !== "") {
49
+ content = expr.data.latex ?? "[unsupported]";
40
50
  } else {
41
- // @ts-expect-error: outdated types
42
- content = expr.data.latex ?? "?";
51
+ content = "";
43
52
  }
44
53
  }
45
54
 
@@ -1,6 +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
5
 
5
6
  import { DesmosIncantation, ViewportIncantation } from "../magic/global";
6
7
 
@@ -69,7 +70,7 @@ export function extract_expression(expression: Desmos.ExpressionState): Ast.Expr
69
70
  {
70
71
  let data;
71
72
 
72
- // @ts-expect-error: outdated types
73
+ // @ts-expect-error: check type-narrows
73
74
  if (expression.latex?.trim() === "") {
74
75
  data = { latex: ` ` };
75
76
  } else {
@@ -79,7 +80,7 @@ export function extract_expression(expression: Desmos.ExpressionState): Ast.Expr
79
80
  );
80
81
  }
81
82
 
82
- if ("latex" in data) {
83
+ if (is_latex(expression)) {
83
84
  data.latex = prettify_source(data.latex);
84
85
  }
85
86
 
@@ -3,7 +3,7 @@ export const stringify_json5: (typeof JSON.stringify) = (
3
3
  ) =>
4
4
  {
5
5
  return (
6
- /// @ts-expect-error: random `null`??
6
+ // @ts-expect-error: random `null`??
7
7
  JSON.stringify(data, replacer, space)
8
8
  .slice(1, -1) // strip surrounding {}
9
9
  .trim()
@@ -2,7 +2,15 @@
2
2
 
3
3
  This module implements the Desmos -> Desmost decompiler.
4
4
 
5
- This decompiles a `Desmos.Calculator` instance into the *minimal* raw Desmost source code that could *completely* reproduce the calculator state. Decompilation happens through several lowering stages:
5
+ This decompiles a `Desmos.Calculator` instance into the *minimal* raw Desmost source code that could *completely* reproduce the calculator state. In other words, we want this to hold:[^equality]
6
+
7
+ [^equality]: Using value equality of ‘calculator state’.
8
+
9
+ ```ts
10
+ compile(decompile(calc)) == calc
11
+ ```
12
+
13
+ Decompilation happens through several lowering stages:
6
14
 
7
15
  ```hs
8
16
  Desmos.Calculator -> object[] -> Ast[] -> string
package/src/desmos.ts CHANGED
@@ -11,3 +11,12 @@ export enum DesmosColour
11
11
  ORANGE = "#fa7e19",
12
12
  BLACK = "#000000",
13
13
  }
14
+
15
+
16
+ /**
17
+ * Is `expr` a Desmos LaTeX expression (as opposed to a note or table)?
18
+ */
19
+ export function is_latex(expr: Desmos.ExpressionState): expr is Desmos.Expression
20
+ {
21
+ return (expr.type === undefined || expr.type === "expression");
22
+ }
package/src/errors.ts CHANGED
@@ -10,28 +10,49 @@ export type NoMatch = typeof NO_MATCH & { readonly __brand?: unique symbol };
10
10
  *
11
11
  * 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
12
  */
13
- export class UnrecoverableError extends Error {}
13
+ export class UnrecoverableError extends Error
14
+ {
15
+ info?: UnrecoverableError.Info;
16
+ }
14
17
 
15
18
  export namespace UnrecoverableError
16
19
  {
20
+ export interface Info
21
+ {
22
+ hint?: string,
23
+ note?: string,
24
+ flagged_by?: string,
25
+ }
26
+
17
27
  /** The parser unexpectedly reached the end of its entire input source code. */
18
- export class UnexpectedEnd extends UnrecoverableError {}
28
+ export class UnexpectedEnd extends UnrecoverableError {
29
+ constructor(msg: string, override info?: Info) { super(msg); this.name = "Unexpected End"; }
30
+ }
19
31
 
20
32
  /** The parser did not receive input that it expected. */
21
- export class MissingInput extends UnrecoverableError {}
33
+ export class MissingInput extends UnrecoverableError {
34
+ constructor(msg: string, override info?: Info) { super(msg); this.name = "Missing Input"; }
35
+ }
22
36
 
23
37
  /** The parser received input that did not match what it expected. */
24
- export class UnexpectedInput extends UnrecoverableError {}
38
+ export class UnexpectedInput extends UnrecoverableError {
39
+ constructor(msg: string, override info?: Info) { super(msg); this.name = "Unexpected Input"; }
40
+ }
25
41
 
26
42
  /** The parser received excess input that it did not expect. */
27
- export class ExcessInput extends UnrecoverableError {}
28
-
43
+ export class ExcessInput extends UnrecoverableError {
44
+ constructor(msg: string, override info?: Info) { super(msg); this.name = "Excess Input"; }
45
+ }
29
46
 
30
47
  /** An incantation can't be applied here. */
31
- export class IllegalIncantation extends UnrecoverableError {}
48
+ export class IllegalIncantation extends UnrecoverableError {
49
+ constructor(msg: string, override info?: Info) { super(msg); this.name = "Illegal Incantation"; }
50
+ }
32
51
 
33
52
  /** An incantation's argument couldn't be parsed. */
34
- export class InvalidArgument extends UnrecoverableError {}
53
+ export class InvalidArgument extends UnrecoverableError {
54
+ constructor(msg: string, override info?: Info) { super(msg); this.name = "Invalid Argument"; }
55
+ }
35
56
  }
36
57
 
37
58
 
@@ -1,4 +1,4 @@
1
- export { fill_defaults, DEFAULT_OPTIONS } from "./compiler/options";
1
+ export { DEFAULT_OPTIONS } from "./options";
2
2
  export type { DesmostDebug } from "./compiler/compile";
3
3
 
4
4
  export { Ast } from "./parser";
package/src/index.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { compile, type DesmostOptions } from "./compiler";
2
- export { decompile } from "./decompiler";
1
+ export { compile } from "./compiler";
2
+ export type { DesmostOptions } from "./options";
@@ -1,22 +1,31 @@
1
1
  // import { Incantation, ArgIncantation, type GLOBAL, type LOCAL, type EXPR } from "../incantation";
2
2
 
3
+ // import type { DesmostOptions } from "../../options";
4
+ // import { UnrecoverableError, type Unrecoverable } from "../../errors";
5
+
3
6
 
4
7
  // export class ?Incantation extends ArgIncantation<?>
5
8
  // {
6
- // override readonly description
7
- // = ""
8
-
9
- // override readonly identifier = ?
10
- // override readonly requires_arg = ?
11
- // override readonly arg_type = Incantation.ArgType.?
12
-
13
- // override apply(target: ?, data: ?)
14
- // {
15
- // // TODO
16
- // }
17
-
18
- // override evaluate_arg(raw: ?): ?
19
- // {
20
- // // TODO
21
- // }
9
+ // override readonly description
10
+ // = ""
11
+
12
+ // override readonly identifier = ?
13
+ // override readonly requires_arg = ?
14
+ // override readonly arg_type = Incantation.ArgType.?
15
+
16
+ // override apply(target: ?, data: ?)
17
+ // {
18
+ // // TODO
19
+ // }
20
+
21
+ // override evaluate_arg(raw: ?, options: DesmostOptions): Unrecoverable<?>
22
+ // {
23
+ // let out = super.evaluate_arg(raw, options) as ?;
24
+
25
+ // if (options.check_args) {
26
+ // // TODO
27
+ // }
28
+
29
+ // return out;
30
+ // }
22
31
  // }
@@ -10,15 +10,10 @@ export class LatexIncantation extends ArgIncantation<EXPR>
10
10
  override readonly requires_arg = true
11
11
  override readonly arg_type = Incantation.ArgType.LATEX
12
12
 
13
- apply(target: Desmos.ExpressionState, data: string): void
13
+ apply(target: Desmos.ExpressionState, data: string): asserts target is Desmos.Expression
14
14
  {
15
15
  target.type = "expression";
16
- // @ts-expect-error: outdated types
16
+ // @ts-expect-error: previous assignment type-narrows
17
17
  target.latex = data === "" ? " " : data;
18
18
  }
19
-
20
- override evaluate_arg(data: string): string
21
- {
22
- return data;
23
- }
24
19
  }
@@ -11,10 +11,10 @@ export class TextIncantation extends ArgIncantation<EXPR>
11
11
  override readonly requires_arg = true
12
12
  override readonly arg_type = Incantation.ArgType.STRING
13
13
 
14
- override apply(target: Desmos.ExpressionState, data: string)
14
+ override apply(target: Desmos.ExpressionState, data: string): asserts target is Desmos.Note
15
15
  {
16
16
  target.type = "text";
17
- // @ts-expect-error: outdated types
17
+ // @ts-expect-error: previous assignment type-narrows
18
18
  target.text = data;
19
19
  }
20
20
  }
@@ -10,8 +10,6 @@ export class DarkModeIncantation extends Incantation<GLOBAL>
10
10
 
11
11
  apply(target: Desmos.Calculator)
12
12
  {
13
- target.updateSettings({
14
- invertedColors: true,
15
- });
13
+ target.updateSettings({ invertedColors: true });
16
14
  }
17
15
  }
@@ -1,5 +1,8 @@
1
1
  import { Incantation, ArgIncantation, type GLOBAL } from "../incantation";
2
2
 
3
+ import { UnrecoverableError, type Unrecoverable } from "../../errors";
4
+ import type { DesmostOptions } from "../../options";
5
+
3
6
 
4
7
  type DesmosSettings = Desmos.GraphConfiguration & Desmos.GraphSettings
5
8
 
@@ -7,7 +10,7 @@ type DesmosSettings = Desmos.GraphConfiguration & Desmos.GraphSettings
7
10
  export class DesmosIncantation extends ArgIncantation<GLOBAL>
8
11
  {
9
12
  override readonly description
10
- = "Set the configuration of the calculator via `Calculator.updateSettings()`."
13
+ = "Tweak calculator settings via `Calculator.updateSettings()`."
11
14
 
12
15
  override readonly identifier = "desmos"
13
16
  override readonly requires_arg = false
@@ -22,4 +25,23 @@ export class DesmosIncantation extends ArgIncantation<GLOBAL>
22
25
  target.updateSettings(data);
23
26
  }
24
27
  }
28
+
29
+ override evaluate_arg(raw: string, options: DesmostOptions): Unrecoverable<DesmosSettings>
30
+ {
31
+ let out = super.evaluate_arg(raw, options) as DesmosSettings;
32
+
33
+ if (options.check_args) {
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
+ );
42
+ }
43
+ }
44
+
45
+ return out;
46
+ }
25
47
  }
@@ -1,5 +1,8 @@
1
1
  import { Incantation, ArgIncantation, type GLOBAL } from "../incantation";
2
2
 
3
+ import { UnrecoverableError, type Unrecoverable } from "../../errors";
4
+ import type { DesmostOptions } from "../../options";
5
+
3
6
 
4
7
  interface ViewportBounds
5
8
  {
@@ -7,6 +10,8 @@ interface ViewportBounds
7
10
  bottom?: number; top?: number;
8
11
  }
9
12
 
13
+ const VALID_KEYS = ["left", "right", "bottom", "top"];
14
+
10
15
 
11
16
  export class ViewportIncantation extends ArgIncantation<GLOBAL>
12
17
  {
@@ -17,7 +22,7 @@ export class ViewportIncantation extends ArgIncantation<GLOBAL>
17
22
  override readonly requires_arg = true
18
23
  override readonly arg_type = Incantation.ArgType.OBJECT
19
24
 
20
- apply(target: Desmos.Calculator, data: ViewportBounds)
25
+ override apply(target: Desmos.Calculator, data: ViewportBounds)
21
26
  {
22
27
  let existing = target.graphpaperBounds.mathCoordinates;
23
28
 
@@ -30,4 +35,38 @@ export class ViewportIncantation extends ArgIncantation<GLOBAL>
30
35
 
31
36
  target.setMathBounds({ left, right, bottom, top });
32
37
  }
38
+
39
+ override evaluate_arg(raw: string, options: DesmostOptions): Unrecoverable<ViewportBounds>
40
+ {
41
+ let out = super.evaluate_arg(raw, options) as ViewportBounds;
42
+
43
+ if (options.check_args) {
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
+ );
53
+ }
54
+
55
+ let invalid_keys = Object.keys(out).filter(key => !VALID_KEYS.includes(key));
56
+
57
+ if (invalid_keys.length > 0) {
58
+ let plural = invalid_keys.length > 1 ? "fields" : "field";
59
+
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
+ );
67
+ }
68
+ }
69
+
70
+ return out;
71
+ }
33
72
  }