@shwfed/nuxt 0.7.7 → 0.7.9
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/module.mjs +22 -32
- package/dist/runtime/components/app.d.vue.ts +0 -2
- package/dist/runtime/components/app.vue +1 -7
- package/dist/runtime/components/app.vue.d.ts +0 -2
- package/dist/runtime/components/fields.d.vue.ts +155 -0
- package/dist/runtime/components/fields.vue +312 -0
- package/dist/runtime/components/fields.vue.d.ts +155 -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/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/field/FieldDescription.vue +1 -1
- package/dist/runtime/components/ui/field/FieldError.vue +1 -1
- 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/input/Input.vue +1 -1
- package/dist/runtime/components/ui/input-group/InputGroup.vue +3 -0
- package/dist/runtime/components/ui/input-group/InputGroupCombobox.d.vue.ts +4 -1
- package/dist/runtime/components/ui/input-group/InputGroupCombobox.vue +10 -4
- package/dist/runtime/components/ui/input-group/InputGroupCombobox.vue.d.ts +4 -1
- package/dist/runtime/components/ui/input-group/InputGroupComboboxInput.vue +3 -1
- package/dist/runtime/components/ui/input-group/InputGroupInput.vue +1 -1
- package/dist/runtime/components/ui/input-group/InputGroupNumberField.vue +1 -1
- 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 +1 -1
- package/dist/runtime/components/ui/label/Label.vue +1 -1
- package/dist/runtime/components/ui/native-select/NativeSelect.vue +3 -3
- 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/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/textarea/Textarea.vue +1 -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/utils/coders.d.ts +7 -0
- package/dist/runtime/utils/coders.js +39 -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 +7 -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
|
@@ -0,0 +1,1596 @@
|
|
|
1
|
+
import {EvaluationError} from './errors.js'
|
|
2
|
+
import {UnsignedInt} from './functions.js'
|
|
3
|
+
import {Optional, OPTIONAL_NONE, toggleOptionalTypes} from './optional.js'
|
|
4
|
+
import {isAsync, hasOwn, objFreeze, objKeys, objEntries, RESERVED} from './globals.js'
|
|
5
|
+
|
|
6
|
+
export class Type {
|
|
7
|
+
#name
|
|
8
|
+
constructor(name) {
|
|
9
|
+
this.#name = name
|
|
10
|
+
objFreeze(this)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
get name() {
|
|
14
|
+
return this.#name
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get [Symbol.toStringTag]() {
|
|
18
|
+
return `Type<${this.#name}>`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
toString() {
|
|
22
|
+
return `Type<${this.#name}>`
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const TYPES = {
|
|
27
|
+
string: new Type('string'),
|
|
28
|
+
bool: new Type('bool'),
|
|
29
|
+
int: new Type('int'),
|
|
30
|
+
uint: new Type('uint'),
|
|
31
|
+
double: new Type('double'),
|
|
32
|
+
map: new Type('map'),
|
|
33
|
+
list: new Type('list'),
|
|
34
|
+
bytes: new Type('bytes'),
|
|
35
|
+
null_type: new Type('null'),
|
|
36
|
+
type: new Type('type')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// not exposed to cel expression
|
|
40
|
+
const optionalType = new Type('optional')
|
|
41
|
+
|
|
42
|
+
export class TypeDeclaration {
|
|
43
|
+
#matchesCache = new WeakMap()
|
|
44
|
+
constructor({kind, type, name, keyType, valueType}) {
|
|
45
|
+
this.kind = kind
|
|
46
|
+
this.type = type
|
|
47
|
+
this.name = name
|
|
48
|
+
this.keyType = keyType
|
|
49
|
+
this.valueType = valueType
|
|
50
|
+
|
|
51
|
+
this.unwrappedType = kind === 'dyn' && valueType ? valueType.unwrappedType : this
|
|
52
|
+
this.wrappedType = kind === 'dyn' ? this : _createDynType(this.unwrappedType)
|
|
53
|
+
|
|
54
|
+
this.hasDynType =
|
|
55
|
+
this.kind === 'dyn' || this.valueType?.hasDynType || this.keyType?.hasDynType || false
|
|
56
|
+
|
|
57
|
+
this.hasPlaceholderType =
|
|
58
|
+
this.kind === 'param' ||
|
|
59
|
+
this.keyType?.hasPlaceholderType ||
|
|
60
|
+
this.valueType?.hasPlaceholderType ||
|
|
61
|
+
false
|
|
62
|
+
|
|
63
|
+
if (kind === 'list') this.fieldLazy = this.#getListField
|
|
64
|
+
else if (kind === 'map') this.fieldLazy = this.#getMapField
|
|
65
|
+
else if (kind === 'message') this.fieldLazy = this.#getMessageField
|
|
66
|
+
else if (kind === 'optional') this.fieldLazy = this.#getOptionalField
|
|
67
|
+
|
|
68
|
+
objFreeze(this)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** @deprecated Please use .hasDynType */
|
|
72
|
+
hasDyn() {
|
|
73
|
+
return this.hasDynType
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** @deprecated Please use .hasDynType === false */
|
|
77
|
+
hasNoDynTypes() {
|
|
78
|
+
return this.hasDynType === false
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
isDynOrBool() {
|
|
82
|
+
return this.type === 'bool' || this.kind === 'dyn'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
isEmpty() {
|
|
86
|
+
return this.valueType && this.valueType.kind === 'param'
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** @deprecated Please use .hasPlaceholderType */
|
|
90
|
+
hasPlaceholder() {
|
|
91
|
+
return this.hasPlaceholderType
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
unify(r, t2) {
|
|
95
|
+
const t1 = this
|
|
96
|
+
if (t1 === t2 || t1.kind === 'dyn' || t2.kind === 'param') return t1
|
|
97
|
+
if (t2.kind === 'dyn' || t1.kind === 'param') return t2
|
|
98
|
+
if (t1.kind !== t2.kind) return null
|
|
99
|
+
if (!(t1.hasPlaceholderType || t2.hasPlaceholderType || t1.hasDynType || t2.hasDynType))
|
|
100
|
+
return null
|
|
101
|
+
|
|
102
|
+
const valueType = t1.valueType.unify(r, t2.valueType)
|
|
103
|
+
if (!valueType) return null
|
|
104
|
+
switch (t1.kind) {
|
|
105
|
+
case 'optional':
|
|
106
|
+
return r.getOptionalType(valueType)
|
|
107
|
+
case 'list':
|
|
108
|
+
return r.getListType(valueType)
|
|
109
|
+
case 'map':
|
|
110
|
+
const keyType = t1.keyType.unify(r, t2.keyType)
|
|
111
|
+
return keyType ? r.getMapType(keyType, valueType) : null
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
templated(r, bind) {
|
|
116
|
+
if (!this.hasPlaceholderType) return this
|
|
117
|
+
|
|
118
|
+
switch (this.kind) {
|
|
119
|
+
case 'dyn':
|
|
120
|
+
return this.valueType.templated(r, bind)
|
|
121
|
+
case 'param':
|
|
122
|
+
return bind?.get(this.name) || this
|
|
123
|
+
case 'map':
|
|
124
|
+
return r.getMapType(this.keyType.templated(r, bind), this.valueType.templated(r, bind))
|
|
125
|
+
case 'list':
|
|
126
|
+
return r.getListType(this.valueType.templated(r, bind))
|
|
127
|
+
case 'optional':
|
|
128
|
+
return r.getOptionalType(this.valueType.templated(r, bind))
|
|
129
|
+
default:
|
|
130
|
+
return this
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
toString() {
|
|
135
|
+
return this.name
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
#getOptionalField(obj, key, ast, ev) {
|
|
139
|
+
obj = obj instanceof Optional ? obj.orValue() : obj
|
|
140
|
+
if (obj === undefined) return OPTIONAL_NONE
|
|
141
|
+
|
|
142
|
+
const type = ev.debugType(obj)
|
|
143
|
+
try {
|
|
144
|
+
return Optional.of(type.fieldLazy(obj, key, ast, ev))
|
|
145
|
+
} catch (e) {
|
|
146
|
+
if (e instanceof EvaluationError) return OPTIONAL_NONE
|
|
147
|
+
throw e
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
#getMessageField(obj, key, ast, ev) {
|
|
152
|
+
const message = obj ? ev.objectTypesByConstructor.get(obj.constructor) : undefined
|
|
153
|
+
if (!message) return
|
|
154
|
+
|
|
155
|
+
const type = message.fields ? message.fields[key] : dynType
|
|
156
|
+
if (!type) return undefined
|
|
157
|
+
|
|
158
|
+
const value = obj instanceof Map ? obj.get(key) : obj[key]
|
|
159
|
+
if (value === undefined) return
|
|
160
|
+
|
|
161
|
+
const valueType = ev.debugType(value)
|
|
162
|
+
if (type.matchesDebugType(valueType)) return value
|
|
163
|
+
throw new EvaluationError(`Field '${key}' is not of type '${type}', got '${valueType}'`, ast)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
#getMapField(obj, key, ast, ev) {
|
|
167
|
+
if (obj instanceof Map) {
|
|
168
|
+
const value = obj.get(key)
|
|
169
|
+
if (value === undefined) return
|
|
170
|
+
|
|
171
|
+
const type = ev.debugType(value)
|
|
172
|
+
if (this.valueType.matchesDebugType(type)) return value
|
|
173
|
+
|
|
174
|
+
throw new EvaluationError(
|
|
175
|
+
`Field '${key}' is not of type '${this.valueType}', got '${type}'`,
|
|
176
|
+
ast
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!obj) return
|
|
181
|
+
|
|
182
|
+
// Read through the proxy first so Vue can track missing-to-present updates.
|
|
183
|
+
const value = obj[key]
|
|
184
|
+
if (!hasOwn(obj, key) || value === undefined) return
|
|
185
|
+
|
|
186
|
+
const type = ev.debugType(value)
|
|
187
|
+
if (this.valueType.matchesDebugType(type)) return value
|
|
188
|
+
|
|
189
|
+
throw new EvaluationError(
|
|
190
|
+
`Field '${key}' is not of type '${this.valueType}', got '${type}'`,
|
|
191
|
+
ast
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
#getListElementAtIndex(list, pos) {
|
|
196
|
+
switch (list?.constructor) {
|
|
197
|
+
case Array:
|
|
198
|
+
return list[pos]
|
|
199
|
+
case Set: {
|
|
200
|
+
let i = 0
|
|
201
|
+
for (const item of list) {
|
|
202
|
+
if (i++ !== pos) continue
|
|
203
|
+
return item
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
#getListField(obj, key, ast, ev) {
|
|
210
|
+
if (typeof key === 'bigint') key = Number(key)
|
|
211
|
+
else if (typeof key !== 'number') return
|
|
212
|
+
|
|
213
|
+
const value = this.#getListElementAtIndex(obj, key)
|
|
214
|
+
if (value === undefined) {
|
|
215
|
+
if (!obj) return
|
|
216
|
+
throw new EvaluationError(
|
|
217
|
+
`No such key: index out of bounds, index ${key} ${
|
|
218
|
+
key < 0 ? '< 0' : `>= size ${obj.length || obj.size}`
|
|
219
|
+
}`,
|
|
220
|
+
ast
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const type = ev.debugType(value)
|
|
225
|
+
if (this.valueType.matchesDebugType(type)) return value
|
|
226
|
+
|
|
227
|
+
throw new EvaluationError(
|
|
228
|
+
`List item with index '${key}' is not of type '${this.valueType}', got '${type}'`,
|
|
229
|
+
ast
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
fieldLazy() {}
|
|
234
|
+
|
|
235
|
+
field(obj, key, ast, ev) {
|
|
236
|
+
const v = this.fieldLazy(obj, key, ast, ev)
|
|
237
|
+
if (v !== undefined) return v
|
|
238
|
+
throw new EvaluationError(`No such key: ${key}`, ast)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
matchesBoth(other) {
|
|
242
|
+
return this.matches(other) && other.matches(this)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
matchesDebugType(o) {
|
|
246
|
+
return this === o || this === dynType || !!this.valueType
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
matches(o) {
|
|
250
|
+
const s = this.unwrappedType
|
|
251
|
+
o = o.unwrappedType
|
|
252
|
+
if (s === o || s.kind === 'dyn' || o.kind === 'dyn' || o.kind === 'param') return true
|
|
253
|
+
return this.#matchesCache.get(o) ?? this.#matchesCache.set(o, this.#matches(s, o)).get(o)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
#matches(s, o) {
|
|
257
|
+
switch (s.kind) {
|
|
258
|
+
case 'dyn':
|
|
259
|
+
case 'param':
|
|
260
|
+
return true
|
|
261
|
+
case 'list':
|
|
262
|
+
return o.kind === 'list' && s.valueType.matches(o.valueType)
|
|
263
|
+
case 'map':
|
|
264
|
+
return o.kind === 'map' && s.keyType.matches(o.keyType) && s.valueType.matches(o.valueType)
|
|
265
|
+
case 'optional':
|
|
266
|
+
return o.kind === 'optional' && s.valueType.matches(o.valueType)
|
|
267
|
+
default:
|
|
268
|
+
return s.name === o.name
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const macroEvaluateErr = `have a .callAst property or .evaluate(checker, macro, ctx) method.`
|
|
274
|
+
const macroTypeCheckErr = `have a .callAst property or .typeCheck(checker, macro, ctx) method.`
|
|
275
|
+
function wrapMacroExpander(name, handler) {
|
|
276
|
+
const p = `Macro '${name}' must`
|
|
277
|
+
return function macroExpander(opts) {
|
|
278
|
+
const macro = handler(opts)
|
|
279
|
+
if (!macro || typeof macro !== 'object') throw new Error(`${p} return an object.`)
|
|
280
|
+
if (macro.callAst) return macro
|
|
281
|
+
if (!macro.evaluate) throw new Error(`${p} ${macroEvaluateErr}`)
|
|
282
|
+
if (!macro.typeCheck) throw new Error(`${p} ${macroTypeCheckErr}`)
|
|
283
|
+
return macro
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export class VariableDeclaration {
|
|
288
|
+
constructor(name, type, description, value) {
|
|
289
|
+
this.name = name
|
|
290
|
+
this.type = type
|
|
291
|
+
this.description = description ?? null
|
|
292
|
+
this.constant = value !== undefined
|
|
293
|
+
this.value = value
|
|
294
|
+
objFreeze(this)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export class FunctionDeclaration {
|
|
299
|
+
constructor({name, receiverType, returnType, handler, description, params, async}) {
|
|
300
|
+
if (typeof name !== 'string') throw new Error('name must be a string')
|
|
301
|
+
if (typeof handler !== 'function') throw new Error('handler must be a function')
|
|
302
|
+
|
|
303
|
+
this.name = name
|
|
304
|
+
this.async = isAsync(handler, async)
|
|
305
|
+
this.receiverType = receiverType ?? null
|
|
306
|
+
this.returnType = returnType
|
|
307
|
+
this.description = description ?? null
|
|
308
|
+
this.params = params
|
|
309
|
+
this.argTypes = params.map((p) => p.type)
|
|
310
|
+
this.macro = this.argTypes.includes(astType)
|
|
311
|
+
|
|
312
|
+
const receiverString = receiverType ? `${receiverType}.` : ''
|
|
313
|
+
this.signature = `${receiverString}${name}(${this.argTypes.join(', ')}): ${returnType}`
|
|
314
|
+
this.handler = this.macro ? wrapMacroExpander(this.signature, handler) : handler
|
|
315
|
+
this.partitionKey = `${receiverType ? 'rcall' : 'call'}:${name}:${params.length}`
|
|
316
|
+
|
|
317
|
+
this.hasPlaceholderType =
|
|
318
|
+
this.returnType.hasPlaceholderType ||
|
|
319
|
+
this.receiverType?.hasPlaceholderType ||
|
|
320
|
+
this.argTypes.some((t) => t.hasPlaceholderType) ||
|
|
321
|
+
false
|
|
322
|
+
|
|
323
|
+
objFreeze(this)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
hasPlaceholder() {
|
|
327
|
+
return this.hasPlaceholderType
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
matchesArgs(argTypes) {
|
|
331
|
+
return argTypes.length === this.argTypes.length &&
|
|
332
|
+
this.argTypes.every((t, i) => t.matches(argTypes[i]))
|
|
333
|
+
? this
|
|
334
|
+
: null
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export class OperatorDeclaration {
|
|
339
|
+
constructor({op, leftType, rightType, handler, returnType, async}) {
|
|
340
|
+
this.operator = op
|
|
341
|
+
this.leftType = leftType
|
|
342
|
+
this.rightType = rightType || null
|
|
343
|
+
this.handler = handler
|
|
344
|
+
this.async = isAsync(handler, async)
|
|
345
|
+
this.returnType = returnType
|
|
346
|
+
|
|
347
|
+
if (rightType) this.signature = `${leftType} ${op} ${rightType}: ${returnType}`
|
|
348
|
+
else this.signature = `${op}${leftType}: ${returnType}`
|
|
349
|
+
|
|
350
|
+
this.hasPlaceholderType =
|
|
351
|
+
this.leftType.hasPlaceholderType || this.rightType?.hasPlaceholderType || false
|
|
352
|
+
|
|
353
|
+
objFreeze(this)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
hasPlaceholder() {
|
|
357
|
+
return this.hasPlaceholderType
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
equals(other) {
|
|
361
|
+
return (
|
|
362
|
+
this.operator === other.operator &&
|
|
363
|
+
this.leftType === other.leftType &&
|
|
364
|
+
this.rightType === other.rightType
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function _createListType(valueType) {
|
|
370
|
+
return new TypeDeclaration({
|
|
371
|
+
kind: 'list',
|
|
372
|
+
name: `list<${valueType}>`,
|
|
373
|
+
type: 'list',
|
|
374
|
+
valueType
|
|
375
|
+
})
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function _createPrimitiveType(name) {
|
|
379
|
+
return new TypeDeclaration({kind: 'primitive', name, type: name})
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function _createMessageType(name) {
|
|
383
|
+
return new TypeDeclaration({kind: 'message', name, type: name})
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function _createDynType(valueType) {
|
|
387
|
+
const name = valueType ? `dyn<${valueType}>` : 'dyn'
|
|
388
|
+
return new TypeDeclaration({kind: 'dyn', name, type: name, valueType})
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function _createOptionalType(valueType) {
|
|
392
|
+
const name = `optional<${valueType}>`
|
|
393
|
+
return new TypeDeclaration({kind: 'optional', name, type: 'optional', valueType})
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function _createMapType(keyType, valueType) {
|
|
397
|
+
return new TypeDeclaration({
|
|
398
|
+
kind: 'map',
|
|
399
|
+
name: `map<${keyType}, ${valueType}>`,
|
|
400
|
+
type: 'map',
|
|
401
|
+
keyType: keyType,
|
|
402
|
+
valueType: valueType
|
|
403
|
+
})
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function _createPlaceholderType(name) {
|
|
407
|
+
return new TypeDeclaration({kind: 'param', name, type: name})
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Global immutable cache for built-in primitive types (shared across all registries)
|
|
411
|
+
const dynType = _createDynType()
|
|
412
|
+
const astType = _createPrimitiveType('ast')
|
|
413
|
+
const listType = _createListType(dynType)
|
|
414
|
+
const mapType = _createMapType(dynType, dynType)
|
|
415
|
+
export const celTypes = {
|
|
416
|
+
string: _createPrimitiveType('string'),
|
|
417
|
+
bool: _createPrimitiveType('bool'),
|
|
418
|
+
int: _createPrimitiveType('int'),
|
|
419
|
+
uint: _createPrimitiveType('uint'),
|
|
420
|
+
double: _createPrimitiveType('double'),
|
|
421
|
+
bytes: _createPrimitiveType('bytes'),
|
|
422
|
+
dyn: dynType,
|
|
423
|
+
null: _createPrimitiveType('null'),
|
|
424
|
+
type: _createPrimitiveType('type'),
|
|
425
|
+
optional: _createOptionalType(dynType),
|
|
426
|
+
list: listType,
|
|
427
|
+
'list<dyn>': listType,
|
|
428
|
+
map: mapType,
|
|
429
|
+
'map<dyn, dyn>': mapType
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
for (const t of [celTypes.string, celTypes.double, celTypes.int]) {
|
|
433
|
+
const list = _createListType(t)
|
|
434
|
+
const map = _createMapType(celTypes.string, t)
|
|
435
|
+
celTypes[list.name] = list
|
|
436
|
+
celTypes[map.name] = map
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
Object.freeze(celTypes)
|
|
440
|
+
|
|
441
|
+
class Candidates {
|
|
442
|
+
returnType = null
|
|
443
|
+
async = false
|
|
444
|
+
macro = false
|
|
445
|
+
#matchCache = null
|
|
446
|
+
#checkCache = null
|
|
447
|
+
/** @type {Array<FunctionDeclaration>|Array<OperatorDeclaration>} */
|
|
448
|
+
declarations = []
|
|
449
|
+
constructor(registry) {
|
|
450
|
+
this.registry = registry
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
[Symbol.iterator]() {
|
|
454
|
+
return this.declarations[Symbol.iterator]()
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
add(decl) {
|
|
458
|
+
this.returnType =
|
|
459
|
+
(this.returnType || decl.returnType).unify(this.registry, decl.returnType) || dynType
|
|
460
|
+
|
|
461
|
+
if (decl.macro) this.macro = decl
|
|
462
|
+
if (decl.async && !this.async) this.async = true
|
|
463
|
+
this.declarations.push(decl)
|
|
464
|
+
this.#matchCache?.clear()
|
|
465
|
+
this.#checkCache?.clear()
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
findFunction(argTypes, receiverType = null) {
|
|
469
|
+
for (let i = 0; i < this.declarations.length; i++) {
|
|
470
|
+
const match = this.#matchesFunction(this.declarations[i], argTypes, receiverType)
|
|
471
|
+
if (match) return match
|
|
472
|
+
}
|
|
473
|
+
return null
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
findUnaryOverload(left) {
|
|
477
|
+
const cached = (this.#matchCache ??= new Map()).get(left)
|
|
478
|
+
if (cached !== undefined) return cached
|
|
479
|
+
|
|
480
|
+
let value = false
|
|
481
|
+
for (const decl of this.declarations) {
|
|
482
|
+
if (decl.leftType !== left) continue
|
|
483
|
+
value = decl
|
|
484
|
+
break
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
this.#matchCache.set(left, value)
|
|
488
|
+
return value
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
findBinaryOverload(left, right) {
|
|
492
|
+
if (left.kind === 'dyn' && left.valueType) right = right.wrappedType
|
|
493
|
+
else if (right.kind === 'dyn' && right.valueType) left = left.wrappedType
|
|
494
|
+
return (
|
|
495
|
+
(this.#matchCache ??= new Map()).get(left)?.get(right) ??
|
|
496
|
+
this.#cacheBinary(this.#matchCache, left, right, this.#findBinaryUncached(left, right))
|
|
497
|
+
)
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
checkBinaryOverload(left, right) {
|
|
501
|
+
return (
|
|
502
|
+
(this.#checkCache ??= new Map()).get(left)?.get(right) ??
|
|
503
|
+
this.#cacheBinary(this.#checkCache, left, right, this.#checkBinaryUncached(left, right))
|
|
504
|
+
)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
#cacheBinary(cache, left, right, result) {
|
|
508
|
+
cache = cache.get(left) || cache.set(left, new Map()).get(left)
|
|
509
|
+
return (cache.set(right, result), result)
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
#findBinaryUncached(left, right) {
|
|
513
|
+
const ops = this.#findBinaryOverloads(left, right)
|
|
514
|
+
if (ops.length === 0) return false
|
|
515
|
+
if (ops.length === 1) return ops[0]
|
|
516
|
+
throw new Error(`Operator overload '${ops[0].signature}' overlaps with '${ops[1].signature}'.`)
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
#checkBinaryUncached(left, right) {
|
|
520
|
+
const ops = this.#findBinaryOverloads(left, right)
|
|
521
|
+
if (ops.length === 0) return false
|
|
522
|
+
let rt = ops[0].returnType
|
|
523
|
+
for (let i = 1; i < ops.length; i++) rt = rt.unify(this.registry, ops[i].returnType) || dynType
|
|
524
|
+
return rt
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
#findBinaryOverloads(leftType, rightType) {
|
|
528
|
+
const nonexactMatches = []
|
|
529
|
+
for (const decl of this.declarations) {
|
|
530
|
+
if (decl.leftType === leftType && decl.rightType === rightType) return [decl]
|
|
531
|
+
const secondary = this.#matchBinaryOverload(decl, leftType, rightType)
|
|
532
|
+
if (secondary) nonexactMatches.push(secondary)
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (nonexactMatches.length === 0) {
|
|
536
|
+
const op = this.declarations[0]?.operator
|
|
537
|
+
if ((op === '==' || op === '!=') && leftType.kind === 'dyn') {
|
|
538
|
+
return fallbackDynEqualityMatchers[op]
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return nonexactMatches
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
#matchBinaryOverload(decl, actualLeft, actualRight) {
|
|
546
|
+
const bindings = decl.hasPlaceholderType ? new Map() : null
|
|
547
|
+
const leftType = this.registry.matchTypeWithPlaceholders(decl.leftType, actualLeft, bindings)
|
|
548
|
+
if (!leftType) return
|
|
549
|
+
const rightType = this.registry.matchTypeWithPlaceholders(decl.rightType, actualRight, bindings)
|
|
550
|
+
if (!rightType) return
|
|
551
|
+
|
|
552
|
+
if (
|
|
553
|
+
(decl.operator === '==' || decl.operator === '!=') &&
|
|
554
|
+
decl.leftType.kind === 'dyn' &&
|
|
555
|
+
decl.leftType.valueType &&
|
|
556
|
+
actualLeft.kind !== 'dyn' &&
|
|
557
|
+
actualRight.kind !== 'dyn'
|
|
558
|
+
)
|
|
559
|
+
return false
|
|
560
|
+
|
|
561
|
+
return decl.hasPlaceholderType
|
|
562
|
+
? {
|
|
563
|
+
async: decl.async,
|
|
564
|
+
signature: decl.signature,
|
|
565
|
+
handler: decl.handler,
|
|
566
|
+
leftType,
|
|
567
|
+
rightType,
|
|
568
|
+
returnType: decl.returnType.templated(this.registry, bindings)
|
|
569
|
+
}
|
|
570
|
+
: decl
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
#matchesFunction(fn, argTypes, receiverType) {
|
|
574
|
+
if (fn.hasPlaceholderType) return this.#matchWithPlaceholders(fn, argTypes, receiverType)
|
|
575
|
+
if (receiverType && fn.receiverType && !receiverType.matches(fn.receiverType)) return
|
|
576
|
+
return fn.matchesArgs(argTypes)
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
#matchWithPlaceholders(fn, argTypes, receiverType) {
|
|
580
|
+
const bindings = new Map()
|
|
581
|
+
if (receiverType && fn.receiverType) {
|
|
582
|
+
if (!this.registry.matchTypeWithPlaceholders(fn.receiverType, receiverType, bindings)) {
|
|
583
|
+
return null
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
for (let i = 0; i < argTypes.length; i++) {
|
|
588
|
+
if (!this.registry.matchTypeWithPlaceholders(fn.argTypes[i], argTypes[i], bindings)) {
|
|
589
|
+
return null
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
return {
|
|
594
|
+
async: fn.async,
|
|
595
|
+
handler: fn.handler,
|
|
596
|
+
signature: fn.signature,
|
|
597
|
+
returnType: fn.returnType.templated(this.registry, bindings)
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Helper function for splitting map type parameters
|
|
603
|
+
function splitByComma(str) {
|
|
604
|
+
const parts = []
|
|
605
|
+
let current = ''
|
|
606
|
+
let depth = 0
|
|
607
|
+
|
|
608
|
+
for (const char of str) {
|
|
609
|
+
if (char === '<') depth++
|
|
610
|
+
else if (char === '>') depth--
|
|
611
|
+
else if (char === ',' && depth === 0) {
|
|
612
|
+
parts.push(current.trim())
|
|
613
|
+
current = ''
|
|
614
|
+
continue
|
|
615
|
+
}
|
|
616
|
+
current += char
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if (current) parts.push(current.trim())
|
|
620
|
+
return parts
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const objTypesDecls = [
|
|
624
|
+
[UnsignedInt, 'uint', TYPES.uint, celTypes.uint],
|
|
625
|
+
[Type, 'type', TYPES.type, celTypes.type],
|
|
626
|
+
[Optional, 'optional', optionalType, celTypes.optional],
|
|
627
|
+
[Uint8Array, 'bytes', TYPES.bytes, celTypes.bytes],
|
|
628
|
+
...(typeof Buffer !== 'undefined' ? [[Buffer, 'bytes', TYPES.bytes, celTypes.bytes]] : [])
|
|
629
|
+
].map(([ctor, name, typeType, type]) => Object.freeze({name, typeType, type, ctor}))
|
|
630
|
+
|
|
631
|
+
const objTypes = objTypesDecls.map((t) => [t.name, t])
|
|
632
|
+
const objTypesCtor = objTypesDecls.map((t) => [t.ctor, t])
|
|
633
|
+
|
|
634
|
+
const invalidVar = (postfix) => new Error(`Invalid variable declaration: ${postfix}`)
|
|
635
|
+
const invalidType = (postfix) => new Error(`Invalid type declaration: ${postfix}`)
|
|
636
|
+
|
|
637
|
+
const fallbackDynEqualityMatchers = {
|
|
638
|
+
'==': [{handler: (a, b) => a === b, returnType: celTypes.bool}],
|
|
639
|
+
'!=': [{handler: (a, b) => a !== b, returnType: celTypes.bool}]
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
export class Registry {
|
|
643
|
+
#parent = null
|
|
644
|
+
#typeDeclarations
|
|
645
|
+
|
|
646
|
+
#ownsVariables = true
|
|
647
|
+
#operators = null
|
|
648
|
+
#functions = null
|
|
649
|
+
#operatorsByOp = null
|
|
650
|
+
#functionsByKey = null
|
|
651
|
+
#listTypes = null
|
|
652
|
+
#mapTypes = null
|
|
653
|
+
#optionalTypes = null
|
|
654
|
+
#others = null
|
|
655
|
+
|
|
656
|
+
#locked = false
|
|
657
|
+
|
|
658
|
+
constructor(opts = {}) {
|
|
659
|
+
this.enableOptionalTypes = opts.enableOptionalTypes ?? false
|
|
660
|
+
this.unlistedVariablesAreDyn = opts.unlistedVariablesAreDyn ?? false
|
|
661
|
+
|
|
662
|
+
const parent = opts.parent instanceof Registry ? opts.parent : null
|
|
663
|
+
if (parent) {
|
|
664
|
+
this.#parent = parent
|
|
665
|
+
|
|
666
|
+
let opParent = parent
|
|
667
|
+
while (opParent && !opParent.#operators) opParent = opParent.#parent
|
|
668
|
+
|
|
669
|
+
let fnParent = parent
|
|
670
|
+
while (fnParent && !fnParent.#functions) fnParent = fnParent.#parent
|
|
671
|
+
|
|
672
|
+
this.#operatorsByOp = parent.#operatorsByOp
|
|
673
|
+
this.#functionsByKey = parent.#functionsByKey
|
|
674
|
+
this.#others = {operators: opParent.#operators, functions: fnParent.#functions}
|
|
675
|
+
|
|
676
|
+
this.objectTypes = new Map(parent.objectTypes)
|
|
677
|
+
this.objectTypesByConstructor = new Map(parent.objectTypesByConstructor)
|
|
678
|
+
this.variables = parent.variables
|
|
679
|
+
this.#ownsVariables = false
|
|
680
|
+
this.#typeDeclarations = parent.#typeDeclarations
|
|
681
|
+
this.#listTypes = parent.#listTypes
|
|
682
|
+
this.#mapTypes = parent.#mapTypes
|
|
683
|
+
this.#optionalTypes = parent.#optionalTypes
|
|
684
|
+
|
|
685
|
+
if (
|
|
686
|
+
this.enableOptionalTypes !== parent.enableOptionalTypes ||
|
|
687
|
+
this.unlistedVariablesAreDyn !== parent.unlistedVariablesAreDyn
|
|
688
|
+
) {
|
|
689
|
+
toggleOptionalTypes(this, this.enableOptionalTypes)
|
|
690
|
+
}
|
|
691
|
+
} else {
|
|
692
|
+
this.#operators = []
|
|
693
|
+
this.#functions = []
|
|
694
|
+
this.objectTypes = new Map(objTypes)
|
|
695
|
+
this.objectTypesByConstructor = new Map(objTypesCtor)
|
|
696
|
+
this.#typeDeclarations = new Map(objEntries(celTypes))
|
|
697
|
+
this.#listTypes = new Map()
|
|
698
|
+
this.#mapTypes = new Map()
|
|
699
|
+
this.#optionalTypes = new Map()
|
|
700
|
+
this.variables = new Map()
|
|
701
|
+
this.variables.dyn = this.unlistedVariablesAreDyn
|
|
702
|
+
for (const n in TYPES) this.registerConstant(n, 'type', TYPES[n])
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
#ensureOwnVariables() {
|
|
707
|
+
if (this.#ownsVariables) return
|
|
708
|
+
this.variables = new Map(this.variables)
|
|
709
|
+
this.variables.dyn = this.unlistedVariablesAreDyn
|
|
710
|
+
this.#ownsVariables = true
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// Used by toggleOptionalTypes in optional.js to clear a variable before re-registering it
|
|
714
|
+
deleteVariable(name) {
|
|
715
|
+
this.#ensureOwnVariables()
|
|
716
|
+
this.variables.delete(name)
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
#pushOperator(decl) {
|
|
720
|
+
if (!this.#operators) this.#operatorsByOp = null
|
|
721
|
+
this.operatorCandidates(decl.operator).add(decl)
|
|
722
|
+
this.#operators.push(decl)
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
#pushFunction(decl) {
|
|
726
|
+
if (!this.#functions) this.#functionsByKey = null
|
|
727
|
+
this.#functionCandidates(decl.partitionKey).add(decl)
|
|
728
|
+
this.#functions.push(decl)
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
#ensureCandiate(c, key) {
|
|
732
|
+
return c.get(key) || c.set(key, new Candidates(this)).get(key)
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
#getOperators() {
|
|
736
|
+
if (this.#operators) return this.#operators
|
|
737
|
+
return (this.#operators = [...this.#others.operators])
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
#getFunctions() {
|
|
741
|
+
if (this.#functions) return this.#functions
|
|
742
|
+
return (this.#functions = [...this.#others.functions])
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
operatorCandidates(op) {
|
|
746
|
+
if (this.#operatorsByOp) return this.#ensureCandiate(this.#operatorsByOp, op)
|
|
747
|
+
const c = (this.#operatorsByOp = new Map())
|
|
748
|
+
for (const decl of this.#getOperators()) this.#ensureCandiate(c, decl.operator).add(decl)
|
|
749
|
+
return this.#ensureCandiate(c, op)
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
functionCandidates(rec, name, argLen) {
|
|
753
|
+
return this.#functionCandidates(`${rec ? 'rcall' : 'call'}:${name}:${argLen}`)
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
#functionCandidates(key) {
|
|
757
|
+
if (this.#functionsByKey) return this.#ensureCandiate(this.#functionsByKey, key)
|
|
758
|
+
const c = (this.#functionsByKey = new Map())
|
|
759
|
+
for (const decl of this.#getFunctions()) this.#ensureCandiate(c, decl.partitionKey).add(decl)
|
|
760
|
+
return this.#ensureCandiate(c, key)
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
registerVariable(name, type, opts) {
|
|
764
|
+
if (this.#locked) throw new Error('Cannot modify frozen registry')
|
|
765
|
+
let description = opts?.description
|
|
766
|
+
let value
|
|
767
|
+
if (
|
|
768
|
+
typeof name === 'string' &&
|
|
769
|
+
typeof type === 'object' &&
|
|
770
|
+
!(type instanceof TypeDeclaration)
|
|
771
|
+
) {
|
|
772
|
+
description = type.description
|
|
773
|
+
value = type.value
|
|
774
|
+
if (type.schema) type = this.registerType({name: `$${name}`, schema: type.schema}).type
|
|
775
|
+
else type = type.type
|
|
776
|
+
} else if (typeof name === 'object') {
|
|
777
|
+
if (name.schema) type = this.registerType({name: `$${name.name}`, schema: name.schema}).type
|
|
778
|
+
else type = name.type
|
|
779
|
+
description = name.description
|
|
780
|
+
value = name.value
|
|
781
|
+
name = name.name
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (typeof name !== 'string' || !name) throw invalidVar(`name must be a string`)
|
|
785
|
+
if (RESERVED.has(name)) throw invalidVar(`'${name}' is a reserved name`)
|
|
786
|
+
if (this.variables.get(name) !== undefined) throw invalidVar(`'${name}' is already registered`)
|
|
787
|
+
|
|
788
|
+
if (typeof type === 'string') type = this.getType(type)
|
|
789
|
+
else if (!(type instanceof TypeDeclaration)) throw invalidVar(`type is required`)
|
|
790
|
+
|
|
791
|
+
this.#ensureOwnVariables()
|
|
792
|
+
this.variables.set(name, new VariableDeclaration(name, type, description, value))
|
|
793
|
+
return this
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
#registerSchemaAsType(name, schema) {
|
|
797
|
+
const fields = Object.create(null)
|
|
798
|
+
for (const key of objKeys(schema)) {
|
|
799
|
+
const def = schema[key]
|
|
800
|
+
if (typeof def === 'object' && def) {
|
|
801
|
+
fields[key] = this.registerType({name: `${name}.${key}`, schema: def}).type.name
|
|
802
|
+
} else if (typeof def === 'string') {
|
|
803
|
+
fields[key] = def
|
|
804
|
+
} else {
|
|
805
|
+
throw new Error(`Invalid field definition for '${name}.${key}'`)
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return fields
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
registerConstant(name, type, value) {
|
|
812
|
+
if (typeof name === 'object') this.registerVariable(name)
|
|
813
|
+
else this.registerVariable({name, type, value})
|
|
814
|
+
return this
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
getType(typename) {
|
|
818
|
+
return this.#parseTypeString(typename, true)
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
getListType(type) {
|
|
822
|
+
return (
|
|
823
|
+
this.#listTypes.get(type) ||
|
|
824
|
+
this.#listTypes.set(type, this.#parseTypeString(`list<${type}>`, true)).get(type)
|
|
825
|
+
)
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
getMapType(a, b) {
|
|
829
|
+
return (
|
|
830
|
+
this.#mapTypes.get(a)?.get(b) ||
|
|
831
|
+
(this.#mapTypes.get(a) || this.#mapTypes.set(a, new Map()).get(a))
|
|
832
|
+
.set(b, this.#parseTypeString(`map<${a}, ${b}>`, true))
|
|
833
|
+
.get(b)
|
|
834
|
+
)
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
getOptionalType(type) {
|
|
838
|
+
return (
|
|
839
|
+
this.#optionalTypes.get(type) ||
|
|
840
|
+
this.#optionalTypes.set(type, this.#parseTypeString(`optional<${type}>`, true)).get(type)
|
|
841
|
+
)
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
assertType(typename, type, signature) {
|
|
845
|
+
try {
|
|
846
|
+
return this.#parseTypeString(typename, true)
|
|
847
|
+
} catch (e) {
|
|
848
|
+
e.message = `Invalid ${type} '${e.unknownType || typename}' in '${signature}'`
|
|
849
|
+
throw e
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
getFunctionType(typename) {
|
|
854
|
+
if (typename === 'ast') return astType
|
|
855
|
+
const t = this.#parseTypeString(typename, true)
|
|
856
|
+
if (t.kind === 'dyn' && t.valueType) throw new Error(`type '${t.name}' is not supported`)
|
|
857
|
+
return t
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
registerType(name, _d) {
|
|
861
|
+
if (this.#locked) throw new Error('Cannot modify frozen registry')
|
|
862
|
+
if (typeof name === 'object') ((_d = name), (name = _d.fullName || _d.name || _d.ctor?.name))
|
|
863
|
+
if (typeof name === 'string' && name[0] === '.') name = name.slice(1)
|
|
864
|
+
if (typeof name !== 'string' || name.length < 2 || RESERVED.has(name)) {
|
|
865
|
+
throw invalidType(`name '${name}' is not valid`)
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
if (this.objectTypes.has(name)) throw invalidType(`type '${name}' already registered`)
|
|
869
|
+
|
|
870
|
+
const type = this.#parseTypeString(name, false)
|
|
871
|
+
if (type.kind !== 'message') throw invalidType(`type '${name}' is not valid`)
|
|
872
|
+
|
|
873
|
+
const decl = {
|
|
874
|
+
name,
|
|
875
|
+
typeType: new Type(name),
|
|
876
|
+
type,
|
|
877
|
+
ctor: typeof _d === 'function' ? _d : _d?.ctor,
|
|
878
|
+
convert: typeof _d === 'function' ? undefined : _d?.convert,
|
|
879
|
+
fields:
|
|
880
|
+
typeof _d?.schema === 'object'
|
|
881
|
+
? this.#normalizeFields(name, this.#registerSchemaAsType(name, _d.schema))
|
|
882
|
+
: this.#normalizeFields(name, typeof _d === 'function' ? undefined : _d?.fields)
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
if (typeof decl.ctor !== 'function') {
|
|
886
|
+
if (!decl.fields) throw invalidType(`type '${name}' requires a constructor or fields`)
|
|
887
|
+
Object.assign(decl, this.#createDefaultConvert(name, decl.fields))
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
this.objectTypes.set(name, Object.freeze(decl))
|
|
891
|
+
this.objectTypesByConstructor.set(decl.ctor, decl)
|
|
892
|
+
this.registerFunctionOverload(`type(${name}): type`, () => decl.typeType, {async: false})
|
|
893
|
+
return decl
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/** @returns {TypeDeclaration} */
|
|
897
|
+
#parseTypeString(typeStr, requireKnownTypes = true) {
|
|
898
|
+
let match = this.#typeDeclarations.get(typeStr)
|
|
899
|
+
if (match) return match
|
|
900
|
+
|
|
901
|
+
if (typeof typeStr !== 'string' || !typeStr.length) {
|
|
902
|
+
throw new Error(`Invalid type: must be a string`)
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
match = typeStr.match(/^[A-Z]$/)
|
|
906
|
+
if (match) return this.#createDeclaration(_createPlaceholderType, typeStr, typeStr)
|
|
907
|
+
|
|
908
|
+
match = typeStr.match(/^(dyn|list|map|optional)<(.+)>$/)
|
|
909
|
+
if (!match) {
|
|
910
|
+
if (requireKnownTypes) {
|
|
911
|
+
const err = new Error(`Unknown type: ${typeStr}`)
|
|
912
|
+
err.unknownType = typeStr
|
|
913
|
+
throw err
|
|
914
|
+
}
|
|
915
|
+
return this.#createDeclaration(_createMessageType, typeStr, typeStr)
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const kind = match[1]
|
|
919
|
+
const inner = match[2].trim()
|
|
920
|
+
switch (kind) {
|
|
921
|
+
case 'dyn': {
|
|
922
|
+
const type = this.#parseTypeString(inner, requireKnownTypes).wrappedType
|
|
923
|
+
this.#typeDeclarations.set(type.name, type)
|
|
924
|
+
return type
|
|
925
|
+
}
|
|
926
|
+
case 'list': {
|
|
927
|
+
const vType = this.#parseTypeString(inner, requireKnownTypes)
|
|
928
|
+
return this.#createDeclaration(_createListType, `list<${vType}>`, vType)
|
|
929
|
+
}
|
|
930
|
+
case 'map': {
|
|
931
|
+
const parts = splitByComma(inner)
|
|
932
|
+
if (parts.length !== 2) throw new Error(`Invalid map type: ${typeStr}`)
|
|
933
|
+
const kType = this.#parseTypeString(parts[0], requireKnownTypes)
|
|
934
|
+
const vType = this.#parseTypeString(parts[1], requireKnownTypes)
|
|
935
|
+
return this.#createDeclaration(_createMapType, `map<${kType}, ${vType}>`, kType, vType)
|
|
936
|
+
}
|
|
937
|
+
case 'optional': {
|
|
938
|
+
const vType = this.#parseTypeString(inner, requireKnownTypes)
|
|
939
|
+
return this.#createDeclaration(_createOptionalType, `optional<${vType}>`, vType)
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
#createDeclaration(creator, key, ...args) {
|
|
945
|
+
return (
|
|
946
|
+
this.#typeDeclarations.get(key) || this.#typeDeclarations.set(key, creator(...args)).get(key)
|
|
947
|
+
)
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
findMacro(name, hasReceiver, argLen) {
|
|
951
|
+
return this.functionCandidates(hasReceiver, name, argLen).macro
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
findUnaryOverload(op, left) {
|
|
955
|
+
return this.operatorCandidates(op).findUnaryOverload(left)
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
findBinaryOverload(op, left, right) {
|
|
959
|
+
return this.operatorCandidates(op).findBinaryOverload(left, right)
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
matchTypeWithPlaceholders(declared, actual, bindings) {
|
|
963
|
+
if (!declared.hasPlaceholderType) return actual.matches(declared) ? actual : null
|
|
964
|
+
|
|
965
|
+
const treatAsDyn = actual.kind === 'dyn'
|
|
966
|
+
if (!this.#collectPlaceholderBindings(declared, actual, bindings, treatAsDyn)) return null
|
|
967
|
+
if (treatAsDyn) return actual
|
|
968
|
+
return actual.matches(declared.templated(this, bindings)) ? actual : null
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
#bindPlaceholder(name, candidateType, bindings) {
|
|
972
|
+
const existing = bindings.get(name)
|
|
973
|
+
if (!existing) return bindings.set(name, candidateType) && true
|
|
974
|
+
return existing.kind === 'dyn' || candidateType.kind === 'dyn'
|
|
975
|
+
? true
|
|
976
|
+
: existing.matchesBoth(candidateType)
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
#collectPlaceholderBindings(declared, actual, bindings, fromDyn = false) {
|
|
980
|
+
if (!declared.hasPlaceholderType) return true
|
|
981
|
+
if (!actual) return false
|
|
982
|
+
|
|
983
|
+
const treatAsDyn = fromDyn || actual.kind === 'dyn'
|
|
984
|
+
actual = actual.unwrappedType
|
|
985
|
+
|
|
986
|
+
switch (declared.kind) {
|
|
987
|
+
case 'param': {
|
|
988
|
+
const candidateType = treatAsDyn ? dynType : actual
|
|
989
|
+
return this.#bindPlaceholder(declared.name, candidateType, bindings)
|
|
990
|
+
}
|
|
991
|
+
case 'list': {
|
|
992
|
+
if (actual.name === 'dyn') actual = declared
|
|
993
|
+
if (actual.kind !== 'list') return false
|
|
994
|
+
return this.#collectPlaceholderBindings(
|
|
995
|
+
declared.valueType,
|
|
996
|
+
actual.valueType,
|
|
997
|
+
bindings,
|
|
998
|
+
treatAsDyn
|
|
999
|
+
)
|
|
1000
|
+
}
|
|
1001
|
+
case 'map': {
|
|
1002
|
+
if (actual.name === 'dyn') actual = declared
|
|
1003
|
+
if (actual.kind !== 'map') return false
|
|
1004
|
+
return (
|
|
1005
|
+
this.#collectPlaceholderBindings(
|
|
1006
|
+
declared.keyType,
|
|
1007
|
+
actual.keyType,
|
|
1008
|
+
bindings,
|
|
1009
|
+
treatAsDyn
|
|
1010
|
+
) &&
|
|
1011
|
+
this.#collectPlaceholderBindings(
|
|
1012
|
+
declared.valueType,
|
|
1013
|
+
actual.valueType,
|
|
1014
|
+
bindings,
|
|
1015
|
+
treatAsDyn
|
|
1016
|
+
)
|
|
1017
|
+
)
|
|
1018
|
+
}
|
|
1019
|
+
case 'optional': {
|
|
1020
|
+
if (actual.name === 'dyn') actual = declared
|
|
1021
|
+
if (actual.kind !== 'optional') return false
|
|
1022
|
+
return this.#collectPlaceholderBindings(
|
|
1023
|
+
declared.valueType,
|
|
1024
|
+
actual.valueType,
|
|
1025
|
+
bindings,
|
|
1026
|
+
treatAsDyn
|
|
1027
|
+
)
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
return true
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
#toCelFieldType(field) {
|
|
1034
|
+
if (typeof field === 'string') return {type: field}
|
|
1035
|
+
if (field.id) return protobufjsFieldToCelType(field)
|
|
1036
|
+
return field
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
#toCelFieldDeclaration(typename, fields, k, requireKnownTypes = false) {
|
|
1040
|
+
try {
|
|
1041
|
+
const field = this.#toCelFieldType(fields[k])
|
|
1042
|
+
if (typeof field?.type !== 'string') throw new Error(`unsupported declaration`)
|
|
1043
|
+
return this.#parseTypeString(field.type, requireKnownTypes)
|
|
1044
|
+
} catch (e) {
|
|
1045
|
+
e.message =
|
|
1046
|
+
`Field '${k}' in type '${typename}' has unsupported declaration: ` +
|
|
1047
|
+
`${JSON.stringify(fields[k])}`
|
|
1048
|
+
throw e
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
#normalizeFields(typename, fields) {
|
|
1053
|
+
if (!fields) return
|
|
1054
|
+
const all = Object.create(null)
|
|
1055
|
+
for (const k of objKeys(fields)) all[k] = this.#toCelFieldDeclaration(typename, fields, k)
|
|
1056
|
+
return all
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
#createDefaultConvert(name, fields) {
|
|
1060
|
+
const keys = objKeys(fields)
|
|
1061
|
+
|
|
1062
|
+
const conversions = Object.create(null)
|
|
1063
|
+
for (const k of keys) {
|
|
1064
|
+
const type = fields[k]
|
|
1065
|
+
const decl = type.kind === 'message' && this.objectTypes.get(type.name)
|
|
1066
|
+
if (decl === false) conversions[k] = false
|
|
1067
|
+
else conversions[k] = decl.convert ? decl : false
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
const Ctor = {
|
|
1071
|
+
[name]: class extends Map {
|
|
1072
|
+
#raw
|
|
1073
|
+
constructor(v) {
|
|
1074
|
+
super()
|
|
1075
|
+
this.#raw = v
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
[Symbol.iterator]() {
|
|
1079
|
+
if (this.size !== keys.length) for (const k of keys) this.get(k)
|
|
1080
|
+
return super[Symbol.iterator]()
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
get(field) {
|
|
1084
|
+
let v = super.get(field)
|
|
1085
|
+
if (v !== undefined || this.has(field)) return v
|
|
1086
|
+
|
|
1087
|
+
const dec = conversions[field]
|
|
1088
|
+
if (dec === undefined) return
|
|
1089
|
+
|
|
1090
|
+
v = this.#raw instanceof Map ? this.#raw.get(field) : this.#raw?.[field]
|
|
1091
|
+
if (dec && v && typeof v === 'object') {
|
|
1092
|
+
switch (v.constructor) {
|
|
1093
|
+
case undefined:
|
|
1094
|
+
case Object:
|
|
1095
|
+
case Map:
|
|
1096
|
+
v = dec.convert(v)
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
return (super.set(field, v), v)
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
}[name]
|
|
1103
|
+
|
|
1104
|
+
return {
|
|
1105
|
+
ctor: Ctor,
|
|
1106
|
+
convert(v) {
|
|
1107
|
+
if (!v) return
|
|
1108
|
+
if (v.constructor === Ctor) return v
|
|
1109
|
+
return new Ctor(v)
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
clone(opts) {
|
|
1115
|
+
this.#locked = true
|
|
1116
|
+
return new Registry({
|
|
1117
|
+
parent: this,
|
|
1118
|
+
unlistedVariablesAreDyn: opts.unlistedVariablesAreDyn,
|
|
1119
|
+
enableOptionalTypes: opts.enableOptionalTypes
|
|
1120
|
+
})
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
getDefinitions() {
|
|
1124
|
+
const variables = []
|
|
1125
|
+
const functions = []
|
|
1126
|
+
for (const [, varDecl] of this.variables) {
|
|
1127
|
+
if (!varDecl) continue
|
|
1128
|
+
variables.push({
|
|
1129
|
+
name: varDecl.name,
|
|
1130
|
+
description: varDecl.description || null,
|
|
1131
|
+
type: varDecl.type.name
|
|
1132
|
+
})
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
for (const decl of this.#getFunctions()) {
|
|
1136
|
+
functions.push({
|
|
1137
|
+
signature: decl.signature,
|
|
1138
|
+
name: decl.name,
|
|
1139
|
+
description: decl.description,
|
|
1140
|
+
receiverType: decl.receiverType ? decl.receiverType.name : null,
|
|
1141
|
+
returnType: decl.returnType.name,
|
|
1142
|
+
params: decl.params.map((p) => ({
|
|
1143
|
+
name: p.name,
|
|
1144
|
+
type: p.type.name,
|
|
1145
|
+
description: p.description
|
|
1146
|
+
}))
|
|
1147
|
+
})
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
return {variables, functions}
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
#parseSignature(signature) {
|
|
1154
|
+
if (typeof signature !== 'string') throw new Error('Invalid signature: must be a string')
|
|
1155
|
+
const match = signature.match(/^(?:([a-zA-Z0-9.<>]+)\.)?(\w+)\((.*?)\):\s*(.+)$/)
|
|
1156
|
+
if (!match) throw new Error(`Invalid signature: ${signature}`)
|
|
1157
|
+
return {
|
|
1158
|
+
receiverType: match[1] || null,
|
|
1159
|
+
name: match[2],
|
|
1160
|
+
argTypes: splitByComma(match[3]),
|
|
1161
|
+
returnType: match[4].trim()
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
/**
|
|
1166
|
+
* @param {FunctionDeclaration} a
|
|
1167
|
+
* @param {FunctionDeclaration} b
|
|
1168
|
+
*/
|
|
1169
|
+
#functionSignatureOverlaps(a, b) {
|
|
1170
|
+
if (a.name !== b.name) return false
|
|
1171
|
+
if (a.argTypes.length !== b.argTypes.length) return false
|
|
1172
|
+
if ((a.receiverType || b.receiverType) && (!a.receiverType || !b.receiverType)) return false
|
|
1173
|
+
|
|
1174
|
+
const isDifferentReceiver =
|
|
1175
|
+
a.receiverType !== b.receiverType && a.receiverType !== dynType && b.receiverType !== dynType
|
|
1176
|
+
|
|
1177
|
+
return (
|
|
1178
|
+
!isDifferentReceiver &&
|
|
1179
|
+
(b.macro ||
|
|
1180
|
+
a.macro ||
|
|
1181
|
+
b.argTypes.every((t, i) => {
|
|
1182
|
+
const o = a.argTypes[i]
|
|
1183
|
+
return t === o || t === dynType || o === dynType
|
|
1184
|
+
}))
|
|
1185
|
+
)
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
/** @param {FunctionDeclaration} newDec */
|
|
1189
|
+
#checkOverlappingSignatures(newDec) {
|
|
1190
|
+
for (const decl of this.#functionCandidates(newDec.partitionKey)) {
|
|
1191
|
+
if (!this.#functionSignatureOverlaps(decl, newDec)) continue
|
|
1192
|
+
throw new Error(
|
|
1193
|
+
`Function signature '${newDec.signature}' overlaps with existing overload '${decl.signature}'.`
|
|
1194
|
+
)
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
#normalizeParam(i, aType, param) {
|
|
1199
|
+
if (!param) return {type: this.getFunctionType(aType), name: `arg${i}`, description: null}
|
|
1200
|
+
|
|
1201
|
+
const type = param.type || aType
|
|
1202
|
+
if (!type) throw new Error(`params[${i}].type is required`)
|
|
1203
|
+
if (aType && type !== aType) throw new Error(`params[${i}].type not equal to signature type`)
|
|
1204
|
+
return {
|
|
1205
|
+
name: param.name || `arg${i}`,
|
|
1206
|
+
type: this.getFunctionType(type),
|
|
1207
|
+
description: param.description ?? null
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
registerFunctionOverload(s, handler, opts) {
|
|
1212
|
+
if (this.#locked) throw new Error('Cannot modify frozen registry')
|
|
1213
|
+
if (typeof s === 'object') opts = s
|
|
1214
|
+
else if (typeof handler === 'object') opts = handler
|
|
1215
|
+
else if (!opts) opts = {}
|
|
1216
|
+
|
|
1217
|
+
const sig = typeof s === 'string' ? s : (opts.signature ?? undefined)
|
|
1218
|
+
const parsed = sig !== undefined ? this.#parseSignature(sig) : undefined
|
|
1219
|
+
const name = parsed?.name || opts.name
|
|
1220
|
+
const receiverType = parsed?.receiverType || opts.receiverType
|
|
1221
|
+
const argTypes = parsed?.argTypes
|
|
1222
|
+
const returnType = parsed?.returnType || opts.returnType
|
|
1223
|
+
const params = opts.params
|
|
1224
|
+
handler = typeof handler === 'function' ? handler : opts.handler
|
|
1225
|
+
|
|
1226
|
+
let dec
|
|
1227
|
+
try {
|
|
1228
|
+
if (!name) throw new Error(`signature or name are required`)
|
|
1229
|
+
if (!returnType) throw new Error(`must have a returnType`)
|
|
1230
|
+
|
|
1231
|
+
if (params) {
|
|
1232
|
+
if (argTypes && params.length !== argTypes.length) {
|
|
1233
|
+
throw new Error(`mismatched length in params and args in signature`)
|
|
1234
|
+
}
|
|
1235
|
+
} else if (!argTypes) throw new Error(`signature or params are required`)
|
|
1236
|
+
|
|
1237
|
+
dec = new FunctionDeclaration({
|
|
1238
|
+
name,
|
|
1239
|
+
async: opts?.async,
|
|
1240
|
+
receiverType: receiverType ? this.getType(receiverType) : null,
|
|
1241
|
+
returnType: this.getType(returnType),
|
|
1242
|
+
handler,
|
|
1243
|
+
description: opts.description,
|
|
1244
|
+
params: (argTypes || params).map((_, i) =>
|
|
1245
|
+
this.#normalizeParam(i, argTypes?.[i], params?.[i])
|
|
1246
|
+
)
|
|
1247
|
+
})
|
|
1248
|
+
} catch (e) {
|
|
1249
|
+
if (typeof sig === 'string') e.message = `Invalid function declaration '${sig}': ${e.message}`
|
|
1250
|
+
else if (name) e.message = `Invalid function declaration '${name}': ${e.message}`
|
|
1251
|
+
else e.message = `Invalid function declaration: ${e.message}`
|
|
1252
|
+
throw e
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
this.#checkOverlappingSignatures(dec)
|
|
1256
|
+
this.#pushFunction(dec)
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
registerOperatorOverload(string, handler, opts) {
|
|
1260
|
+
// Parse with optional return type: "Vector + Vector: Vector" or "Vector + Vector"
|
|
1261
|
+
const unaryParts = string.match(/^([-!])([\w.<>]+)(?::\s*([\w.<>]+))?$/)
|
|
1262
|
+
if (unaryParts) {
|
|
1263
|
+
const [, op, operandType, returnType] = unaryParts
|
|
1264
|
+
return this.unaryOverload(op, operandType, handler, returnType, opts?.async)
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
const parts = string.match(
|
|
1268
|
+
/^([\w.<>]+) ([-+*%/]|==|!=|<|<=|>|>=|in) ([\w.<>]+)(?::\s*([\w.<>]+))?$/
|
|
1269
|
+
)
|
|
1270
|
+
if (!parts) throw new Error(`Operator overload invalid: ${string}`)
|
|
1271
|
+
const [, leftType, op, rightType, returnType] = parts
|
|
1272
|
+
return this.binaryOverload(leftType, op, rightType, handler, returnType)
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
unaryOverload(op, typeStr, handler, returnTypeStr, async) {
|
|
1276
|
+
if (this.#locked) throw new Error('Cannot modify frozen registry')
|
|
1277
|
+
const leftType = this.assertType(typeStr, 'type', `${op}${typeStr}`)
|
|
1278
|
+
const returnType = this.assertType(
|
|
1279
|
+
returnTypeStr || typeStr,
|
|
1280
|
+
'return type',
|
|
1281
|
+
`${op}${typeStr}: ${returnTypeStr || typeStr}`
|
|
1282
|
+
)
|
|
1283
|
+
|
|
1284
|
+
const d = new OperatorDeclaration({op: `${op}_`, leftType, returnType, handler, async})
|
|
1285
|
+
this.#pushOperator(this.#assertOverload(d))
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
#hasOverload(d) {
|
|
1289
|
+
for (const o of this.operatorCandidates(d.operator)) if (d.equals(o)) return true
|
|
1290
|
+
return false
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
#assertOverload(decl) {
|
|
1294
|
+
if (!this.#hasOverload(decl)) return decl
|
|
1295
|
+
throw new Error(`Operator overload already registered: ${decl.signature}`)
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
binaryOverload(leftTypeStr, op, rightTypeStr, handler, returnTypeStr, async) {
|
|
1299
|
+
if (this.#locked) throw new Error('Cannot modify frozen registry')
|
|
1300
|
+
returnTypeStr ??= isRelational(op) ? 'bool' : leftTypeStr
|
|
1301
|
+
|
|
1302
|
+
const sig = `${leftTypeStr} ${op} ${rightTypeStr}: ${returnTypeStr}`
|
|
1303
|
+
let leftType = this.assertType(leftTypeStr, 'left type', sig)
|
|
1304
|
+
let rightType = this.assertType(rightTypeStr, 'right type', sig)
|
|
1305
|
+
const returnType = this.assertType(returnTypeStr, 'return type', sig)
|
|
1306
|
+
|
|
1307
|
+
// Register both types as wrapped with dyn<> if one of them is wrapped
|
|
1308
|
+
if (leftType.kind === 'dyn' && leftType.valueType) rightType = rightType.wrappedType
|
|
1309
|
+
else if (rightType.kind === 'dyn' && rightType.valueType) leftType = leftType.wrappedType
|
|
1310
|
+
|
|
1311
|
+
if (isRelational(op) && returnType.type !== 'bool') {
|
|
1312
|
+
throw new Error(`Comparison operator '${op}' must return 'bool', got '${returnType.type}'`)
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
const dec = new OperatorDeclaration({op, leftType, rightType, returnType, handler, async})
|
|
1316
|
+
if (dec.hasPlaceholderType && !(rightType.hasPlaceholderType && leftType.hasPlaceholderType)) {
|
|
1317
|
+
throw new Error(
|
|
1318
|
+
`Operator overload with placeholders must use them in both left and right types: ${sig}`
|
|
1319
|
+
)
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
this.#assertOverload(dec)
|
|
1323
|
+
if (op === '==') {
|
|
1324
|
+
const declarations = [
|
|
1325
|
+
new OperatorDeclaration({
|
|
1326
|
+
op: '!=',
|
|
1327
|
+
leftType,
|
|
1328
|
+
rightType,
|
|
1329
|
+
handler(a, b, ast, ev) {
|
|
1330
|
+
return !handler(a, b, ast, ev)
|
|
1331
|
+
},
|
|
1332
|
+
returnType,
|
|
1333
|
+
async
|
|
1334
|
+
})
|
|
1335
|
+
]
|
|
1336
|
+
|
|
1337
|
+
if (leftType !== rightType) {
|
|
1338
|
+
declarations.push(
|
|
1339
|
+
new OperatorDeclaration({
|
|
1340
|
+
op: '==',
|
|
1341
|
+
leftType: rightType,
|
|
1342
|
+
rightType: leftType,
|
|
1343
|
+
handler(a, b, ast, ev) {
|
|
1344
|
+
return handler(b, a, ast, ev)
|
|
1345
|
+
},
|
|
1346
|
+
returnType,
|
|
1347
|
+
async
|
|
1348
|
+
}),
|
|
1349
|
+
new OperatorDeclaration({
|
|
1350
|
+
op: '!=',
|
|
1351
|
+
leftType: rightType,
|
|
1352
|
+
rightType: leftType,
|
|
1353
|
+
handler(a, b, ast, ev) {
|
|
1354
|
+
return !handler(b, a, ast, ev)
|
|
1355
|
+
},
|
|
1356
|
+
returnType,
|
|
1357
|
+
async
|
|
1358
|
+
})
|
|
1359
|
+
)
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
for (const decl of declarations) this.#assertOverload(decl)
|
|
1363
|
+
for (const decl of declarations) this.#pushOperator(decl)
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
this.#pushOperator(dec)
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
function isRelational(op) {
|
|
1371
|
+
return (
|
|
1372
|
+
op === '<' ||
|
|
1373
|
+
op === '<=' ||
|
|
1374
|
+
op === '>' ||
|
|
1375
|
+
op === '>=' ||
|
|
1376
|
+
op === '==' ||
|
|
1377
|
+
op === '!=' ||
|
|
1378
|
+
op === 'in'
|
|
1379
|
+
)
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
export function createRegistry(opts) {
|
|
1383
|
+
return new Registry(opts)
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
export class RootContext {
|
|
1387
|
+
#vars
|
|
1388
|
+
#contextObj
|
|
1389
|
+
#contextMap
|
|
1390
|
+
#convertCache
|
|
1391
|
+
constructor(registry, context) {
|
|
1392
|
+
this.#vars = registry.variables
|
|
1393
|
+
if (context === undefined || context === null) return
|
|
1394
|
+
if (typeof context !== 'object') throw new EvaluationError('Context must be an object')
|
|
1395
|
+
if (context instanceof Map) this.#contextMap = context
|
|
1396
|
+
else this.#contextObj = context
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
getType(name) {
|
|
1400
|
+
return this.getVariable(name)?.type
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
getValue(key) {
|
|
1404
|
+
return (
|
|
1405
|
+
this.#convertCache?.get(key) ||
|
|
1406
|
+
(this.#contextObj ? this.#contextObj[key] : this.#contextMap?.get(key))
|
|
1407
|
+
)
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
getVariable(name) {
|
|
1411
|
+
return (
|
|
1412
|
+
this.#vars.get(name) ??
|
|
1413
|
+
(this.#vars.dyn && !RESERVED.has(name) ? new VariableDeclaration(name, dynType) : undefined)
|
|
1414
|
+
)
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
getCheckedValue(ev, ast) {
|
|
1418
|
+
const v = this.getValue(ast.args)
|
|
1419
|
+
if (v === undefined) throw new ev.Error(`Unknown variable: ${ast.args}`, ast)
|
|
1420
|
+
|
|
1421
|
+
if (ast.checkedType === dynType) {
|
|
1422
|
+
// prettier-ignore
|
|
1423
|
+
switch (typeof v) {
|
|
1424
|
+
case 'string': case 'bigint': case 'number': case 'boolean': return v
|
|
1425
|
+
case 'object':
|
|
1426
|
+
switch (v ? v.constructor : v) {
|
|
1427
|
+
case null: return v
|
|
1428
|
+
case undefined: case Object: case Map: case Array: case Set: return v
|
|
1429
|
+
default: if (ev.objectTypesByConstructor.get(v.constructor)) return v
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
const type = ast.checkedType
|
|
1435
|
+
const valueType = ev.debugType(v)
|
|
1436
|
+
if (type.matchesDebugType(valueType)) return v
|
|
1437
|
+
|
|
1438
|
+
// Convert plain objects to typed instances when a convert function is registered
|
|
1439
|
+
if (type.kind === 'message' && valueType.kind === 'map') {
|
|
1440
|
+
const c = ev.objectTypes.get(type.name)?.convert?.(v)
|
|
1441
|
+
if (c) return ((this.#convertCache ??= new Map()).set(ast.args, c), c)
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
throw new ev.Error(`Variable '${ast.args}' is not of type '${type}', got '${valueType}'`, ast)
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
forkWithVariable(iterVar, iterType) {
|
|
1448
|
+
return new OverlayContext(this, iterVar, iterType)
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
class OverlayContext {
|
|
1453
|
+
#parent
|
|
1454
|
+
accuType
|
|
1455
|
+
accuValue
|
|
1456
|
+
iterValue
|
|
1457
|
+
constructor(parent, iterVar, iterType) {
|
|
1458
|
+
this.#parent = parent
|
|
1459
|
+
this.iterVar = iterVar
|
|
1460
|
+
this.iterType = iterType
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
forkWithVariable(iterVar, iterType) {
|
|
1464
|
+
return new OverlayContext(this, iterVar, iterType)
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
reuse(parent) {
|
|
1468
|
+
if (!this.async) return ((this.#parent = parent), this)
|
|
1469
|
+
const ctx = new OverlayContext(parent, this.iterVar, this.iterType)
|
|
1470
|
+
ctx.accuType = this.accuType
|
|
1471
|
+
return ctx
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
setIterValue(v, ev) {
|
|
1475
|
+
if (this.iterType === dynType) {
|
|
1476
|
+
// prettier-ignore
|
|
1477
|
+
switch (typeof v) {
|
|
1478
|
+
case 'string': case 'bigint': case 'number': case 'boolean':
|
|
1479
|
+
return ((this.iterValue = v), this)
|
|
1480
|
+
case 'object':
|
|
1481
|
+
switch (v ? v.constructor : v) {
|
|
1482
|
+
case null: case undefined: case Object: case Map: case Array: case Set:
|
|
1483
|
+
return ((this.iterValue = v), this)
|
|
1484
|
+
default:
|
|
1485
|
+
if (ev.objectTypesByConstructor.get(v.constructor))
|
|
1486
|
+
return ((this.iterValue = v), this)
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
const valueType = ev.debugType(v)
|
|
1492
|
+
if (this.iterType.matchesDebugType(valueType)) return ((this.iterValue = v), this)
|
|
1493
|
+
|
|
1494
|
+
// Convert plain objects to typed instances when a convert function is registered
|
|
1495
|
+
if (this.iterType.kind === 'message' && valueType.kind === 'map') {
|
|
1496
|
+
const c = ev.objectTypes.get(this.iterType.name)?.convert?.(v)
|
|
1497
|
+
if (c) return ((this.iterValue = c), this)
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
throw new ev.Error(
|
|
1501
|
+
`Variable '${this.iterVar}' is not of type '${this.iterType}', got '${valueType}'`
|
|
1502
|
+
)
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
setAccuType(type) {
|
|
1506
|
+
return ((this.accuType = type), this)
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
setAccuValue(v) {
|
|
1510
|
+
return ((this.accuValue = v), this)
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
getValue(key) {
|
|
1514
|
+
return this.iterVar === key ? this.iterValue : this.#parent.getValue(key)
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
getCheckedValue(ev, ast) {
|
|
1518
|
+
if (this.iterVar === ast.args) return this.iterValue
|
|
1519
|
+
return this.#parent.getCheckedValue(ev, ast)
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
getVariable(name) {
|
|
1523
|
+
if (this.iterVar === name) return new VariableDeclaration(name, this.iterType)
|
|
1524
|
+
return this.#parent.getVariable(name)
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
setConverted(name, value) {
|
|
1528
|
+
return this.iterVar === name ? (this.iterValue = value) : this.#parent.setConverted(name, value)
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
// getVariable makes this obsolete
|
|
1532
|
+
getType(key) {
|
|
1533
|
+
return this.iterVar === key ? this.iterType : this.#parent.getType(key)
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
/**
|
|
1538
|
+
* Extract CEL field declarations from a protobufjs message type.
|
|
1539
|
+
* Maps protobuf types to CEL types.
|
|
1540
|
+
* @param {protobuf.Type} messageType - The protobufjs message type
|
|
1541
|
+
* @returns {Object} Field declarations in CEL format {fieldName: 'celType'}
|
|
1542
|
+
*/
|
|
1543
|
+
function protobufjsFieldToCelType(field) {
|
|
1544
|
+
let fieldType
|
|
1545
|
+
if (field.map) {
|
|
1546
|
+
const keyType = protobufjsTypeToCelType(field.keyType, field.resolvedKeyType)
|
|
1547
|
+
const valueType = protobufjsTypeToCelType(field.type, field.resolvedType)
|
|
1548
|
+
fieldType = `map<${keyType}, ${valueType}>`
|
|
1549
|
+
} else {
|
|
1550
|
+
fieldType = protobufjsTypeToCelType(field.type, field.resolvedType)
|
|
1551
|
+
}
|
|
1552
|
+
return {type: field.repeated ? `list<${fieldType}>` : fieldType}
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
/**
|
|
1556
|
+
* Map protobuf type names to CEL type names.
|
|
1557
|
+
* @param {string} protoType - The protobuf type name
|
|
1558
|
+
* @param {protobuf.Type|null} resolvedType - The resolved type for message/enum fields
|
|
1559
|
+
* @returns {string} The CEL type name
|
|
1560
|
+
*/
|
|
1561
|
+
function protobufjsTypeToCelType(protoType, resolvedType) {
|
|
1562
|
+
switch (protoType) {
|
|
1563
|
+
case 'string':
|
|
1564
|
+
return 'string'
|
|
1565
|
+
case 'bytes':
|
|
1566
|
+
return 'bytes'
|
|
1567
|
+
case 'bool':
|
|
1568
|
+
return 'bool'
|
|
1569
|
+
// protobufjs uses JavaScript numbers for all numeric types
|
|
1570
|
+
case 'double':
|
|
1571
|
+
case 'float':
|
|
1572
|
+
case 'int32':
|
|
1573
|
+
case 'int64':
|
|
1574
|
+
case 'sint32':
|
|
1575
|
+
case 'sint64':
|
|
1576
|
+
case 'sfixed32':
|
|
1577
|
+
case 'sfixed64':
|
|
1578
|
+
case 'uint32':
|
|
1579
|
+
case 'uint64':
|
|
1580
|
+
case 'fixed32':
|
|
1581
|
+
case 'fixed64':
|
|
1582
|
+
return 'double'
|
|
1583
|
+
default:
|
|
1584
|
+
switch (resolvedType?.constructor.name) {
|
|
1585
|
+
case 'Type':
|
|
1586
|
+
return resolvedType.fullName.slice(1)
|
|
1587
|
+
case 'Enum':
|
|
1588
|
+
return 'int'
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
if (protoType?.includes('.')) return protoType
|
|
1592
|
+
|
|
1593
|
+
// Unknown type, treat as dyn
|
|
1594
|
+
return 'dyn'
|
|
1595
|
+
}
|
|
1596
|
+
}
|