@1771technologies/lytenyte-pro 2.0.4-dev.3 → 2.1.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.
Files changed (180) hide show
  1. package/README.md +54 -11
  2. package/css/components/expression-editor.css +106 -0
  3. package/dist/components/column-manager/column-manager.d.ts +6 -1
  4. package/dist/components/column-manager/column-manager.js +23 -2
  5. package/dist/components/expressions-editor/create-completion-provider.d.ts +10 -0
  6. package/dist/components/expressions-editor/create-completion-provider.js +391 -0
  7. package/dist/components/expressions-editor/create-key-down-handler.d.ts +18 -0
  8. package/dist/components/expressions-editor/create-key-down-handler.js +49 -0
  9. package/dist/components/expressions-editor/cursor-position/measure-cursor-at-pointer.d.ts +2 -0
  10. package/dist/components/expressions-editor/cursor-position/measure-cursor-at-pointer.js +56 -0
  11. package/dist/components/expressions-editor/cursor-position/use-cursor-position.d.ts +5 -0
  12. package/dist/components/expressions-editor/cursor-position/use-cursor-position.js +33 -0
  13. package/dist/components/expressions-editor/expression-editor.d.ts +3 -0
  14. package/dist/components/expressions-editor/expression-editor.js +158 -0
  15. package/dist/components/expressions-editor/get-indentation.d.ts +1 -0
  16. package/dist/components/expressions-editor/get-indentation.js +7 -0
  17. package/dist/components/expressions-editor/handle-completion-key-down.d.ts +2 -0
  18. package/dist/components/expressions-editor/handle-completion-key-down.js +22 -0
  19. package/dist/components/expressions-editor/handle-enter-key.d.ts +4 -0
  20. package/dist/components/expressions-editor/handle-enter-key.js +11 -0
  21. package/dist/components/expressions-editor/handle-manual-trigger.d.ts +2 -0
  22. package/dist/components/expressions-editor/handle-manual-trigger.js +3 -0
  23. package/dist/components/expressions-editor/index.d.ts +18 -0
  24. package/dist/components/expressions-editor/index.js +11 -0
  25. package/dist/components/expressions-editor/intellisence/completion-context.d.ts +17 -0
  26. package/dist/components/expressions-editor/intellisence/completion-context.js +7 -0
  27. package/dist/components/expressions-editor/intellisence/completion-item.d.ts +5 -0
  28. package/dist/components/expressions-editor/intellisence/completion-item.js +11 -0
  29. package/dist/components/expressions-editor/intellisence/completion-list.d.ts +8 -0
  30. package/dist/components/expressions-editor/intellisence/completion-list.js +18 -0
  31. package/dist/components/expressions-editor/intellisence/completion-popover.d.ts +1 -0
  32. package/dist/components/expressions-editor/intellisence/completion-popover.js +47 -0
  33. package/dist/components/expressions-editor/intellisence/filter-completions-by-prefix.d.ts +2 -0
  34. package/dist/components/expressions-editor/intellisence/filter-completions-by-prefix.js +6 -0
  35. package/dist/components/expressions-editor/intellisence/get-word-at-cursor.d.ts +2 -0
  36. package/dist/components/expressions-editor/intellisence/get-word-at-cursor.js +12 -0
  37. package/dist/components/expressions-editor/intellisence/use-complete-trigger.d.ts +13 -0
  38. package/dist/components/expressions-editor/intellisence/use-complete-trigger.js +41 -0
  39. package/dist/components/expressions-editor/intellisence/use-completions.d.ts +14 -0
  40. package/dist/components/expressions-editor/intellisence/use-completions.js +78 -0
  41. package/dist/components/expressions-editor/replace-word-at-cursor.d.ts +5 -0
  42. package/dist/components/expressions-editor/replace-word-at-cursor.js +7 -0
  43. package/dist/components/expressions-editor/token-highlighter-default.d.ts +4 -0
  44. package/dist/components/expressions-editor/token-highlighter-default.js +20 -0
  45. package/dist/components/expressions-editor/types.d.ts +48 -0
  46. package/dist/components/expressions-editor/types.js +1 -0
  47. package/dist/components/expressions-editor/use-keyboard-navigation.d.ts +10 -0
  48. package/dist/components/expressions-editor/use-keyboard-navigation.js +37 -0
  49. package/dist/components/pill-manager/container.js +55 -9
  50. package/dist/components/pill-manager/item.js +15 -1
  51. package/dist/components/select-all.js +1 -1
  52. package/dist/components/tree-view/root.d.ts +9 -0
  53. package/dist/components/tree-view/root.js +4 -2
  54. package/dist/data-source-client/hooks/use-flattened-groups.js +2 -2
  55. package/dist/expressions/compiler/compile.d.ts +3 -0
  56. package/dist/expressions/compiler/compile.js +4 -0
  57. package/dist/expressions/compiler/eval-constant.d.ts +2 -0
  58. package/dist/expressions/compiler/eval-constant.js +19 -0
  59. package/dist/expressions/compiler/is-constant.d.ts +2 -0
  60. package/dist/expressions/compiler/is-constant.js +6 -0
  61. package/dist/expressions/compiler/make-constant.d.ts +2 -0
  62. package/dist/expressions/compiler/make-constant.js +11 -0
  63. package/dist/expressions/compiler/optimize.d.ts +3 -0
  64. package/dist/expressions/compiler/optimize.js +47 -0
  65. package/dist/expressions/compiler/value-of.d.ts +2 -0
  66. package/dist/expressions/compiler/value-of.js +11 -0
  67. package/dist/expressions/errors/clamp-offset.d.ts +1 -0
  68. package/dist/expressions/errors/clamp-offset.js +3 -0
  69. package/dist/expressions/errors/expression-error.d.ts +10 -0
  70. package/dist/expressions/errors/expression-error.js +19 -0
  71. package/dist/expressions/errors/format-error.d.ts +3 -0
  72. package/dist/expressions/errors/format-error.js +27 -0
  73. package/dist/expressions/errors/get-line-end.d.ts +1 -0
  74. package/dist/expressions/errors/get-line-end.js +8 -0
  75. package/dist/expressions/errors/get-line-start.d.ts +1 -0
  76. package/dist/expressions/errors/get-line-start.js +8 -0
  77. package/dist/expressions/errors/index.d.ts +7 -0
  78. package/dist/expressions/errors/index.js +6 -0
  79. package/dist/expressions/errors/offset-to-position.d.ts +2 -0
  80. package/dist/expressions/errors/offset-to-position.js +17 -0
  81. package/dist/expressions/errors/types.d.ts +10 -0
  82. package/dist/expressions/errors/types.js +1 -0
  83. package/dist/expressions/evaluator/evaluate-binary.d.ts +4 -0
  84. package/dist/expressions/evaluator/evaluate-binary.js +21 -0
  85. package/dist/expressions/evaluator/evaluate-unary.d.ts +2 -0
  86. package/dist/expressions/evaluator/evaluate-unary.js +10 -0
  87. package/dist/expressions/evaluator/evaluate.d.ts +15 -0
  88. package/dist/expressions/evaluator/evaluate.js +62 -0
  89. package/dist/expressions/index.d.ts +7 -0
  90. package/dist/expressions/index.js +5 -0
  91. package/dist/expressions/lexer/predicates/is-alpha-numeric.d.ts +1 -0
  92. package/dist/expressions/lexer/predicates/is-alpha-numeric.js +5 -0
  93. package/dist/expressions/lexer/predicates/is-alpha.d.ts +1 -0
  94. package/dist/expressions/lexer/predicates/is-alpha.js +3 -0
  95. package/dist/expressions/lexer/predicates/is-binary-digit.d.ts +1 -0
  96. package/dist/expressions/lexer/predicates/is-binary-digit.js +3 -0
  97. package/dist/expressions/lexer/predicates/is-digit.d.ts +1 -0
  98. package/dist/expressions/lexer/predicates/is-digit.js +3 -0
  99. package/dist/expressions/lexer/predicates/is-hex-digit.d.ts +1 -0
  100. package/dist/expressions/lexer/predicates/is-hex-digit.js +4 -0
  101. package/dist/expressions/lexer/predicates/is-octal-digit.d.ts +1 -0
  102. package/dist/expressions/lexer/predicates/is-octal-digit.js +3 -0
  103. package/dist/expressions/lexer/tokenize/consume-digits.d.ts +1 -0
  104. package/dist/expressions/lexer/tokenize/consume-digits.js +5 -0
  105. package/dist/expressions/lexer/tokenize/scan-identifier.d.ts +6 -0
  106. package/dist/expressions/lexer/tokenize/scan-identifier.js +20 -0
  107. package/dist/expressions/lexer/tokenize/scan-number.d.ts +4 -0
  108. package/dist/expressions/lexer/tokenize/scan-number.js +32 -0
  109. package/dist/expressions/lexer/tokenize/scan-operator.d.ts +6 -0
  110. package/dist/expressions/lexer/tokenize/scan-operator.js +71 -0
  111. package/dist/expressions/lexer/tokenize/scan-string-literal.d.ts +4 -0
  112. package/dist/expressions/lexer/tokenize/scan-string-literal.js +22 -0
  113. package/dist/expressions/lexer/tokenize/scan-string.d.ts +4 -0
  114. package/dist/expressions/lexer/tokenize/scan-string.js +98 -0
  115. package/dist/expressions/lexer/tokenize/scan-template-literal.d.ts +4 -0
  116. package/dist/expressions/lexer/tokenize/scan-template-literal.js +51 -0
  117. package/dist/expressions/lexer/tokenize/tokenize.d.ts +4 -0
  118. package/dist/expressions/lexer/tokenize/tokenize.js +99 -0
  119. package/dist/expressions/lexer/types.d.ts +12 -0
  120. package/dist/expressions/lexer/types.js +1 -0
  121. package/dist/expressions/parser/parse-array-literal.d.ts +3 -0
  122. package/dist/expressions/parser/parse-array-literal.js +15 -0
  123. package/dist/expressions/parser/parse-expression.d.ts +3 -0
  124. package/dist/expressions/parser/parse-expression.js +74 -0
  125. package/dist/expressions/parser/parse-object-literal.d.ts +3 -0
  126. package/dist/expressions/parser/parse-object-literal.js +69 -0
  127. package/dist/expressions/parser/parse-pipe-transform.d.ts +3 -0
  128. package/dist/expressions/parser/parse-pipe-transform.js +26 -0
  129. package/dist/expressions/parser/parse-postfix.d.ts +3 -0
  130. package/dist/expressions/parser/parse-postfix.js +23 -0
  131. package/dist/expressions/parser/parse-primary.d.ts +3 -0
  132. package/dist/expressions/parser/parse-primary.js +35 -0
  133. package/dist/expressions/parser/parse-template-literal.d.ts +3 -0
  134. package/dist/expressions/parser/parse-template-literal.js +41 -0
  135. package/dist/expressions/parser/parse-unary.d.ts +3 -0
  136. package/dist/expressions/parser/parse-unary.js +28 -0
  137. package/dist/expressions/parser/parse.d.ts +3 -0
  138. package/dist/expressions/parser/parse.js +26 -0
  139. package/dist/expressions/parser/parser-context.d.ts +12 -0
  140. package/dist/expressions/parser/parser-context.js +14 -0
  141. package/dist/expressions/parser/precedence.d.ts +1 -0
  142. package/dist/expressions/parser/precedence.js +8 -0
  143. package/dist/expressions/parser/types.d.ts +101 -0
  144. package/dist/expressions/parser/types.js +1 -0
  145. package/dist/expressions/plugin.d.ts +20 -0
  146. package/dist/expressions/plugin.js +1 -0
  147. package/dist/expressions/plugins/access.d.ts +2 -0
  148. package/dist/expressions/plugins/access.js +138 -0
  149. package/dist/expressions/plugins/arrows.d.ts +2 -0
  150. package/dist/expressions/plugins/arrows.js +83 -0
  151. package/dist/expressions/plugins/booleans.d.ts +2 -0
  152. package/dist/expressions/plugins/booleans.js +34 -0
  153. package/dist/expressions/plugins/collections.d.ts +2 -0
  154. package/dist/expressions/plugins/collections.js +70 -0
  155. package/dist/expressions/plugins/comparison.d.ts +2 -0
  156. package/dist/expressions/plugins/comparison.js +51 -0
  157. package/dist/expressions/plugins/date-identifier.d.ts +4 -0
  158. package/dist/expressions/plugins/date-identifier.js +29 -0
  159. package/dist/expressions/plugins/date-literal.d.ts +2 -0
  160. package/dist/expressions/plugins/date-literal.js +71 -0
  161. package/dist/expressions/plugins/logical.d.ts +2 -0
  162. package/dist/expressions/plugins/logical.js +103 -0
  163. package/dist/expressions/plugins/membership.d.ts +2 -0
  164. package/dist/expressions/plugins/membership.js +71 -0
  165. package/dist/expressions/plugins/pipe.d.ts +2 -0
  166. package/dist/expressions/plugins/pipe.js +57 -0
  167. package/dist/expressions/plugins/quoted-identifier.d.ts +2 -0
  168. package/dist/expressions/plugins/quoted-identifier.js +35 -0
  169. package/dist/expressions/plugins/resolved-identifier.d.ts +5 -0
  170. package/dist/expressions/plugins/resolved-identifier.js +19 -0
  171. package/dist/expressions/plugins/standard.d.ts +15 -0
  172. package/dist/expressions/plugins/standard.js +27 -0
  173. package/dist/expressions/plugins/strings.d.ts +2 -0
  174. package/dist/expressions/plugins/strings.js +44 -0
  175. package/dist/expressions/plugins/ternary.d.ts +2 -0
  176. package/dist/expressions/plugins/ternary.js +58 -0
  177. package/dist/expressions.d.ts +2 -0
  178. package/dist/expressions.js +2 -0
  179. package/dist/index.d.ts +1 -0
  180. package/package.json +12 -5
@@ -0,0 +1,51 @@
1
+ import { ExpressionError } from "../../errors/index.js";
2
+ import { scanStringLiteral } from "./scan-string-literal.js";
3
+ export function scanTemplateLiteral(source, start) {
4
+ let raw = "`";
5
+ let i = start + 1;
6
+ while (i < source.length && source[i] !== "`") {
7
+ if (source[i] === "\\" && i + 1 < source.length) {
8
+ raw += source[i] + source[i + 1];
9
+ i += 2;
10
+ continue;
11
+ }
12
+ if (source[i] === "$" && i + 1 < source.length && source[i + 1] === "{") {
13
+ raw += "${";
14
+ i += 2;
15
+ let depth = 1;
16
+ while (i < source.length && depth > 0) {
17
+ const c = source[i];
18
+ if (c === '"' || c === "'") {
19
+ const result = scanStringLiteral(source, i);
20
+ raw += result.raw;
21
+ i = result.end;
22
+ continue;
23
+ }
24
+ if (c === "`") {
25
+ const result = scanTemplateLiteral(source, i);
26
+ raw += result.raw;
27
+ i = result.end;
28
+ continue;
29
+ }
30
+ if (c === "{")
31
+ depth++;
32
+ else if (c === "}")
33
+ depth--;
34
+ if (depth > 0)
35
+ raw += c;
36
+ i++;
37
+ }
38
+ raw += "}";
39
+ }
40
+ else {
41
+ raw += source[i];
42
+ i++;
43
+ }
44
+ }
45
+ if (i >= source.length) {
46
+ throw new ExpressionError("Unterminated template literal", { source, start, end: i });
47
+ }
48
+ raw += "`";
49
+ i++;
50
+ return { raw, end: i };
51
+ }
@@ -0,0 +1,4 @@
1
+ import type { Token } from "../types.js";
2
+ import type { Plugin } from "../../plugin.js";
3
+ export declare function tokenizeSafe(source: string, plugins?: Plugin[], tokensizeWhitespace?: boolean): Token[];
4
+ export declare function tokenize(source: string, plugins?: Plugin[], tokensizeWhitespace?: boolean): Token[];
@@ -0,0 +1,99 @@
1
+ import { isDigit } from "../predicates/is-digit.js";
2
+ import { isAlpha } from "../predicates/is-alpha.js";
3
+ import { scanTemplateLiteral } from "./scan-template-literal.js";
4
+ import { scanNumber } from "./scan-number.js";
5
+ import { scanString } from "./scan-string.js";
6
+ import { scanIdentifier } from "./scan-identifier.js";
7
+ import { scanOperator } from "./scan-operator.js";
8
+ import { ExpressionError } from "../../errors/expression-error.js";
9
+ export function tokenizeSafe(source, plugins, tokensizeWhitespace = false) {
10
+ const tokens = [];
11
+ try {
12
+ let i = 0;
13
+ while (i < source.length) {
14
+ const ch = source[i];
15
+ if (/\s/.test(ch)) {
16
+ if (tokensizeWhitespace) {
17
+ tokens.push({
18
+ value: ch,
19
+ start: i,
20
+ end: i + 1,
21
+ type: "Whitespace",
22
+ });
23
+ }
24
+ i++;
25
+ continue;
26
+ }
27
+ const start = i;
28
+ let result = null;
29
+ if (plugins) {
30
+ for (const plugin of plugins) {
31
+ if (plugin.scan) {
32
+ result = plugin.scan(source, i);
33
+ if (result)
34
+ break;
35
+ }
36
+ }
37
+ }
38
+ if (!result) {
39
+ if (isDigit(ch)) {
40
+ const r = scanNumber(source, i);
41
+ result = { type: "Number", value: r.value, end: r.end };
42
+ }
43
+ else if (ch === '"' || ch === "'") {
44
+ const r = scanString(source, i);
45
+ result = { type: "String", value: r.value, end: r.end };
46
+ }
47
+ else if (ch === "`") {
48
+ const r = scanTemplateLiteral(source, i);
49
+ result = { type: "TemplateLiteral", value: r.raw, end: r.end };
50
+ }
51
+ else if (isAlpha(ch)) {
52
+ result = scanIdentifier(source, i);
53
+ }
54
+ else {
55
+ result = scanOperator(source, i);
56
+ }
57
+ }
58
+ i = result.end;
59
+ tokens.push({ type: result.type, value: result.value, start, end: i });
60
+ }
61
+ tokens.push({ type: "EOF", value: "", start: i, end: i });
62
+ }
63
+ catch (e) {
64
+ if (e instanceof ExpressionError) {
65
+ const error = {
66
+ type: "ExpressionError",
67
+ start: e.start,
68
+ end: e.end,
69
+ value: source.slice(e.start, e.end),
70
+ error: e,
71
+ };
72
+ tokens.push(error);
73
+ const left = source.slice(e.end);
74
+ if (left) {
75
+ const remaining = {
76
+ type: "Unparsed",
77
+ start: e.end,
78
+ end: source.length,
79
+ value: left,
80
+ };
81
+ tokens.push(remaining);
82
+ }
83
+ tokens.push({ type: "EOF", value: "", start: source.length, end: source.length });
84
+ return tokens;
85
+ }
86
+ else {
87
+ throw e;
88
+ }
89
+ }
90
+ return tokens;
91
+ }
92
+ export function tokenize(source, plugins, tokensizeWhitespace) {
93
+ const result = tokenizeSafe(source, plugins, tokensizeWhitespace);
94
+ if (result.at(-2)?.type === "ExpressionError")
95
+ throw result.at(-2).error;
96
+ if (result.at(-3)?.type === "ExpressionError")
97
+ throw result.at(-3).error;
98
+ return result;
99
+ }
@@ -0,0 +1,12 @@
1
+ export type BinaryOperator = "||" | "&&" | "==" | "!=" | "<" | ">" | "<=" | ">=" | "+" | "-" | "*" | "/" | "%" | "**" | "in" | "not";
2
+ export type UnaryOperator = "!" | "-" | "+";
3
+ export type BinaryExpressionOperator = Exclude<BinaryOperator, "not"> | "not in" | "??";
4
+ export type OperatorValue = BinaryOperator | UnaryOperator;
5
+ export type PunctuationValue = "(" | ")" | "[" | "]" | "{" | "}" | "," | "." | ":" | "?";
6
+ export interface Token {
7
+ readonly type: string;
8
+ readonly start: number;
9
+ readonly end: number;
10
+ readonly value: string;
11
+ readonly error?: unknown;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { ASTNode } from "./types.js";
2
+ import type { ParserContext } from "./parser-context.js";
3
+ export declare function parseArrayLiteral(ctx: ParserContext): ASTNode;
@@ -0,0 +1,15 @@
1
+ import { current, expect } from "./parser-context.js";
2
+ import { parseExpression } from "./parse-expression.js";
3
+ export function parseArrayLiteral(ctx) {
4
+ const start = expect(ctx, "Punctuation", "[").start;
5
+ const elements = [];
6
+ while (!(current(ctx).type === "Punctuation" && current(ctx).value === "]")) {
7
+ if (elements.length > 0)
8
+ expect(ctx, "Punctuation", ",");
9
+ if (current(ctx).type === "Punctuation" && current(ctx).value === "]")
10
+ break;
11
+ elements.push(parseExpression(ctx, 0));
12
+ }
13
+ const end = expect(ctx, "Punctuation", "]").end;
14
+ return { type: "ArrayLiteral", elements, start, end };
15
+ }
@@ -0,0 +1,3 @@
1
+ import type { ASTNode } from "./types.js";
2
+ import type { ParserContext } from "./parser-context.js";
3
+ export declare function parseExpression(ctx: ParserContext, minPrec?: number): ASTNode;
@@ -0,0 +1,74 @@
1
+ import { current, advance } from "./parser-context.js";
2
+ import { PRECEDENCE } from "./precedence.js";
3
+ import { parseUnary } from "./parse-unary.js";
4
+ export function parseExpression(ctx, minPrec = 0) {
5
+ let left = parseUnary(ctx);
6
+ while (true) {
7
+ const tok = current(ctx);
8
+ let prec;
9
+ // Core precedence (arithmetic only)
10
+ if (tok.type === "Operator") {
11
+ prec = PRECEDENCE[tok.value];
12
+ }
13
+ // Plugin precedence
14
+ if (prec === undefined && ctx.plugins) {
15
+ for (const plugin of ctx.plugins) {
16
+ if (plugin.infixPrecedence) {
17
+ prec = plugin.infixPrecedence(ctx);
18
+ if (prec !== undefined)
19
+ break;
20
+ }
21
+ }
22
+ }
23
+ if (prec !== undefined && prec >= minPrec) {
24
+ // Try plugin parseInfix first (for ternary, pipe, membership, etc.)
25
+ if (ctx.plugins) {
26
+ let handled = false;
27
+ for (const plugin of ctx.plugins) {
28
+ if (plugin.parseInfix) {
29
+ const node = plugin.parseInfix(ctx, left, minPrec);
30
+ if (node) {
31
+ left = node;
32
+ handled = true;
33
+ break;
34
+ }
35
+ }
36
+ }
37
+ if (handled)
38
+ continue;
39
+ }
40
+ // Default: standard binary expression
41
+ const op = advance(ctx);
42
+ const isRightAssoc = op.value === "**";
43
+ const nextMinPrec = isRightAssoc ? prec : prec + 1;
44
+ const right = parseExpression(ctx, nextMinPrec);
45
+ left = {
46
+ type: "BinaryExpression",
47
+ operator: op.value,
48
+ left,
49
+ right,
50
+ start: left.start,
51
+ end: right.end,
52
+ };
53
+ continue;
54
+ }
55
+ // No precedence — try plugin parseInfix (for things without precedence entry)
56
+ if (prec === undefined && ctx.plugins) {
57
+ let handled = false;
58
+ for (const plugin of ctx.plugins) {
59
+ if (plugin.parseInfix) {
60
+ const node = plugin.parseInfix(ctx, left, minPrec);
61
+ if (node) {
62
+ left = node;
63
+ handled = true;
64
+ break;
65
+ }
66
+ }
67
+ }
68
+ if (handled)
69
+ continue;
70
+ }
71
+ break;
72
+ }
73
+ return left;
74
+ }
@@ -0,0 +1,3 @@
1
+ import type { ASTNode } from "./types.js";
2
+ import type { ParserContext } from "./parser-context.js";
3
+ export declare function parseObjectLiteral(ctx: ParserContext): ASTNode;
@@ -0,0 +1,69 @@
1
+ import { current, advance, expect } from "./parser-context.js";
2
+ import { ExpressionError } from "../errors/expression-error.js";
3
+ import { parseExpression } from "./parse-expression.js";
4
+ export function parseObjectLiteral(ctx) {
5
+ const start = expect(ctx, "Punctuation", "{").start;
6
+ const properties = [];
7
+ while (!(current(ctx).type === "Punctuation" && current(ctx).value === "}")) {
8
+ if (properties.length > 0)
9
+ expect(ctx, "Punctuation", ",");
10
+ if (current(ctx).type === "Punctuation" && current(ctx).value === "}")
11
+ break;
12
+ let key;
13
+ let computed = false;
14
+ if (current(ctx).type === "Punctuation" && current(ctx).value === "[") {
15
+ advance(ctx);
16
+ key = parseExpression(ctx, 0);
17
+ expect(ctx, "Punctuation", "]");
18
+ computed = true;
19
+ }
20
+ else {
21
+ const tok = current(ctx);
22
+ if (tok.type === "Identifier") {
23
+ advance(ctx);
24
+ key = { type: "Identifier", name: tok.value, start: tok.start, end: tok.end };
25
+ const next = current(ctx);
26
+ if (next.type === "Punctuation" && (next.value === "," || next.value === "}")) {
27
+ const value = {
28
+ type: "Identifier",
29
+ name: tok.value,
30
+ start: tok.start,
31
+ end: tok.end,
32
+ };
33
+ properties.push({
34
+ type: "ObjectProperty",
35
+ key,
36
+ value,
37
+ computed: false,
38
+ start: key.start,
39
+ end: value.end,
40
+ });
41
+ continue;
42
+ }
43
+ }
44
+ else if (tok.type === "String") {
45
+ advance(ctx);
46
+ key = { type: "StringLiteral", value: tok.value.slice(1, -1), start: tok.start, end: tok.end };
47
+ }
48
+ else {
49
+ throw new ExpressionError(`Expected property name but got "${tok.value}"`, {
50
+ source: ctx.source,
51
+ start: tok.start,
52
+ end: tok.end,
53
+ });
54
+ }
55
+ }
56
+ expect(ctx, "Punctuation", ":");
57
+ const value = parseExpression(ctx, 0);
58
+ properties.push({
59
+ type: "ObjectProperty",
60
+ key,
61
+ value,
62
+ computed,
63
+ start: key.start,
64
+ end: value.end,
65
+ });
66
+ }
67
+ const end = expect(ctx, "Punctuation", "}").end;
68
+ return { type: "ObjectLiteral", properties, start, end };
69
+ }
@@ -0,0 +1,3 @@
1
+ import type { ASTNode } from "./types.js";
2
+ import type { ParserContext } from "./parser-context.js";
3
+ export declare function parsePipeTransform(ctx: ParserContext): ASTNode;
@@ -0,0 +1,26 @@
1
+ import { current, advance, expect } from "./parser-context.js";
2
+ import { parsePrimary } from "./parse-primary.js";
3
+ import { parseExpression } from "./parse-expression.js";
4
+ export function parsePipeTransform(ctx) {
5
+ let node = parsePrimary(ctx);
6
+ if (current(ctx).type === "Punctuation" && current(ctx).value === "(") {
7
+ advance(ctx);
8
+ const args = [];
9
+ while (!(current(ctx).type === "Punctuation" && current(ctx).value === ")")) {
10
+ if (args.length > 0)
11
+ expect(ctx, "Punctuation", ",");
12
+ if (current(ctx).type === "Punctuation" && current(ctx).value === ")")
13
+ break;
14
+ args.push(parseExpression(ctx, 0));
15
+ }
16
+ const end = expect(ctx, "Punctuation", ")").end;
17
+ node = {
18
+ type: "CallExpression",
19
+ callee: node,
20
+ args,
21
+ start: node.start,
22
+ end,
23
+ };
24
+ }
25
+ return node;
26
+ }
@@ -0,0 +1,3 @@
1
+ import type { ASTNode } from "./types.js";
2
+ import type { ParserContext } from "./parser-context.js";
3
+ export declare function parsePostfix(ctx: ParserContext): ASTNode;
@@ -0,0 +1,23 @@
1
+ import { parsePrimary } from "./parse-primary.js";
2
+ export function parsePostfix(ctx) {
3
+ let node = parsePrimary(ctx);
4
+ while (true) {
5
+ if (ctx.plugins) {
6
+ let handled = false;
7
+ for (const plugin of ctx.plugins) {
8
+ if (plugin.parsePostfix) {
9
+ const result = plugin.parsePostfix(ctx, node);
10
+ if (result) {
11
+ node = result;
12
+ handled = true;
13
+ break;
14
+ }
15
+ }
16
+ }
17
+ if (handled)
18
+ continue;
19
+ }
20
+ break;
21
+ }
22
+ return node;
23
+ }
@@ -0,0 +1,3 @@
1
+ import type { ASTNode } from "./types.js";
2
+ import type { ParserContext } from "./parser-context.js";
3
+ export declare function parsePrimary(ctx: ParserContext): ASTNode;
@@ -0,0 +1,35 @@
1
+ import { current, advance, expect } from "./parser-context.js";
2
+ import { ExpressionError } from "../errors/expression-error.js";
3
+ import { parseExpression } from "./parse-expression.js";
4
+ export function parsePrimary(ctx) {
5
+ // Plugin prefix hooks
6
+ if (ctx.plugins) {
7
+ for (const plugin of ctx.plugins) {
8
+ if (plugin.parsePrefix) {
9
+ const node = plugin.parsePrefix(ctx);
10
+ if (node)
11
+ return node;
12
+ }
13
+ }
14
+ }
15
+ const tok = current(ctx);
16
+ // Core: number literals
17
+ if (tok.type === "Number") {
18
+ advance(ctx);
19
+ const raw = tok.value.replace(/_/g, "");
20
+ return { type: "NumberLiteral", value: Number(raw), start: tok.start, end: tok.end };
21
+ }
22
+ // Core: identifiers
23
+ if (tok.type === "Identifier") {
24
+ advance(ctx);
25
+ return { type: "Identifier", name: tok.value, start: tok.start, end: tok.end };
26
+ }
27
+ // Core: parenthesized expressions (grouping)
28
+ if (tok.type === "Punctuation" && tok.value === "(") {
29
+ advance(ctx);
30
+ const expr = parseExpression(ctx, 0);
31
+ expect(ctx, "Punctuation", ")");
32
+ return expr;
33
+ }
34
+ throw new ExpressionError(`Unexpected ${tok.type === "EOF" ? "end of expression" : `token "${tok.value}"`}`, { source: ctx.source, start: tok.start, end: tok.end });
35
+ }
@@ -0,0 +1,3 @@
1
+ import type { ASTNode } from "./types.js";
2
+ import type { ParserContext } from "./parser-context.js";
3
+ export declare function parseTemplateLiteral(ctx: ParserContext): ASTNode;
@@ -0,0 +1,41 @@
1
+ import { advance } from "./parser-context.js";
2
+ import { parse } from "./parse.js";
3
+ export function parseTemplateLiteral(ctx) {
4
+ const tok = advance(ctx);
5
+ const raw = tok.value.slice(1, -1);
6
+ const parts = [];
7
+ let i = 0;
8
+ let textStart = 0;
9
+ while (i < raw.length) {
10
+ if (raw[i] === "$" && i + 1 < raw.length && raw[i + 1] === "{") {
11
+ if (i > textStart) {
12
+ const text = raw.slice(textStart, i);
13
+ parts.push({ type: "StringLiteral", value: text, start: tok.start, end: tok.end });
14
+ }
15
+ i += 2;
16
+ let depth = 1;
17
+ const exprStart = i;
18
+ while (i < raw.length && depth > 0) {
19
+ if (raw[i] === "{")
20
+ depth++;
21
+ else if (raw[i] === "}")
22
+ depth--;
23
+ if (depth > 0)
24
+ i++;
25
+ }
26
+ const exprSource = raw.slice(exprStart, i);
27
+ i++;
28
+ textStart = i;
29
+ const exprAst = parse(exprSource, ctx.depth + 1, ctx.plugins);
30
+ parts.push(exprAst);
31
+ }
32
+ else {
33
+ i++;
34
+ }
35
+ }
36
+ if (textStart < raw.length) {
37
+ const text = raw.slice(textStart);
38
+ parts.push({ type: "StringLiteral", value: text, start: tok.start, end: tok.end });
39
+ }
40
+ return { type: "TemplateLiteral", parts, start: tok.start, end: tok.end };
41
+ }
@@ -0,0 +1,3 @@
1
+ import type { ASTNode } from "./types.js";
2
+ import type { ParserContext } from "./parser-context.js";
3
+ export declare function parseUnary(ctx: ParserContext): ASTNode;
@@ -0,0 +1,28 @@
1
+ import { current, advance } from "./parser-context.js";
2
+ import { parsePostfix } from "./parse-postfix.js";
3
+ export function parseUnary(ctx) {
4
+ // Plugin unary hooks
5
+ if (ctx.plugins) {
6
+ for (const plugin of ctx.plugins) {
7
+ if (plugin.parseUnary) {
8
+ const node = plugin.parseUnary(ctx, parseUnary);
9
+ if (node)
10
+ return node;
11
+ }
12
+ }
13
+ }
14
+ const tok = current(ctx);
15
+ // Core: arithmetic unary (-, +)
16
+ if (tok.type === "Operator" && (tok.value === "-" || tok.value === "+")) {
17
+ advance(ctx);
18
+ const operand = parseUnary(ctx);
19
+ return {
20
+ type: "UnaryExpression",
21
+ operator: tok.value,
22
+ operand,
23
+ start: tok.start,
24
+ end: operand.end,
25
+ };
26
+ }
27
+ return parsePostfix(ctx);
28
+ }
@@ -0,0 +1,3 @@
1
+ import type { ASTNode } from "./types.js";
2
+ import type { Plugin } from "../plugin.js";
3
+ export declare function parse(source: string, depth?: number, plugins?: Plugin[]): ASTNode;
@@ -0,0 +1,26 @@
1
+ import { current } from "./parser-context.js";
2
+ import { tokenize } from "../lexer/tokenize/tokenize.js";
3
+ import { ExpressionError } from "../errors/expression-error.js";
4
+ import { parseExpression } from "./parse-expression.js";
5
+ const MAX_PARSE_DEPTH = 32;
6
+ export function parse(source, depth = 0, plugins) {
7
+ if (depth > MAX_PARSE_DEPTH) {
8
+ throw new ExpressionError("Maximum template nesting depth exceeded", {
9
+ source,
10
+ start: 0,
11
+ end: source.length,
12
+ });
13
+ }
14
+ const tokens = tokenize(source, plugins);
15
+ const ctx = { tokens, pos: 0, source, depth, plugins };
16
+ const result = parseExpression(ctx, 0);
17
+ if (current(ctx).type !== "EOF") {
18
+ const tok = current(ctx);
19
+ throw new ExpressionError(`Unexpected token "${tok.value}"`, {
20
+ source,
21
+ start: tok.start,
22
+ end: tok.end,
23
+ });
24
+ }
25
+ return result;
26
+ }
@@ -0,0 +1,12 @@
1
+ import type { Token } from "../lexer/types.js";
2
+ import type { Plugin } from "../plugin.js";
3
+ export interface ParserContext {
4
+ tokens: Token[];
5
+ pos: number;
6
+ source: string;
7
+ depth: number;
8
+ plugins?: Plugin[];
9
+ }
10
+ export declare function current(ctx: ParserContext): Token;
11
+ export declare function advance(ctx: ParserContext): Token;
12
+ export declare function expect(ctx: ParserContext, type: string, value?: string): Token;
@@ -0,0 +1,14 @@
1
+ import { ExpressionError } from "../errors/expression-error.js";
2
+ export function current(ctx) {
3
+ return ctx.tokens[ctx.pos];
4
+ }
5
+ export function advance(ctx) {
6
+ return ctx.tokens[ctx.pos++];
7
+ }
8
+ export function expect(ctx, type, value) {
9
+ const tok = current(ctx);
10
+ if (tok.type !== type || (value !== undefined && tok.value !== value)) {
11
+ throw new ExpressionError(`Expected ${value ? `"${value}"` : type} but got ${tok.value ? `"${tok.value}"` : tok.type}`, { source: ctx.source, start: tok.start, end: tok.end });
12
+ }
13
+ return advance(ctx);
14
+ }
@@ -0,0 +1 @@
1
+ export declare const PRECEDENCE: Readonly<Record<string, number | undefined>>;
@@ -0,0 +1,8 @@
1
+ export const PRECEDENCE = {
2
+ "+": 50,
3
+ "-": 50,
4
+ "*": 60,
5
+ "/": 60,
6
+ "%": 60,
7
+ "**": 70,
8
+ };