@rcrsr/rill 0.8.6 → 0.10.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/dist/ast-nodes.d.ts +189 -49
- package/dist/ast-nodes.d.ts.map +1 -1
- package/dist/ast-unions.d.ts +1 -1
- package/dist/ast-unions.d.ts.map +1 -1
- package/dist/constants.d.ts +14 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +30 -0
- package/dist/constants.js.map +1 -0
- package/dist/error-classes.d.ts +3 -1
- package/dist/error-classes.d.ts.map +1 -1
- package/dist/error-classes.js +11 -5
- package/dist/error-classes.js.map +1 -1
- package/dist/error-registry.d.ts.map +1 -1
- package/dist/error-registry.js +313 -11
- package/dist/error-registry.js.map +1 -1
- package/dist/ext/crypto/index.d.ts +2 -1
- package/dist/ext/crypto/index.d.ts.map +1 -1
- package/dist/ext/crypto/index.js +7 -0
- package/dist/ext/crypto/index.js.map +1 -1
- package/dist/ext/exec/index.d.ts +2 -1
- package/dist/ext/exec/index.d.ts.map +1 -1
- package/dist/ext/exec/index.js +6 -0
- package/dist/ext/exec/index.js.map +1 -1
- package/dist/ext/fetch/index.d.ts +2 -1
- package/dist/ext/fetch/index.d.ts.map +1 -1
- package/dist/ext/fetch/index.js +6 -0
- package/dist/ext/fetch/index.js.map +1 -1
- package/dist/ext/fs/index.d.ts +2 -1
- package/dist/ext/fs/index.d.ts.map +1 -1
- package/dist/ext/fs/index.js +3 -0
- package/dist/ext/fs/index.js.map +1 -1
- package/dist/ext/kv/index.d.ts +2 -1
- package/dist/ext/kv/index.d.ts.map +1 -1
- package/dist/ext/kv/index.js +5 -1
- package/dist/ext/kv/index.js.map +1 -1
- package/dist/generated/introspection-data.d.ts +1 -1
- package/dist/generated/introspection-data.d.ts.map +1 -1
- package/dist/generated/introspection-data.js +194 -185
- package/dist/generated/introspection-data.js.map +1 -1
- package/dist/generated/version-data.d.ts +1 -1
- package/dist/generated/version-data.d.ts.map +1 -1
- package/dist/generated/version-data.js +3 -3
- package/dist/generated/version-data.js.map +1 -1
- package/dist/highlight-map.d.ts.map +1 -1
- package/dist/highlight-map.js +8 -2
- package/dist/highlight-map.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/lexer/operators.d.ts.map +1 -1
- package/dist/lexer/operators.js +0 -2
- package/dist/lexer/operators.js.map +1 -1
- package/dist/lexer/readers.d.ts +18 -1
- package/dist/lexer/readers.d.ts.map +1 -1
- package/dist/lexer/readers.js +55 -0
- package/dist/lexer/readers.js.map +1 -1
- package/dist/parser/helpers.d.ts +8 -13
- package/dist/parser/helpers.d.ts.map +1 -1
- package/dist/parser/helpers.js +42 -35
- package/dist/parser/helpers.js.map +1 -1
- package/dist/parser/index.d.ts +1 -0
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +1 -0
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/parser-collect.d.ts.map +1 -1
- package/dist/parser/parser-collect.js +34 -5
- package/dist/parser/parser-collect.js.map +1 -1
- package/dist/parser/parser-control.d.ts +1 -1
- package/dist/parser/parser-control.d.ts.map +1 -1
- package/dist/parser/parser-control.js +11 -2
- package/dist/parser/parser-control.js.map +1 -1
- package/dist/parser/parser-expr.d.ts +3 -1
- package/dist/parser/parser-expr.d.ts.map +1 -1
- package/dist/parser/parser-expr.js +377 -100
- package/dist/parser/parser-expr.js.map +1 -1
- package/dist/parser/parser-extract.d.ts +3 -5
- package/dist/parser/parser-extract.d.ts.map +1 -1
- package/dist/parser/parser-extract.js +37 -69
- package/dist/parser/parser-extract.js.map +1 -1
- package/dist/parser/parser-functions.d.ts +2 -2
- package/dist/parser/parser-functions.d.ts.map +1 -1
- package/dist/parser/parser-functions.js +112 -36
- package/dist/parser/parser-functions.js.map +1 -1
- package/dist/parser/parser-literals.d.ts +5 -4
- package/dist/parser/parser-literals.d.ts.map +1 -1
- package/dist/parser/parser-literals.js +316 -47
- package/dist/parser/parser-literals.js.map +1 -1
- package/dist/parser/parser-script.d.ts.map +1 -1
- package/dist/parser/parser-script.js +25 -12
- package/dist/parser/parser-script.js.map +1 -1
- package/dist/parser/parser-shape.d.ts +13 -0
- package/dist/parser/parser-shape.d.ts.map +1 -0
- package/dist/parser/parser-shape.js +72 -0
- package/dist/parser/parser-shape.js.map +1 -0
- package/dist/parser/parser-types.d.ts +31 -0
- package/dist/parser/parser-types.d.ts.map +1 -0
- package/dist/parser/parser-types.js +78 -0
- package/dist/parser/parser-types.js.map +1 -0
- package/dist/parser/parser-variables.d.ts.map +1 -1
- package/dist/parser/parser-variables.js +10 -1
- package/dist/parser/parser-variables.js.map +1 -1
- package/dist/runtime/core/callable.d.ts +27 -22
- package/dist/runtime/core/callable.d.ts.map +1 -1
- package/dist/runtime/core/callable.js +30 -26
- package/dist/runtime/core/callable.js.map +1 -1
- package/dist/runtime/core/context.d.ts.map +1 -1
- package/dist/runtime/core/context.js +8 -8
- package/dist/runtime/core/context.js.map +1 -1
- package/dist/runtime/core/equals.d.ts.map +1 -1
- package/dist/runtime/core/equals.js +179 -30
- package/dist/runtime/core/equals.js.map +1 -1
- package/dist/runtime/core/eval/base.d.ts +2 -2
- package/dist/runtime/core/eval/base.d.ts.map +1 -1
- package/dist/runtime/core/eval/evaluator.d.ts.map +1 -1
- package/dist/runtime/core/eval/evaluator.js +3 -1
- package/dist/runtime/core/eval/evaluator.js.map +1 -1
- package/dist/runtime/core/eval/index.d.ts +18 -3
- package/dist/runtime/core/eval/index.d.ts.map +1 -1
- package/dist/runtime/core/eval/index.js +22 -2
- package/dist/runtime/core/eval/index.js.map +1 -1
- package/dist/runtime/core/eval/mixins/annotations.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/annotations.js +14 -8
- package/dist/runtime/core/eval/mixins/annotations.js.map +1 -1
- package/dist/runtime/core/eval/mixins/closures.d.ts +0 -2
- package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/closures.js +341 -105
- package/dist/runtime/core/eval/mixins/closures.js.map +1 -1
- package/dist/runtime/core/eval/mixins/collections.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/collections.js +65 -25
- package/dist/runtime/core/eval/mixins/collections.js.map +1 -1
- package/dist/runtime/core/eval/mixins/control-flow.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/control-flow.js +21 -17
- package/dist/runtime/core/eval/mixins/control-flow.js.map +1 -1
- package/dist/runtime/core/eval/mixins/conversion.d.ts +30 -0
- package/dist/runtime/core/eval/mixins/conversion.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/conversion.js +212 -0
- package/dist/runtime/core/eval/mixins/conversion.js.map +1 -0
- package/dist/runtime/core/eval/mixins/core.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/core.js +101 -32
- package/dist/runtime/core/eval/mixins/core.js.map +1 -1
- package/dist/runtime/core/eval/mixins/extraction.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/extraction.js +136 -30
- package/dist/runtime/core/eval/mixins/extraction.js.map +1 -1
- package/dist/runtime/core/eval/mixins/list-dispatch.d.ts +17 -0
- package/dist/runtime/core/eval/mixins/list-dispatch.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/list-dispatch.js +97 -0
- package/dist/runtime/core/eval/mixins/list-dispatch.js.map +1 -0
- package/dist/runtime/core/eval/mixins/literals.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/literals.js +73 -83
- package/dist/runtime/core/eval/mixins/literals.js.map +1 -1
- package/dist/runtime/core/eval/mixins/types.d.ts +4 -0
- package/dist/runtime/core/eval/mixins/types.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/types.js +323 -3
- package/dist/runtime/core/eval/mixins/types.js.map +1 -1
- package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/variables.js +45 -7
- package/dist/runtime/core/eval/mixins/variables.js.map +1 -1
- package/dist/runtime/core/execute.d.ts.map +1 -1
- package/dist/runtime/core/execute.js +3 -16
- package/dist/runtime/core/execute.js.map +1 -1
- package/dist/runtime/core/field-descriptor.d.ts +29 -0
- package/dist/runtime/core/field-descriptor.d.ts.map +1 -0
- package/dist/runtime/core/field-descriptor.js +27 -0
- package/dist/runtime/core/field-descriptor.js.map +1 -0
- package/dist/runtime/core/types.d.ts +15 -6
- package/dist/runtime/core/types.d.ts.map +1 -1
- package/dist/runtime/core/types.js.map +1 -1
- package/dist/runtime/core/values.d.ts +114 -9
- package/dist/runtime/core/values.d.ts.map +1 -1
- package/dist/runtime/core/values.js +529 -43
- package/dist/runtime/core/values.js.map +1 -1
- package/dist/runtime/ext/builtins.d.ts.map +1 -1
- package/dist/runtime/ext/builtins.js +47 -107
- package/dist/runtime/ext/builtins.js.map +1 -1
- package/dist/runtime/ext/extensions.d.ts +21 -2
- package/dist/runtime/ext/extensions.d.ts.map +1 -1
- package/dist/runtime/ext/extensions.js.map +1 -1
- package/dist/runtime/index.d.ts +6 -4
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +7 -2
- package/dist/runtime/index.js.map +1 -1
- package/dist/token-types.d.ts +7 -2
- package/dist/token-types.d.ts.map +1 -1
- package/dist/token-types.js +9 -2
- package/dist/token-types.js.map +1 -1
- package/dist/value-types.d.ts +32 -1
- package/dist/value-types.d.ts.map +1 -1
- package/dist/value-types.js +1 -1
- package/dist/value-types.js.map +1 -1
- package/package.json +4 -1
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Core value types that flow through Rill programs.
|
|
5
5
|
* Public API for host applications.
|
|
6
6
|
*/
|
|
7
|
+
import { RuntimeError } from '../../types.js';
|
|
7
8
|
import { callableEquals, isCallable, isDict, isScriptCallable, } from './callable.js';
|
|
8
9
|
/** Type guard for RillTuple (spread args) */
|
|
9
10
|
export function isTuple(value) {
|
|
@@ -19,24 +20,27 @@ export function isVector(value) {
|
|
|
19
20
|
'__rill_vector' in value &&
|
|
20
21
|
value.__rill_vector === true);
|
|
21
22
|
}
|
|
22
|
-
/**
|
|
23
|
-
export function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
entries.set(i, val);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return { __rill_tuple: true, entries };
|
|
23
|
+
/** Type guard for RillOrdered (named spread args) */
|
|
24
|
+
export function isOrdered(value) {
|
|
25
|
+
return (typeof value === 'object' &&
|
|
26
|
+
value !== null &&
|
|
27
|
+
'__rill_ordered' in value &&
|
|
28
|
+
value.__rill_ordered === true);
|
|
32
29
|
}
|
|
33
|
-
/**
|
|
34
|
-
export function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
/** Type guard for RillTypeValue */
|
|
31
|
+
export function isTypeValue(value) {
|
|
32
|
+
return (typeof value === 'object' &&
|
|
33
|
+
value !== null &&
|
|
34
|
+
'__rill_type' in value &&
|
|
35
|
+
value.__rill_type === true);
|
|
36
|
+
}
|
|
37
|
+
/** Create ordered from entries array (named, preserves insertion order) */
|
|
38
|
+
export function createOrdered(entries) {
|
|
39
|
+
return Object.freeze({ __rill_ordered: true, entries: [...entries] });
|
|
40
|
+
}
|
|
41
|
+
/** Create tuple from entries array (positional, preserves order) */
|
|
42
|
+
export function createTuple(entries) {
|
|
43
|
+
return Object.freeze({ __rill_tuple: true, entries: [...entries] });
|
|
40
44
|
}
|
|
41
45
|
/**
|
|
42
46
|
* Create vector from Float32Array with model name.
|
|
@@ -60,10 +64,16 @@ export function inferType(value) {
|
|
|
60
64
|
return 'bool';
|
|
61
65
|
if (isTuple(value))
|
|
62
66
|
return 'tuple';
|
|
67
|
+
if (isOrdered(value))
|
|
68
|
+
return 'ordered';
|
|
63
69
|
if (isVector(value))
|
|
64
70
|
return 'vector';
|
|
65
71
|
if (Array.isArray(value))
|
|
66
72
|
return 'list';
|
|
73
|
+
if (isTypeValue(value))
|
|
74
|
+
return 'type';
|
|
75
|
+
if (isRillIterator(value))
|
|
76
|
+
return 'iterator';
|
|
67
77
|
if (typeof value === 'object' &&
|
|
68
78
|
'__type' in value &&
|
|
69
79
|
value.__type === 'callable') {
|
|
@@ -73,6 +83,346 @@ export function inferType(value) {
|
|
|
73
83
|
return 'dict';
|
|
74
84
|
return 'string'; // fallback
|
|
75
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Infer the element type for a homogeneous list.
|
|
88
|
+
* Empty arrays return { type: 'any' }.
|
|
89
|
+
* Mixed types throw RILL-R002.
|
|
90
|
+
*/
|
|
91
|
+
export function inferElementType(elements) {
|
|
92
|
+
if (elements.length === 0)
|
|
93
|
+
return { type: 'any' };
|
|
94
|
+
const firstElem = elements[0];
|
|
95
|
+
const firstType = inferStructuralType(firstElem);
|
|
96
|
+
for (let i = 1; i < elements.length; i++) {
|
|
97
|
+
const elem = elements[i];
|
|
98
|
+
const elemType = inferStructuralType(elem);
|
|
99
|
+
if (!structuralTypeEquals(firstType, elemType)) {
|
|
100
|
+
throw new RuntimeError('RILL-R002', `List elements must be the same type: expected ${formatStructuralType(firstType)}, got ${formatStructuralType(elemType)} at index ${i}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return firstType;
|
|
104
|
+
}
|
|
105
|
+
/** Compare two structural types for equality. */
|
|
106
|
+
export function structuralTypeEquals(a, b) {
|
|
107
|
+
if (a.type !== b.type)
|
|
108
|
+
return false;
|
|
109
|
+
// Leaf variants compare by type alone
|
|
110
|
+
if (a.type === 'number' ||
|
|
111
|
+
a.type === 'string' ||
|
|
112
|
+
a.type === 'bool' ||
|
|
113
|
+
a.type === 'vector' ||
|
|
114
|
+
a.type === 'type' ||
|
|
115
|
+
a.type === 'any') {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
if (a.type === 'list' && b.type === 'list') {
|
|
119
|
+
if (a.element === undefined && b.element === undefined)
|
|
120
|
+
return true;
|
|
121
|
+
if (a.element === undefined || b.element === undefined)
|
|
122
|
+
return false;
|
|
123
|
+
return structuralTypeEquals(a.element, b.element);
|
|
124
|
+
}
|
|
125
|
+
if (a.type === 'dict' && b.type === 'dict') {
|
|
126
|
+
if (a.fields === undefined && b.fields === undefined)
|
|
127
|
+
return true;
|
|
128
|
+
if (a.fields === undefined || b.fields === undefined)
|
|
129
|
+
return false;
|
|
130
|
+
const aKeys = Object.keys(a.fields).sort();
|
|
131
|
+
const bKeys = Object.keys(b.fields).sort();
|
|
132
|
+
if (aKeys.length !== bKeys.length)
|
|
133
|
+
return false;
|
|
134
|
+
for (let i = 0; i < aKeys.length; i++) {
|
|
135
|
+
const key = aKeys[i];
|
|
136
|
+
if (key !== bKeys[i])
|
|
137
|
+
return false;
|
|
138
|
+
const aField = a.fields[key];
|
|
139
|
+
const bField = b.fields[key];
|
|
140
|
+
if (!structuralTypeEquals(aField, bField))
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
if (a.type === 'tuple' && b.type === 'tuple') {
|
|
146
|
+
if (a.elements === undefined && b.elements === undefined)
|
|
147
|
+
return true;
|
|
148
|
+
if (a.elements === undefined || b.elements === undefined)
|
|
149
|
+
return false;
|
|
150
|
+
if (a.elements.length !== b.elements.length)
|
|
151
|
+
return false;
|
|
152
|
+
for (let i = 0; i < a.elements.length; i++) {
|
|
153
|
+
if (!structuralTypeEquals(a.elements[i], b.elements[i]))
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
if (a.type === 'ordered' && b.type === 'ordered') {
|
|
159
|
+
if (a.fields === undefined && b.fields === undefined)
|
|
160
|
+
return true;
|
|
161
|
+
if (a.fields === undefined || b.fields === undefined)
|
|
162
|
+
return false;
|
|
163
|
+
if (a.fields.length !== b.fields.length)
|
|
164
|
+
return false;
|
|
165
|
+
for (let i = 0; i < a.fields.length; i++) {
|
|
166
|
+
const aField = a.fields[i];
|
|
167
|
+
const bField = b.fields[i];
|
|
168
|
+
if (aField[0] !== bField[0])
|
|
169
|
+
return false;
|
|
170
|
+
if (!structuralTypeEquals(aField[1], bField[1]))
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
if (a.type === 'closure' && b.type === 'closure') {
|
|
176
|
+
if (a.params === undefined && b.params === undefined) {
|
|
177
|
+
// Both absent: compare ret
|
|
178
|
+
}
|
|
179
|
+
else if (a.params === undefined || b.params === undefined) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
if (a.params.length !== b.params.length)
|
|
184
|
+
return false;
|
|
185
|
+
for (let i = 0; i < a.params.length; i++) {
|
|
186
|
+
const aParam = a.params[i];
|
|
187
|
+
const bParam = b.params[i];
|
|
188
|
+
if (aParam[0] !== bParam[0])
|
|
189
|
+
return false;
|
|
190
|
+
if (!structuralTypeEquals(aParam[1], bParam[1]))
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (a.ret === undefined && b.ret === undefined)
|
|
195
|
+
return true;
|
|
196
|
+
if (a.ret === undefined || b.ret === undefined)
|
|
197
|
+
return false;
|
|
198
|
+
return structuralTypeEquals(a.ret, b.ret);
|
|
199
|
+
}
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
/** Infer the structural type descriptor for any Rill value. */
|
|
203
|
+
export function inferStructuralType(value) {
|
|
204
|
+
if (value === null || typeof value === 'string') {
|
|
205
|
+
return { type: 'string' };
|
|
206
|
+
}
|
|
207
|
+
if (typeof value === 'number') {
|
|
208
|
+
return { type: 'number' };
|
|
209
|
+
}
|
|
210
|
+
if (typeof value === 'boolean') {
|
|
211
|
+
return { type: 'bool' };
|
|
212
|
+
}
|
|
213
|
+
if (isTypeValue(value)) {
|
|
214
|
+
return { type: 'type' };
|
|
215
|
+
}
|
|
216
|
+
if (Array.isArray(value)) {
|
|
217
|
+
return { type: 'list', element: inferElementType(value) };
|
|
218
|
+
}
|
|
219
|
+
if (isTuple(value)) {
|
|
220
|
+
return {
|
|
221
|
+
type: 'tuple',
|
|
222
|
+
elements: value.entries.map(inferStructuralType),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
if (isOrdered(value)) {
|
|
226
|
+
return {
|
|
227
|
+
type: 'ordered',
|
|
228
|
+
fields: value.entries.map(([k, v]) => [k, inferStructuralType(v)]),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
if (isVector(value)) {
|
|
232
|
+
return { type: 'vector' };
|
|
233
|
+
}
|
|
234
|
+
if (isCallable(value)) {
|
|
235
|
+
if (isScriptCallable(value)) {
|
|
236
|
+
const params = value.params.map((p) => [
|
|
237
|
+
p.name,
|
|
238
|
+
p.typeName !== null
|
|
239
|
+
? { type: p.typeName }
|
|
240
|
+
: { type: 'any' },
|
|
241
|
+
]);
|
|
242
|
+
let ret = { type: 'any' };
|
|
243
|
+
if (isTypeValue(value.returnShape)) {
|
|
244
|
+
ret = value.returnShape.structure;
|
|
245
|
+
}
|
|
246
|
+
return { type: 'closure', params, ret };
|
|
247
|
+
}
|
|
248
|
+
// Non-script callables have no annotations
|
|
249
|
+
return { type: 'closure', params: [], ret: { type: 'any' } };
|
|
250
|
+
}
|
|
251
|
+
if (typeof value === 'object') {
|
|
252
|
+
const dict = value;
|
|
253
|
+
const fields = {};
|
|
254
|
+
for (const [k, v] of Object.entries(dict)) {
|
|
255
|
+
fields[k] = inferStructuralType(v);
|
|
256
|
+
}
|
|
257
|
+
return { type: 'dict', fields };
|
|
258
|
+
}
|
|
259
|
+
throw new RuntimeError('RILL-R004', `Cannot infer structural type for ${formatValue(value)}`);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Check if a value matches a structural type descriptor.
|
|
263
|
+
* Used for runtime type checking (`:?` operator).
|
|
264
|
+
*/
|
|
265
|
+
export function structuralTypeMatches(value, type) {
|
|
266
|
+
if (typeof value === 'undefined') {
|
|
267
|
+
throw new RuntimeError('RILL-R004', 'Cannot type-check non-value');
|
|
268
|
+
}
|
|
269
|
+
if (type.type === 'any')
|
|
270
|
+
return true;
|
|
271
|
+
// Leaf primitive variants: match by inferred type name
|
|
272
|
+
if (type.type === 'number' ||
|
|
273
|
+
type.type === 'string' ||
|
|
274
|
+
type.type === 'bool' ||
|
|
275
|
+
type.type === 'vector' ||
|
|
276
|
+
type.type === 'type') {
|
|
277
|
+
return inferType(value) === type.type;
|
|
278
|
+
}
|
|
279
|
+
if (type.type === 'list') {
|
|
280
|
+
if (!Array.isArray(value))
|
|
281
|
+
return false;
|
|
282
|
+
// Absent element sub-field: matches any list value
|
|
283
|
+
if (type.element === undefined)
|
|
284
|
+
return true;
|
|
285
|
+
if (type.element.type === 'any')
|
|
286
|
+
return true;
|
|
287
|
+
return value.every((elem) => structuralTypeMatches(elem, type.element));
|
|
288
|
+
}
|
|
289
|
+
if (type.type === 'dict') {
|
|
290
|
+
if (!isDict(value))
|
|
291
|
+
return false;
|
|
292
|
+
// Absent fields sub-field: matches any dict value
|
|
293
|
+
if (type.fields === undefined)
|
|
294
|
+
return true;
|
|
295
|
+
const dictKeys = Object.keys(type.fields);
|
|
296
|
+
// Empty fields object matches any dict
|
|
297
|
+
if (dictKeys.length === 0)
|
|
298
|
+
return true;
|
|
299
|
+
const dict = value;
|
|
300
|
+
for (const key of dictKeys) {
|
|
301
|
+
if (!(key in dict))
|
|
302
|
+
return false;
|
|
303
|
+
if (!structuralTypeMatches(dict[key], type.fields[key]))
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
if (type.type === 'tuple') {
|
|
309
|
+
if (!isTuple(value))
|
|
310
|
+
return false;
|
|
311
|
+
// Absent elements sub-field: matches any tuple value
|
|
312
|
+
if (type.elements === undefined)
|
|
313
|
+
return true;
|
|
314
|
+
if (type.elements.length === 0)
|
|
315
|
+
return value.entries.length === 0;
|
|
316
|
+
if (value.entries.length !== type.elements.length)
|
|
317
|
+
return false;
|
|
318
|
+
for (let i = 0; i < type.elements.length; i++) {
|
|
319
|
+
if (!structuralTypeMatches(value.entries[i], type.elements[i]))
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
if (type.type === 'ordered') {
|
|
325
|
+
if (!isOrdered(value))
|
|
326
|
+
return false;
|
|
327
|
+
// Absent fields sub-field: matches any ordered value
|
|
328
|
+
if (type.fields === undefined)
|
|
329
|
+
return true;
|
|
330
|
+
if (type.fields.length === 0)
|
|
331
|
+
return value.entries.length === 0;
|
|
332
|
+
if (value.entries.length !== type.fields.length)
|
|
333
|
+
return false;
|
|
334
|
+
for (let i = 0; i < type.fields.length; i++) {
|
|
335
|
+
const [expectedName, expectedType] = type.fields[i];
|
|
336
|
+
const [actualName, actualValue] = value.entries[i];
|
|
337
|
+
if (actualName !== expectedName)
|
|
338
|
+
return false;
|
|
339
|
+
if (!structuralTypeMatches(actualValue, expectedType))
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
if (type.type === 'closure') {
|
|
345
|
+
if (!isCallable(value))
|
|
346
|
+
return false;
|
|
347
|
+
// Absent params sub-field: matches any closure value of that compound type
|
|
348
|
+
if (type.params === undefined)
|
|
349
|
+
return true;
|
|
350
|
+
if (!isScriptCallable(value)) {
|
|
351
|
+
// Non-script callables: match if type has no param constraints
|
|
352
|
+
return (type.params.every((_, i) => type.params[i][1].type === 'any') &&
|
|
353
|
+
(type.ret === undefined || type.ret.type === 'any'));
|
|
354
|
+
}
|
|
355
|
+
if (value.params.length !== type.params.length)
|
|
356
|
+
return false;
|
|
357
|
+
for (let i = 0; i < type.params.length; i++) {
|
|
358
|
+
const [expectedName, expectedType] = type.params[i];
|
|
359
|
+
const param = value.params[i];
|
|
360
|
+
if (param.name !== expectedName)
|
|
361
|
+
return false;
|
|
362
|
+
const paramType = param.typeStructure !== undefined
|
|
363
|
+
? param.typeStructure
|
|
364
|
+
: param.typeName !== null
|
|
365
|
+
? { type: param.typeName }
|
|
366
|
+
: { type: 'any' };
|
|
367
|
+
if (!structuralTypeEquals(paramType, expectedType))
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
let retType = { type: 'any' };
|
|
371
|
+
if (isTypeValue(value.returnShape)) {
|
|
372
|
+
retType = value.returnShape.structure;
|
|
373
|
+
}
|
|
374
|
+
if (type.ret === undefined)
|
|
375
|
+
return true;
|
|
376
|
+
return structuralTypeEquals(retType, type.ret);
|
|
377
|
+
}
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
/** Format a structural type descriptor as a human-readable string. */
|
|
381
|
+
export function formatStructuralType(type) {
|
|
382
|
+
if (type.type === 'any' ||
|
|
383
|
+
type.type === 'number' ||
|
|
384
|
+
type.type === 'string' ||
|
|
385
|
+
type.type === 'bool' ||
|
|
386
|
+
type.type === 'vector' ||
|
|
387
|
+
type.type === 'type') {
|
|
388
|
+
return type.type;
|
|
389
|
+
}
|
|
390
|
+
if (type.type === 'list') {
|
|
391
|
+
if (type.element === undefined)
|
|
392
|
+
return 'list';
|
|
393
|
+
return `list(${formatStructuralType(type.element)})`;
|
|
394
|
+
}
|
|
395
|
+
if (type.type === 'dict') {
|
|
396
|
+
if (type.fields === undefined)
|
|
397
|
+
return 'dict';
|
|
398
|
+
const parts = Object.keys(type.fields)
|
|
399
|
+
.sort()
|
|
400
|
+
.map((k) => `${k}: ${formatStructuralType(type.fields[k])}`);
|
|
401
|
+
return `dict(${parts.join(', ')})`;
|
|
402
|
+
}
|
|
403
|
+
if (type.type === 'tuple') {
|
|
404
|
+
if (type.elements === undefined)
|
|
405
|
+
return 'tuple';
|
|
406
|
+
const parts = type.elements.map(formatStructuralType);
|
|
407
|
+
return `tuple(${parts.join(', ')})`;
|
|
408
|
+
}
|
|
409
|
+
if (type.type === 'ordered') {
|
|
410
|
+
if (type.fields === undefined)
|
|
411
|
+
return 'ordered';
|
|
412
|
+
const parts = type.fields.map(([k, t]) => `${k}: ${formatStructuralType(t)}`);
|
|
413
|
+
return `ordered(${parts.join(', ')})`;
|
|
414
|
+
}
|
|
415
|
+
if (type.type === 'closure') {
|
|
416
|
+
if (type.params === undefined)
|
|
417
|
+
return 'closure';
|
|
418
|
+
const params = type.params
|
|
419
|
+
.map(([name, t]) => `${name}: ${formatStructuralType(t)}`)
|
|
420
|
+
.join(', ');
|
|
421
|
+
const ret = type.ret !== undefined ? formatStructuralType(type.ret) : 'any';
|
|
422
|
+
return `|${params}| :${ret}`;
|
|
423
|
+
}
|
|
424
|
+
return 'any';
|
|
425
|
+
}
|
|
76
426
|
/**
|
|
77
427
|
* Check if a value is of the expected type.
|
|
78
428
|
* Returns true if the value matches the expected type, false otherwise.
|
|
@@ -91,7 +441,9 @@ export function isTruthy(value) {
|
|
|
91
441
|
if (typeof value === 'string')
|
|
92
442
|
return value.length > 0;
|
|
93
443
|
if (isTuple(value))
|
|
94
|
-
return value.entries.
|
|
444
|
+
return value.entries.length > 0;
|
|
445
|
+
if (isOrdered(value))
|
|
446
|
+
return value.entries.length > 0;
|
|
95
447
|
if (isVector(value))
|
|
96
448
|
return true; // Vectors always truthy (non-empty by construction)
|
|
97
449
|
if (Array.isArray(value))
|
|
@@ -110,40 +462,140 @@ export function isEmpty(value) {
|
|
|
110
462
|
/** Format a value for display */
|
|
111
463
|
export function formatValue(value) {
|
|
112
464
|
if (value === null)
|
|
113
|
-
return '';
|
|
465
|
+
return 'type(null)';
|
|
114
466
|
if (typeof value === 'string')
|
|
115
467
|
return value;
|
|
116
468
|
if (typeof value === 'number')
|
|
117
469
|
return String(value);
|
|
118
470
|
if (typeof value === 'boolean')
|
|
119
471
|
return value ? 'true' : 'false';
|
|
472
|
+
if (isCallable(value)) {
|
|
473
|
+
return 'type(closure)';
|
|
474
|
+
}
|
|
120
475
|
if (isTuple(value)) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
476
|
+
return `tuple[${value.entries.map(formatValue).join(', ')}]`;
|
|
477
|
+
}
|
|
478
|
+
if (isOrdered(value)) {
|
|
479
|
+
const parts = value.entries.map(([k, v]) => `${k}: ${formatValue(v)}`);
|
|
480
|
+
return `ordered[${parts.join(', ')}]`;
|
|
481
|
+
}
|
|
482
|
+
if (isRillIterator(value)) {
|
|
483
|
+
return 'type(iterator)';
|
|
484
|
+
}
|
|
485
|
+
if (Array.isArray(value)) {
|
|
486
|
+
return `list[${value.map(formatValue).join(', ')}]`;
|
|
131
487
|
}
|
|
132
488
|
if (isVector(value)) {
|
|
133
489
|
return `vector(${value.model}, ${value.data.length}d)`;
|
|
134
490
|
}
|
|
491
|
+
if (isTypeValue(value)) {
|
|
492
|
+
return formatStructuralType(value.structure);
|
|
493
|
+
}
|
|
494
|
+
// Plain dict
|
|
495
|
+
if (typeof value === 'object') {
|
|
496
|
+
const dict = value;
|
|
497
|
+
const parts = Object.entries(dict).map(([k, v]) => `${k}: ${formatValue(v)}`);
|
|
498
|
+
return `dict[${parts.join(', ')}]`;
|
|
499
|
+
}
|
|
500
|
+
return String(value);
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Convert a RillValue to a JSON-serializable value.
|
|
504
|
+
* @throws {Error} plain Error (not RuntimeError) for non-serializable types
|
|
505
|
+
*/
|
|
506
|
+
export function valueToJSON(value) {
|
|
507
|
+
if (value === null)
|
|
508
|
+
return null;
|
|
509
|
+
if (typeof value === 'string')
|
|
510
|
+
return value;
|
|
511
|
+
if (typeof value === 'number')
|
|
512
|
+
return value;
|
|
513
|
+
if (typeof value === 'boolean')
|
|
514
|
+
return value;
|
|
515
|
+
if (Array.isArray(value)) {
|
|
516
|
+
return value.map(valueToJSON);
|
|
517
|
+
}
|
|
518
|
+
if (isCallable(value)) {
|
|
519
|
+
throw new Error('closures are not JSON-serializable');
|
|
520
|
+
}
|
|
521
|
+
if (isTuple(value)) {
|
|
522
|
+
throw new Error('tuples are not JSON-serializable');
|
|
523
|
+
}
|
|
524
|
+
if (isOrdered(value)) {
|
|
525
|
+
throw new Error('ordered values are not JSON-serializable');
|
|
526
|
+
}
|
|
527
|
+
if (isVector(value)) {
|
|
528
|
+
throw new Error('vectors are not JSON-serializable');
|
|
529
|
+
}
|
|
530
|
+
if (isTypeValue(value)) {
|
|
531
|
+
throw new Error('type values are not JSON-serializable');
|
|
532
|
+
}
|
|
135
533
|
if (isRillIterator(value)) {
|
|
136
|
-
|
|
534
|
+
throw new Error('iterators are not JSON-serializable');
|
|
137
535
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
536
|
+
// Plain dict
|
|
537
|
+
const dict = value;
|
|
538
|
+
const result = {};
|
|
539
|
+
for (const [k, v] of Object.entries(dict)) {
|
|
540
|
+
result[k] = valueToJSON(v);
|
|
143
541
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
542
|
+
return result;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Convert a RillValue to a NativeResult for host consumption.
|
|
546
|
+
* Non-representable types (closures, vectors, type values, iterators) produce descriptor objects.
|
|
547
|
+
* Tuples convert to native arrays. Ordered values convert to plain objects.
|
|
548
|
+
*/
|
|
549
|
+
export function toNative(value) {
|
|
550
|
+
const rillTypeName = inferType(value);
|
|
551
|
+
const rillTypeSignature = formatStructuralType(inferStructuralType(value));
|
|
552
|
+
const nativeValue = toNativeValue(value);
|
|
553
|
+
return { rillTypeName, rillTypeSignature, value: nativeValue };
|
|
554
|
+
}
|
|
555
|
+
function toNativeValue(value) {
|
|
556
|
+
if (value === null)
|
|
557
|
+
return null;
|
|
558
|
+
if (typeof value === 'string')
|
|
559
|
+
return value;
|
|
560
|
+
if (typeof value === 'number')
|
|
561
|
+
return value;
|
|
562
|
+
if (typeof value === 'boolean')
|
|
563
|
+
return value;
|
|
564
|
+
if (Array.isArray(value)) {
|
|
565
|
+
return value.map(toNativeValue);
|
|
566
|
+
}
|
|
567
|
+
if (isCallable(value)) {
|
|
568
|
+
return { signature: formatStructuralType(inferStructuralType(value)) };
|
|
569
|
+
}
|
|
570
|
+
if (isTuple(value)) {
|
|
571
|
+
return value.entries.map(toNativeValue);
|
|
572
|
+
}
|
|
573
|
+
if (isOrdered(value)) {
|
|
574
|
+
const result = {};
|
|
575
|
+
for (const [k, v] of value.entries) {
|
|
576
|
+
result[k] = toNativeValue(v);
|
|
577
|
+
}
|
|
578
|
+
return result;
|
|
579
|
+
}
|
|
580
|
+
if (isVector(value)) {
|
|
581
|
+
return { model: value.model, dimensions: value.data.length };
|
|
582
|
+
}
|
|
583
|
+
if (isTypeValue(value)) {
|
|
584
|
+
return {
|
|
585
|
+
name: value.typeName,
|
|
586
|
+
signature: formatStructuralType(value.structure),
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
if (isRillIterator(value)) {
|
|
590
|
+
return { done: value.done };
|
|
591
|
+
}
|
|
592
|
+
// Plain dict
|
|
593
|
+
const dict = value;
|
|
594
|
+
const result = {};
|
|
595
|
+
for (const [k, v] of Object.entries(dict)) {
|
|
596
|
+
result[k] = toNativeValue(v);
|
|
597
|
+
}
|
|
598
|
+
return result;
|
|
147
599
|
}
|
|
148
600
|
/**
|
|
149
601
|
* Deep structural equality for all Rill values.
|
|
@@ -165,17 +617,43 @@ export function deepEquals(a, b) {
|
|
|
165
617
|
// Both are non-null objects at this point
|
|
166
618
|
const aObj = a;
|
|
167
619
|
const bObj = b;
|
|
168
|
-
// Check for tuples (spread args)
|
|
620
|
+
// Check for tuples (positional spread args)
|
|
169
621
|
const aIsTuple = isTuple(a);
|
|
170
622
|
const bIsTuple = isTuple(b);
|
|
171
623
|
if (aIsTuple !== bIsTuple)
|
|
172
624
|
return false;
|
|
173
625
|
if (aIsTuple && bIsTuple) {
|
|
174
|
-
if (a.entries.
|
|
626
|
+
if (a.entries.length !== b.entries.length)
|
|
627
|
+
return false;
|
|
628
|
+
for (let i = 0; i < a.entries.length; i++) {
|
|
629
|
+
const aVal = a.entries[i];
|
|
630
|
+
const bVal = b.entries[i];
|
|
631
|
+
if (aVal === undefined || bVal === undefined) {
|
|
632
|
+
if (aVal !== bVal)
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
else if (!deepEquals(aVal, bVal)) {
|
|
636
|
+
return false;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return true;
|
|
640
|
+
}
|
|
641
|
+
// Check for ordered (named spread args)
|
|
642
|
+
const aIsOrdered = isOrdered(a);
|
|
643
|
+
const bIsOrdered = isOrdered(b);
|
|
644
|
+
if (aIsOrdered !== bIsOrdered)
|
|
645
|
+
return false;
|
|
646
|
+
if (aIsOrdered && bIsOrdered) {
|
|
647
|
+
if (a.entries.length !== b.entries.length)
|
|
175
648
|
return false;
|
|
176
|
-
for (
|
|
177
|
-
const
|
|
178
|
-
|
|
649
|
+
for (let i = 0; i < a.entries.length; i++) {
|
|
650
|
+
const aEntry = a.entries[i];
|
|
651
|
+
const bEntry = b.entries[i];
|
|
652
|
+
if (aEntry === undefined || bEntry === undefined)
|
|
653
|
+
return false;
|
|
654
|
+
if (aEntry[0] !== bEntry[0])
|
|
655
|
+
return false;
|
|
656
|
+
if (!deepEquals(aEntry[1], bEntry[1]))
|
|
179
657
|
return false;
|
|
180
658
|
}
|
|
181
659
|
return true;
|
|
@@ -199,6 +677,14 @@ export function deepEquals(a, b) {
|
|
|
199
677
|
}
|
|
200
678
|
return true;
|
|
201
679
|
}
|
|
680
|
+
// Check for type values (first-class type names)
|
|
681
|
+
const aIsTypeValue = isTypeValue(a);
|
|
682
|
+
const bIsTypeValue = isTypeValue(b);
|
|
683
|
+
if (aIsTypeValue !== bIsTypeValue)
|
|
684
|
+
return false;
|
|
685
|
+
if (aIsTypeValue && bIsTypeValue) {
|
|
686
|
+
return structuralTypeEquals(a.structure, b.structure);
|
|
687
|
+
}
|
|
202
688
|
// Check for arrays (lists)
|
|
203
689
|
const aIsArray = Array.isArray(a);
|
|
204
690
|
const bIsArray = Array.isArray(b);
|