@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
|
@@ -3,673 +3,28 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Core value types that flow through Rill programs.
|
|
5
5
|
* Public API for host applications.
|
|
6
|
-
*/
|
|
7
|
-
import { RuntimeError } from '../../types.js';
|
|
8
|
-
import { VALID_TYPE_NAMES } from '../../constants.js';
|
|
9
|
-
import { callableEquals, isCallable, isDict, isScriptCallable, } from './callable.js';
|
|
10
|
-
/** Type guard for RillTuple (spread args) */
|
|
11
|
-
export function isTuple(value) {
|
|
12
|
-
return (typeof value === 'object' &&
|
|
13
|
-
value !== null &&
|
|
14
|
-
'__rill_tuple' in value &&
|
|
15
|
-
value.__rill_tuple === true);
|
|
16
|
-
}
|
|
17
|
-
/** Type guard for RillVector */
|
|
18
|
-
export function isVector(value) {
|
|
19
|
-
return (typeof value === 'object' &&
|
|
20
|
-
value !== null &&
|
|
21
|
-
'__rill_vector' in value &&
|
|
22
|
-
value.__rill_vector === true);
|
|
23
|
-
}
|
|
24
|
-
/** Type guard for RillOrdered (named spread args) */
|
|
25
|
-
export function isOrdered(value) {
|
|
26
|
-
return (typeof value === 'object' &&
|
|
27
|
-
value !== null &&
|
|
28
|
-
'__rill_ordered' in value &&
|
|
29
|
-
value.__rill_ordered === true);
|
|
30
|
-
}
|
|
31
|
-
/** Type guard for RillTypeValue */
|
|
32
|
-
export function isTypeValue(value) {
|
|
33
|
-
return (typeof value === 'object' &&
|
|
34
|
-
value !== null &&
|
|
35
|
-
'__rill_type' in value &&
|
|
36
|
-
value.__rill_type === true);
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Create ordered from entries array (named, preserves insertion order).
|
|
40
|
-
* Entries may be 2-element [name, value] or 3-element [name, value, default]
|
|
41
|
-
* tuples; the third element carries a default value for `.^input` reflection.
|
|
42
|
-
*/
|
|
43
|
-
export function createOrdered(entries) {
|
|
44
|
-
return Object.freeze({ __rill_ordered: true, entries: [...entries] });
|
|
45
|
-
}
|
|
46
|
-
/** Create tuple from entries array (positional, preserves order) */
|
|
47
|
-
export function createTuple(entries) {
|
|
48
|
-
return Object.freeze({ __rill_tuple: true, entries: [...entries] });
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Create vector from Float32Array with model name.
|
|
52
|
-
* @throws {Error} if data.length is 0 (zero-dimension vectors not allowed)
|
|
53
|
-
*/
|
|
54
|
-
export function createVector(data, model) {
|
|
55
|
-
if (data.length === 0) {
|
|
56
|
-
throw new Error('Vector data must have at least one dimension');
|
|
57
|
-
}
|
|
58
|
-
return { __rill_vector: true, data, model };
|
|
59
|
-
}
|
|
60
|
-
/** Infer the Rill type from a runtime value */
|
|
61
|
-
export function inferType(value) {
|
|
62
|
-
if (value === null)
|
|
63
|
-
return 'string'; // null treated as empty string
|
|
64
|
-
if (typeof value === 'string')
|
|
65
|
-
return 'string';
|
|
66
|
-
if (typeof value === 'number')
|
|
67
|
-
return 'number';
|
|
68
|
-
if (typeof value === 'boolean')
|
|
69
|
-
return 'bool';
|
|
70
|
-
if (isTuple(value))
|
|
71
|
-
return 'tuple';
|
|
72
|
-
if (isOrdered(value))
|
|
73
|
-
return 'ordered';
|
|
74
|
-
if (isVector(value))
|
|
75
|
-
return 'vector';
|
|
76
|
-
if (Array.isArray(value))
|
|
77
|
-
return 'list';
|
|
78
|
-
if (isTypeValue(value))
|
|
79
|
-
return 'type';
|
|
80
|
-
if (isRillIterator(value))
|
|
81
|
-
return 'iterator';
|
|
82
|
-
if (typeof value === 'object' &&
|
|
83
|
-
'__type' in value &&
|
|
84
|
-
value.__type === 'callable') {
|
|
85
|
-
return 'closure';
|
|
86
|
-
}
|
|
87
|
-
if (typeof value === 'object')
|
|
88
|
-
return 'dict';
|
|
89
|
-
return 'string'; // fallback
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Infer the element type for a homogeneous list.
|
|
93
|
-
* Empty arrays return { type: 'any' }.
|
|
94
|
-
* Mixed types throw RILL-R002.
|
|
95
|
-
*/
|
|
96
|
-
export function inferElementType(elements) {
|
|
97
|
-
if (elements.length === 0)
|
|
98
|
-
return { type: 'any' };
|
|
99
|
-
const firstElem = elements[0];
|
|
100
|
-
let accType = inferStructuralType(firstElem);
|
|
101
|
-
for (let i = 1; i < elements.length; i++) {
|
|
102
|
-
const elem = elements[i];
|
|
103
|
-
const elemType = inferStructuralType(elem);
|
|
104
|
-
const merged = commonType(accType, elemType);
|
|
105
|
-
if (merged === null) {
|
|
106
|
-
throw new RuntimeError('RILL-R002', `List elements must be the same type: expected ${formatStructuralType(accType)}, got ${formatStructuralType(elemType)} at index ${i}`);
|
|
107
|
-
}
|
|
108
|
-
accType = merged;
|
|
109
|
-
}
|
|
110
|
-
return accType;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Merge uniform value types from two sides of the same compound type.
|
|
114
|
-
* Sub-case A: both carry valueType -> recurse commonType.
|
|
115
|
-
* Sub-case B: both carry structural fields -> extract value types, merge all.
|
|
116
|
-
* Returns the merged RillType on success, undefined when no uniform merge applies.
|
|
117
|
-
*/
|
|
118
|
-
function mergeUniformValueType(aValue, bValue, aFields, bFields) {
|
|
119
|
-
// Sub-case A: both carry valueType
|
|
120
|
-
if (aValue !== undefined && bValue !== undefined) {
|
|
121
|
-
const merged = commonType(aValue, bValue);
|
|
122
|
-
if (merged !== null)
|
|
123
|
-
return merged;
|
|
124
|
-
return undefined;
|
|
125
|
-
}
|
|
126
|
-
// Sub-case B: both carry structural fields
|
|
127
|
-
if (aFields !== undefined && bFields !== undefined) {
|
|
128
|
-
const allTypes = [
|
|
129
|
-
...aFields.map((f) => f.type),
|
|
130
|
-
...bFields.map((f) => f.type),
|
|
131
|
-
];
|
|
132
|
-
if (allTypes.length === 0)
|
|
133
|
-
return undefined;
|
|
134
|
-
let merged = allTypes[0];
|
|
135
|
-
for (let i = 1; i < allTypes.length; i++) {
|
|
136
|
-
const next = commonType(merged, allTypes[i]);
|
|
137
|
-
if (next === null)
|
|
138
|
-
return undefined;
|
|
139
|
-
merged = next;
|
|
140
|
-
}
|
|
141
|
-
return merged;
|
|
142
|
-
}
|
|
143
|
-
return undefined;
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Return the most specific shared type for two RillType values.
|
|
147
|
-
* Returns null when types are incompatible at the top level.
|
|
148
6
|
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
* 4. Bare type fallback: same compound type but structural mismatch
|
|
155
|
-
* 5. Incompatible: different top-level types return null
|
|
156
|
-
*/
|
|
157
|
-
export function commonType(a, b) {
|
|
158
|
-
// 1. Any-narrowing
|
|
159
|
-
if (a.type === 'any')
|
|
160
|
-
return b;
|
|
161
|
-
if (b.type === 'any')
|
|
162
|
-
return a;
|
|
163
|
-
// 5. Incompatible top-level types (checked early to short-circuit)
|
|
164
|
-
if (a.type !== b.type)
|
|
165
|
-
return null;
|
|
166
|
-
// 2. Structural match
|
|
167
|
-
if (structuralTypeEquals(a, b))
|
|
168
|
-
return a;
|
|
169
|
-
// 3. Recursive list element merging
|
|
170
|
-
if (a.type === 'list' && b.type === 'list') {
|
|
171
|
-
if (a.element !== undefined && b.element !== undefined) {
|
|
172
|
-
const inner = commonType(a.element, b.element);
|
|
173
|
-
if (inner !== null)
|
|
174
|
-
return { type: 'list', element: inner };
|
|
175
|
-
}
|
|
176
|
-
return { type: 'list' };
|
|
177
|
-
}
|
|
178
|
-
// 3b. Uniform valueType merging for dict/tuple/ordered
|
|
179
|
-
if (a.type === 'dict' && b.type === 'dict') {
|
|
180
|
-
const merged = mergeUniformValueType(a.valueType, b.valueType, a.fields ? Object.values(a.fields) : undefined, b.fields ? Object.values(b.fields) : undefined);
|
|
181
|
-
if (merged !== undefined)
|
|
182
|
-
return { type: 'dict', valueType: merged };
|
|
183
|
-
}
|
|
184
|
-
if (a.type === 'tuple' && b.type === 'tuple') {
|
|
185
|
-
const merged = mergeUniformValueType(a.valueType, b.valueType, a.elements, b.elements);
|
|
186
|
-
if (merged !== undefined)
|
|
187
|
-
return { type: 'tuple', valueType: merged };
|
|
188
|
-
}
|
|
189
|
-
if (a.type === 'ordered' && b.type === 'ordered') {
|
|
190
|
-
const merged = mergeUniformValueType(a.valueType, b.valueType, a.fields, b.fields);
|
|
191
|
-
if (merged !== undefined)
|
|
192
|
-
return { type: 'ordered', valueType: merged };
|
|
193
|
-
}
|
|
194
|
-
// 4. Bare type fallback for compound types.
|
|
195
|
-
// The cast is safe for closure/dict/tuple/ordered (all sub-fields optional).
|
|
196
|
-
// For union, members is required by RillType but omitted here intentionally:
|
|
197
|
-
// bare union signals structural incompatibility without enumerating members.
|
|
198
|
-
if (a.type === 'closure' ||
|
|
199
|
-
a.type === 'dict' ||
|
|
200
|
-
a.type === 'tuple' ||
|
|
201
|
-
a.type === 'ordered' ||
|
|
202
|
-
a.type === 'union') {
|
|
203
|
-
return { type: a.type };
|
|
204
|
-
}
|
|
205
|
-
return null;
|
|
206
|
-
}
|
|
207
|
-
/** Compare two structural types for equality. */
|
|
208
|
-
export function structuralTypeEquals(a, b) {
|
|
209
|
-
if (a.type !== b.type)
|
|
210
|
-
return false;
|
|
211
|
-
// Leaf variants compare by type alone
|
|
212
|
-
if (a.type === 'number' ||
|
|
213
|
-
a.type === 'string' ||
|
|
214
|
-
a.type === 'bool' ||
|
|
215
|
-
a.type === 'vector' ||
|
|
216
|
-
a.type === 'type' ||
|
|
217
|
-
a.type === 'any') {
|
|
218
|
-
return true;
|
|
219
|
-
}
|
|
220
|
-
if (a.type === 'list' && b.type === 'list') {
|
|
221
|
-
if (a.element === undefined && b.element === undefined)
|
|
222
|
-
return true;
|
|
223
|
-
if (a.element === undefined || b.element === undefined)
|
|
224
|
-
return false;
|
|
225
|
-
return structuralTypeEquals(a.element, b.element);
|
|
226
|
-
}
|
|
227
|
-
if (a.type === 'dict' && b.type === 'dict') {
|
|
228
|
-
// Uniform valueType comparison (mirrors list element at line 308)
|
|
229
|
-
const aHasValue = a.valueType !== undefined;
|
|
230
|
-
const bHasValue = b.valueType !== undefined;
|
|
231
|
-
if (aHasValue || bHasValue) {
|
|
232
|
-
if (!aHasValue || !bHasValue)
|
|
233
|
-
return false;
|
|
234
|
-
return structuralTypeEquals(a.valueType, b.valueType);
|
|
235
|
-
}
|
|
236
|
-
// Structural fields comparison
|
|
237
|
-
if (a.fields === undefined && b.fields === undefined)
|
|
238
|
-
return true;
|
|
239
|
-
if (a.fields === undefined || b.fields === undefined)
|
|
240
|
-
return false;
|
|
241
|
-
const aKeys = Object.keys(a.fields).sort();
|
|
242
|
-
const bKeys = Object.keys(b.fields).sort();
|
|
243
|
-
if (aKeys.length !== bKeys.length)
|
|
244
|
-
return false;
|
|
245
|
-
for (let i = 0; i < aKeys.length; i++) {
|
|
246
|
-
const key = aKeys[i];
|
|
247
|
-
if (key !== bKeys[i])
|
|
248
|
-
return false;
|
|
249
|
-
const aField = a.fields[key];
|
|
250
|
-
const bField = b.fields[key];
|
|
251
|
-
const aHasDefault = aField.defaultValue !== undefined;
|
|
252
|
-
const bHasDefault = bField.defaultValue !== undefined;
|
|
253
|
-
if (aHasDefault !== bHasDefault)
|
|
254
|
-
return false;
|
|
255
|
-
if (!structuralTypeEquals(aField.type, bField.type))
|
|
256
|
-
return false;
|
|
257
|
-
if (aHasDefault && bHasDefault) {
|
|
258
|
-
if (!deepEquals(aField.defaultValue, bField.defaultValue))
|
|
259
|
-
return false;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
return true;
|
|
263
|
-
}
|
|
264
|
-
if (a.type === 'tuple' && b.type === 'tuple') {
|
|
265
|
-
// Uniform valueType comparison (mirrors list element at line 308)
|
|
266
|
-
const aHasValue = a.valueType !== undefined;
|
|
267
|
-
const bHasValue = b.valueType !== undefined;
|
|
268
|
-
if (aHasValue || bHasValue) {
|
|
269
|
-
if (!aHasValue || !bHasValue)
|
|
270
|
-
return false;
|
|
271
|
-
return structuralTypeEquals(a.valueType, b.valueType);
|
|
272
|
-
}
|
|
273
|
-
// Structural elements comparison
|
|
274
|
-
if (a.elements === undefined && b.elements === undefined)
|
|
275
|
-
return true;
|
|
276
|
-
if (a.elements === undefined || b.elements === undefined)
|
|
277
|
-
return false;
|
|
278
|
-
if (a.elements.length !== b.elements.length)
|
|
279
|
-
return false;
|
|
280
|
-
for (let i = 0; i < a.elements.length; i++) {
|
|
281
|
-
const aElem = a.elements[i];
|
|
282
|
-
const bElem = b.elements[i];
|
|
283
|
-
if (!structuralTypeEquals(aElem.type, bElem.type))
|
|
284
|
-
return false;
|
|
285
|
-
const aDefault = aElem.defaultValue;
|
|
286
|
-
const bDefault = bElem.defaultValue;
|
|
287
|
-
if (aDefault === undefined && bDefault === undefined)
|
|
288
|
-
continue;
|
|
289
|
-
if (aDefault === undefined || bDefault === undefined)
|
|
290
|
-
return false;
|
|
291
|
-
if (!deepEquals(aDefault, bDefault))
|
|
292
|
-
return false;
|
|
293
|
-
}
|
|
294
|
-
return true;
|
|
295
|
-
}
|
|
296
|
-
if (a.type === 'ordered' && b.type === 'ordered') {
|
|
297
|
-
// Uniform valueType comparison (mirrors list element at line 308)
|
|
298
|
-
const aHasValue = a.valueType !== undefined;
|
|
299
|
-
const bHasValue = b.valueType !== undefined;
|
|
300
|
-
if (aHasValue || bHasValue) {
|
|
301
|
-
if (!aHasValue || !bHasValue)
|
|
302
|
-
return false;
|
|
303
|
-
return structuralTypeEquals(a.valueType, b.valueType);
|
|
304
|
-
}
|
|
305
|
-
// Structural fields comparison
|
|
306
|
-
if (a.fields === undefined && b.fields === undefined)
|
|
307
|
-
return true;
|
|
308
|
-
if (a.fields === undefined || b.fields === undefined)
|
|
309
|
-
return false;
|
|
310
|
-
if (a.fields.length !== b.fields.length)
|
|
311
|
-
return false;
|
|
312
|
-
for (let i = 0; i < a.fields.length; i++) {
|
|
313
|
-
const aField = a.fields[i];
|
|
314
|
-
const bField = b.fields[i];
|
|
315
|
-
if (aField.name !== bField.name)
|
|
316
|
-
return false;
|
|
317
|
-
if (!structuralTypeEquals(aField.type, bField.type))
|
|
318
|
-
return false;
|
|
319
|
-
const aDefault = aField.defaultValue;
|
|
320
|
-
const bDefault = bField.defaultValue;
|
|
321
|
-
if (aDefault === undefined && bDefault === undefined)
|
|
322
|
-
continue;
|
|
323
|
-
if (aDefault === undefined || bDefault === undefined)
|
|
324
|
-
return false;
|
|
325
|
-
if (!deepEquals(aDefault, bDefault))
|
|
326
|
-
return false;
|
|
327
|
-
}
|
|
328
|
-
return true;
|
|
329
|
-
}
|
|
330
|
-
if (a.type === 'union' && b.type === 'union') {
|
|
331
|
-
if (a.members.length !== b.members.length)
|
|
332
|
-
return false;
|
|
333
|
-
for (let i = 0; i < a.members.length; i++) {
|
|
334
|
-
if (!structuralTypeEquals(a.members[i], b.members[i]))
|
|
335
|
-
return false;
|
|
336
|
-
}
|
|
337
|
-
return true;
|
|
338
|
-
}
|
|
339
|
-
if (a.type === 'closure' && b.type === 'closure') {
|
|
340
|
-
if (a.params === undefined && b.params === undefined) {
|
|
341
|
-
// Both absent: compare ret
|
|
342
|
-
}
|
|
343
|
-
else if (a.params === undefined || b.params === undefined) {
|
|
344
|
-
return false;
|
|
345
|
-
}
|
|
346
|
-
else {
|
|
347
|
-
if (a.params.length !== b.params.length)
|
|
348
|
-
return false;
|
|
349
|
-
for (let i = 0; i < a.params.length; i++) {
|
|
350
|
-
const aParam = a.params[i];
|
|
351
|
-
const bParam = b.params[i];
|
|
352
|
-
if (aParam.name !== bParam.name)
|
|
353
|
-
return false;
|
|
354
|
-
if (!structuralTypeEquals(aParam.type, bParam.type))
|
|
355
|
-
return false;
|
|
356
|
-
const aDefault = aParam.defaultValue;
|
|
357
|
-
const bDefault = bParam.defaultValue;
|
|
358
|
-
if (aDefault === undefined && bDefault === undefined)
|
|
359
|
-
continue;
|
|
360
|
-
if (aDefault === undefined || bDefault === undefined)
|
|
361
|
-
return false;
|
|
362
|
-
if (!deepEquals(aDefault, bDefault))
|
|
363
|
-
return false;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
if (a.ret === undefined && b.ret === undefined)
|
|
367
|
-
return true;
|
|
368
|
-
if (a.ret === undefined || b.ret === undefined)
|
|
369
|
-
return false;
|
|
370
|
-
return structuralTypeEquals(a.ret, b.ret);
|
|
371
|
-
}
|
|
372
|
-
return false;
|
|
373
|
-
}
|
|
374
|
-
/** Infer the structural type descriptor for any Rill value. */
|
|
375
|
-
export function inferStructuralType(value) {
|
|
376
|
-
if (value === null || typeof value === 'string') {
|
|
377
|
-
return { type: 'string' };
|
|
378
|
-
}
|
|
379
|
-
if (typeof value === 'number') {
|
|
380
|
-
return { type: 'number' };
|
|
381
|
-
}
|
|
382
|
-
if (typeof value === 'boolean') {
|
|
383
|
-
return { type: 'bool' };
|
|
384
|
-
}
|
|
385
|
-
if (isTypeValue(value)) {
|
|
386
|
-
return { type: 'type' };
|
|
387
|
-
}
|
|
388
|
-
if (Array.isArray(value)) {
|
|
389
|
-
return { type: 'list', element: inferElementType(value) };
|
|
390
|
-
}
|
|
391
|
-
if (isTuple(value)) {
|
|
392
|
-
return {
|
|
393
|
-
type: 'tuple',
|
|
394
|
-
elements: value.entries.map((e) => ({
|
|
395
|
-
type: inferStructuralType(e),
|
|
396
|
-
})),
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
if (isOrdered(value)) {
|
|
400
|
-
return {
|
|
401
|
-
type: 'ordered',
|
|
402
|
-
fields: value.entries.map(([k, v]) => ({
|
|
403
|
-
name: k,
|
|
404
|
-
type: inferStructuralType(v),
|
|
405
|
-
})),
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
if (isVector(value)) {
|
|
409
|
-
return { type: 'vector' };
|
|
410
|
-
}
|
|
411
|
-
if (isCallable(value)) {
|
|
412
|
-
const params = (value.params ?? []).map((p) => paramToFieldDef(p.name, p.type ?? { type: 'any' }, p.defaultValue));
|
|
413
|
-
const ret = value.returnType.structure;
|
|
414
|
-
return { type: 'closure', params, ret };
|
|
415
|
-
}
|
|
416
|
-
if (typeof value === 'object') {
|
|
417
|
-
const dict = value;
|
|
418
|
-
const fields = {};
|
|
419
|
-
for (const [k, v] of Object.entries(dict)) {
|
|
420
|
-
fields[k] = { type: inferStructuralType(v) };
|
|
421
|
-
}
|
|
422
|
-
return { type: 'dict', fields };
|
|
423
|
-
}
|
|
424
|
-
throw new RuntimeError('RILL-R004', `Cannot infer structural type for ${formatValue(value)}`);
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* Check if a value matches a structural type descriptor.
|
|
428
|
-
* Used for runtime type checking (`:?` operator).
|
|
7
|
+
* Structural operations (structureEquals, structureMatches, formatStructure,
|
|
8
|
+
* inferStructure, commonType) live in types/operations.ts and are re-exported.
|
|
9
|
+
*
|
|
10
|
+
* Dispatch functions (inferType, formatValue, deepEquals, serializeValue,
|
|
11
|
+
* copyValue) re-export from type-registrations.ts protocol implementations.
|
|
429
12
|
*/
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
if (!Array.isArray(value))
|
|
446
|
-
return false;
|
|
447
|
-
// Absent element sub-field: matches any list value
|
|
448
|
-
if (type.element === undefined)
|
|
449
|
-
return true;
|
|
450
|
-
if (type.element.type === 'any')
|
|
451
|
-
return true;
|
|
452
|
-
return value.every((elem) => structuralTypeMatches(elem, type.element));
|
|
453
|
-
}
|
|
454
|
-
if (type.type === 'dict') {
|
|
455
|
-
if (!isDict(value))
|
|
456
|
-
return false;
|
|
457
|
-
// Uniform value type check: every value must match valueType
|
|
458
|
-
if (type.valueType !== undefined) {
|
|
459
|
-
const vals = Object.values(value);
|
|
460
|
-
return vals.every((v) => structuralTypeMatches(v, type.valueType));
|
|
461
|
-
}
|
|
462
|
-
// Absent fields sub-field: matches any dict value
|
|
463
|
-
if (type.fields === undefined)
|
|
464
|
-
return true;
|
|
465
|
-
const dictKeys = Object.keys(type.fields);
|
|
466
|
-
// Empty fields object matches any dict
|
|
467
|
-
if (dictKeys.length === 0)
|
|
468
|
-
return true;
|
|
469
|
-
const dict = value;
|
|
470
|
-
for (const key of dictKeys) {
|
|
471
|
-
if (!(key in dict)) {
|
|
472
|
-
const field = type.fields[key];
|
|
473
|
-
if (field.defaultValue !== undefined)
|
|
474
|
-
continue;
|
|
475
|
-
return false;
|
|
476
|
-
}
|
|
477
|
-
const field = type.fields[key];
|
|
478
|
-
if (!structuralTypeMatches(dict[key], field.type))
|
|
479
|
-
return false;
|
|
480
|
-
}
|
|
481
|
-
return true;
|
|
482
|
-
}
|
|
483
|
-
if (type.type === 'tuple') {
|
|
484
|
-
if (!isTuple(value))
|
|
485
|
-
return false;
|
|
486
|
-
// Uniform value type check: every entry must match valueType
|
|
487
|
-
if (type.valueType !== undefined) {
|
|
488
|
-
return value.entries.every((v) => structuralTypeMatches(v, type.valueType));
|
|
489
|
-
}
|
|
490
|
-
// Absent elements sub-field: matches any tuple value
|
|
491
|
-
if (type.elements === undefined)
|
|
492
|
-
return true;
|
|
493
|
-
if (type.elements.length === 0)
|
|
494
|
-
return value.entries.length === 0;
|
|
495
|
-
// Reject if value has more entries than type elements
|
|
496
|
-
if (value.entries.length > type.elements.length)
|
|
497
|
-
return false;
|
|
498
|
-
// Reject if value is shorter and any trailing missing element lacks a default
|
|
499
|
-
if (value.entries.length < type.elements.length) {
|
|
500
|
-
for (let i = value.entries.length; i < type.elements.length; i++) {
|
|
501
|
-
const field = type.elements[i];
|
|
502
|
-
if (field.defaultValue === undefined)
|
|
503
|
-
return false;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
for (let i = 0; i < value.entries.length; i++) {
|
|
507
|
-
if (!structuralTypeMatches(value.entries[i], type.elements[i].type))
|
|
508
|
-
return false;
|
|
509
|
-
}
|
|
510
|
-
return true;
|
|
511
|
-
}
|
|
512
|
-
if (type.type === 'ordered') {
|
|
513
|
-
if (!isOrdered(value))
|
|
514
|
-
return false;
|
|
515
|
-
// Uniform value type check: every entry value must match valueType
|
|
516
|
-
if (type.valueType !== undefined) {
|
|
517
|
-
return value.entries.every(([, v]) => structuralTypeMatches(v, type.valueType));
|
|
518
|
-
}
|
|
519
|
-
// Absent fields sub-field: matches any ordered value
|
|
520
|
-
if (type.fields === undefined)
|
|
521
|
-
return true;
|
|
522
|
-
if (type.fields.length === 0)
|
|
523
|
-
return value.entries.length === 0;
|
|
524
|
-
// Reject if value has more entries than type fields
|
|
525
|
-
if (value.entries.length > type.fields.length)
|
|
526
|
-
return false;
|
|
527
|
-
// Reject if value is shorter and any trailing missing field lacks a default
|
|
528
|
-
if (value.entries.length < type.fields.length) {
|
|
529
|
-
for (let i = value.entries.length; i < type.fields.length; i++) {
|
|
530
|
-
const field = type.fields[i];
|
|
531
|
-
if (field.defaultValue === undefined)
|
|
532
|
-
return false;
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
for (let i = 0; i < value.entries.length; i++) {
|
|
536
|
-
const field = type.fields[i];
|
|
537
|
-
const [actualName, actualValue] = value.entries[i];
|
|
538
|
-
if (actualName !== field.name)
|
|
539
|
-
return false;
|
|
540
|
-
if (!structuralTypeMatches(actualValue, field.type))
|
|
541
|
-
return false;
|
|
542
|
-
}
|
|
543
|
-
return true;
|
|
544
|
-
}
|
|
545
|
-
if (type.type === 'closure') {
|
|
546
|
-
if (!isCallable(value))
|
|
547
|
-
return false;
|
|
548
|
-
// Absent params sub-field: matches any closure value of that compound type
|
|
549
|
-
if (type.params === undefined)
|
|
550
|
-
return true;
|
|
551
|
-
const valueParams = value.params ?? [];
|
|
552
|
-
if (valueParams.length !== type.params.length)
|
|
553
|
-
return false;
|
|
554
|
-
for (let i = 0; i < type.params.length; i++) {
|
|
555
|
-
const field = type.params[i];
|
|
556
|
-
const param = valueParams[i];
|
|
557
|
-
if (param.name !== field.name)
|
|
558
|
-
return false;
|
|
559
|
-
const paramType = param.type ?? { type: 'any' };
|
|
560
|
-
if (!structuralTypeEquals(paramType, field.type))
|
|
561
|
-
return false;
|
|
562
|
-
}
|
|
563
|
-
const retType = value.returnType.structure;
|
|
564
|
-
if (type.ret === undefined)
|
|
565
|
-
return true;
|
|
566
|
-
return structuralTypeEquals(retType, type.ret);
|
|
567
|
-
}
|
|
568
|
-
if (type.type === 'union') {
|
|
569
|
-
return type.members.some((member) => structuralTypeMatches(value, member));
|
|
570
|
-
}
|
|
571
|
-
return false;
|
|
572
|
-
}
|
|
573
|
-
/** Build a closure param field definition from name, type, and optional default. */
|
|
574
|
-
export function paramToFieldDef(name, type, defaultValue) {
|
|
575
|
-
const field = { name, type };
|
|
576
|
-
if (defaultValue !== undefined)
|
|
577
|
-
field.defaultValue = defaultValue;
|
|
578
|
-
return field;
|
|
579
|
-
}
|
|
580
|
-
/** Format a RillValue as a rill literal for use in type signatures. */
|
|
581
|
-
function formatRillLiteral(value) {
|
|
582
|
-
if (typeof value === 'string') {
|
|
583
|
-
const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
584
|
-
return `"${escaped}"`;
|
|
585
|
-
}
|
|
586
|
-
if (typeof value === 'number')
|
|
587
|
-
return String(value);
|
|
588
|
-
if (typeof value === 'boolean')
|
|
589
|
-
return value ? 'true' : 'false';
|
|
590
|
-
if (value === null)
|
|
591
|
-
return 'null';
|
|
592
|
-
return formatValue(value);
|
|
593
|
-
}
|
|
594
|
-
/** Format a structural type descriptor as a human-readable string. */
|
|
595
|
-
export function formatStructuralType(type) {
|
|
596
|
-
if (type.type === 'any' ||
|
|
597
|
-
type.type === 'number' ||
|
|
598
|
-
type.type === 'string' ||
|
|
599
|
-
type.type === 'bool' ||
|
|
600
|
-
type.type === 'vector' ||
|
|
601
|
-
type.type === 'type') {
|
|
602
|
-
return type.type;
|
|
603
|
-
}
|
|
604
|
-
if (type.type === 'list') {
|
|
605
|
-
if (type.element === undefined)
|
|
606
|
-
return 'list';
|
|
607
|
-
return `list(${formatStructuralType(type.element)})`;
|
|
608
|
-
}
|
|
609
|
-
if (type.type === 'dict') {
|
|
610
|
-
if (type.valueType !== undefined && type.fields === undefined) {
|
|
611
|
-
return `dict(${formatStructuralType(type.valueType)})`;
|
|
612
|
-
}
|
|
613
|
-
if (type.fields === undefined)
|
|
614
|
-
return 'dict';
|
|
615
|
-
const parts = Object.keys(type.fields)
|
|
616
|
-
.sort()
|
|
617
|
-
.map((k) => {
|
|
618
|
-
const field = type.fields[k];
|
|
619
|
-
const base = `${k}: ${formatStructuralType(field.type)}`;
|
|
620
|
-
if (field.defaultValue === undefined)
|
|
621
|
-
return base;
|
|
622
|
-
return `${base} = ${formatRillLiteral(field.defaultValue)}`;
|
|
623
|
-
});
|
|
624
|
-
return `dict(${parts.join(', ')})`;
|
|
625
|
-
}
|
|
626
|
-
if (type.type === 'tuple') {
|
|
627
|
-
if (type.valueType !== undefined && type.elements === undefined) {
|
|
628
|
-
return `tuple(${formatStructuralType(type.valueType)})`;
|
|
629
|
-
}
|
|
630
|
-
if (type.elements === undefined)
|
|
631
|
-
return 'tuple';
|
|
632
|
-
const parts = type.elements.map((field) => {
|
|
633
|
-
const base = formatStructuralType(field.type);
|
|
634
|
-
if (field.defaultValue === undefined)
|
|
635
|
-
return base;
|
|
636
|
-
return `${base} = ${formatRillLiteral(field.defaultValue)}`;
|
|
637
|
-
});
|
|
638
|
-
return `tuple(${parts.join(', ')})`;
|
|
639
|
-
}
|
|
640
|
-
if (type.type === 'ordered') {
|
|
641
|
-
if (type.valueType !== undefined && type.fields === undefined) {
|
|
642
|
-
return `ordered(${formatStructuralType(type.valueType)})`;
|
|
643
|
-
}
|
|
644
|
-
if (type.fields === undefined)
|
|
645
|
-
return 'ordered';
|
|
646
|
-
const parts = type.fields.map((field) => {
|
|
647
|
-
const base = `${field.name}: ${formatStructuralType(field.type)}`;
|
|
648
|
-
if (field.defaultValue === undefined)
|
|
649
|
-
return base;
|
|
650
|
-
return `${base} = ${formatRillLiteral(field.defaultValue)}`;
|
|
651
|
-
});
|
|
652
|
-
return `ordered(${parts.join(', ')})`;
|
|
653
|
-
}
|
|
654
|
-
if (type.type === 'closure') {
|
|
655
|
-
if (type.params === undefined)
|
|
656
|
-
return 'closure';
|
|
657
|
-
const params = type.params
|
|
658
|
-
.map((field) => {
|
|
659
|
-
const base = `${field.name}: ${formatStructuralType(field.type)}`;
|
|
660
|
-
if (field.defaultValue === undefined)
|
|
661
|
-
return base;
|
|
662
|
-
return `${base} = ${formatRillLiteral(field.defaultValue)}`;
|
|
663
|
-
})
|
|
664
|
-
.join(', ');
|
|
665
|
-
const ret = type.ret !== undefined ? formatStructuralType(type.ret) : 'any';
|
|
666
|
-
return `|${params}| :${ret}`;
|
|
667
|
-
}
|
|
668
|
-
if (type.type === 'union') {
|
|
669
|
-
return type.members.map(formatStructuralType).join('|');
|
|
670
|
-
}
|
|
671
|
-
return 'any';
|
|
672
|
-
}
|
|
13
|
+
import { VALID_TYPE_NAMES } from '../../constants.js';
|
|
14
|
+
import { isCallable as _isCallableGuard, isDict, isIterator, isOrdered, isStream, isTuple, isTypeValue, isVector, } from './types/guards.js';
|
|
15
|
+
/** isCallable guard widened to narrow to full RillCallable (not just CallableMarker) */
|
|
16
|
+
const isCallable = _isCallableGuard;
|
|
17
|
+
import { inferType as registryInferType, formatValue as registryFormatValue, deepEquals as registryDeepEquals, serializeValue as registrySerializeValue, } from './types/registrations.js';
|
|
18
|
+
// Re-export guards from canonical source (types/guards.ts)
|
|
19
|
+
export { isCallable, isDict, isIterator, isOrdered, isStream, isTuple, isTypeValue, isVector, };
|
|
20
|
+
// Value constructors re-exported from canonical source (types/constructors.ts)
|
|
21
|
+
export { copyValue, createOrdered, createTuple, createVector, emptyForType, } from './types/constructors.js';
|
|
22
|
+
// Structural operations re-exported from types/operations.ts
|
|
23
|
+
export { commonType, compareStructuredFields, formatStructure, inferElementType, inferStructure, paramToFieldDef, structureEquals, structureMatches, } from './types/operations.js';
|
|
24
|
+
// Re-import for local use (toNative, structureToTypeValue)
|
|
25
|
+
import { formatStructure, inferStructure } from './types/operations.js';
|
|
26
|
+
/** Infer the Rill type from a runtime value. Delegates to type-registrations. */
|
|
27
|
+
export const inferType = registryInferType;
|
|
673
28
|
/**
|
|
674
29
|
* Check if a value is of the expected type.
|
|
675
30
|
* Returns true if the value matches the expected type, false otherwise.
|
|
@@ -706,88 +61,10 @@ export function isTruthy(value) {
|
|
|
706
61
|
export function isEmpty(value) {
|
|
707
62
|
return !isTruthy(value);
|
|
708
63
|
}
|
|
709
|
-
/** Format a value for display */
|
|
710
|
-
export
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
if (typeof value === 'string')
|
|
714
|
-
return value;
|
|
715
|
-
if (typeof value === 'number')
|
|
716
|
-
return String(value);
|
|
717
|
-
if (typeof value === 'boolean')
|
|
718
|
-
return value ? 'true' : 'false';
|
|
719
|
-
if (isCallable(value)) {
|
|
720
|
-
return 'type(closure)';
|
|
721
|
-
}
|
|
722
|
-
if (isTuple(value)) {
|
|
723
|
-
return `tuple[${value.entries.map(formatValue).join(', ')}]`;
|
|
724
|
-
}
|
|
725
|
-
if (isOrdered(value)) {
|
|
726
|
-
const parts = value.entries.map(([k, v]) => `${k}: ${formatValue(v)}`);
|
|
727
|
-
return `ordered[${parts.join(', ')}]`;
|
|
728
|
-
}
|
|
729
|
-
if (isRillIterator(value)) {
|
|
730
|
-
return 'type(iterator)';
|
|
731
|
-
}
|
|
732
|
-
if (Array.isArray(value)) {
|
|
733
|
-
return `list[${value.map(formatValue).join(', ')}]`;
|
|
734
|
-
}
|
|
735
|
-
if (isVector(value)) {
|
|
736
|
-
return `vector(${value.model}, ${value.data.length}d)`;
|
|
737
|
-
}
|
|
738
|
-
if (isTypeValue(value)) {
|
|
739
|
-
return formatStructuralType(value.structure);
|
|
740
|
-
}
|
|
741
|
-
// Plain dict
|
|
742
|
-
if (typeof value === 'object') {
|
|
743
|
-
const dict = value;
|
|
744
|
-
const parts = Object.entries(dict).map(([k, v]) => `${k}: ${formatValue(v)}`);
|
|
745
|
-
return `dict[${parts.join(', ')}]`;
|
|
746
|
-
}
|
|
747
|
-
return String(value);
|
|
748
|
-
}
|
|
749
|
-
/**
|
|
750
|
-
* Convert a RillValue to a JSON-serializable value.
|
|
751
|
-
* @throws {Error} plain Error (not RuntimeError) for non-serializable types
|
|
752
|
-
*/
|
|
753
|
-
export function valueToJSON(value) {
|
|
754
|
-
if (value === null)
|
|
755
|
-
return null;
|
|
756
|
-
if (typeof value === 'string')
|
|
757
|
-
return value;
|
|
758
|
-
if (typeof value === 'number')
|
|
759
|
-
return value;
|
|
760
|
-
if (typeof value === 'boolean')
|
|
761
|
-
return value;
|
|
762
|
-
if (Array.isArray(value)) {
|
|
763
|
-
return value.map(valueToJSON);
|
|
764
|
-
}
|
|
765
|
-
if (isCallable(value)) {
|
|
766
|
-
throw new Error('closures are not JSON-serializable');
|
|
767
|
-
}
|
|
768
|
-
if (isTuple(value)) {
|
|
769
|
-
throw new Error('tuples are not JSON-serializable');
|
|
770
|
-
}
|
|
771
|
-
if (isOrdered(value)) {
|
|
772
|
-
throw new Error('ordered values are not JSON-serializable');
|
|
773
|
-
}
|
|
774
|
-
if (isVector(value)) {
|
|
775
|
-
throw new Error('vectors are not JSON-serializable');
|
|
776
|
-
}
|
|
777
|
-
if (isTypeValue(value)) {
|
|
778
|
-
throw new Error('type values are not JSON-serializable');
|
|
779
|
-
}
|
|
780
|
-
if (isRillIterator(value)) {
|
|
781
|
-
throw new Error('iterators are not JSON-serializable');
|
|
782
|
-
}
|
|
783
|
-
// Plain dict
|
|
784
|
-
const dict = value;
|
|
785
|
-
const result = {};
|
|
786
|
-
for (const [k, v] of Object.entries(dict)) {
|
|
787
|
-
result[k] = valueToJSON(v);
|
|
788
|
-
}
|
|
789
|
-
return result;
|
|
790
|
-
}
|
|
64
|
+
/** Format a value for display. Delegates to type-registrations. */
|
|
65
|
+
export const formatValue = registryFormatValue;
|
|
66
|
+
/** Serialize a Rill value for JSON transport. Delegates to type-registrations. */
|
|
67
|
+
export const serializeValue = registrySerializeValue;
|
|
791
68
|
/**
|
|
792
69
|
* Convert a RillValue to a NativeResult for host consumption.
|
|
793
70
|
* Non-representable types (closures, vectors, type values, iterators) produce descriptor objects.
|
|
@@ -795,7 +72,7 @@ export function valueToJSON(value) {
|
|
|
795
72
|
*/
|
|
796
73
|
export function toNative(value) {
|
|
797
74
|
const rillTypeName = inferType(value);
|
|
798
|
-
const rillTypeSignature =
|
|
75
|
+
const rillTypeSignature = formatStructure(inferStructure(value));
|
|
799
76
|
const nativeValue = toNativeValue(value);
|
|
800
77
|
return { rillTypeName, rillTypeSignature, value: nativeValue };
|
|
801
78
|
}
|
|
@@ -812,7 +89,7 @@ function toNativeValue(value) {
|
|
|
812
89
|
return value.map(toNativeValue);
|
|
813
90
|
}
|
|
814
91
|
if (isCallable(value)) {
|
|
815
|
-
return { signature:
|
|
92
|
+
return { signature: formatStructure(inferStructure(value)) };
|
|
816
93
|
}
|
|
817
94
|
if (isTuple(value)) {
|
|
818
95
|
return value.entries.map(toNativeValue);
|
|
@@ -830,10 +107,21 @@ function toNativeValue(value) {
|
|
|
830
107
|
if (isTypeValue(value)) {
|
|
831
108
|
return {
|
|
832
109
|
name: value.typeName,
|
|
833
|
-
signature:
|
|
110
|
+
signature: formatStructure(value.structure),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (isStream(value)) {
|
|
114
|
+
const descriptor = {
|
|
115
|
+
__type: 'stream',
|
|
116
|
+
done: value.done,
|
|
834
117
|
};
|
|
118
|
+
const chunkType = value['__rill_stream_chunk_type'];
|
|
119
|
+
const retType = value['__rill_stream_ret_type'];
|
|
120
|
+
descriptor['chunkType'] = chunkType ? formatStructure(chunkType) : null;
|
|
121
|
+
descriptor['resolutionType'] = retType ? formatStructure(retType) : null;
|
|
122
|
+
return descriptor;
|
|
835
123
|
}
|
|
836
|
-
if (
|
|
124
|
+
if (isIterator(value)) {
|
|
837
125
|
return { done: value.done };
|
|
838
126
|
}
|
|
839
127
|
// Plain dict
|
|
@@ -844,152 +132,8 @@ function toNativeValue(value) {
|
|
|
844
132
|
}
|
|
845
133
|
return result;
|
|
846
134
|
}
|
|
847
|
-
/**
|
|
848
|
-
|
|
849
|
-
* - Primitives: value equality
|
|
850
|
-
* - Tuples: length + recursive element equality
|
|
851
|
-
* - Dicts: same keys + recursive value equality (order-independent)
|
|
852
|
-
*/
|
|
853
|
-
export function deepEquals(a, b) {
|
|
854
|
-
// Handle primitives and null
|
|
855
|
-
if (a === b)
|
|
856
|
-
return true;
|
|
857
|
-
if (a === null || b === null)
|
|
858
|
-
return false;
|
|
859
|
-
if (typeof a !== typeof b)
|
|
860
|
-
return false;
|
|
861
|
-
// Primitives (string, number, boolean) - covered by === above
|
|
862
|
-
if (typeof a !== 'object' || typeof b !== 'object')
|
|
863
|
-
return false;
|
|
864
|
-
// Both are non-null objects at this point
|
|
865
|
-
const aObj = a;
|
|
866
|
-
const bObj = b;
|
|
867
|
-
// Check for tuples (positional spread args)
|
|
868
|
-
const aIsTuple = isTuple(a);
|
|
869
|
-
const bIsTuple = isTuple(b);
|
|
870
|
-
if (aIsTuple !== bIsTuple)
|
|
871
|
-
return false;
|
|
872
|
-
if (aIsTuple && bIsTuple) {
|
|
873
|
-
if (a.entries.length !== b.entries.length)
|
|
874
|
-
return false;
|
|
875
|
-
for (let i = 0; i < a.entries.length; i++) {
|
|
876
|
-
const aVal = a.entries[i];
|
|
877
|
-
const bVal = b.entries[i];
|
|
878
|
-
if (aVal === undefined || bVal === undefined) {
|
|
879
|
-
if (aVal !== bVal)
|
|
880
|
-
return false;
|
|
881
|
-
}
|
|
882
|
-
else if (!deepEquals(aVal, bVal)) {
|
|
883
|
-
return false;
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
return true;
|
|
887
|
-
}
|
|
888
|
-
// Check for ordered (named spread args)
|
|
889
|
-
const aIsOrdered = isOrdered(a);
|
|
890
|
-
const bIsOrdered = isOrdered(b);
|
|
891
|
-
if (aIsOrdered !== bIsOrdered)
|
|
892
|
-
return false;
|
|
893
|
-
if (aIsOrdered && bIsOrdered) {
|
|
894
|
-
if (a.entries.length !== b.entries.length)
|
|
895
|
-
return false;
|
|
896
|
-
for (let i = 0; i < a.entries.length; i++) {
|
|
897
|
-
const aEntry = a.entries[i];
|
|
898
|
-
const bEntry = b.entries[i];
|
|
899
|
-
if (aEntry === undefined || bEntry === undefined)
|
|
900
|
-
return false;
|
|
901
|
-
if (aEntry[0] !== bEntry[0])
|
|
902
|
-
return false;
|
|
903
|
-
if (!deepEquals(aEntry[1], bEntry[1]))
|
|
904
|
-
return false;
|
|
905
|
-
}
|
|
906
|
-
return true;
|
|
907
|
-
}
|
|
908
|
-
// Check for vectors
|
|
909
|
-
const aIsVector = isVector(a);
|
|
910
|
-
const bIsVector = isVector(b);
|
|
911
|
-
if (aIsVector !== bIsVector)
|
|
912
|
-
return false;
|
|
913
|
-
if (aIsVector && bIsVector) {
|
|
914
|
-
// Vectors equal when model matches AND all float elements match
|
|
915
|
-
if (a.model !== b.model)
|
|
916
|
-
return false;
|
|
917
|
-
if (a.data.length !== b.data.length)
|
|
918
|
-
return false;
|
|
919
|
-
for (let i = 0; i < a.data.length; i++) {
|
|
920
|
-
const aVal = a.data[i];
|
|
921
|
-
const bVal = b.data[i];
|
|
922
|
-
if (aVal !== bVal)
|
|
923
|
-
return false;
|
|
924
|
-
}
|
|
925
|
-
return true;
|
|
926
|
-
}
|
|
927
|
-
// Check for type values (first-class type names)
|
|
928
|
-
const aIsTypeValue = isTypeValue(a);
|
|
929
|
-
const bIsTypeValue = isTypeValue(b);
|
|
930
|
-
if (aIsTypeValue !== bIsTypeValue)
|
|
931
|
-
return false;
|
|
932
|
-
if (aIsTypeValue && bIsTypeValue) {
|
|
933
|
-
return structuralTypeEquals(a.structure, b.structure);
|
|
934
|
-
}
|
|
935
|
-
// Check for arrays (lists)
|
|
936
|
-
const aIsArray = Array.isArray(a);
|
|
937
|
-
const bIsArray = Array.isArray(b);
|
|
938
|
-
if (aIsArray !== bIsArray)
|
|
939
|
-
return false;
|
|
940
|
-
if (aIsArray && bIsArray) {
|
|
941
|
-
if (a.length !== b.length)
|
|
942
|
-
return false;
|
|
943
|
-
for (let i = 0; i < a.length; i++) {
|
|
944
|
-
const aElem = a[i];
|
|
945
|
-
const bElem = b[i];
|
|
946
|
-
if (aElem === undefined || bElem === undefined) {
|
|
947
|
-
if (aElem !== bElem)
|
|
948
|
-
return false;
|
|
949
|
-
}
|
|
950
|
-
else if (!deepEquals(aElem, bElem)) {
|
|
951
|
-
return false;
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
return true;
|
|
955
|
-
}
|
|
956
|
-
// Both are dicts (plain objects) or callables
|
|
957
|
-
// For script callables, use structural equality (params + body AST + captured values)
|
|
958
|
-
// For runtime/application callables, use reference equality
|
|
959
|
-
if ('__type' in aObj || '__type' in bObj) {
|
|
960
|
-
// Both must be callables to be equal
|
|
961
|
-
if (!('__type' in aObj) || !('__type' in bObj))
|
|
962
|
-
return false;
|
|
963
|
-
if (aObj.__type !== 'callable' || bObj.__type !== 'callable')
|
|
964
|
-
return false;
|
|
965
|
-
// Script callables: structural equality
|
|
966
|
-
if (isScriptCallable(a) && isScriptCallable(b)) {
|
|
967
|
-
return callableEquals(a, b, deepEquals);
|
|
968
|
-
}
|
|
969
|
-
// Runtime/application callables: reference equality
|
|
970
|
-
return a === b;
|
|
971
|
-
}
|
|
972
|
-
const aDict = a;
|
|
973
|
-
const bDict = b;
|
|
974
|
-
const aKeys = Object.keys(aDict);
|
|
975
|
-
const bKeys = Object.keys(bDict);
|
|
976
|
-
if (aKeys.length !== bKeys.length)
|
|
977
|
-
return false;
|
|
978
|
-
for (const key of aKeys) {
|
|
979
|
-
if (!(key in bDict))
|
|
980
|
-
return false;
|
|
981
|
-
const aVal = aDict[key];
|
|
982
|
-
const bVal = bDict[key];
|
|
983
|
-
if (aVal === undefined || bVal === undefined) {
|
|
984
|
-
if (aVal !== bVal)
|
|
985
|
-
return false;
|
|
986
|
-
}
|
|
987
|
-
else if (!deepEquals(aVal, bVal)) {
|
|
988
|
-
return false;
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
return true;
|
|
992
|
-
}
|
|
135
|
+
/** Deep structural equality for all Rill values. Delegates to type-registrations. */
|
|
136
|
+
export const deepEquals = registryDeepEquals;
|
|
993
137
|
/** Reserved dict method names that cannot be overridden */
|
|
994
138
|
export const RESERVED_DICT_METHODS = ['keys', 'values', 'entries'];
|
|
995
139
|
/**
|
|
@@ -999,19 +143,19 @@ export const RESERVED_DICT_METHODS = ['keys', 'values', 'entries'];
|
|
|
999
143
|
export const anyTypeValue = Object.freeze({
|
|
1000
144
|
__rill_type: true,
|
|
1001
145
|
typeName: 'any',
|
|
1002
|
-
structure: {
|
|
146
|
+
structure: { kind: 'any' },
|
|
1003
147
|
});
|
|
1004
148
|
/**
|
|
1005
|
-
* Convert a
|
|
1006
|
-
* Uses the
|
|
149
|
+
* Convert a TypeStructure descriptor to a RillTypeValue.
|
|
150
|
+
* Uses the TypeStructure's `kind` field as the `typeName`.
|
|
1007
151
|
* Falls back to 'any' for compound types that lack a direct RillTypeName mapping.
|
|
1008
152
|
*/
|
|
1009
|
-
export function
|
|
153
|
+
export function structureToTypeValue(type) {
|
|
1010
154
|
const validNames = VALID_TYPE_NAMES;
|
|
1011
155
|
return Object.freeze({
|
|
1012
156
|
__rill_type: true,
|
|
1013
|
-
typeName: (validNames.includes(type.
|
|
1014
|
-
? type.
|
|
157
|
+
typeName: (validNames.includes(type.kind)
|
|
158
|
+
? type.kind
|
|
1015
159
|
: 'any'),
|
|
1016
160
|
structure: type,
|
|
1017
161
|
});
|
|
@@ -1022,73 +166,17 @@ export function rillTypeToTypeValue(type) {
|
|
|
1022
166
|
* synthesized and hydrated.
|
|
1023
167
|
*/
|
|
1024
168
|
export function hasCollectionFields(type) {
|
|
1025
|
-
return ((type.
|
|
1026
|
-
(
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
if (type.type === 'dict')
|
|
1035
|
-
return {};
|
|
1036
|
-
if (type.type === 'ordered')
|
|
1037
|
-
return createOrdered([]);
|
|
1038
|
-
if (type.type === 'tuple')
|
|
1039
|
-
return createTuple([]);
|
|
1040
|
-
return {};
|
|
169
|
+
return ((type.kind === 'dict' &&
|
|
170
|
+
(!!type.fields ||
|
|
171
|
+
!!type.valueType)) ||
|
|
172
|
+
(type.kind === 'ordered' &&
|
|
173
|
+
(!!type.fields ||
|
|
174
|
+
!!type.valueType)) ||
|
|
175
|
+
(type.kind === 'tuple' &&
|
|
176
|
+
(!!type.elements ||
|
|
177
|
+
!!type.valueType)));
|
|
1041
178
|
}
|
|
1042
179
|
/** Check if a key name is reserved */
|
|
1043
180
|
export function isReservedMethod(name) {
|
|
1044
181
|
return RESERVED_DICT_METHODS.includes(name);
|
|
1045
182
|
}
|
|
1046
|
-
/**
|
|
1047
|
-
* Type guard for Rill iterator (lazy sequence).
|
|
1048
|
-
* An iterator is a dict with:
|
|
1049
|
-
* - done: boolean - whether iteration is complete
|
|
1050
|
-
* - next: callable - function to get next iterator
|
|
1051
|
-
* - value: any (only required when not done) - current element
|
|
1052
|
-
*/
|
|
1053
|
-
export function isRillIterator(value) {
|
|
1054
|
-
if (!isDict(value))
|
|
1055
|
-
return false;
|
|
1056
|
-
const dict = value;
|
|
1057
|
-
if (!('done' in dict && typeof dict['done'] === 'boolean'))
|
|
1058
|
-
return false;
|
|
1059
|
-
if (!('next' in dict && isCallable(dict['next'])))
|
|
1060
|
-
return false;
|
|
1061
|
-
// 'value' field only required when not done
|
|
1062
|
-
if (!dict['done'] && !('value' in dict))
|
|
1063
|
-
return false;
|
|
1064
|
-
return true;
|
|
1065
|
-
}
|
|
1066
|
-
/**
|
|
1067
|
-
* Deep copy a RillValue, producing a new independent value.
|
|
1068
|
-
* Handles primitives, arrays, plain dicts, and null.
|
|
1069
|
-
* Special markers (closures, tuples, ordered, vectors, type values) are returned
|
|
1070
|
-
* as-is since they are immutable by contract.
|
|
1071
|
-
*/
|
|
1072
|
-
export function deepCopyRillValue(value) {
|
|
1073
|
-
if (value === null || typeof value !== 'object') {
|
|
1074
|
-
return value;
|
|
1075
|
-
}
|
|
1076
|
-
if (Array.isArray(value)) {
|
|
1077
|
-
return value.map(deepCopyRillValue);
|
|
1078
|
-
}
|
|
1079
|
-
// Plain dict: copy recursively. Special markers (RillTuple, RillOrdered, etc.)
|
|
1080
|
-
// carry __rill_* own properties and are treated as immutable; return as-is.
|
|
1081
|
-
if (!('__rill_tuple' in value) &&
|
|
1082
|
-
!('__rill_ordered' in value) &&
|
|
1083
|
-
!('__rill_vector' in value) &&
|
|
1084
|
-
!('__rill_type' in value) &&
|
|
1085
|
-
!('__type' in value) &&
|
|
1086
|
-
!('__rill_field_descriptor' in value)) {
|
|
1087
|
-
const copy = {};
|
|
1088
|
-
for (const [k, v] of Object.entries(value)) {
|
|
1089
|
-
copy[k] = deepCopyRillValue(v);
|
|
1090
|
-
}
|
|
1091
|
-
return copy;
|
|
1092
|
-
}
|
|
1093
|
-
return value;
|
|
1094
|
-
}
|