@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,101 @@
|
|
|
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 interface DateLiteralNode extends BaseNode {
|
|
97
|
+
type: "DateLiteralNode";
|
|
98
|
+
value: string;
|
|
99
|
+
}
|
|
100
|
+
export type ASTNode = NumberLiteral | StringLiteral | BooleanLiteral | NullLiteral | UndefinedLiteral | Identifier | BinaryExpression | UnaryExpression | ConditionalExpression | MemberExpression | OptionalMemberExpression | ArrayLiteral | ObjectLiteral | CallExpression | PipeExpression | TemplateLiteral | SpreadElement | ArrowFunctionExpression | DateLiteralNode;
|
|
101
|
+
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
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { current, advance, expect } from "../parser/parser-context.js";
|
|
2
|
+
import { parseExpression } from "../parser/parse-expression.js";
|
|
3
|
+
function isArrowParams(ctx) {
|
|
4
|
+
let i = ctx.pos + 1;
|
|
5
|
+
let depth = 1;
|
|
6
|
+
while (i < ctx.tokens.length && depth > 0) {
|
|
7
|
+
const t = ctx.tokens[i];
|
|
8
|
+
if (t.type === "Punctuation" && t.value === "(")
|
|
9
|
+
depth++;
|
|
10
|
+
else if (t.type === "Punctuation" && t.value === ")")
|
|
11
|
+
depth--;
|
|
12
|
+
if (depth > 0)
|
|
13
|
+
i++;
|
|
14
|
+
}
|
|
15
|
+
const after = ctx.tokens[i + 1];
|
|
16
|
+
return after?.type === "Arrow";
|
|
17
|
+
}
|
|
18
|
+
function parseArrowFunction(ctx) {
|
|
19
|
+
const start = current(ctx).start;
|
|
20
|
+
advance(ctx); // consume (
|
|
21
|
+
const params = [];
|
|
22
|
+
while (!(current(ctx).type === "Punctuation" && current(ctx).value === ")")) {
|
|
23
|
+
if (params.length > 0)
|
|
24
|
+
expect(ctx, "Punctuation", ",");
|
|
25
|
+
const param = expect(ctx, "Identifier");
|
|
26
|
+
params.push(param.value);
|
|
27
|
+
}
|
|
28
|
+
expect(ctx, "Punctuation", ")"); // consume )
|
|
29
|
+
advance(ctx); // consume =>
|
|
30
|
+
const body = parseExpression(ctx, 0);
|
|
31
|
+
return {
|
|
32
|
+
type: "ArrowFunctionExpression",
|
|
33
|
+
params,
|
|
34
|
+
body,
|
|
35
|
+
start,
|
|
36
|
+
end: body.end,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export const arrowsPlugin = {
|
|
40
|
+
name: "arrows",
|
|
41
|
+
parsePrefix: (ctx) => {
|
|
42
|
+
const tok = current(ctx);
|
|
43
|
+
// Single-param arrow: identifier => expr
|
|
44
|
+
if (tok.type === "Identifier" && ctx.tokens[ctx.pos + 1]?.type === "Arrow") {
|
|
45
|
+
advance(ctx); // consume identifier
|
|
46
|
+
advance(ctx); // consume =>
|
|
47
|
+
const body = parseExpression(ctx, 0);
|
|
48
|
+
return {
|
|
49
|
+
type: "ArrowFunctionExpression",
|
|
50
|
+
params: [tok.value],
|
|
51
|
+
body,
|
|
52
|
+
start: tok.start,
|
|
53
|
+
end: body.end,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
// Multi-param arrow: (a, b) => expr
|
|
57
|
+
if (tok.type === "Punctuation" && tok.value === "(" && isArrowParams(ctx)) {
|
|
58
|
+
return parseArrowFunction(ctx);
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
},
|
|
62
|
+
optimize: (node, opt) => {
|
|
63
|
+
if (node.type === "ArrowFunctionExpression") {
|
|
64
|
+
return { ...node, body: opt(node.body) };
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
},
|
|
68
|
+
evaluate: (node, context, evalFn) => {
|
|
69
|
+
if (node.type === "ArrowFunctionExpression") {
|
|
70
|
+
const n = node;
|
|
71
|
+
return {
|
|
72
|
+
value: (...args) => {
|
|
73
|
+
const childContext = { ...context };
|
|
74
|
+
for (let i = 0; i < n.params.length; i++) {
|
|
75
|
+
childContext[n.params[i]] = args[i];
|
|
76
|
+
}
|
|
77
|
+
return evalFn(n.body, childContext);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
},
|
|
83
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { current, advance } from "../parser/parser-context.js";
|
|
2
|
+
export const booleansPlugin = {
|
|
3
|
+
name: "booleans",
|
|
4
|
+
parsePrefix: (ctx) => {
|
|
5
|
+
const tok = current(ctx);
|
|
6
|
+
if (tok.type === "Boolean") {
|
|
7
|
+
advance(ctx);
|
|
8
|
+
return {
|
|
9
|
+
type: "BooleanLiteral",
|
|
10
|
+
value: tok.value === "true",
|
|
11
|
+
start: tok.start,
|
|
12
|
+
end: tok.end,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
if (tok.type === "Null") {
|
|
16
|
+
advance(ctx);
|
|
17
|
+
return { type: "NullLiteral", value: null, start: tok.start, end: tok.end };
|
|
18
|
+
}
|
|
19
|
+
if (tok.type === "Undefined") {
|
|
20
|
+
advance(ctx);
|
|
21
|
+
return { type: "UndefinedLiteral", value: undefined, start: tok.start, end: tok.end };
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
},
|
|
25
|
+
evaluate: (node) => {
|
|
26
|
+
if (node.type === "BooleanLiteral")
|
|
27
|
+
return { value: node.value };
|
|
28
|
+
if (node.type === "NullLiteral")
|
|
29
|
+
return { value: null };
|
|
30
|
+
if (node.type === "UndefinedLiteral")
|
|
31
|
+
return { value: undefined };
|
|
32
|
+
return null;
|
|
33
|
+
},
|
|
34
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { current, advance } from "../parser/parser-context.js";
|
|
2
|
+
import { parseArrayLiteral } from "../parser/parse-array-literal.js";
|
|
3
|
+
import { parseObjectLiteral } from "../parser/parse-object-literal.js";
|
|
4
|
+
import { parseExpression } from "../parser/parse-expression.js";
|
|
5
|
+
export const collectionsPlugin = {
|
|
6
|
+
name: "collections",
|
|
7
|
+
parsePrefix: (ctx) => {
|
|
8
|
+
const tok = current(ctx);
|
|
9
|
+
// Array literal: [elements]
|
|
10
|
+
if (tok.type === "Punctuation" && tok.value === "[") {
|
|
11
|
+
return parseArrayLiteral(ctx);
|
|
12
|
+
}
|
|
13
|
+
// Object literal: { props }
|
|
14
|
+
if (tok.type === "Punctuation" && tok.value === "{") {
|
|
15
|
+
return parseObjectLiteral(ctx);
|
|
16
|
+
}
|
|
17
|
+
// Spread element: ...expr
|
|
18
|
+
if (tok.type === "Spread") {
|
|
19
|
+
advance(ctx);
|
|
20
|
+
const argument = parseExpression(ctx, 0);
|
|
21
|
+
return { type: "SpreadElement", argument, start: tok.start, end: argument.end };
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
},
|
|
25
|
+
optimize: (node, opt) => {
|
|
26
|
+
if (node.type === "ArrayLiteral") {
|
|
27
|
+
return { ...node, elements: node.elements.map((e) => opt(e)) };
|
|
28
|
+
}
|
|
29
|
+
if (node.type === "ObjectLiteral") {
|
|
30
|
+
return {
|
|
31
|
+
...node,
|
|
32
|
+
properties: node.properties.map((p) => ({
|
|
33
|
+
...p,
|
|
34
|
+
key: p.computed ? opt(p.key) : p.key,
|
|
35
|
+
value: opt(p.value),
|
|
36
|
+
})),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
},
|
|
41
|
+
evaluate: (node, context, evalFn) => {
|
|
42
|
+
if (node.type === "ArrayLiteral") {
|
|
43
|
+
const result = [];
|
|
44
|
+
for (const element of node.elements) {
|
|
45
|
+
if (element.type === "SpreadElement") {
|
|
46
|
+
const spread = evalFn(element.argument, context);
|
|
47
|
+
result.push(...spread);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
result.push(evalFn(element, context));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { value: result };
|
|
54
|
+
}
|
|
55
|
+
if (node.type === "ObjectLiteral") {
|
|
56
|
+
const result = {};
|
|
57
|
+
for (const prop of node.properties) {
|
|
58
|
+
const key = prop.computed
|
|
59
|
+
? evalFn(prop.key, context)
|
|
60
|
+
: prop.key.type === "Identifier"
|
|
61
|
+
? prop.key.name
|
|
62
|
+
: prop.key.value;
|
|
63
|
+
const value = evalFn(prop.value, context);
|
|
64
|
+
result[key] = value;
|
|
65
|
+
}
|
|
66
|
+
return { value: result };
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
},
|
|
70
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { current } from "../parser/parser-context.js";
|
|
2
|
+
const COMPARISON_PRECEDENCE = {
|
|
3
|
+
"==": 30,
|
|
4
|
+
"!=": 30,
|
|
5
|
+
"<": 40,
|
|
6
|
+
">": 40,
|
|
7
|
+
"<=": 40,
|
|
8
|
+
">=": 40,
|
|
9
|
+
};
|
|
10
|
+
export const comparisonPlugin = {
|
|
11
|
+
name: "comparison",
|
|
12
|
+
infixPrecedence: (ctx) => {
|
|
13
|
+
const tok = current(ctx);
|
|
14
|
+
if (tok.type === "Operator") {
|
|
15
|
+
return COMPARISON_PRECEDENCE[tok.value];
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
},
|
|
19
|
+
optimize: (node, opt) => {
|
|
20
|
+
if (node.type === "BinaryExpression" && node.operator in COMPARISON_PRECEDENCE) {
|
|
21
|
+
const left = opt(node.left);
|
|
22
|
+
const right = opt(node.right);
|
|
23
|
+
return { ...node, left, right };
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
},
|
|
27
|
+
evaluate: (node, context, evalFn) => {
|
|
28
|
+
if (node.type === "BinaryExpression") {
|
|
29
|
+
const op = node.operator;
|
|
30
|
+
if (op in COMPARISON_PRECEDENCE) {
|
|
31
|
+
const left = evalFn(node.left, context);
|
|
32
|
+
const right = evalFn(node.right, context);
|
|
33
|
+
switch (op) {
|
|
34
|
+
case "==":
|
|
35
|
+
return { value: left === right };
|
|
36
|
+
case "!=":
|
|
37
|
+
return { value: left !== right };
|
|
38
|
+
case "<":
|
|
39
|
+
return { value: left < right };
|
|
40
|
+
case ">":
|
|
41
|
+
return { value: left > right };
|
|
42
|
+
case "<=":
|
|
43
|
+
return { value: left <= right };
|
|
44
|
+
case ">=":
|
|
45
|
+
return { value: left >= right };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
function toDate(value) {
|
|
2
|
+
if (value instanceof Date)
|
|
3
|
+
return value;
|
|
4
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
5
|
+
const d = new Date(value);
|
|
6
|
+
return isNaN(d.getTime()) ? null : d;
|
|
7
|
+
}
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
export function createDateIdentifierPlugin(options) {
|
|
11
|
+
const identifierSet = new Set(options.identifiers);
|
|
12
|
+
return {
|
|
13
|
+
name: "date-identifier",
|
|
14
|
+
evaluate: (node, context) => {
|
|
15
|
+
if (node.type !== "Identifier")
|
|
16
|
+
return null;
|
|
17
|
+
if (!identifierSet.has(node.name))
|
|
18
|
+
return null;
|
|
19
|
+
const raw = context[node.name];
|
|
20
|
+
const date = toDate(raw);
|
|
21
|
+
if (date === null) {
|
|
22
|
+
if (raw === undefined || raw === null)
|
|
23
|
+
return { value: null };
|
|
24
|
+
throw new Error(`Context value for "${node.name}" cannot be coerced to a Date`);
|
|
25
|
+
}
|
|
26
|
+
return { value: date };
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { scanString } from "../lexer/tokenize/scan-string.js";
|
|
2
|
+
import { current, advance } from "../parser/parser-context.js";
|
|
3
|
+
import { ExpressionError } from "../errors/expression-error.js";
|
|
4
|
+
const DATE_OPS = new Set(["==", "!=", "<", ">", "<=", ">="]);
|
|
5
|
+
export const dateLiteralPlugin = {
|
|
6
|
+
name: "date-literal",
|
|
7
|
+
scan(source, pos) {
|
|
8
|
+
if (source[pos] !== "d")
|
|
9
|
+
return null;
|
|
10
|
+
const next = source[pos + 1];
|
|
11
|
+
if (next !== '"' && next !== "'")
|
|
12
|
+
return null;
|
|
13
|
+
try {
|
|
14
|
+
const r = scanString(source, pos + 1);
|
|
15
|
+
// Store the full source text (d"2023-02-11") so the editor overlay renders it correctly.
|
|
16
|
+
return { type: "DateLiteral", value: source.slice(pos, r.end), end: r.end };
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
if (e instanceof ExpressionError) {
|
|
20
|
+
throw new ExpressionError(e.rawMessage, { source, start: pos, end: e.end });
|
|
21
|
+
}
|
|
22
|
+
throw e;
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
parsePrefix(ctx) {
|
|
26
|
+
const tok = current(ctx);
|
|
27
|
+
if (tok.type !== "DateLiteral")
|
|
28
|
+
return null;
|
|
29
|
+
advance(ctx);
|
|
30
|
+
// tok.value is the full d"2023-02-11" — strip the d" prefix and closing quote for the AST.
|
|
31
|
+
const dateStr = tok.value.slice(2, -1);
|
|
32
|
+
return { type: "DateLiteralNode", value: dateStr, start: tok.start, end: tok.end };
|
|
33
|
+
},
|
|
34
|
+
evaluate(node, context, evalFn) {
|
|
35
|
+
if (node.type === "DateLiteralNode") {
|
|
36
|
+
const d = new Date(node.value);
|
|
37
|
+
if (isNaN(d.getTime())) {
|
|
38
|
+
throw new Error(`Invalid date literal: "${node.value}"`);
|
|
39
|
+
}
|
|
40
|
+
return { value: d };
|
|
41
|
+
}
|
|
42
|
+
// Intercept comparison operators when at least one operand is a Date.
|
|
43
|
+
// This must run before comparisonPlugin (place dateLiteralPlugin first in the array).
|
|
44
|
+
if (node.type === "BinaryExpression") {
|
|
45
|
+
const op = node.operator;
|
|
46
|
+
if (!DATE_OPS.has(op))
|
|
47
|
+
return null;
|
|
48
|
+
const left = evalFn(node.left, context);
|
|
49
|
+
const right = evalFn(node.right, context);
|
|
50
|
+
if (!(left instanceof Date) && !(right instanceof Date))
|
|
51
|
+
return null;
|
|
52
|
+
const lt = left instanceof Date ? left.getTime() : Number(left);
|
|
53
|
+
const rt = right instanceof Date ? right.getTime() : Number(right);
|
|
54
|
+
switch (op) {
|
|
55
|
+
case "==":
|
|
56
|
+
return { value: lt === rt };
|
|
57
|
+
case "!=":
|
|
58
|
+
return { value: lt !== rt };
|
|
59
|
+
case "<":
|
|
60
|
+
return { value: lt < rt };
|
|
61
|
+
case ">":
|
|
62
|
+
return { value: lt > rt };
|
|
63
|
+
case "<=":
|
|
64
|
+
return { value: lt <= rt };
|
|
65
|
+
case ">=":
|
|
66
|
+
return { value: lt >= rt };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
},
|
|
71
|
+
};
|