@shwfed/nuxt 0.7.8 → 0.7.10
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/dist/module.json +1 -1
- package/dist/runtime/components/app.d.vue.ts +7 -58
- package/dist/runtime/components/app.vue +18 -411
- package/dist/runtime/components/app.vue.d.ts +7 -58
- package/dist/runtime/components/fields.d.vue.ts +177 -0
- package/dist/runtime/components/fields.vue +47 -0
- package/dist/runtime/components/fields.vue.d.ts +177 -0
- package/dist/runtime/components/table.d.vue.ts +63 -59
- package/dist/runtime/components/table.vue +52 -617
- package/dist/runtime/components/table.vue.d.ts +63 -59
- package/dist/runtime/components/ui/app/App.d.vue.ts +86 -0
- package/dist/runtime/components/ui/app/App.vue +414 -0
- package/dist/runtime/components/ui/app/App.vue.d.ts +86 -0
- package/dist/runtime/components/ui/button-group/ButtonGroupSeparator.vue +1 -1
- package/dist/runtime/components/ui/button-group/ButtonGroupText.vue +1 -1
- package/dist/runtime/components/ui/calendar/Calendar.d.vue.ts +5 -12
- package/dist/runtime/components/ui/calendar/Calendar.vue +77 -92
- package/dist/runtime/components/ui/calendar/Calendar.vue.d.ts +5 -12
- package/dist/runtime/components/ui/calendar/CalendarCellTrigger.vue +1 -1
- package/dist/runtime/components/ui/calendar/index.d.ts +1 -1
- package/dist/runtime/components/ui/checkbox/Checkbox.vue +6 -2
- package/dist/runtime/components/ui/command/CommandGroup.vue +4 -0
- package/dist/runtime/components/ui/dialog/DialogOverlay.vue +1 -1
- package/dist/runtime/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +1 -1
- package/dist/runtime/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +1 -1
- package/dist/runtime/components/ui/expression-editor/ExpressionEditor.d.vue.ts +30 -0
- package/dist/runtime/components/ui/expression-editor/ExpressionEditor.vue +87 -0
- package/dist/runtime/components/ui/expression-editor/ExpressionEditor.vue.d.ts +30 -0
- package/dist/runtime/components/ui/expression-editor/index.d.ts +1 -0
- package/dist/runtime/components/ui/expression-editor/index.js +1 -0
- package/dist/runtime/components/ui/field/FieldContent.vue +1 -1
- package/dist/runtime/components/ui/field/FieldDescription.vue +1 -1
- package/dist/runtime/components/ui/field/FieldError.vue +2 -2
- package/dist/runtime/components/ui/field/FieldLabel.vue +1 -1
- package/dist/runtime/components/ui/field/FieldSeparator.vue +1 -1
- package/dist/runtime/components/ui/field/index.js +7 -5
- package/dist/runtime/components/ui/fields/Fields.d.vue.ts +376 -0
- package/dist/runtime/components/ui/fields/Fields.vue +441 -0
- package/dist/runtime/components/ui/fields/Fields.vue.d.ts +376 -0
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.d.vue.ts +163 -0
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue +363 -0
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue.d.ts +163 -0
- package/dist/runtime/components/ui/input/Input.d.vue.ts +1 -0
- package/dist/runtime/components/ui/input/Input.vue +3 -1
- package/dist/runtime/components/ui/input/Input.vue.d.ts +1 -0
- package/dist/runtime/components/ui/input-group/InputGroup.vue +3 -0
- package/dist/runtime/components/ui/input-group/InputGroupAddon.vue +4 -1
- package/dist/runtime/components/ui/input-group/InputGroupCombobox.d.vue.ts +11 -3
- package/dist/runtime/components/ui/input-group/InputGroupCombobox.vue +16 -5
- package/dist/runtime/components/ui/input-group/InputGroupCombobox.vue.d.ts +11 -3
- package/dist/runtime/components/ui/input-group/InputGroupComboboxInput.d.vue.ts +8 -1
- package/dist/runtime/components/ui/input-group/InputGroupComboboxInput.vue +12 -1
- package/dist/runtime/components/ui/input-group/InputGroupComboboxInput.vue.d.ts +8 -1
- package/dist/runtime/components/ui/input-group/InputGroupInput.vue +1 -1
- package/dist/runtime/components/ui/input-group/InputGroupNumberField.d.vue.ts +5 -2
- package/dist/runtime/components/ui/input-group/InputGroupNumberField.vue +10 -4
- package/dist/runtime/components/ui/input-group/InputGroupNumberField.vue.d.ts +5 -2
- package/dist/runtime/components/ui/input-group/InputGroupText.vue +1 -1
- package/dist/runtime/components/ui/input-group/InputGroupTextarea.vue +1 -1
- package/dist/runtime/components/ui/input-group/index.js +2 -2
- package/dist/runtime/components/ui/label/Label.vue +1 -1
- package/dist/runtime/components/ui/locale/Locale.d.vue.ts +20 -0
- package/dist/runtime/components/ui/locale/Locale.vue +291 -0
- package/dist/runtime/components/ui/locale/Locale.vue.d.ts +20 -0
- package/dist/runtime/components/ui/locale/index.d.ts +1 -0
- package/dist/runtime/components/ui/locale/index.js +1 -0
- package/dist/runtime/components/ui/native-select/NativeSelect.vue +3 -3
- package/dist/runtime/components/ui/native-select/NativeSelectOption.d.vue.ts +1 -0
- package/dist/runtime/components/ui/native-select/NativeSelectOption.vue +4 -1
- package/dist/runtime/components/ui/native-select/NativeSelectOption.vue.d.ts +1 -0
- package/dist/runtime/components/ui/navigation-menu/NavigationMenuLink.vue +1 -1
- package/dist/runtime/components/ui/navigation-menu/NavigationMenuViewport.vue +1 -1
- package/dist/runtime/components/ui/number-field/NumberFieldInput.vue +1 -1
- package/dist/runtime/components/ui/range-calendar/RangeCalendarCell.vue +1 -1
- package/dist/runtime/components/ui/range-calendar/RangeCalendarCellTrigger.vue +1 -1
- package/dist/runtime/components/ui/sheet/SheetOverlay.vue +1 -1
- package/dist/runtime/components/ui/switch/Switch.d.vue.ts +24 -0
- package/dist/runtime/components/ui/switch/Switch.vue +46 -0
- package/dist/runtime/components/ui/switch/Switch.vue.d.ts +24 -0
- package/dist/runtime/components/ui/switch/index.d.ts +1 -0
- package/dist/runtime/components/ui/switch/index.js +1 -0
- package/dist/runtime/components/ui/table/Table.d.vue.ts +81 -0
- package/dist/runtime/components/ui/table/Table.vue +792 -0
- package/dist/runtime/components/ui/table/Table.vue.d.ts +81 -0
- package/dist/runtime/components/ui/table/schema.d.ts +48 -0
- package/dist/runtime/components/ui/table/schema.js +126 -0
- package/dist/runtime/components/ui/table-configurator/TableConfiguratorDialog.d.vue.ts +62 -0
- package/dist/runtime/components/ui/table-configurator/TableConfiguratorDialog.vue +2233 -0
- package/dist/runtime/components/ui/table-configurator/TableConfiguratorDialog.vue.d.ts +62 -0
- package/dist/runtime/components/ui/table-configurator/menu.d.ts +37 -0
- package/dist/runtime/components/ui/table-configurator/menu.js +227 -0
- package/dist/runtime/components/ui/tabs/Tabs.d.vue.ts +24 -0
- package/dist/runtime/components/ui/tabs/Tabs.vue +30 -0
- package/dist/runtime/components/ui/tabs/Tabs.vue.d.ts +24 -0
- package/dist/runtime/components/ui/tabs/TabsContent.d.vue.ts +18 -0
- package/dist/runtime/components/ui/tabs/TabsContent.vue +23 -0
- package/dist/runtime/components/ui/tabs/TabsContent.vue.d.ts +18 -0
- package/dist/runtime/components/ui/tabs/TabsList.d.vue.ts +18 -0
- package/dist/runtime/components/ui/tabs/TabsList.vue +25 -0
- package/dist/runtime/components/ui/tabs/TabsList.vue.d.ts +18 -0
- package/dist/runtime/components/ui/tabs/TabsTrigger.d.vue.ts +18 -0
- package/dist/runtime/components/ui/tabs/TabsTrigger.vue +27 -0
- package/dist/runtime/components/ui/tabs/TabsTrigger.vue.d.ts +18 -0
- package/dist/runtime/components/ui/tabs/index.d.ts +4 -0
- package/dist/runtime/components/ui/tabs/index.js +4 -0
- package/dist/runtime/components/ui/textarea/Textarea.d.vue.ts +1 -0
- package/dist/runtime/components/ui/textarea/Textarea.vue +4 -2
- package/dist/runtime/components/ui/textarea/Textarea.vue.d.ts +1 -0
- package/dist/runtime/components/ui/toggle/Toggle.d.vue.ts +34 -0
- package/dist/runtime/components/ui/toggle/Toggle.vue +32 -0
- package/dist/runtime/components/ui/toggle/Toggle.vue.d.ts +34 -0
- package/dist/runtime/components/ui/toggle/index.d.ts +7 -0
- package/dist/runtime/components/ui/toggle/index.js +22 -0
- package/dist/runtime/composables/useTableRenderers.d.ts +2 -1
- package/dist/runtime/composables/useTableRenderers.js +2 -1
- package/dist/runtime/plugins/cel/env.d.ts +2 -2
- package/dist/runtime/plugins/cel/env.js +5 -4
- package/dist/runtime/plugins/cel/index.d.ts +3 -3
- package/dist/runtime/plugins/cel/index.js +7 -3
- package/dist/runtime/plugins/markdown/index.d.ts +1 -1
- package/dist/runtime/style.css +1 -1
- package/dist/runtime/table-renderers/builtins.js +213 -98
- package/dist/runtime/table-renderers/registry.d.ts +1 -0
- package/dist/runtime/table-renderers/registry.js +3 -0
- package/dist/runtime/utils/coders.d.ts +32 -0
- package/dist/runtime/utils/coders.js +64 -0
- package/dist/runtime/vendor/cel/index.d.ts +17 -0
- package/dist/runtime/vendor/cel/index.js +10 -0
- package/dist/runtime/vendor/cel-js/LICENSE +21 -0
- package/dist/runtime/vendor/cel-js/UPSTREAM.md +17 -0
- package/dist/runtime/vendor/cel-js/lib/errors.d.ts +21 -0
- package/dist/runtime/vendor/cel-js/lib/errors.js +97 -0
- package/dist/runtime/vendor/cel-js/lib/evaluator.d.ts +4 -0
- package/dist/runtime/vendor/cel-js/lib/evaluator.js +192 -0
- package/dist/runtime/vendor/cel-js/lib/functions.d.ts +53 -0
- package/dist/runtime/vendor/cel-js/lib/functions.js +513 -0
- package/dist/runtime/vendor/cel-js/lib/globals.d.ts +27 -0
- package/dist/runtime/vendor/cel-js/lib/globals.js +33 -0
- package/dist/runtime/vendor/cel-js/lib/index.d.ts +469 -0
- package/dist/runtime/vendor/cel-js/lib/index.js +18 -0
- package/dist/runtime/vendor/cel-js/lib/macros.d.ts +1 -0
- package/dist/runtime/vendor/cel-js/lib/macros.js +230 -0
- package/dist/runtime/vendor/cel-js/lib/operators.d.ts +117 -0
- package/dist/runtime/vendor/cel-js/lib/operators.js +739 -0
- package/dist/runtime/vendor/cel-js/lib/optional.d.ts +14 -0
- package/dist/runtime/vendor/cel-js/lib/optional.js +161 -0
- package/dist/runtime/vendor/cel-js/lib/options.d.ts +23 -0
- package/dist/runtime/vendor/cel-js/lib/options.js +47 -0
- package/dist/runtime/vendor/cel-js/lib/overloads.d.ts +1 -0
- package/dist/runtime/vendor/cel-js/lib/overloads.js +214 -0
- package/dist/runtime/vendor/cel-js/lib/parser.d.ts +56 -0
- package/dist/runtime/vendor/cel-js/lib/parser.js +827 -0
- package/dist/runtime/vendor/cel-js/lib/registry.d.ts +279 -0
- package/dist/runtime/vendor/cel-js/lib/registry.js +1596 -0
- package/dist/runtime/vendor/cel-js/lib/serialize.d.ts +1 -0
- package/dist/runtime/vendor/cel-js/lib/serialize.js +259 -0
- package/dist/runtime/vendor/cel-js/lib/type-checker.d.ts +26 -0
- package/dist/runtime/vendor/cel-js/lib/type-checker.js +81 -0
- package/package.json +9 -4
- package/dist/runtime/components/locale.d.vue.ts +0 -14
- package/dist/runtime/components/locale.vue +0 -89
- package/dist/runtime/components/locale.vue.d.ts +0 -14
- package/dist/runtime/components/query.d.vue.ts +0 -30
- package/dist/runtime/components/query.vue +0 -266
- package/dist/runtime/components/query.vue.d.ts +0 -30
- package/dist/runtime/utilities/query-config/global.d.ts +0 -4
- package/dist/runtime/utilities/query-config/global.js +0 -18
- package/dist/runtime/utilities/query-config/index.d.ts +0 -3
- package/dist/runtime/utilities/query-config/index.js +0 -14
- package/dist/runtime/utilities/query-config/schema.d.ts +0 -96
- package/dist/runtime/utilities/query-config/schema.js +0 -51
- /package/dist/runtime/components/{logo.d.vue.ts → ui/logo/Logo.d.vue.ts} +0 -0
- /package/dist/runtime/components/{logo.vue → ui/logo/Logo.vue} +0 -0
- /package/dist/runtime/components/{logo.vue.d.ts → ui/logo/Logo.vue.d.ts} +0 -0
|
@@ -0,0 +1,827 @@
|
|
|
1
|
+
import {UnsignedInt} from './functions.js'
|
|
2
|
+
import {ParseError} from './errors.js'
|
|
3
|
+
import {OPERATORS as OPS} from './operators.js'
|
|
4
|
+
import {RESERVED, isAsync} from './globals.js'
|
|
5
|
+
|
|
6
|
+
const TOKEN = {
|
|
7
|
+
EOF: 0,
|
|
8
|
+
NUMBER: 1,
|
|
9
|
+
STRING: 2,
|
|
10
|
+
BOOLEAN: 3,
|
|
11
|
+
NULL: 4,
|
|
12
|
+
IDENTIFIER: 5,
|
|
13
|
+
PLUS: 6,
|
|
14
|
+
MINUS: 7,
|
|
15
|
+
MULTIPLY: 8,
|
|
16
|
+
DIVIDE: 9,
|
|
17
|
+
MODULO: 10,
|
|
18
|
+
EQ: 11,
|
|
19
|
+
NE: 12,
|
|
20
|
+
LT: 13,
|
|
21
|
+
LE: 14,
|
|
22
|
+
GT: 15,
|
|
23
|
+
GE: 16,
|
|
24
|
+
AND: 17,
|
|
25
|
+
OR: 18,
|
|
26
|
+
NOT: 19,
|
|
27
|
+
IN: 20,
|
|
28
|
+
LPAREN: 21,
|
|
29
|
+
RPAREN: 22,
|
|
30
|
+
LBRACKET: 23,
|
|
31
|
+
RBRACKET: 24,
|
|
32
|
+
LBRACE: 25,
|
|
33
|
+
RBRACE: 26,
|
|
34
|
+
DOT: 27,
|
|
35
|
+
COMMA: 28,
|
|
36
|
+
COLON: 29,
|
|
37
|
+
QUESTION: 30,
|
|
38
|
+
BYTES: 31
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const OP_FOR_TOKEN = {
|
|
42
|
+
[TOKEN.EQ]: OPS['=='],
|
|
43
|
+
[TOKEN.PLUS]: OPS['+'],
|
|
44
|
+
[TOKEN.MINUS]: OPS['-'],
|
|
45
|
+
[TOKEN.MULTIPLY]: OPS['*'],
|
|
46
|
+
[TOKEN.DIVIDE]: OPS['/'],
|
|
47
|
+
[TOKEN.MODULO]: OPS['%'],
|
|
48
|
+
[TOKEN.LE]: OPS['<='],
|
|
49
|
+
[TOKEN.LT]: OPS['<'],
|
|
50
|
+
[TOKEN.GE]: OPS['>='],
|
|
51
|
+
[TOKEN.GT]: OPS['>'],
|
|
52
|
+
[TOKEN.NE]: OPS['!='],
|
|
53
|
+
[TOKEN.IN]: OPS['in']
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const TOKEN_BY_NUMBER = {}
|
|
57
|
+
for (const key in TOKEN) TOKEN_BY_NUMBER[TOKEN[key]] = key
|
|
58
|
+
|
|
59
|
+
const HEX_CODES = new Uint8Array(128)
|
|
60
|
+
for (const ch of '0123456789abcdefABCDEF') HEX_CODES[ch.charCodeAt(0)] = 1
|
|
61
|
+
|
|
62
|
+
const STRING_ESCAPES = {
|
|
63
|
+
'\\': '\\',
|
|
64
|
+
'?': '?',
|
|
65
|
+
'"': '"',
|
|
66
|
+
"'": "'",
|
|
67
|
+
'`': '`',
|
|
68
|
+
a: '\x07',
|
|
69
|
+
b: '\b',
|
|
70
|
+
f: '\f',
|
|
71
|
+
n: '\n',
|
|
72
|
+
r: '\r',
|
|
73
|
+
t: '\t',
|
|
74
|
+
v: '\v'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export class ASTNode {
|
|
78
|
+
#meta
|
|
79
|
+
constructor(input, pos, op, args) {
|
|
80
|
+
this.#meta = {input, pos, evaluate: op.evaluate, check: op.check}
|
|
81
|
+
this.op = op.name
|
|
82
|
+
this.args = args
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
clone(op, args) {
|
|
86
|
+
return new ASTNode(this.#meta.input, this.#meta.pos, op, args)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
get meta() {
|
|
90
|
+
return this.#meta
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
#computeIsAsync() {
|
|
94
|
+
const ast = this.#meta.alternate ?? this
|
|
95
|
+
|
|
96
|
+
// prettier-ignore
|
|
97
|
+
switch (ast.op) {
|
|
98
|
+
case 'value': case 'id': case 'accuValue': case 'accuInc':
|
|
99
|
+
return false
|
|
100
|
+
case 'accuPush':
|
|
101
|
+
return ast.args.maybeAsync
|
|
102
|
+
case '!_': case '-_':
|
|
103
|
+
if (ast.candidates?.async !== false) return true
|
|
104
|
+
return ast.args.maybeAsync
|
|
105
|
+
case '!=': case '==': case 'in': case '+': case '-': case '*':
|
|
106
|
+
case '/': case '%': case '<': case '<=': case '>': case '>=':
|
|
107
|
+
if (ast.candidates?.async !== false) return true
|
|
108
|
+
return ast.args.some((a) => a.maybeAsync)
|
|
109
|
+
case 'call': case 'rcall':
|
|
110
|
+
if (ast.candidates?.async !== false) return true
|
|
111
|
+
return (ast.receiverWithArgs || ast.args[1]).some((a) => a.maybeAsync)
|
|
112
|
+
case 'comprehension':
|
|
113
|
+
return ast.args.iterable.maybeAsync || ast.args.step.maybeAsync
|
|
114
|
+
case '.': case '.?':
|
|
115
|
+
return ast.args[0].maybeAsync
|
|
116
|
+
case '?:': case 'list':
|
|
117
|
+
case '[]': case '[?]':
|
|
118
|
+
return ast.args.some((a) => a.maybeAsync)
|
|
119
|
+
case '||': case '&&':
|
|
120
|
+
return ast.args.some((a) => a.maybeAsync)
|
|
121
|
+
case 'map':
|
|
122
|
+
return ast.args.some((a) => a[0].maybeAsync || a[1].maybeAsync)
|
|
123
|
+
default:
|
|
124
|
+
return true
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Check whether we can optimize away async calling
|
|
130
|
+
* If no ast members includes a function, we can optimize the call
|
|
131
|
+
* for which we want to return true here.
|
|
132
|
+
* @returns {boolean}
|
|
133
|
+
*/
|
|
134
|
+
get maybeAsync() {
|
|
135
|
+
return (this.#meta.async ??= this.#computeIsAsync())
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
check(chk, ast, ctx) {
|
|
139
|
+
const meta = this.#meta
|
|
140
|
+
if (meta.alternate) return chk.check(meta.alternate, ctx)
|
|
141
|
+
else if (meta.macro) return meta.macro.typeCheck(chk, meta.macro, ctx)
|
|
142
|
+
return meta.check(chk, ast, ctx)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
evaluate(ev, ast, ctx) {
|
|
146
|
+
const meta = this.#meta
|
|
147
|
+
if (meta.alternate) this.evaluate = this.#evaluateAlternate
|
|
148
|
+
else if (meta.macro) this.evaluate = this.#evaluateMacro
|
|
149
|
+
else this.evaluate = meta.evaluate
|
|
150
|
+
return this.evaluate(ev, ast, ctx)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#evaluateAlternate(ev, ast, ctx) {
|
|
154
|
+
return (ast = this.#meta.alternate).evaluate(ev, ast, ctx)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#evaluateMacro(ev, ast, ctx) {
|
|
158
|
+
return (ast = this.#meta.macro).evaluate(ev, ast, ctx)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
setMeta(key, value) {
|
|
162
|
+
return ((this.#meta[key] = value), this)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
get input() {
|
|
166
|
+
return this.#meta.input
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
get pos() {
|
|
170
|
+
return this.#meta.pos
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
toOldStructure() {
|
|
174
|
+
const args = Array.isArray(this.args) ? this.args : [this.args]
|
|
175
|
+
return [this.op, ...args.map((a) => (a instanceof ASTNode ? a.toOldStructure() : a))]
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
class Lexer {
|
|
180
|
+
input
|
|
181
|
+
pos
|
|
182
|
+
length
|
|
183
|
+
|
|
184
|
+
tokenPos
|
|
185
|
+
tokenType
|
|
186
|
+
tokenValue
|
|
187
|
+
|
|
188
|
+
reset(input) {
|
|
189
|
+
this.pos = 0
|
|
190
|
+
this.input = input
|
|
191
|
+
this.length = input.length
|
|
192
|
+
return input
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
token(pos, type, value) {
|
|
196
|
+
this.tokenPos = pos
|
|
197
|
+
this.tokenType = type
|
|
198
|
+
this.tokenValue = value
|
|
199
|
+
return this
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Read next token
|
|
203
|
+
nextToken() {
|
|
204
|
+
while (true) {
|
|
205
|
+
const {pos, input, length} = this
|
|
206
|
+
if (pos >= length) return this.token(pos, TOKEN.EOF)
|
|
207
|
+
|
|
208
|
+
const ch = input[pos]
|
|
209
|
+
switch (ch) {
|
|
210
|
+
// Whitespaces
|
|
211
|
+
case ' ':
|
|
212
|
+
case '\t':
|
|
213
|
+
case '\n':
|
|
214
|
+
case '\r':
|
|
215
|
+
this.pos++
|
|
216
|
+
continue
|
|
217
|
+
|
|
218
|
+
// Operators
|
|
219
|
+
case '=':
|
|
220
|
+
if (input[pos + 1] !== '=') break
|
|
221
|
+
return this.token((this.pos += 2) - 2, TOKEN.EQ)
|
|
222
|
+
case '&':
|
|
223
|
+
if (input[pos + 1] !== '&') break
|
|
224
|
+
return this.token((this.pos += 2) - 2, TOKEN.AND)
|
|
225
|
+
case '|':
|
|
226
|
+
if (input[pos + 1] !== '|') break
|
|
227
|
+
return this.token((this.pos += 2) - 2, TOKEN.OR)
|
|
228
|
+
case '+':
|
|
229
|
+
return this.token(this.pos++, TOKEN.PLUS)
|
|
230
|
+
case '-':
|
|
231
|
+
return this.token(this.pos++, TOKEN.MINUS)
|
|
232
|
+
case '*':
|
|
233
|
+
return this.token(this.pos++, TOKEN.MULTIPLY)
|
|
234
|
+
case '/':
|
|
235
|
+
if (input[pos + 1] === '/') {
|
|
236
|
+
while (this.pos < length && this.input[this.pos] !== '\n') this.pos++
|
|
237
|
+
continue
|
|
238
|
+
}
|
|
239
|
+
return this.token(this.pos++, TOKEN.DIVIDE)
|
|
240
|
+
case '%':
|
|
241
|
+
return this.token(this.pos++, TOKEN.MODULO)
|
|
242
|
+
case '<':
|
|
243
|
+
if (input[pos + 1] === '=') return this.token((this.pos += 2) - 2, TOKEN.LE)
|
|
244
|
+
return this.token(this.pos++, TOKEN.LT)
|
|
245
|
+
case '>':
|
|
246
|
+
if (input[pos + 1] === '=') return this.token((this.pos += 2) - 2, TOKEN.GE)
|
|
247
|
+
return this.token(this.pos++, TOKEN.GT)
|
|
248
|
+
case '!':
|
|
249
|
+
if (input[pos + 1] === '=') return this.token((this.pos += 2) - 2, TOKEN.NE)
|
|
250
|
+
return this.token(this.pos++, TOKEN.NOT)
|
|
251
|
+
case '(':
|
|
252
|
+
return this.token(this.pos++, TOKEN.LPAREN)
|
|
253
|
+
case ')':
|
|
254
|
+
return this.token(this.pos++, TOKEN.RPAREN)
|
|
255
|
+
case '[':
|
|
256
|
+
return this.token(this.pos++, TOKEN.LBRACKET)
|
|
257
|
+
case ']':
|
|
258
|
+
return this.token(this.pos++, TOKEN.RBRACKET)
|
|
259
|
+
case '{':
|
|
260
|
+
return this.token(this.pos++, TOKEN.LBRACE)
|
|
261
|
+
case '}':
|
|
262
|
+
return this.token(this.pos++, TOKEN.RBRACE)
|
|
263
|
+
case '.':
|
|
264
|
+
return this.token(this.pos++, TOKEN.DOT)
|
|
265
|
+
case ',':
|
|
266
|
+
return this.token(this.pos++, TOKEN.COMMA)
|
|
267
|
+
case ':':
|
|
268
|
+
return this.token(this.pos++, TOKEN.COLON)
|
|
269
|
+
case '?':
|
|
270
|
+
return this.token(this.pos++, TOKEN.QUESTION)
|
|
271
|
+
case `"`:
|
|
272
|
+
case `'`:
|
|
273
|
+
return this.readString(ch)
|
|
274
|
+
// Check for string prefixes (b, B, r, R followed by quote)
|
|
275
|
+
case 'b':
|
|
276
|
+
case 'B':
|
|
277
|
+
case 'r':
|
|
278
|
+
case 'R': {
|
|
279
|
+
// This is a prefixed string, advance past the prefix and read string
|
|
280
|
+
const next = input[pos + 1]
|
|
281
|
+
if (next === '"' || next === "'") return ++this.pos && this.readString(next, ch)
|
|
282
|
+
return this.readIdentifier()
|
|
283
|
+
}
|
|
284
|
+
default: {
|
|
285
|
+
const code = ch.charCodeAt(0)
|
|
286
|
+
if (code <= 57 && code >= 48) return this.readNumber()
|
|
287
|
+
if (this._isIdentifierCharCode(code)) return this.readIdentifier()
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
throw new ParseError(`Unexpected character: ${ch}`, {pos, input})
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_
|
|
296
|
+
_isIdentifierCharCode(c) {
|
|
297
|
+
if (c < 48 || c > 122) return false
|
|
298
|
+
return c >= 97 || (c >= 65 && c <= 90) || c <= 57 || c === 95
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
_parseAsDouble(start, end) {
|
|
302
|
+
const value = Number(this.input.substring(start, end))
|
|
303
|
+
if (Number.isFinite(value)) return this.token(start, TOKEN.NUMBER, value)
|
|
304
|
+
throw new ParseError(`Invalid number: ${value}`, {pos: start, input: this.input})
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
_parseAsBigInt(start, end, isHex, unsigned) {
|
|
308
|
+
const string = this.input.substring(start, end)
|
|
309
|
+
if (unsigned === 'u' || unsigned === 'U') {
|
|
310
|
+
this.pos++
|
|
311
|
+
try {
|
|
312
|
+
return this.token(start, TOKEN.NUMBER, new UnsignedInt(string))
|
|
313
|
+
} catch (_err) {}
|
|
314
|
+
} else {
|
|
315
|
+
try {
|
|
316
|
+
return this.token(start, TOKEN.NUMBER, BigInt(string))
|
|
317
|
+
} catch (_err) {}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
throw new ParseError(isHex ? `Invalid hex integer: ${string}` : `Invalid integer: ${string}`, {
|
|
321
|
+
pos: start,
|
|
322
|
+
input: this.input
|
|
323
|
+
})
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
_readDigits(input, length, pos, code) {
|
|
327
|
+
while (pos < length && (code = input.charCodeAt(pos)) && !(code > 57 || code < 48)) pos++
|
|
328
|
+
return pos
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
_readExponent(input, length, pos) {
|
|
332
|
+
let ch = pos < length && input[pos]
|
|
333
|
+
if (ch === 'e' || ch === 'E') {
|
|
334
|
+
ch = ++pos < length && input[pos]
|
|
335
|
+
if (ch === '-' || ch === '+') pos++
|
|
336
|
+
const start = pos
|
|
337
|
+
pos = this._readDigits(input, length, pos)
|
|
338
|
+
if (start === pos) throw new ParseError('Invalid exponent', {pos, input})
|
|
339
|
+
}
|
|
340
|
+
return pos
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
readNumber() {
|
|
344
|
+
const {input, length, pos: start} = this
|
|
345
|
+
let pos = start
|
|
346
|
+
if (input[pos] === '0' && (input[pos + 1] === 'x' || input[pos + 1] === 'X')) {
|
|
347
|
+
pos += 2
|
|
348
|
+
while (pos < length && HEX_CODES[input[pos].charCodeAt(0)]) pos++
|
|
349
|
+
return this._parseAsBigInt(start, (this.pos = pos), true, input[pos])
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
pos = this._readDigits(input, length, pos)
|
|
353
|
+
if (pos + 1 < length) {
|
|
354
|
+
let isDouble = false
|
|
355
|
+
let afterpos = input[pos] === '.' ? this._readDigits(input, length, pos + 1) : pos + 1
|
|
356
|
+
if (afterpos !== pos + 1) (isDouble = true) && (pos = afterpos)
|
|
357
|
+
|
|
358
|
+
afterpos = this._readExponent(input, length, pos)
|
|
359
|
+
if (afterpos !== pos) (isDouble = true) && (pos = afterpos)
|
|
360
|
+
if (isDouble) return this._parseAsDouble(start, (this.pos = pos))
|
|
361
|
+
}
|
|
362
|
+
return this._parseAsBigInt(start, (this.pos = pos), false, input[pos])
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
readString(del, prefix) {
|
|
366
|
+
const {input: i, pos: s} = this
|
|
367
|
+
if (i[s + 1] === del && i[s + 2] === del) return this.readTripleQuotedString(del, prefix)
|
|
368
|
+
return this.readSingleQuotedString(del, prefix)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
_closeQuotedString(rawValue, prefix, pos) {
|
|
372
|
+
switch (prefix) {
|
|
373
|
+
case 'b':
|
|
374
|
+
case 'B': {
|
|
375
|
+
const processed = this.processEscapes(rawValue, true)
|
|
376
|
+
const bytes = new Uint8Array(processed.length)
|
|
377
|
+
for (let i = 0; i < processed.length; i++) bytes[i] = processed.charCodeAt(i) & 0xff
|
|
378
|
+
return this.token(pos - 1, TOKEN.BYTES, bytes)
|
|
379
|
+
}
|
|
380
|
+
case 'r':
|
|
381
|
+
case 'R': {
|
|
382
|
+
return this.token(pos - 1, TOKEN.STRING, rawValue)
|
|
383
|
+
}
|
|
384
|
+
default: {
|
|
385
|
+
const value = this.processEscapes(rawValue, false)
|
|
386
|
+
return this.token(pos, TOKEN.STRING, value)
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
readSingleQuotedString(delimiter, prefix) {
|
|
392
|
+
const {input, length, pos: start} = this
|
|
393
|
+
|
|
394
|
+
let ch
|
|
395
|
+
let pos = this.pos + 1
|
|
396
|
+
while (pos < length && (ch = input[pos])) {
|
|
397
|
+
switch (ch) {
|
|
398
|
+
case delimiter:
|
|
399
|
+
const rawValue = input.slice(start + 1, pos)
|
|
400
|
+
this.pos = ++pos
|
|
401
|
+
return this._closeQuotedString(rawValue, prefix, start)
|
|
402
|
+
case '\n':
|
|
403
|
+
case '\r':
|
|
404
|
+
throw new ParseError('Newlines not allowed in single-quoted strings', {pos: start, input})
|
|
405
|
+
case '\\':
|
|
406
|
+
pos++
|
|
407
|
+
}
|
|
408
|
+
pos++
|
|
409
|
+
}
|
|
410
|
+
throw new ParseError('Unterminated string', {pos: start, input})
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
readTripleQuotedString(delimiter, prefix) {
|
|
414
|
+
const {input, length, pos: start} = this
|
|
415
|
+
|
|
416
|
+
let ch
|
|
417
|
+
let pos = this.pos + 3
|
|
418
|
+
while (pos < length && (ch = input[pos])) {
|
|
419
|
+
switch (ch) {
|
|
420
|
+
case delimiter:
|
|
421
|
+
if (input[pos + 1] === delimiter && input[pos + 2] === delimiter) {
|
|
422
|
+
const rawValue = input.slice(start + 3, pos)
|
|
423
|
+
this.pos = pos + 3
|
|
424
|
+
return this._closeQuotedString(rawValue, prefix, start)
|
|
425
|
+
}
|
|
426
|
+
break
|
|
427
|
+
case '\\':
|
|
428
|
+
pos++
|
|
429
|
+
}
|
|
430
|
+
pos++
|
|
431
|
+
}
|
|
432
|
+
throw new ParseError('Unterminated triple-quoted string', {pos: start, input})
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
processEscapes(str, isBytes) {
|
|
436
|
+
if (!str.includes('\\')) return str
|
|
437
|
+
|
|
438
|
+
let result = ''
|
|
439
|
+
let i = 0
|
|
440
|
+
while (i < str.length) {
|
|
441
|
+
if (str[i] !== '\\' || i + 1 >= str.length) {
|
|
442
|
+
result += str[i++]
|
|
443
|
+
continue
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const next = str[i + 1]
|
|
447
|
+
if (STRING_ESCAPES[next]) {
|
|
448
|
+
result += STRING_ESCAPES[next]
|
|
449
|
+
i += 2
|
|
450
|
+
} else if (next === 'u') {
|
|
451
|
+
if (isBytes) throw new ParseError('\\u not allowed in bytes literals')
|
|
452
|
+
const hex = str.substring(i + 2, (i += 6))
|
|
453
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) throw new ParseError(`Invalid Unicode escape: \\u${hex}`)
|
|
454
|
+
const c = Number.parseInt(hex, 16)
|
|
455
|
+
if (c >= 0xd800 && c <= 0xdfff) throw new ParseError(`Invalid Unicode surrogate: \\u${hex}`)
|
|
456
|
+
result += String.fromCharCode(c)
|
|
457
|
+
} else if (next === 'U') {
|
|
458
|
+
if (isBytes) throw new ParseError('\\U not allowed in bytes literals')
|
|
459
|
+
const hex = str.substring(i + 2, (i += 10))
|
|
460
|
+
if (!/^[0-9a-fA-F]{8}$/.test(hex)) throw new ParseError(`Invalid Unicode escape: \\U${hex}`)
|
|
461
|
+
const c = Number.parseInt(hex, 16)
|
|
462
|
+
if (c > 0x10ffff) throw new ParseError(`Invalid Unicode escape: \\U${hex}`)
|
|
463
|
+
if (c >= 0xd800 && c <= 0xdfff) throw new ParseError(`Invalid Unicode surrogate: \\U${hex}`)
|
|
464
|
+
result += String.fromCodePoint(c)
|
|
465
|
+
} else if (next === 'x' || next === 'X') {
|
|
466
|
+
const h = str.substring(i + 2, (i += 4))
|
|
467
|
+
if (!/^[0-9a-fA-F]{2}$/.test(h)) throw new ParseError(`Invalid hex escape: \\${next}${h}`)
|
|
468
|
+
result += String.fromCharCode(Number.parseInt(h, 16))
|
|
469
|
+
} else if (next >= '0' && next <= '7') {
|
|
470
|
+
const o = str.substring(i + 1, (i += 4))
|
|
471
|
+
if (!/^[0-7]{3}$/.test(o)) throw new ParseError('Octal escape must be 3 digits')
|
|
472
|
+
const value = Number.parseInt(o, 8)
|
|
473
|
+
if (value > 0xff) throw new ParseError(`Octal escape out of range: \\${o}`)
|
|
474
|
+
result += String.fromCharCode(value)
|
|
475
|
+
} else {
|
|
476
|
+
throw new ParseError(`Invalid escape sequence: \\${next}`)
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return result
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
readIdentifier() {
|
|
484
|
+
const {pos, input, length} = this
|
|
485
|
+
let p = pos
|
|
486
|
+
while (p < length && this._isIdentifierCharCode(input[p].charCodeAt(0))) p++
|
|
487
|
+
const value = input.substring(pos, (this.pos = p))
|
|
488
|
+
switch (value) {
|
|
489
|
+
case 'true':
|
|
490
|
+
return this.token(pos, TOKEN.BOOLEAN, true)
|
|
491
|
+
case 'false':
|
|
492
|
+
return this.token(pos, TOKEN.BOOLEAN, false)
|
|
493
|
+
case 'null':
|
|
494
|
+
return this.token(pos, TOKEN.NULL, null)
|
|
495
|
+
case 'in':
|
|
496
|
+
return this.token(pos, TOKEN.IN)
|
|
497
|
+
default:
|
|
498
|
+
return this.token(pos, TOKEN.IDENTIFIER, value)
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
export class Parser {
|
|
504
|
+
lexer = null
|
|
505
|
+
input = null
|
|
506
|
+
maxDepthRemaining = null
|
|
507
|
+
astNodesRemaining = null
|
|
508
|
+
|
|
509
|
+
type = null
|
|
510
|
+
pos = null
|
|
511
|
+
|
|
512
|
+
constructor(limits, registry) {
|
|
513
|
+
this.limits = limits
|
|
514
|
+
this.registry = registry
|
|
515
|
+
this.lexer = new Lexer()
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
#limitExceeded(limitKey, pos = this.pos) {
|
|
519
|
+
throw new ParseError(`Exceeded ${limitKey} (${this.limits[limitKey]})`, {
|
|
520
|
+
pos,
|
|
521
|
+
input: this.input
|
|
522
|
+
})
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
#node(pos, op, args) {
|
|
526
|
+
const node = new ASTNode(this.input, pos, op, args)
|
|
527
|
+
if (!this.astNodesRemaining--) this.#limitExceeded('maxAstNodes', pos)
|
|
528
|
+
return node
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
#advanceToken(returnValue = this.pos) {
|
|
532
|
+
const l = this.lexer.nextToken()
|
|
533
|
+
this.pos = l.tokenPos
|
|
534
|
+
this.type = l.tokenType
|
|
535
|
+
return returnValue
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// The value of the current token is accessed less regularly,
|
|
539
|
+
// so we use a getter to reduce assignment overhead
|
|
540
|
+
get value() {
|
|
541
|
+
return this.lexer.tokenValue
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
consume(expectedType) {
|
|
545
|
+
if (this.type === expectedType) return this.#advanceToken()
|
|
546
|
+
throw new ParseError(
|
|
547
|
+
`Expected ${TOKEN_BY_NUMBER[expectedType]}, got ${TOKEN_BY_NUMBER[this.type]}`,
|
|
548
|
+
{pos: this.pos, input: this.input}
|
|
549
|
+
)
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
match(type) {
|
|
553
|
+
return this.type === type
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Parse entry point
|
|
557
|
+
parse(input) {
|
|
558
|
+
if (typeof input !== 'string') throw new ParseError('Expression must be a string')
|
|
559
|
+
this.input = this.lexer.reset(input)
|
|
560
|
+
this.#advanceToken()
|
|
561
|
+
this.maxDepthRemaining = this.limits.maxDepth
|
|
562
|
+
this.astNodesRemaining = this.limits.maxAstNodes
|
|
563
|
+
|
|
564
|
+
const result = this.parseExpression()
|
|
565
|
+
if (this.match(TOKEN.EOF)) return result
|
|
566
|
+
|
|
567
|
+
throw new ParseError(`Unexpected character: '${this.input[this.lexer.pos - 1]}'`, {
|
|
568
|
+
pos: this.pos,
|
|
569
|
+
input: this.input
|
|
570
|
+
})
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
#expandMacro(pos, op, args) {
|
|
574
|
+
const [methodName, receiver, fnArgs] = op === OPS.rcall ? args : [args[0], null, args[1]]
|
|
575
|
+
const decl = this.registry.findMacro(methodName, !!receiver, fnArgs.length)
|
|
576
|
+
const ast = this.#node(pos, op, args)
|
|
577
|
+
if (!decl) return ast
|
|
578
|
+
const macro = decl.handler({ast, args: fnArgs, receiver, methodName, parser: this})
|
|
579
|
+
if (macro.callAst) return ast.setMeta('alternate', macro.callAst)
|
|
580
|
+
return ast.setMeta('macro', macro).setMeta('async', isAsync(macro.evaluate, macro.async))
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Expression ::= LogicalOr ('?' Expression ':' Expression)?
|
|
584
|
+
parseExpression() {
|
|
585
|
+
if (!this.maxDepthRemaining--) this.#limitExceeded('maxDepth')
|
|
586
|
+
const expr = this.parseLogicalOr()
|
|
587
|
+
if (!this.match(TOKEN.QUESTION)) return ++this.maxDepthRemaining && expr
|
|
588
|
+
|
|
589
|
+
const questionPos = this.#advanceToken()
|
|
590
|
+
const consequent = this.parseExpression()
|
|
591
|
+
this.consume(TOKEN.COLON)
|
|
592
|
+
const alternate = this.parseExpression()
|
|
593
|
+
this.maxDepthRemaining++
|
|
594
|
+
return this.#node(questionPos, OPS.ternary, [expr, consequent, alternate])
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// LogicalOr ::= LogicalAnd ('||' LogicalAnd)*
|
|
598
|
+
parseLogicalOr() {
|
|
599
|
+
let expr = this.parseLogicalAnd()
|
|
600
|
+
while (this.match(TOKEN.OR))
|
|
601
|
+
expr = this.#node(this.#advanceToken(), OPS['||'], [expr, this.parseLogicalAnd()])
|
|
602
|
+
return expr
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// LogicalAnd ::= Equality ('&&' Equality)*
|
|
606
|
+
parseLogicalAnd() {
|
|
607
|
+
let expr = this.parseEquality()
|
|
608
|
+
while (this.match(TOKEN.AND))
|
|
609
|
+
expr = this.#node(this.#advanceToken(), OPS['&&'], [expr, this.parseEquality()])
|
|
610
|
+
return expr
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Equality ::= Relational (('==' | '!=') Relational)*
|
|
614
|
+
parseEquality() {
|
|
615
|
+
let expr = this.parseRelational()
|
|
616
|
+
while (this.match(TOKEN.EQ) || this.match(TOKEN.NE)) {
|
|
617
|
+
const op = OP_FOR_TOKEN[this.type]
|
|
618
|
+
expr = this.#node(this.#advanceToken(), op, [expr, this.parseRelational()])
|
|
619
|
+
}
|
|
620
|
+
return expr
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Relational ::= Additive (('<' | '<=' | '>' | '>=' | 'in') Additive)*
|
|
624
|
+
parseRelational() {
|
|
625
|
+
let expr = this.parseAdditive()
|
|
626
|
+
while (
|
|
627
|
+
this.match(TOKEN.LT) ||
|
|
628
|
+
this.match(TOKEN.LE) ||
|
|
629
|
+
this.match(TOKEN.GT) ||
|
|
630
|
+
this.match(TOKEN.GE) ||
|
|
631
|
+
this.match(TOKEN.IN)
|
|
632
|
+
) {
|
|
633
|
+
const op = OP_FOR_TOKEN[this.type]
|
|
634
|
+
expr = this.#node(this.#advanceToken(), op, [expr, this.parseAdditive()])
|
|
635
|
+
}
|
|
636
|
+
return expr
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Additive ::= Multiplicative (('+' | '-') Multiplicative)*
|
|
640
|
+
parseAdditive() {
|
|
641
|
+
let expr = this.parseMultiplicative()
|
|
642
|
+
while (this.match(TOKEN.PLUS) || this.match(TOKEN.MINUS)) {
|
|
643
|
+
const op = OP_FOR_TOKEN[this.type]
|
|
644
|
+
expr = this.#node(this.#advanceToken(), op, [expr, this.parseMultiplicative()])
|
|
645
|
+
}
|
|
646
|
+
return expr
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// Multiplicative ::= Unary (('*' | '/' | '%') Unary)*
|
|
650
|
+
parseMultiplicative() {
|
|
651
|
+
let expr = this.parseUnary()
|
|
652
|
+
while (this.match(TOKEN.MULTIPLY) || this.match(TOKEN.DIVIDE) || this.match(TOKEN.MODULO)) {
|
|
653
|
+
const op = OP_FOR_TOKEN[this.type]
|
|
654
|
+
expr = this.#node(this.#advanceToken(), op, [expr, this.parseUnary()])
|
|
655
|
+
}
|
|
656
|
+
return expr
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Unary ::= ('!' | '-')* Postfix
|
|
660
|
+
parseUnary() {
|
|
661
|
+
if (this.type === TOKEN.NOT)
|
|
662
|
+
return this.#node(this.#advanceToken(), OPS.unaryNot, this.parseUnary())
|
|
663
|
+
if (this.type === TOKEN.MINUS)
|
|
664
|
+
return this.#node(this.#advanceToken(), OPS.unaryMinus, this.parseUnary())
|
|
665
|
+
return this.parsePostfix()
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Postfix ::= Primary (('.' IDENTIFIER ('(' ArgumentList ')')? | '[' Expression ']'))*
|
|
669
|
+
parsePostfix() {
|
|
670
|
+
let expr = this.parsePrimary()
|
|
671
|
+
const depth = this.maxDepthRemaining
|
|
672
|
+
while (true) {
|
|
673
|
+
if (this.match(TOKEN.DOT)) {
|
|
674
|
+
const dot = this.#advanceToken()
|
|
675
|
+
if (!this.maxDepthRemaining--) this.#limitExceeded('maxDepth', dot)
|
|
676
|
+
|
|
677
|
+
const op =
|
|
678
|
+
this.match(TOKEN.QUESTION) && this.registry.enableOptionalTypes && this.#advanceToken()
|
|
679
|
+
? OPS.optionalFieldAccess
|
|
680
|
+
: OPS.fieldAccess
|
|
681
|
+
|
|
682
|
+
const propertyValue = this.value
|
|
683
|
+
const propertyPos = this.consume(TOKEN.IDENTIFIER)
|
|
684
|
+
if (op === OPS.fieldAccess && this.match(TOKEN.LPAREN) && this.#advanceToken()) {
|
|
685
|
+
const args = this.parseArgumentList()
|
|
686
|
+
this.consume(TOKEN.RPAREN)
|
|
687
|
+
expr = this.#expandMacro(propertyPos, OPS.rcall, [propertyValue, expr, args])
|
|
688
|
+
} else {
|
|
689
|
+
expr = this.#node(propertyPos, op, [expr, propertyValue])
|
|
690
|
+
}
|
|
691
|
+
continue
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (this.match(TOKEN.LBRACKET)) {
|
|
695
|
+
const bracket = this.#advanceToken()
|
|
696
|
+
if (!this.maxDepthRemaining--) this.#limitExceeded('maxDepth', bracket)
|
|
697
|
+
|
|
698
|
+
const op =
|
|
699
|
+
this.match(TOKEN.QUESTION) && this.registry.enableOptionalTypes && this.#advanceToken()
|
|
700
|
+
? OPS.optionalBracketAccess
|
|
701
|
+
: OPS.bracketAccess
|
|
702
|
+
|
|
703
|
+
const index = this.parseExpression()
|
|
704
|
+
this.consume(TOKEN.RBRACKET)
|
|
705
|
+
expr = this.#node(bracket, op, [expr, index])
|
|
706
|
+
continue
|
|
707
|
+
}
|
|
708
|
+
break
|
|
709
|
+
}
|
|
710
|
+
this.maxDepthRemaining = depth
|
|
711
|
+
return expr
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// Primary ::= NUMBER | STRING | BOOLEAN | NULL | IDENTIFIER | '(' Expression ')' | Array | Object
|
|
715
|
+
parsePrimary() {
|
|
716
|
+
switch (this.type) {
|
|
717
|
+
case TOKEN.NUMBER:
|
|
718
|
+
case TOKEN.STRING:
|
|
719
|
+
case TOKEN.BYTES:
|
|
720
|
+
case TOKEN.BOOLEAN:
|
|
721
|
+
case TOKEN.NULL:
|
|
722
|
+
return this.#consumeLiteral()
|
|
723
|
+
case TOKEN.IDENTIFIER:
|
|
724
|
+
return this.#parseIdentifierPrimary()
|
|
725
|
+
case TOKEN.LPAREN:
|
|
726
|
+
return this.#parseParenthesizedExpression()
|
|
727
|
+
case TOKEN.LBRACKET:
|
|
728
|
+
return this.parseList()
|
|
729
|
+
case TOKEN.LBRACE:
|
|
730
|
+
return this.parseMap()
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
throw new ParseError(`Unexpected token: ${TOKEN_BY_NUMBER[this.type]}`, {
|
|
734
|
+
pos: this.pos,
|
|
735
|
+
input: this.input
|
|
736
|
+
})
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
#consumeLiteral() {
|
|
740
|
+
return this.#advanceToken(this.#node(this.pos, OPS.value, this.value))
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
#parseIdentifierPrimary() {
|
|
744
|
+
const value = this.value
|
|
745
|
+
const pos = this.consume(TOKEN.IDENTIFIER)
|
|
746
|
+
if (RESERVED.has(value)) {
|
|
747
|
+
throw new ParseError(`Reserved identifier: ${value}`, {
|
|
748
|
+
pos: pos,
|
|
749
|
+
input: this.input
|
|
750
|
+
})
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
if (!this.match(TOKEN.LPAREN)) return this.#node(pos, OPS.id, value)
|
|
754
|
+
this.#advanceToken()
|
|
755
|
+
const args = this.parseArgumentList()
|
|
756
|
+
this.consume(TOKEN.RPAREN)
|
|
757
|
+
return this.#expandMacro(pos, OPS.call, [value, args])
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
#parseParenthesizedExpression() {
|
|
761
|
+
this.consume(TOKEN.LPAREN)
|
|
762
|
+
const expr = this.parseExpression()
|
|
763
|
+
this.consume(TOKEN.RPAREN)
|
|
764
|
+
return expr
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
parseList() {
|
|
768
|
+
const token = this.consume(TOKEN.LBRACKET)
|
|
769
|
+
const elements = []
|
|
770
|
+
let remainingElements = this.limits.maxListElements
|
|
771
|
+
|
|
772
|
+
if (!this.match(TOKEN.RBRACKET)) {
|
|
773
|
+
elements.push(this.parseExpression())
|
|
774
|
+
if (!remainingElements--) this.#limitExceeded('maxListElements', elements.at(-1).pos)
|
|
775
|
+
while (this.match(TOKEN.COMMA)) {
|
|
776
|
+
this.#advanceToken()
|
|
777
|
+
if (this.match(TOKEN.RBRACKET)) break
|
|
778
|
+
elements.push(this.parseExpression())
|
|
779
|
+
if (!remainingElements--) this.#limitExceeded('maxListElements', elements.at(-1).pos)
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
this.consume(TOKEN.RBRACKET)
|
|
784
|
+
return this.#node(token, OPS.list, elements)
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
parseMap() {
|
|
788
|
+
const token = this.consume(TOKEN.LBRACE)
|
|
789
|
+
const props = []
|
|
790
|
+
let remainingEntries = this.limits.maxMapEntries
|
|
791
|
+
|
|
792
|
+
if (!this.match(TOKEN.RBRACE)) {
|
|
793
|
+
props.push(this.parseProperty())
|
|
794
|
+
if (!remainingEntries--) this.#limitExceeded('maxMapEntries', props.at(-1)[0].pos)
|
|
795
|
+
while (this.match(TOKEN.COMMA)) {
|
|
796
|
+
this.#advanceToken()
|
|
797
|
+
if (this.match(TOKEN.RBRACE)) break
|
|
798
|
+
props.push(this.parseProperty())
|
|
799
|
+
if (!remainingEntries--) this.#limitExceeded('maxMapEntries', props.at(-1)[0].pos)
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
this.consume(TOKEN.RBRACE)
|
|
804
|
+
return this.#node(token, OPS.map, props)
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
parseProperty() {
|
|
808
|
+
return [this.parseExpression(), (this.consume(TOKEN.COLON), this.parseExpression())]
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
parseArgumentList() {
|
|
812
|
+
const args = []
|
|
813
|
+
let remainingArgs = this.limits.maxCallArguments
|
|
814
|
+
|
|
815
|
+
if (!this.match(TOKEN.RPAREN)) {
|
|
816
|
+
args.push(this.parseExpression())
|
|
817
|
+
if (!remainingArgs--) this.#limitExceeded('maxCallArguments', args.at(-1).pos)
|
|
818
|
+
while (this.match(TOKEN.COMMA)) {
|
|
819
|
+
this.#advanceToken()
|
|
820
|
+
if (this.match(TOKEN.RPAREN)) break
|
|
821
|
+
args.push(this.parseExpression())
|
|
822
|
+
if (!remainingArgs--) this.#limitExceeded('maxCallArguments', args.at(-1).pos)
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return args
|
|
826
|
+
}
|
|
827
|
+
}
|