@1771technologies/lytenyte-pro 2.0.4-dev.2 → 2.0.4-dev.4
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/css/components/expression-editor.css +105 -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/(expressions)/expression.play.d.ts +4 -0
- package/dist/components/expressions-editor/(expressions)/expression.play.js +69 -0
- package/dist/components/expressions-editor/create-completion-provider.d.ts +3 -0
- package/dist/components/expressions-editor/create-completion-provider.js +161 -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 +17 -0
- package/dist/components/expressions-editor/expression-editor.d.ts +3 -0
- package/dist/components/expressions-editor/expression-editor.js +138 -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 +17 -0
- package/dist/components/expressions-editor/index.js +11 -0
- package/dist/components/expressions-editor/intellisence/completion-context.d.ts +16 -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 +30 -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 +18 -0
- package/dist/components/expressions-editor/types.d.ts +47 -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 +16 -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 +5 -0
- package/dist/expressions/index.js +3 -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 +97 -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/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/resolved-identifier.d.ts +5 -0
- package/dist/expressions/plugins/resolved-identifier.js +19 -0
- package/dist/expressions/plugins/standard.d.ts +13 -0
- package/dist/expressions/plugins/standard.js +23 -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 +11 -5
|
@@ -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>>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { BinaryExpressionOperator, UnaryOperator } from "../lexer/types.js";
|
|
2
|
+
interface BaseNode {
|
|
3
|
+
start: number;
|
|
4
|
+
end: number;
|
|
5
|
+
}
|
|
6
|
+
export interface NumberLiteral extends BaseNode {
|
|
7
|
+
type: "NumberLiteral";
|
|
8
|
+
value: number;
|
|
9
|
+
}
|
|
10
|
+
export interface StringLiteral extends BaseNode {
|
|
11
|
+
type: "StringLiteral";
|
|
12
|
+
value: string;
|
|
13
|
+
}
|
|
14
|
+
export interface BooleanLiteral extends BaseNode {
|
|
15
|
+
type: "BooleanLiteral";
|
|
16
|
+
value: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface NullLiteral extends BaseNode {
|
|
19
|
+
type: "NullLiteral";
|
|
20
|
+
value: null;
|
|
21
|
+
}
|
|
22
|
+
export interface UndefinedLiteral extends BaseNode {
|
|
23
|
+
type: "UndefinedLiteral";
|
|
24
|
+
value: undefined;
|
|
25
|
+
}
|
|
26
|
+
export interface Identifier extends BaseNode {
|
|
27
|
+
type: "Identifier";
|
|
28
|
+
name: string;
|
|
29
|
+
}
|
|
30
|
+
export interface BinaryExpression extends BaseNode {
|
|
31
|
+
type: "BinaryExpression";
|
|
32
|
+
operator: BinaryExpressionOperator;
|
|
33
|
+
left: ASTNode;
|
|
34
|
+
right: ASTNode;
|
|
35
|
+
}
|
|
36
|
+
export interface UnaryExpression extends BaseNode {
|
|
37
|
+
type: "UnaryExpression";
|
|
38
|
+
operator: UnaryOperator;
|
|
39
|
+
operand: ASTNode;
|
|
40
|
+
}
|
|
41
|
+
export interface ConditionalExpression extends BaseNode {
|
|
42
|
+
type: "ConditionalExpression";
|
|
43
|
+
test: ASTNode;
|
|
44
|
+
consequent: ASTNode;
|
|
45
|
+
alternate: ASTNode;
|
|
46
|
+
}
|
|
47
|
+
export interface MemberExpression extends BaseNode {
|
|
48
|
+
type: "MemberExpression";
|
|
49
|
+
object: ASTNode;
|
|
50
|
+
property: ASTNode;
|
|
51
|
+
computed: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface OptionalMemberExpression extends BaseNode {
|
|
54
|
+
type: "OptionalMemberExpression";
|
|
55
|
+
object: ASTNode;
|
|
56
|
+
property: ASTNode;
|
|
57
|
+
computed: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface ArrayLiteral extends BaseNode {
|
|
60
|
+
type: "ArrayLiteral";
|
|
61
|
+
readonly elements: readonly (ASTNode | SpreadElement)[];
|
|
62
|
+
}
|
|
63
|
+
export interface ObjectLiteral extends BaseNode {
|
|
64
|
+
type: "ObjectLiteral";
|
|
65
|
+
readonly properties: readonly ObjectProperty[];
|
|
66
|
+
}
|
|
67
|
+
export interface ObjectProperty extends BaseNode {
|
|
68
|
+
type: "ObjectProperty";
|
|
69
|
+
key: ASTNode;
|
|
70
|
+
value: ASTNode;
|
|
71
|
+
computed: boolean;
|
|
72
|
+
}
|
|
73
|
+
export interface CallExpression extends BaseNode {
|
|
74
|
+
type: "CallExpression";
|
|
75
|
+
callee: ASTNode;
|
|
76
|
+
readonly args: readonly ASTNode[];
|
|
77
|
+
}
|
|
78
|
+
export interface PipeExpression extends BaseNode {
|
|
79
|
+
type: "PipeExpression";
|
|
80
|
+
input: ASTNode;
|
|
81
|
+
transform: ASTNode;
|
|
82
|
+
}
|
|
83
|
+
export interface TemplateLiteral extends BaseNode {
|
|
84
|
+
type: "TemplateLiteral";
|
|
85
|
+
readonly parts: readonly (StringLiteral | ASTNode)[];
|
|
86
|
+
}
|
|
87
|
+
export interface SpreadElement extends BaseNode {
|
|
88
|
+
type: "SpreadElement";
|
|
89
|
+
argument: ASTNode;
|
|
90
|
+
}
|
|
91
|
+
export interface ArrowFunctionExpression extends BaseNode {
|
|
92
|
+
type: "ArrowFunctionExpression";
|
|
93
|
+
params: string[];
|
|
94
|
+
body: ASTNode;
|
|
95
|
+
}
|
|
96
|
+
export type ASTNode = NumberLiteral | StringLiteral | BooleanLiteral | NullLiteral | UndefinedLiteral | Identifier | BinaryExpression | UnaryExpression | ConditionalExpression | MemberExpression | OptionalMemberExpression | ArrayLiteral | ObjectLiteral | CallExpression | PipeExpression | TemplateLiteral | SpreadElement | ArrowFunctionExpression;
|
|
97
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ASTNode } from "./parser/types.js";
|
|
2
|
+
import type { ParserContext } from "./parser/parser-context.js";
|
|
3
|
+
export type EvalFn = (node: ASTNode, context: Record<string, unknown>) => unknown;
|
|
4
|
+
export interface Plugin {
|
|
5
|
+
name: string;
|
|
6
|
+
scan?: (source: string, pos: number) => {
|
|
7
|
+
type: string;
|
|
8
|
+
value: string;
|
|
9
|
+
end: number;
|
|
10
|
+
} | null;
|
|
11
|
+
parsePrefix?: (ctx: ParserContext) => ASTNode | null;
|
|
12
|
+
parseInfix?: (ctx: ParserContext, left: ASTNode, minPrec: number) => ASTNode | null;
|
|
13
|
+
parsePostfix?: (ctx: ParserContext, node: ASTNode) => ASTNode | null;
|
|
14
|
+
parseUnary?: (ctx: ParserContext, parseNext: (ctx: ParserContext) => ASTNode) => ASTNode | null;
|
|
15
|
+
infixPrecedence?: (ctx: ParserContext) => number | undefined;
|
|
16
|
+
optimize?: (node: ASTNode, optimize: (n: ASTNode) => ASTNode) => ASTNode | null;
|
|
17
|
+
evaluate?: (node: ASTNode, context: Record<string, unknown>, evaluate: EvalFn) => {
|
|
18
|
+
value: unknown;
|
|
19
|
+
} | null;
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { current, advance, expect } from "../parser/parser-context.js";
|
|
2
|
+
import { parseExpression } from "../parser/parse-expression.js";
|
|
3
|
+
export const accessPlugin = {
|
|
4
|
+
name: "access",
|
|
5
|
+
parsePostfix: (ctx, node) => {
|
|
6
|
+
const tok = current(ctx);
|
|
7
|
+
// Member access: .prop
|
|
8
|
+
if (tok.type === "Punctuation" && tok.value === ".") {
|
|
9
|
+
advance(ctx);
|
|
10
|
+
const prop = expect(ctx, "Identifier");
|
|
11
|
+
return {
|
|
12
|
+
type: "MemberExpression",
|
|
13
|
+
object: node,
|
|
14
|
+
property: { type: "Identifier", name: prop.value, start: prop.start, end: prop.end },
|
|
15
|
+
computed: false,
|
|
16
|
+
start: node.start,
|
|
17
|
+
end: prop.end,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
// Computed member access: [expr]
|
|
21
|
+
if (tok.type === "Punctuation" && tok.value === "[") {
|
|
22
|
+
advance(ctx);
|
|
23
|
+
const property = parseExpression(ctx, 0);
|
|
24
|
+
const end = expect(ctx, "Punctuation", "]").end;
|
|
25
|
+
return {
|
|
26
|
+
type: "MemberExpression",
|
|
27
|
+
object: node,
|
|
28
|
+
property,
|
|
29
|
+
computed: true,
|
|
30
|
+
start: node.start,
|
|
31
|
+
end,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Optional chaining: ?.prop or ?.[expr]
|
|
35
|
+
if (tok.type === "OptionalChain") {
|
|
36
|
+
advance(ctx);
|
|
37
|
+
if (current(ctx).type === "Punctuation" && current(ctx).value === "[") {
|
|
38
|
+
advance(ctx);
|
|
39
|
+
const property = parseExpression(ctx, 0);
|
|
40
|
+
const end = expect(ctx, "Punctuation", "]").end;
|
|
41
|
+
return {
|
|
42
|
+
type: "OptionalMemberExpression",
|
|
43
|
+
object: node,
|
|
44
|
+
property,
|
|
45
|
+
computed: true,
|
|
46
|
+
start: node.start,
|
|
47
|
+
end,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const prop = expect(ctx, "Identifier");
|
|
51
|
+
return {
|
|
52
|
+
type: "OptionalMemberExpression",
|
|
53
|
+
object: node,
|
|
54
|
+
property: { type: "Identifier", name: prop.value, start: prop.start, end: prop.end },
|
|
55
|
+
computed: false,
|
|
56
|
+
start: node.start,
|
|
57
|
+
end: prop.end,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Function call: (args)
|
|
61
|
+
if (tok.type === "Punctuation" && tok.value === "(") {
|
|
62
|
+
advance(ctx);
|
|
63
|
+
const args = [];
|
|
64
|
+
while (!(current(ctx).type === "Punctuation" && current(ctx).value === ")")) {
|
|
65
|
+
if (args.length > 0)
|
|
66
|
+
expect(ctx, "Punctuation", ",");
|
|
67
|
+
if (current(ctx).type === "Punctuation" && current(ctx).value === ")")
|
|
68
|
+
break;
|
|
69
|
+
args.push(parseExpression(ctx, 0));
|
|
70
|
+
}
|
|
71
|
+
const end = expect(ctx, "Punctuation", ")").end;
|
|
72
|
+
return {
|
|
73
|
+
type: "CallExpression",
|
|
74
|
+
callee: node,
|
|
75
|
+
args,
|
|
76
|
+
start: node.start,
|
|
77
|
+
end,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
},
|
|
82
|
+
optimize: (node, opt) => {
|
|
83
|
+
if (node.type === "MemberExpression" || node.type === "OptionalMemberExpression") {
|
|
84
|
+
const n = node;
|
|
85
|
+
return {
|
|
86
|
+
...node,
|
|
87
|
+
object: opt(n.object),
|
|
88
|
+
property: n.computed ? opt(n.property) : n.property,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (node.type === "CallExpression") {
|
|
92
|
+
const n = node;
|
|
93
|
+
return { ...node, callee: opt(n.callee), args: n.args.map((a) => opt(a)) };
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
},
|
|
97
|
+
evaluate: (node, context, evalFn) => {
|
|
98
|
+
if (node.type === "MemberExpression") {
|
|
99
|
+
const n = node;
|
|
100
|
+
const object = evalFn(n.object, context);
|
|
101
|
+
if (n.computed) {
|
|
102
|
+
const property = evalFn(n.property, context);
|
|
103
|
+
return { value: object[property] };
|
|
104
|
+
}
|
|
105
|
+
return { value: object[n.property.name] };
|
|
106
|
+
}
|
|
107
|
+
if (node.type === "OptionalMemberExpression") {
|
|
108
|
+
const n = node;
|
|
109
|
+
const object = evalFn(n.object, context);
|
|
110
|
+
if (object == null)
|
|
111
|
+
return { value: undefined };
|
|
112
|
+
if (n.computed) {
|
|
113
|
+
const property = evalFn(n.property, context);
|
|
114
|
+
return { value: object[property] };
|
|
115
|
+
}
|
|
116
|
+
return { value: object[n.property.name] };
|
|
117
|
+
}
|
|
118
|
+
if (node.type === "CallExpression") {
|
|
119
|
+
const n = node;
|
|
120
|
+
const args = n.args.map((a) => evalFn(a, context));
|
|
121
|
+
// Method call — preserve this binding
|
|
122
|
+
if (n.callee.type === "MemberExpression" || n.callee.type === "OptionalMemberExpression") {
|
|
123
|
+
const obj = evalFn(n.callee.object, context);
|
|
124
|
+
if (n.callee.type === "OptionalMemberExpression" && obj == null) {
|
|
125
|
+
return { value: undefined };
|
|
126
|
+
}
|
|
127
|
+
const prop = n.callee.computed
|
|
128
|
+
? evalFn(n.callee.property, context)
|
|
129
|
+
: n.callee.property.name;
|
|
130
|
+
const method = obj[prop];
|
|
131
|
+
return { value: method.apply(obj, args) };
|
|
132
|
+
}
|
|
133
|
+
const fn = evalFn(n.callee, context);
|
|
134
|
+
return { value: fn(...args) };
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
},
|
|
138
|
+
};
|