@rcrsr/rill 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -21
- package/dist/ast-nodes.d.ts +14 -4
- package/dist/ast-unions.d.ts +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -0
- package/dist/error-registry.js +228 -0
- package/dist/ext/crypto/index.d.ts +3 -3
- package/dist/ext/crypto/index.js +62 -59
- package/dist/ext/exec/index.d.ts +3 -3
- package/dist/ext/exec/index.js +15 -9
- package/dist/ext/fetch/index.d.ts +3 -3
- package/dist/ext/fetch/index.js +17 -12
- package/dist/ext/fetch/request.js +1 -1
- package/dist/ext/fs/index.d.ts +3 -3
- package/dist/ext/fs/index.js +256 -266
- package/dist/ext/fs/sandbox.d.ts +18 -0
- package/dist/ext/fs/sandbox.js +33 -0
- package/dist/ext/kv/index.d.ts +3 -3
- package/dist/ext/kv/index.js +198 -196
- package/dist/ext/kv/store.d.ts +1 -1
- package/dist/ext/kv/store.js +2 -1
- package/dist/ext-parse-bridge.d.ts +10 -0
- package/dist/ext-parse-bridge.js +10 -0
- package/dist/generated/introspection-data.d.ts +1 -1
- package/dist/generated/introspection-data.js +385 -296
- package/dist/generated/version-data.d.ts +1 -1
- package/dist/generated/version-data.js +2 -2
- package/dist/highlight-map.js +1 -0
- package/dist/index.d.ts +1 -4
- package/dist/index.js +1 -5
- package/dist/lexer/operators.js +1 -0
- package/dist/parser/helpers.js +1 -0
- package/dist/parser/parser-expr.js +44 -5
- package/dist/parser/parser-literals.js +111 -4
- package/dist/parser/parser-shape.js +2 -2
- package/dist/parser/parser-types.js +12 -0
- package/dist/parser/parser-use.js +26 -3
- package/dist/parser/parser.d.ts +2 -0
- package/dist/parser/parser.js +2 -0
- package/dist/runtime/core/callable.d.ts +24 -13
- package/dist/runtime/core/callable.js +71 -38
- package/dist/runtime/core/context.d.ts +2 -13
- package/dist/runtime/core/context.js +80 -79
- package/dist/runtime/core/eval/base.d.ts +2 -2
- package/dist/runtime/core/eval/base.js +2 -0
- package/dist/runtime/core/eval/evaluator.d.ts +1 -1
- package/dist/runtime/core/eval/index.d.ts +3 -3
- package/dist/runtime/core/eval/index.js +11 -0
- package/dist/runtime/core/eval/mixins/closures.js +381 -41
- package/dist/runtime/core/eval/mixins/collections.js +81 -6
- package/dist/runtime/core/eval/mixins/control-flow.js +1 -1
- package/dist/runtime/core/eval/mixins/conversion.js +61 -115
- package/dist/runtime/core/eval/mixins/core.js +17 -4
- package/dist/runtime/core/eval/mixins/expressions.js +36 -27
- package/dist/runtime/core/eval/mixins/extraction.js +2 -3
- package/dist/runtime/core/eval/mixins/list-dispatch.js +1 -1
- package/dist/runtime/core/eval/mixins/literals.js +17 -6
- package/dist/runtime/core/eval/mixins/types.js +73 -54
- package/dist/runtime/core/eval/mixins/variables.js +12 -8
- package/dist/runtime/core/execute.d.ts +1 -1
- package/dist/runtime/core/field-descriptor.d.ts +3 -3
- package/dist/runtime/core/field-descriptor.js +2 -1
- package/dist/runtime/core/introspection.d.ts +2 -2
- package/dist/runtime/core/introspection.js +7 -6
- package/dist/runtime/core/resolvers.d.ts +1 -1
- package/dist/runtime/core/signals.d.ts +6 -1
- package/dist/runtime/core/signals.js +9 -0
- package/dist/runtime/core/types/constructors.d.ts +54 -0
- package/dist/runtime/core/types/constructors.js +201 -0
- package/dist/runtime/core/types/guards.d.ts +42 -0
- package/dist/runtime/core/types/guards.js +88 -0
- package/dist/runtime/core/types/index.d.ts +18 -0
- package/dist/runtime/core/types/index.js +19 -0
- package/dist/runtime/core/types/markers.d.ts +12 -0
- package/dist/runtime/core/types/markers.js +7 -0
- package/dist/runtime/core/types/operations.d.ts +98 -0
- package/dist/runtime/core/types/operations.js +804 -0
- package/dist/runtime/core/types/registrations.d.ts +126 -0
- package/dist/runtime/core/types/registrations.js +751 -0
- package/dist/runtime/core/{types.d.ts → types/runtime.d.ts} +22 -10
- package/dist/runtime/core/types/structures.d.ts +146 -0
- package/dist/runtime/core/types/structures.js +12 -0
- package/dist/runtime/core/values.d.ts +29 -209
- package/dist/runtime/core/values.js +56 -968
- package/dist/runtime/ext/builtins.js +88 -68
- package/dist/runtime/ext/extensions.d.ts +31 -125
- package/dist/runtime/ext/extensions.js +2 -94
- package/dist/runtime/ext/test-context.d.ts +28 -0
- package/dist/runtime/ext/test-context.js +155 -0
- package/dist/runtime/index.d.ts +12 -12
- package/dist/runtime/index.js +13 -5
- package/dist/signature-parser.d.ts +2 -2
- package/dist/signature-parser.js +14 -14
- package/dist/token-types.d.ts +1 -0
- package/dist/token-types.js +1 -0
- package/package.json +1 -1
- /package/dist/runtime/core/{types.js → types/runtime.js} +0 -0
|
@@ -21,14 +21,13 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { RuntimeError } from '../../types.js';
|
|
23
23
|
import { astEquals } from './equals.js';
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
24
|
+
import { isCallable as _isCallableGuard, isDict, isOrdered, isTuple, } from './types/guards.js';
|
|
25
|
+
import { formatValue, inferType } from './types/registrations.js';
|
|
26
|
+
import { formatStructure, paramToFieldDef, structureEquals, structureMatches, } from './types/operations.js';
|
|
27
|
+
import { createOrdered, copyValue, emptyForType, } from './types/constructors.js';
|
|
28
|
+
import { anyTypeValue, hasCollectionFields } from './values.js';
|
|
29
|
+
/** Type guard for any callable (delegates to types/guards.ts) */
|
|
30
|
+
export const isCallable = _isCallableGuard;
|
|
32
31
|
/** Type guard for script callable */
|
|
33
32
|
export function isScriptCallable(value) {
|
|
34
33
|
return isCallable(value) && value.kind === 'script';
|
|
@@ -61,14 +60,37 @@ export function callable(fn, isProperty = false) {
|
|
|
61
60
|
isProperty,
|
|
62
61
|
};
|
|
63
62
|
}
|
|
64
|
-
/**
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Convert a RillFunction to an ApplicationCallable.
|
|
65
|
+
*
|
|
66
|
+
* Validates the input and produces a callable value accepted by the loader.
|
|
67
|
+
* Pure function with no side effects.
|
|
68
|
+
*
|
|
69
|
+
* @param def - Host function definition to convert
|
|
70
|
+
* @returns ApplicationCallable with __type, kind, isProperty, and preserved annotations
|
|
71
|
+
*/
|
|
72
|
+
export function toCallable(def, isProperty = false) {
|
|
73
|
+
if (def == null) {
|
|
74
|
+
throw new TypeError('RillFunction cannot be null or undefined');
|
|
75
|
+
}
|
|
76
|
+
if (typeof def.fn !== 'function') {
|
|
77
|
+
throw new TypeError('RillFunction.fn must be a function');
|
|
78
|
+
}
|
|
79
|
+
if (!Array.isArray(def.params)) {
|
|
80
|
+
throw new TypeError('RillFunction.params must be an array');
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
__type: 'callable',
|
|
84
|
+
kind: 'application',
|
|
85
|
+
isProperty,
|
|
86
|
+
fn: def.fn,
|
|
87
|
+
params: def.params,
|
|
88
|
+
returnType: def.returnType,
|
|
89
|
+
annotations: def.annotations ?? {},
|
|
90
|
+
};
|
|
71
91
|
}
|
|
92
|
+
// isDict imported from ./types/guards.js and re-exported
|
|
93
|
+
export { isDict };
|
|
72
94
|
/** Format a callable for display */
|
|
73
95
|
export function formatCallable(callable) {
|
|
74
96
|
if (callable.kind === 'script') {
|
|
@@ -116,14 +138,14 @@ export function callableEquals(a, b, valueEquals = (x, y) => formatValue(x) ===
|
|
|
116
138
|
return false;
|
|
117
139
|
if (ap.name !== bp.name)
|
|
118
140
|
return false;
|
|
119
|
-
// Compare type via
|
|
141
|
+
// Compare type via structureEquals; absent type (any-typed) matches absent type
|
|
120
142
|
if (ap.type === undefined && bp.type !== undefined)
|
|
121
143
|
return false;
|
|
122
144
|
if (ap.type !== undefined && bp.type === undefined)
|
|
123
145
|
return false;
|
|
124
146
|
if (ap.type !== undefined &&
|
|
125
147
|
bp.type !== undefined &&
|
|
126
|
-
!
|
|
148
|
+
!structureEquals(ap.type, bp.type))
|
|
127
149
|
return false;
|
|
128
150
|
if (!valueEquals(ap.defaultValue ?? null, bp.defaultValue ?? null)) {
|
|
129
151
|
return false;
|
|
@@ -146,24 +168,24 @@ export function callableEquals(a, b, valueEquals = (x, y) => formatValue(x) ===
|
|
|
146
168
|
return true;
|
|
147
169
|
}
|
|
148
170
|
/**
|
|
149
|
-
* Build a
|
|
171
|
+
* Build a TypeStructure closure variant from a closure's parameter list.
|
|
150
172
|
*
|
|
151
173
|
* Called at closure creation time to build the structural type for `$fn.^input`.
|
|
152
174
|
* - Typed params use param.type directly when present
|
|
153
|
-
* - Untyped params (type: undefined) map to {
|
|
154
|
-
* - Return type is always {
|
|
175
|
+
* - Untyped params (type: undefined) map to { kind: 'any' }
|
|
176
|
+
* - Return type is always { kind: 'any' }
|
|
155
177
|
*
|
|
156
178
|
* No validation: parser already validates type names.
|
|
157
179
|
*
|
|
158
180
|
* @param params - Closure parameter definitions (RillParam[])
|
|
159
|
-
* @returns Frozen
|
|
181
|
+
* @returns Frozen TypeStructure with closure variant
|
|
160
182
|
*/
|
|
161
183
|
export function paramsToStructuralType(params) {
|
|
162
|
-
const closureParams = params.map((param) => paramToFieldDef(param.name, param.type ?? {
|
|
184
|
+
const closureParams = params.map((param) => paramToFieldDef(param.name, param.type ?? { kind: 'any' }, param.defaultValue));
|
|
163
185
|
return Object.freeze({
|
|
164
|
-
|
|
186
|
+
kind: 'closure',
|
|
165
187
|
params: closureParams,
|
|
166
|
-
ret: {
|
|
188
|
+
ret: { kind: 'any' },
|
|
167
189
|
});
|
|
168
190
|
}
|
|
169
191
|
/**
|
|
@@ -182,10 +204,10 @@ export function validateDefaultValueType(param, _functionName) {
|
|
|
182
204
|
// Skip validation when type is undefined (any-typed, all defaults valid)
|
|
183
205
|
if (param.type === undefined)
|
|
184
206
|
return;
|
|
185
|
-
if (!
|
|
207
|
+
if (!structureMatches(param.defaultValue, param.type)) {
|
|
186
208
|
const actualType = inferType(param.defaultValue);
|
|
187
|
-
const expectedType =
|
|
188
|
-
throw new
|
|
209
|
+
const expectedType = formatStructure(param.type);
|
|
210
|
+
throw new RuntimeError('RILL-R077', `Invalid defaultValue for parameter '${param.name}': expected ${expectedType}, got ${actualType}`);
|
|
189
211
|
}
|
|
190
212
|
}
|
|
191
213
|
/**
|
|
@@ -198,16 +220,17 @@ export function validateDefaultValueType(param, _functionName) {
|
|
|
198
220
|
* Pure function: no class context, no evaluator, no side effects.
|
|
199
221
|
*/
|
|
200
222
|
export function hydrateFieldDefaults(value, type) {
|
|
201
|
-
if (type.
|
|
223
|
+
if (type.kind === 'dict' && type.fields && isDict(value)) {
|
|
224
|
+
const t = type;
|
|
202
225
|
const dictValue = value;
|
|
203
226
|
// Seed with all input entries so extra keys survive (structural match allows extras)
|
|
204
227
|
const result = { ...dictValue };
|
|
205
|
-
for (const [fieldName, fieldDef] of Object.entries(
|
|
228
|
+
for (const [fieldName, fieldDef] of Object.entries(t.fields)) {
|
|
206
229
|
if (fieldName in dictValue) {
|
|
207
230
|
result[fieldName] = hydrateFieldDefaults(dictValue[fieldName], fieldDef.type);
|
|
208
231
|
}
|
|
209
232
|
else if (fieldDef.defaultValue !== undefined) {
|
|
210
|
-
result[fieldName] = hydrateFieldDefaults(
|
|
233
|
+
result[fieldName] = hydrateFieldDefaults(copyValue(fieldDef.defaultValue), fieldDef.type);
|
|
211
234
|
}
|
|
212
235
|
else if (hasCollectionFields(fieldDef.type)) {
|
|
213
236
|
result[fieldName] = hydrateFieldDefaults(emptyForType(fieldDef.type), fieldDef.type);
|
|
@@ -216,11 +239,14 @@ export function hydrateFieldDefaults(value, type) {
|
|
|
216
239
|
}
|
|
217
240
|
return result;
|
|
218
241
|
}
|
|
219
|
-
if (type.
|
|
242
|
+
if (type.kind === 'ordered' &&
|
|
243
|
+
type.fields &&
|
|
244
|
+
isOrdered(value)) {
|
|
245
|
+
const t = type;
|
|
220
246
|
const lookup = new Map(value.entries.map(([k, v]) => [k, v]));
|
|
221
|
-
const fieldNames = new Set(
|
|
247
|
+
const fieldNames = new Set(t.fields.map((f) => f.name ?? ''));
|
|
222
248
|
const resultEntries = [];
|
|
223
|
-
for (const field of
|
|
249
|
+
for (const field of t.fields) {
|
|
224
250
|
const name = field.name ?? '';
|
|
225
251
|
if (lookup.has(name)) {
|
|
226
252
|
resultEntries.push([
|
|
@@ -231,7 +257,7 @@ export function hydrateFieldDefaults(value, type) {
|
|
|
231
257
|
else if (field.defaultValue !== undefined) {
|
|
232
258
|
resultEntries.push([
|
|
233
259
|
name,
|
|
234
|
-
hydrateFieldDefaults(
|
|
260
|
+
hydrateFieldDefaults(copyValue(field.defaultValue), field.type),
|
|
235
261
|
]);
|
|
236
262
|
}
|
|
237
263
|
else if (hasCollectionFields(field.type)) {
|
|
@@ -250,7 +276,9 @@ export function hydrateFieldDefaults(value, type) {
|
|
|
250
276
|
}
|
|
251
277
|
return createOrdered(resultEntries);
|
|
252
278
|
}
|
|
253
|
-
if (type.
|
|
279
|
+
if (type.kind === 'tuple' &&
|
|
280
|
+
type.elements &&
|
|
281
|
+
isTuple(value)) {
|
|
254
282
|
const elements = type.elements;
|
|
255
283
|
const entries = value.entries;
|
|
256
284
|
// All fields present: recurse into nested types for present positions
|
|
@@ -270,7 +298,7 @@ export function hydrateFieldDefaults(value, type) {
|
|
|
270
298
|
resultEntries.push(hydrateFieldDefaults(entries[i], el.type));
|
|
271
299
|
}
|
|
272
300
|
else if (el.defaultValue !== undefined) {
|
|
273
|
-
resultEntries.push(hydrateFieldDefaults(
|
|
301
|
+
resultEntries.push(hydrateFieldDefaults(copyValue(el.defaultValue), el.type));
|
|
274
302
|
}
|
|
275
303
|
else if (hasCollectionFields(el.type)) {
|
|
276
304
|
resultEntries.push(hydrateFieldDefaults(emptyForType(el.type), el.type));
|
|
@@ -327,6 +355,11 @@ export function marshalArgs(args, params, options) {
|
|
|
327
355
|
if (param.defaultValue !== undefined) {
|
|
328
356
|
value = param.defaultValue;
|
|
329
357
|
}
|
|
358
|
+
else if (param.type !== undefined && hasCollectionFields(param.type)) {
|
|
359
|
+
// Collection-typed param with field-level defaults: synthesize empty
|
|
360
|
+
// collection so Stage 2.5 (hydrateFieldDefaults) can fill in defaults
|
|
361
|
+
value = emptyForType(param.type);
|
|
362
|
+
}
|
|
330
363
|
else {
|
|
331
364
|
// Stage 2: Missing required parameter
|
|
332
365
|
throw new RuntimeError('RILL-R044', `Missing argument for parameter '${param.name}'`, location, {
|
|
@@ -341,8 +374,8 @@ export function marshalArgs(args, params, options) {
|
|
|
341
374
|
}
|
|
342
375
|
// Stage 3: Type check when param.type is defined
|
|
343
376
|
if (param.type !== undefined) {
|
|
344
|
-
if (!
|
|
345
|
-
const expectedType =
|
|
377
|
+
if (!structureMatches(value, param.type)) {
|
|
378
|
+
const expectedType = formatStructure(param.type);
|
|
346
379
|
const actualType = inferType(value);
|
|
347
380
|
throw new RuntimeError('RILL-R001', `Parameter type mismatch: ${param.name} expects ${expectedType}, got ${actualType}`, location, {
|
|
348
381
|
functionName,
|
|
@@ -4,20 +4,9 @@
|
|
|
4
4
|
* Creates and configures the runtime context for script execution.
|
|
5
5
|
* Public API for host applications.
|
|
6
6
|
*/
|
|
7
|
-
import type { RuntimeContext, RuntimeOptions } from './types.js';
|
|
8
|
-
import {
|
|
9
|
-
import { type RillFunction } from './callable.js';
|
|
7
|
+
import type { RuntimeContext, RuntimeOptions } from './types/runtime.js';
|
|
8
|
+
import type { RillValue } from './types/structures.js';
|
|
10
9
|
export declare const UNVALIDATED_METHOD_PARAMS: Set<string>;
|
|
11
|
-
export declare const UNVALIDATED_METHOD_RECEIVERS: Set<string>;
|
|
12
|
-
/**
|
|
13
|
-
* Build a ReadonlyMap of frozen ApplicationCallable dicts from an array of
|
|
14
|
-
* [typeName, methods] pairs. Accepts pairs (not a plain object) so the same
|
|
15
|
-
* typeName can appear more than once — duplicate method names across entries
|
|
16
|
-
* for the same type trigger an Error (EC-6).
|
|
17
|
-
*
|
|
18
|
-
* Re-exported from the public barrel index for host integration use.
|
|
19
|
-
*/
|
|
20
|
-
export declare function buildTypeMethodDicts(pairs: Array<[string, Record<string, RillFunction>]>): ReadonlyMap<string, Readonly<Record<string, RillValue>>>;
|
|
21
10
|
/**
|
|
22
11
|
* Create a runtime context for script execution.
|
|
23
12
|
* This is the main entry point for configuring the Rill runtime.
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* Public API for host applications.
|
|
6
6
|
*/
|
|
7
7
|
import { RuntimeError } from '../../types.js';
|
|
8
|
-
import { BUILTIN_FUNCTIONS
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
8
|
+
import { BUILTIN_FUNCTIONS } from '../ext/builtins.js';
|
|
9
|
+
import { BUILT_IN_TYPES } from './types/registrations.js';
|
|
10
|
+
import { bindDictCallables } from './types/runtime.js';
|
|
11
|
+
import { inferType } from './types/registrations.js';
|
|
11
12
|
import { callable, validateDefaultValueType, } from './callable.js';
|
|
12
13
|
// Built-in functions that are genuinely variadic and must skip arg validation.
|
|
13
14
|
// log: tests call log("msg", extraValue) — extra args are silently ignored.
|
|
@@ -17,69 +18,23 @@ const UNTYPED_BUILTINS = new Set(['log', 'chain']);
|
|
|
17
18
|
// messages expected by protected language tests. Generic marshalArgs
|
|
18
19
|
// must not fire before the method body's own check.
|
|
19
20
|
export const UNVALIDATED_METHOD_PARAMS = new Set(['has', 'has_any', 'has_all']);
|
|
20
|
-
// Built-in methods that perform their own receiver type checking with specific
|
|
21
|
-
// error messages. Generic RILL-R003 must not fire before the method body runs.
|
|
22
|
-
// Mirrors the old flat-structure convention of receiverTypes: [].
|
|
23
|
-
export const UNVALIDATED_METHOD_RECEIVERS = new Set([
|
|
24
|
-
'head',
|
|
25
|
-
'tail',
|
|
26
|
-
'first',
|
|
27
|
-
'at',
|
|
28
|
-
'eq',
|
|
29
|
-
'ne',
|
|
30
|
-
'keys',
|
|
31
|
-
'values',
|
|
32
|
-
'entries',
|
|
33
|
-
'has',
|
|
34
|
-
'has_any',
|
|
35
|
-
'has_all',
|
|
36
|
-
'dimensions',
|
|
37
|
-
'model',
|
|
38
|
-
'similarity',
|
|
39
|
-
'dot',
|
|
40
|
-
'distance',
|
|
41
|
-
'norm',
|
|
42
|
-
'normalize',
|
|
43
|
-
]);
|
|
44
21
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* for the same type trigger an Error (EC-6).
|
|
49
|
-
*
|
|
50
|
-
* Re-exported from the public barrel index for host integration use.
|
|
22
|
+
* Derive the set of method names that handle their own receiver type checking.
|
|
23
|
+
* Collects names from methods where skipReceiverValidation is true.
|
|
24
|
+
* Methods without the flag default to standard RILL-R003 receiver validation.
|
|
51
25
|
*/
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const existing = result.get(typeName) ?? {};
|
|
59
|
-
const dict = { ...existing };
|
|
60
|
-
for (const [name, fn] of Object.entries(methods)) {
|
|
61
|
-
if (seen.has(name)) {
|
|
62
|
-
throw new Error(`Duplicate method '${name}' on type '${typeName}'`);
|
|
26
|
+
function deriveUnvalidatedMethodReceivers(registrations) {
|
|
27
|
+
const bypass = new Set();
|
|
28
|
+
for (const reg of registrations) {
|
|
29
|
+
for (const [name, method] of Object.entries(reg.methods)) {
|
|
30
|
+
if (method.skipReceiverValidation) {
|
|
31
|
+
bypass.add(name);
|
|
63
32
|
}
|
|
64
|
-
seen.add(name);
|
|
65
|
-
const appCallable = {
|
|
66
|
-
__type: 'callable',
|
|
67
|
-
kind: 'application',
|
|
68
|
-
params: fn.params,
|
|
69
|
-
returnType: fn.returnType,
|
|
70
|
-
annotations: fn.annotations ?? {},
|
|
71
|
-
isProperty: false,
|
|
72
|
-
fn: fn.fn,
|
|
73
|
-
};
|
|
74
|
-
dict[name] = appCallable;
|
|
75
33
|
}
|
|
76
|
-
result.set(typeName, Object.freeze(dict));
|
|
77
34
|
}
|
|
78
|
-
return
|
|
35
|
+
return Object.freeze(bypass);
|
|
79
36
|
}
|
|
80
37
|
const BUILTIN_FN_CACHE = new Map();
|
|
81
|
-
const BUILTIN_METHOD_PARAMS_CACHE = new Map();
|
|
82
|
-
const BUILTIN_METHOD_RECEIVER_TYPES_CACHE = new Map();
|
|
83
38
|
function initBuiltinCaches() {
|
|
84
39
|
for (const [name, entry] of Object.entries(BUILTIN_FUNCTIONS)) {
|
|
85
40
|
if (UNTYPED_BUILTINS.has(name)) {
|
|
@@ -94,21 +49,6 @@ function initBuiltinCaches() {
|
|
|
94
49
|
};
|
|
95
50
|
BUILTIN_FN_CACHE.set(name, { appCallable: typedCallable });
|
|
96
51
|
}
|
|
97
|
-
for (const [typeName, methods] of Object.entries(BUILTIN_METHODS)) {
|
|
98
|
-
for (const [name, impl] of Object.entries(methods)) {
|
|
99
|
-
// Accumulate receiver types across all type groups for this method name.
|
|
100
|
-
// Skip methods that perform their own receiver type checking.
|
|
101
|
-
if (!UNVALIDATED_METHOD_RECEIVERS.has(name)) {
|
|
102
|
-
const existing = BUILTIN_METHOD_RECEIVER_TYPES_CACHE.get(name);
|
|
103
|
-
BUILTIN_METHOD_RECEIVER_TYPES_CACHE.set(name, existing !== undefined ? [...existing, typeName] : [typeName]);
|
|
104
|
-
}
|
|
105
|
-
if (!UNVALIDATED_METHOD_PARAMS.has(name)) {
|
|
106
|
-
if (impl.params.length > 0) {
|
|
107
|
-
BUILTIN_METHOD_PARAMS_CACHE.set(name, impl.params);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
52
|
}
|
|
113
53
|
// Initialise once at module load.
|
|
114
54
|
initBuiltinCaches();
|
|
@@ -153,7 +93,7 @@ export function createRuntimeContext(options = {}) {
|
|
|
153
93
|
if (description === undefined ||
|
|
154
94
|
typeof description !== 'string' ||
|
|
155
95
|
description.trim().length === 0) {
|
|
156
|
-
throw new
|
|
96
|
+
throw new RuntimeError('RILL-R069', `Function '${name}' requires description (requireDescriptions enabled)`);
|
|
157
97
|
}
|
|
158
98
|
// Check parameter descriptions (EC-11)
|
|
159
99
|
for (const param of params) {
|
|
@@ -161,7 +101,7 @@ export function createRuntimeContext(options = {}) {
|
|
|
161
101
|
if (paramDesc === undefined ||
|
|
162
102
|
typeof paramDesc !== 'string' ||
|
|
163
103
|
paramDesc.trim().length === 0) {
|
|
164
|
-
throw new
|
|
104
|
+
throw new RuntimeError('RILL-R070', `Parameter '${param.name}' of function '${name}' requires description (requireDescriptions enabled)`);
|
|
165
105
|
}
|
|
166
106
|
}
|
|
167
107
|
}
|
|
@@ -194,15 +134,74 @@ export function createRuntimeContext(options = {}) {
|
|
|
194
134
|
const resolverConfigs = new Map(options.configurations?.resolvers
|
|
195
135
|
? Object.entries(options.configurations.resolvers)
|
|
196
136
|
: []);
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
const
|
|
137
|
+
// EC-1: Validate no duplicate type names in registrations.
|
|
138
|
+
const seenTypeNames = new Set();
|
|
139
|
+
for (const reg of BUILT_IN_TYPES) {
|
|
140
|
+
if (seenTypeNames.has(reg.name)) {
|
|
141
|
+
throw new RuntimeError('RILL-R071', `Duplicate type registration '${reg.name}'`);
|
|
142
|
+
}
|
|
143
|
+
seenTypeNames.add(reg.name);
|
|
144
|
+
}
|
|
145
|
+
// EC-2: Validate every registration has protocol.format.
|
|
146
|
+
for (const reg of BUILT_IN_TYPES) {
|
|
147
|
+
if (!reg.protocol.format) {
|
|
148
|
+
throw new RuntimeError('RILL-R072', `Type '${reg.name}' missing required format protocol`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Derive typeNames from registrations (replaces VALID_TYPE_NAMES in context).
|
|
152
|
+
const typeNames = Object.freeze(BUILT_IN_TYPES.map((r) => r.name));
|
|
153
|
+
// Derive leafTypes from registrations where isLeaf === true, plus 'any'
|
|
154
|
+
// which has no registration but rejects type arguments (AC-4).
|
|
155
|
+
const leafTypes = Object.freeze(new Set([
|
|
156
|
+
...BUILT_IN_TYPES.filter((r) => r.isLeaf).map((r) => r.name),
|
|
157
|
+
'any',
|
|
158
|
+
]));
|
|
159
|
+
// Derive method dicts from registration.methods (absorbs buildTypeMethodDicts
|
|
160
|
+
// logic). Validates EC-6: duplicate method on same type.
|
|
161
|
+
const methodRegistry = new Map();
|
|
162
|
+
const typeMethodDicts = new Map();
|
|
163
|
+
for (const reg of BUILT_IN_TYPES) {
|
|
164
|
+
const methods = reg.methods;
|
|
165
|
+
if (!methods || Object.keys(methods).length === 0)
|
|
166
|
+
continue;
|
|
167
|
+
const seen = methodRegistry.get(reg.name) ?? new Set();
|
|
168
|
+
methodRegistry.set(reg.name, seen);
|
|
169
|
+
const existing = typeMethodDicts.get(reg.name) ?? {};
|
|
170
|
+
const dict = { ...existing };
|
|
171
|
+
for (const [name, fn] of Object.entries(methods)) {
|
|
172
|
+
if (seen.has(name)) {
|
|
173
|
+
throw new RuntimeError('RILL-R073', `Duplicate method '${name}' on type '${reg.name}'`);
|
|
174
|
+
}
|
|
175
|
+
seen.add(name);
|
|
176
|
+
const appCallable = {
|
|
177
|
+
__type: 'callable',
|
|
178
|
+
kind: 'application',
|
|
179
|
+
params: fn.params,
|
|
180
|
+
returnType: fn.returnType,
|
|
181
|
+
annotations: fn.annotations ?? {},
|
|
182
|
+
isProperty: false,
|
|
183
|
+
fn: fn.fn,
|
|
184
|
+
};
|
|
185
|
+
dict[name] = appCallable;
|
|
186
|
+
}
|
|
187
|
+
typeMethodDicts.set(reg.name, Object.freeze(dict));
|
|
188
|
+
}
|
|
189
|
+
// Derive bypass set from registrations: method names that handle their own
|
|
190
|
+
// receiver type checking. Generic RILL-R003 must not fire before the method body.
|
|
191
|
+
const unvalidatedMethodReceivers = deriveUnvalidatedMethodReceivers(BUILT_IN_TYPES);
|
|
192
|
+
// BC-5: Freeze all derived collections after creation.
|
|
193
|
+
Object.freeze(typeNames);
|
|
194
|
+
Object.freeze(typeMethodDicts);
|
|
195
|
+
// Suppress unused-variable warning for typeNames (consumed in later phases).
|
|
196
|
+
void typeNames;
|
|
200
197
|
return {
|
|
201
198
|
parent: undefined,
|
|
202
199
|
variables,
|
|
203
200
|
variableTypes,
|
|
204
201
|
functions,
|
|
205
202
|
typeMethodDicts,
|
|
203
|
+
leafTypes,
|
|
204
|
+
unvalidatedMethodReceivers,
|
|
206
205
|
callbacks: { ...defaultCallbacks, ...options.callbacks },
|
|
207
206
|
observability: options.observability ?? {},
|
|
208
207
|
pipeValue: null,
|
|
@@ -232,6 +231,8 @@ export function createChildContext(parent, overrides) {
|
|
|
232
231
|
variableTypes: new Map(),
|
|
233
232
|
functions: parent.functions,
|
|
234
233
|
typeMethodDicts: parent.typeMethodDicts,
|
|
234
|
+
leafTypes: parent.leafTypes,
|
|
235
|
+
unvalidatedMethodReceivers: parent.unvalidatedMethodReceivers,
|
|
235
236
|
callbacks: parent.callbacks,
|
|
236
237
|
observability: parent.observability,
|
|
237
238
|
pipeValue: parent.pipeValue,
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* @internal
|
|
8
8
|
*/
|
|
9
9
|
import type { ASTNode, CaptureNode, SourceLocation } from '../../../types.js';
|
|
10
|
-
import type { RuntimeContext } from '../types.js';
|
|
11
|
-
import type { RillValue } from '../
|
|
10
|
+
import type { RuntimeContext } from '../types/runtime.js';
|
|
11
|
+
import type { RillValue } from '../types/structures.js';
|
|
12
12
|
/**
|
|
13
13
|
* Base class for the evaluator.
|
|
14
14
|
* Contains shared utilities used by all mixins.
|
|
@@ -74,6 +74,8 @@ export class EvaluatorBase {
|
|
|
74
74
|
* Phase 1-3 use the functional evaluator which has its own handleCapture.
|
|
75
75
|
*/
|
|
76
76
|
handleCapture(_capture, _value) {
|
|
77
|
+
// AC-13: Intentional raw throw - internal mixin guard, not user-reachable.
|
|
78
|
+
// This stub only runs if mixin composition is incomplete (programming error).
|
|
77
79
|
throw new Error('handleCapture requires full Evaluator composition with VariablesMixin');
|
|
78
80
|
}
|
|
79
81
|
/**
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { AnnotatedStatementNode, ASTNode, CaptureNode, ExpressionNode, RillTypeName, SourceLocation, StatementNode } from '../../../types.js';
|
|
10
10
|
import type { RillCallable } from '../callable.js';
|
|
11
|
-
import type { RuntimeContext } from '../types.js';
|
|
12
|
-
import type { RillValue,
|
|
11
|
+
import type { RuntimeContext } from '../types/runtime.js';
|
|
12
|
+
import type { RillValue, TypeStructure } from '../types/structures.js';
|
|
13
13
|
/**
|
|
14
14
|
* Capture information returned by handleCapture.
|
|
15
15
|
*/
|
|
@@ -39,7 +39,7 @@ export declare function handleCapture(capture: CaptureNode | null, value: RillVa
|
|
|
39
39
|
* Assert that a value is of the expected type.
|
|
40
40
|
* Returns the value unchanged if assertion passes, throws on mismatch.
|
|
41
41
|
*/
|
|
42
|
-
export declare function assertType(value: RillValue, expected: RillTypeName |
|
|
42
|
+
export declare function assertType(value: RillValue, expected: RillTypeName | TypeStructure, location?: SourceLocation): RillValue;
|
|
43
43
|
/**
|
|
44
44
|
* Evaluate an expression and return its value.
|
|
45
45
|
* Main entry point for expression evaluation.
|
|
@@ -70,6 +70,17 @@ export function assertType(value, expected, location) {
|
|
|
70
70
|
resolverConfigs: new Map(),
|
|
71
71
|
resolvingSchemes: new Set(),
|
|
72
72
|
typeMethodDicts: new Map(),
|
|
73
|
+
leafTypes: new Set([
|
|
74
|
+
'string',
|
|
75
|
+
'number',
|
|
76
|
+
'bool',
|
|
77
|
+
'vector',
|
|
78
|
+
'type',
|
|
79
|
+
'any',
|
|
80
|
+
'closure',
|
|
81
|
+
'field_descriptor',
|
|
82
|
+
]),
|
|
83
|
+
unvalidatedMethodReceivers: new Set(),
|
|
73
84
|
};
|
|
74
85
|
const evaluator = getEvaluator(minimalContext);
|
|
75
86
|
return evaluator.assertType(value, expected, location);
|