@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.
- package/README.md +54 -11
- package/css/components/expression-editor.css +106 -0
- package/dist/components/column-manager/column-manager.d.ts +6 -1
- package/dist/components/column-manager/column-manager.js +23 -2
- package/dist/components/expressions-editor/create-completion-provider.d.ts +10 -0
- package/dist/components/expressions-editor/create-completion-provider.js +391 -0
- package/dist/components/expressions-editor/create-key-down-handler.d.ts +18 -0
- package/dist/components/expressions-editor/create-key-down-handler.js +49 -0
- package/dist/components/expressions-editor/cursor-position/measure-cursor-at-pointer.d.ts +2 -0
- package/dist/components/expressions-editor/cursor-position/measure-cursor-at-pointer.js +56 -0
- package/dist/components/expressions-editor/cursor-position/use-cursor-position.d.ts +5 -0
- package/dist/components/expressions-editor/cursor-position/use-cursor-position.js +33 -0
- package/dist/components/expressions-editor/expression-editor.d.ts +3 -0
- package/dist/components/expressions-editor/expression-editor.js +158 -0
- package/dist/components/expressions-editor/get-indentation.d.ts +1 -0
- package/dist/components/expressions-editor/get-indentation.js +7 -0
- package/dist/components/expressions-editor/handle-completion-key-down.d.ts +2 -0
- package/dist/components/expressions-editor/handle-completion-key-down.js +22 -0
- package/dist/components/expressions-editor/handle-enter-key.d.ts +4 -0
- package/dist/components/expressions-editor/handle-enter-key.js +11 -0
- package/dist/components/expressions-editor/handle-manual-trigger.d.ts +2 -0
- package/dist/components/expressions-editor/handle-manual-trigger.js +3 -0
- package/dist/components/expressions-editor/index.d.ts +18 -0
- package/dist/components/expressions-editor/index.js +11 -0
- package/dist/components/expressions-editor/intellisence/completion-context.d.ts +17 -0
- package/dist/components/expressions-editor/intellisence/completion-context.js +7 -0
- package/dist/components/expressions-editor/intellisence/completion-item.d.ts +5 -0
- package/dist/components/expressions-editor/intellisence/completion-item.js +11 -0
- package/dist/components/expressions-editor/intellisence/completion-list.d.ts +8 -0
- package/dist/components/expressions-editor/intellisence/completion-list.js +18 -0
- package/dist/components/expressions-editor/intellisence/completion-popover.d.ts +1 -0
- package/dist/components/expressions-editor/intellisence/completion-popover.js +47 -0
- package/dist/components/expressions-editor/intellisence/filter-completions-by-prefix.d.ts +2 -0
- package/dist/components/expressions-editor/intellisence/filter-completions-by-prefix.js +6 -0
- package/dist/components/expressions-editor/intellisence/get-word-at-cursor.d.ts +2 -0
- package/dist/components/expressions-editor/intellisence/get-word-at-cursor.js +12 -0
- package/dist/components/expressions-editor/intellisence/use-complete-trigger.d.ts +13 -0
- package/dist/components/expressions-editor/intellisence/use-complete-trigger.js +41 -0
- package/dist/components/expressions-editor/intellisence/use-completions.d.ts +14 -0
- package/dist/components/expressions-editor/intellisence/use-completions.js +78 -0
- package/dist/components/expressions-editor/replace-word-at-cursor.d.ts +5 -0
- package/dist/components/expressions-editor/replace-word-at-cursor.js +7 -0
- package/dist/components/expressions-editor/token-highlighter-default.d.ts +4 -0
- package/dist/components/expressions-editor/token-highlighter-default.js +20 -0
- package/dist/components/expressions-editor/types.d.ts +48 -0
- package/dist/components/expressions-editor/types.js +1 -0
- package/dist/components/expressions-editor/use-keyboard-navigation.d.ts +10 -0
- package/dist/components/expressions-editor/use-keyboard-navigation.js +37 -0
- package/dist/components/pill-manager/container.js +55 -9
- package/dist/components/pill-manager/item.js +15 -1
- package/dist/components/select-all.js +1 -1
- package/dist/components/tree-view/root.d.ts +9 -0
- package/dist/components/tree-view/root.js +4 -2
- package/dist/data-source-client/hooks/use-flattened-groups.js +2 -2
- package/dist/expressions/compiler/compile.d.ts +3 -0
- package/dist/expressions/compiler/compile.js +4 -0
- package/dist/expressions/compiler/eval-constant.d.ts +2 -0
- package/dist/expressions/compiler/eval-constant.js +19 -0
- package/dist/expressions/compiler/is-constant.d.ts +2 -0
- package/dist/expressions/compiler/is-constant.js +6 -0
- package/dist/expressions/compiler/make-constant.d.ts +2 -0
- package/dist/expressions/compiler/make-constant.js +11 -0
- package/dist/expressions/compiler/optimize.d.ts +3 -0
- package/dist/expressions/compiler/optimize.js +47 -0
- package/dist/expressions/compiler/value-of.d.ts +2 -0
- package/dist/expressions/compiler/value-of.js +11 -0
- package/dist/expressions/errors/clamp-offset.d.ts +1 -0
- package/dist/expressions/errors/clamp-offset.js +3 -0
- package/dist/expressions/errors/expression-error.d.ts +10 -0
- package/dist/expressions/errors/expression-error.js +19 -0
- package/dist/expressions/errors/format-error.d.ts +3 -0
- package/dist/expressions/errors/format-error.js +27 -0
- package/dist/expressions/errors/get-line-end.d.ts +1 -0
- package/dist/expressions/errors/get-line-end.js +8 -0
- package/dist/expressions/errors/get-line-start.d.ts +1 -0
- package/dist/expressions/errors/get-line-start.js +8 -0
- package/dist/expressions/errors/index.d.ts +7 -0
- package/dist/expressions/errors/index.js +6 -0
- package/dist/expressions/errors/offset-to-position.d.ts +2 -0
- package/dist/expressions/errors/offset-to-position.js +17 -0
- package/dist/expressions/errors/types.d.ts +10 -0
- package/dist/expressions/errors/types.js +1 -0
- package/dist/expressions/evaluator/evaluate-binary.d.ts +4 -0
- package/dist/expressions/evaluator/evaluate-binary.js +21 -0
- package/dist/expressions/evaluator/evaluate-unary.d.ts +2 -0
- package/dist/expressions/evaluator/evaluate-unary.js +10 -0
- package/dist/expressions/evaluator/evaluate.d.ts +15 -0
- package/dist/expressions/evaluator/evaluate.js +62 -0
- package/dist/expressions/index.d.ts +7 -0
- package/dist/expressions/index.js +5 -0
- package/dist/expressions/lexer/predicates/is-alpha-numeric.d.ts +1 -0
- package/dist/expressions/lexer/predicates/is-alpha-numeric.js +5 -0
- package/dist/expressions/lexer/predicates/is-alpha.d.ts +1 -0
- package/dist/expressions/lexer/predicates/is-alpha.js +3 -0
- package/dist/expressions/lexer/predicates/is-binary-digit.d.ts +1 -0
- package/dist/expressions/lexer/predicates/is-binary-digit.js +3 -0
- package/dist/expressions/lexer/predicates/is-digit.d.ts +1 -0
- package/dist/expressions/lexer/predicates/is-digit.js +3 -0
- package/dist/expressions/lexer/predicates/is-hex-digit.d.ts +1 -0
- package/dist/expressions/lexer/predicates/is-hex-digit.js +4 -0
- package/dist/expressions/lexer/predicates/is-octal-digit.d.ts +1 -0
- package/dist/expressions/lexer/predicates/is-octal-digit.js +3 -0
- package/dist/expressions/lexer/tokenize/consume-digits.d.ts +1 -0
- package/dist/expressions/lexer/tokenize/consume-digits.js +5 -0
- package/dist/expressions/lexer/tokenize/scan-identifier.d.ts +6 -0
- package/dist/expressions/lexer/tokenize/scan-identifier.js +20 -0
- package/dist/expressions/lexer/tokenize/scan-number.d.ts +4 -0
- package/dist/expressions/lexer/tokenize/scan-number.js +32 -0
- package/dist/expressions/lexer/tokenize/scan-operator.d.ts +6 -0
- package/dist/expressions/lexer/tokenize/scan-operator.js +71 -0
- package/dist/expressions/lexer/tokenize/scan-string-literal.d.ts +4 -0
- package/dist/expressions/lexer/tokenize/scan-string-literal.js +22 -0
- package/dist/expressions/lexer/tokenize/scan-string.d.ts +4 -0
- package/dist/expressions/lexer/tokenize/scan-string.js +98 -0
- package/dist/expressions/lexer/tokenize/scan-template-literal.d.ts +4 -0
- package/dist/expressions/lexer/tokenize/scan-template-literal.js +51 -0
- package/dist/expressions/lexer/tokenize/tokenize.d.ts +4 -0
- package/dist/expressions/lexer/tokenize/tokenize.js +99 -0
- package/dist/expressions/lexer/types.d.ts +12 -0
- package/dist/expressions/lexer/types.js +1 -0
- package/dist/expressions/parser/parse-array-literal.d.ts +3 -0
- package/dist/expressions/parser/parse-array-literal.js +15 -0
- package/dist/expressions/parser/parse-expression.d.ts +3 -0
- package/dist/expressions/parser/parse-expression.js +74 -0
- package/dist/expressions/parser/parse-object-literal.d.ts +3 -0
- package/dist/expressions/parser/parse-object-literal.js +69 -0
- package/dist/expressions/parser/parse-pipe-transform.d.ts +3 -0
- package/dist/expressions/parser/parse-pipe-transform.js +26 -0
- package/dist/expressions/parser/parse-postfix.d.ts +3 -0
- package/dist/expressions/parser/parse-postfix.js +23 -0
- package/dist/expressions/parser/parse-primary.d.ts +3 -0
- package/dist/expressions/parser/parse-primary.js +35 -0
- package/dist/expressions/parser/parse-template-literal.d.ts +3 -0
- package/dist/expressions/parser/parse-template-literal.js +41 -0
- package/dist/expressions/parser/parse-unary.d.ts +3 -0
- package/dist/expressions/parser/parse-unary.js +28 -0
- package/dist/expressions/parser/parse.d.ts +3 -0
- package/dist/expressions/parser/parse.js +26 -0
- package/dist/expressions/parser/parser-context.d.ts +12 -0
- package/dist/expressions/parser/parser-context.js +14 -0
- package/dist/expressions/parser/precedence.d.ts +1 -0
- package/dist/expressions/parser/precedence.js +8 -0
- package/dist/expressions/parser/types.d.ts +101 -0
- package/dist/expressions/parser/types.js +1 -0
- package/dist/expressions/plugin.d.ts +20 -0
- package/dist/expressions/plugin.js +1 -0
- package/dist/expressions/plugins/access.d.ts +2 -0
- package/dist/expressions/plugins/access.js +138 -0
- package/dist/expressions/plugins/arrows.d.ts +2 -0
- package/dist/expressions/plugins/arrows.js +83 -0
- package/dist/expressions/plugins/booleans.d.ts +2 -0
- package/dist/expressions/plugins/booleans.js +34 -0
- package/dist/expressions/plugins/collections.d.ts +2 -0
- package/dist/expressions/plugins/collections.js +70 -0
- package/dist/expressions/plugins/comparison.d.ts +2 -0
- package/dist/expressions/plugins/comparison.js +51 -0
- package/dist/expressions/plugins/date-identifier.d.ts +4 -0
- package/dist/expressions/plugins/date-identifier.js +29 -0
- package/dist/expressions/plugins/date-literal.d.ts +2 -0
- package/dist/expressions/plugins/date-literal.js +71 -0
- package/dist/expressions/plugins/logical.d.ts +2 -0
- package/dist/expressions/plugins/logical.js +103 -0
- package/dist/expressions/plugins/membership.d.ts +2 -0
- package/dist/expressions/plugins/membership.js +71 -0
- package/dist/expressions/plugins/pipe.d.ts +2 -0
- package/dist/expressions/plugins/pipe.js +57 -0
- package/dist/expressions/plugins/quoted-identifier.d.ts +2 -0
- package/dist/expressions/plugins/quoted-identifier.js +35 -0
- package/dist/expressions/plugins/resolved-identifier.d.ts +5 -0
- package/dist/expressions/plugins/resolved-identifier.js +19 -0
- package/dist/expressions/plugins/standard.d.ts +15 -0
- package/dist/expressions/plugins/standard.js +27 -0
- package/dist/expressions/plugins/strings.d.ts +2 -0
- package/dist/expressions/plugins/strings.js +44 -0
- package/dist/expressions/plugins/ternary.d.ts +2 -0
- package/dist/expressions/plugins/ternary.js +58 -0
- package/dist/expressions.d.ts +2 -0
- package/dist/expressions.js +2 -0
- package/dist/index.d.ts +1 -0
- 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,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,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,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,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,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,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,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,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,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>>;
|