@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.
Files changed (191) hide show
  1. package/dist/ast-nodes.d.ts +189 -49
  2. package/dist/ast-nodes.d.ts.map +1 -1
  3. package/dist/ast-unions.d.ts +1 -1
  4. package/dist/ast-unions.d.ts.map +1 -1
  5. package/dist/constants.d.ts +14 -0
  6. package/dist/constants.d.ts.map +1 -0
  7. package/dist/constants.js +30 -0
  8. package/dist/constants.js.map +1 -0
  9. package/dist/error-classes.d.ts +3 -1
  10. package/dist/error-classes.d.ts.map +1 -1
  11. package/dist/error-classes.js +11 -5
  12. package/dist/error-classes.js.map +1 -1
  13. package/dist/error-registry.d.ts.map +1 -1
  14. package/dist/error-registry.js +313 -11
  15. package/dist/error-registry.js.map +1 -1
  16. package/dist/ext/crypto/index.d.ts +2 -1
  17. package/dist/ext/crypto/index.d.ts.map +1 -1
  18. package/dist/ext/crypto/index.js +7 -0
  19. package/dist/ext/crypto/index.js.map +1 -1
  20. package/dist/ext/exec/index.d.ts +2 -1
  21. package/dist/ext/exec/index.d.ts.map +1 -1
  22. package/dist/ext/exec/index.js +6 -0
  23. package/dist/ext/exec/index.js.map +1 -1
  24. package/dist/ext/fetch/index.d.ts +2 -1
  25. package/dist/ext/fetch/index.d.ts.map +1 -1
  26. package/dist/ext/fetch/index.js +6 -0
  27. package/dist/ext/fetch/index.js.map +1 -1
  28. package/dist/ext/fs/index.d.ts +2 -1
  29. package/dist/ext/fs/index.d.ts.map +1 -1
  30. package/dist/ext/fs/index.js +3 -0
  31. package/dist/ext/fs/index.js.map +1 -1
  32. package/dist/ext/kv/index.d.ts +2 -1
  33. package/dist/ext/kv/index.d.ts.map +1 -1
  34. package/dist/ext/kv/index.js +5 -1
  35. package/dist/ext/kv/index.js.map +1 -1
  36. package/dist/generated/introspection-data.d.ts +1 -1
  37. package/dist/generated/introspection-data.d.ts.map +1 -1
  38. package/dist/generated/introspection-data.js +194 -185
  39. package/dist/generated/introspection-data.js.map +1 -1
  40. package/dist/generated/version-data.d.ts +1 -1
  41. package/dist/generated/version-data.d.ts.map +1 -1
  42. package/dist/generated/version-data.js +3 -3
  43. package/dist/generated/version-data.js.map +1 -1
  44. package/dist/highlight-map.d.ts.map +1 -1
  45. package/dist/highlight-map.js +8 -2
  46. package/dist/highlight-map.js.map +1 -1
  47. package/dist/index.d.ts +2 -1
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +5 -1
  50. package/dist/index.js.map +1 -1
  51. package/dist/lexer/operators.d.ts.map +1 -1
  52. package/dist/lexer/operators.js +0 -2
  53. package/dist/lexer/operators.js.map +1 -1
  54. package/dist/lexer/readers.d.ts +18 -1
  55. package/dist/lexer/readers.d.ts.map +1 -1
  56. package/dist/lexer/readers.js +55 -0
  57. package/dist/lexer/readers.js.map +1 -1
  58. package/dist/parser/helpers.d.ts +8 -13
  59. package/dist/parser/helpers.d.ts.map +1 -1
  60. package/dist/parser/helpers.js +42 -35
  61. package/dist/parser/helpers.js.map +1 -1
  62. package/dist/parser/index.d.ts +1 -0
  63. package/dist/parser/index.d.ts.map +1 -1
  64. package/dist/parser/index.js +1 -0
  65. package/dist/parser/index.js.map +1 -1
  66. package/dist/parser/parser-collect.d.ts.map +1 -1
  67. package/dist/parser/parser-collect.js +34 -5
  68. package/dist/parser/parser-collect.js.map +1 -1
  69. package/dist/parser/parser-control.d.ts +1 -1
  70. package/dist/parser/parser-control.d.ts.map +1 -1
  71. package/dist/parser/parser-control.js +11 -2
  72. package/dist/parser/parser-control.js.map +1 -1
  73. package/dist/parser/parser-expr.d.ts +3 -1
  74. package/dist/parser/parser-expr.d.ts.map +1 -1
  75. package/dist/parser/parser-expr.js +377 -100
  76. package/dist/parser/parser-expr.js.map +1 -1
  77. package/dist/parser/parser-extract.d.ts +3 -5
  78. package/dist/parser/parser-extract.d.ts.map +1 -1
  79. package/dist/parser/parser-extract.js +37 -69
  80. package/dist/parser/parser-extract.js.map +1 -1
  81. package/dist/parser/parser-functions.d.ts +2 -2
  82. package/dist/parser/parser-functions.d.ts.map +1 -1
  83. package/dist/parser/parser-functions.js +112 -36
  84. package/dist/parser/parser-functions.js.map +1 -1
  85. package/dist/parser/parser-literals.d.ts +5 -4
  86. package/dist/parser/parser-literals.d.ts.map +1 -1
  87. package/dist/parser/parser-literals.js +316 -47
  88. package/dist/parser/parser-literals.js.map +1 -1
  89. package/dist/parser/parser-script.d.ts.map +1 -1
  90. package/dist/parser/parser-script.js +25 -12
  91. package/dist/parser/parser-script.js.map +1 -1
  92. package/dist/parser/parser-shape.d.ts +13 -0
  93. package/dist/parser/parser-shape.d.ts.map +1 -0
  94. package/dist/parser/parser-shape.js +72 -0
  95. package/dist/parser/parser-shape.js.map +1 -0
  96. package/dist/parser/parser-types.d.ts +31 -0
  97. package/dist/parser/parser-types.d.ts.map +1 -0
  98. package/dist/parser/parser-types.js +78 -0
  99. package/dist/parser/parser-types.js.map +1 -0
  100. package/dist/parser/parser-variables.d.ts.map +1 -1
  101. package/dist/parser/parser-variables.js +10 -1
  102. package/dist/parser/parser-variables.js.map +1 -1
  103. package/dist/runtime/core/callable.d.ts +27 -22
  104. package/dist/runtime/core/callable.d.ts.map +1 -1
  105. package/dist/runtime/core/callable.js +30 -26
  106. package/dist/runtime/core/callable.js.map +1 -1
  107. package/dist/runtime/core/context.d.ts.map +1 -1
  108. package/dist/runtime/core/context.js +8 -8
  109. package/dist/runtime/core/context.js.map +1 -1
  110. package/dist/runtime/core/equals.d.ts.map +1 -1
  111. package/dist/runtime/core/equals.js +179 -30
  112. package/dist/runtime/core/equals.js.map +1 -1
  113. package/dist/runtime/core/eval/base.d.ts +2 -2
  114. package/dist/runtime/core/eval/base.d.ts.map +1 -1
  115. package/dist/runtime/core/eval/evaluator.d.ts.map +1 -1
  116. package/dist/runtime/core/eval/evaluator.js +3 -1
  117. package/dist/runtime/core/eval/evaluator.js.map +1 -1
  118. package/dist/runtime/core/eval/index.d.ts +18 -3
  119. package/dist/runtime/core/eval/index.d.ts.map +1 -1
  120. package/dist/runtime/core/eval/index.js +22 -2
  121. package/dist/runtime/core/eval/index.js.map +1 -1
  122. package/dist/runtime/core/eval/mixins/annotations.d.ts.map +1 -1
  123. package/dist/runtime/core/eval/mixins/annotations.js +14 -8
  124. package/dist/runtime/core/eval/mixins/annotations.js.map +1 -1
  125. package/dist/runtime/core/eval/mixins/closures.d.ts +0 -2
  126. package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -1
  127. package/dist/runtime/core/eval/mixins/closures.js +341 -105
  128. package/dist/runtime/core/eval/mixins/closures.js.map +1 -1
  129. package/dist/runtime/core/eval/mixins/collections.d.ts.map +1 -1
  130. package/dist/runtime/core/eval/mixins/collections.js +65 -25
  131. package/dist/runtime/core/eval/mixins/collections.js.map +1 -1
  132. package/dist/runtime/core/eval/mixins/control-flow.d.ts.map +1 -1
  133. package/dist/runtime/core/eval/mixins/control-flow.js +21 -17
  134. package/dist/runtime/core/eval/mixins/control-flow.js.map +1 -1
  135. package/dist/runtime/core/eval/mixins/conversion.d.ts +30 -0
  136. package/dist/runtime/core/eval/mixins/conversion.d.ts.map +1 -0
  137. package/dist/runtime/core/eval/mixins/conversion.js +212 -0
  138. package/dist/runtime/core/eval/mixins/conversion.js.map +1 -0
  139. package/dist/runtime/core/eval/mixins/core.d.ts.map +1 -1
  140. package/dist/runtime/core/eval/mixins/core.js +101 -32
  141. package/dist/runtime/core/eval/mixins/core.js.map +1 -1
  142. package/dist/runtime/core/eval/mixins/extraction.d.ts.map +1 -1
  143. package/dist/runtime/core/eval/mixins/extraction.js +136 -30
  144. package/dist/runtime/core/eval/mixins/extraction.js.map +1 -1
  145. package/dist/runtime/core/eval/mixins/list-dispatch.d.ts +17 -0
  146. package/dist/runtime/core/eval/mixins/list-dispatch.d.ts.map +1 -0
  147. package/dist/runtime/core/eval/mixins/list-dispatch.js +97 -0
  148. package/dist/runtime/core/eval/mixins/list-dispatch.js.map +1 -0
  149. package/dist/runtime/core/eval/mixins/literals.d.ts.map +1 -1
  150. package/dist/runtime/core/eval/mixins/literals.js +73 -83
  151. package/dist/runtime/core/eval/mixins/literals.js.map +1 -1
  152. package/dist/runtime/core/eval/mixins/types.d.ts +4 -0
  153. package/dist/runtime/core/eval/mixins/types.d.ts.map +1 -1
  154. package/dist/runtime/core/eval/mixins/types.js +323 -3
  155. package/dist/runtime/core/eval/mixins/types.js.map +1 -1
  156. package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -1
  157. package/dist/runtime/core/eval/mixins/variables.js +45 -7
  158. package/dist/runtime/core/eval/mixins/variables.js.map +1 -1
  159. package/dist/runtime/core/execute.d.ts.map +1 -1
  160. package/dist/runtime/core/execute.js +3 -16
  161. package/dist/runtime/core/execute.js.map +1 -1
  162. package/dist/runtime/core/field-descriptor.d.ts +29 -0
  163. package/dist/runtime/core/field-descriptor.d.ts.map +1 -0
  164. package/dist/runtime/core/field-descriptor.js +27 -0
  165. package/dist/runtime/core/field-descriptor.js.map +1 -0
  166. package/dist/runtime/core/types.d.ts +15 -6
  167. package/dist/runtime/core/types.d.ts.map +1 -1
  168. package/dist/runtime/core/types.js.map +1 -1
  169. package/dist/runtime/core/values.d.ts +114 -9
  170. package/dist/runtime/core/values.d.ts.map +1 -1
  171. package/dist/runtime/core/values.js +529 -43
  172. package/dist/runtime/core/values.js.map +1 -1
  173. package/dist/runtime/ext/builtins.d.ts.map +1 -1
  174. package/dist/runtime/ext/builtins.js +47 -107
  175. package/dist/runtime/ext/builtins.js.map +1 -1
  176. package/dist/runtime/ext/extensions.d.ts +21 -2
  177. package/dist/runtime/ext/extensions.d.ts.map +1 -1
  178. package/dist/runtime/ext/extensions.js.map +1 -1
  179. package/dist/runtime/index.d.ts +6 -4
  180. package/dist/runtime/index.d.ts.map +1 -1
  181. package/dist/runtime/index.js +7 -2
  182. package/dist/runtime/index.js.map +1 -1
  183. package/dist/token-types.d.ts +7 -2
  184. package/dist/token-types.d.ts.map +1 -1
  185. package/dist/token-types.js +9 -2
  186. package/dist/token-types.js.map +1 -1
  187. package/dist/value-types.d.ts +32 -1
  188. package/dist/value-types.d.ts.map +1 -1
  189. package/dist/value-types.js +1 -1
  190. package/dist/value-types.js.map +1 -1
  191. 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
- /** Create tuple from a list (positional) */
23
- export function createTupleFromList(list) {
24
- const entries = new Map();
25
- for (let i = 0; i < list.length; i++) {
26
- const val = list[i];
27
- if (val !== undefined) {
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
- /** Create tuple from a dict (named) */
34
- export function createTupleFromDict(dict) {
35
- const entries = new Map();
36
- for (const [key, value] of Object.entries(dict)) {
37
- entries.set(key, value);
38
- }
39
- return { __rill_tuple: true, entries };
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.size > 0;
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
- const parts = [];
122
- for (const [key, val] of value.entries) {
123
- if (typeof key === 'number') {
124
- parts.push(formatValue(val));
125
- }
126
- else {
127
- parts.push(`${key}: ${formatValue(val)}`);
128
- }
129
- }
130
- return `*[${parts.join(', ')}]`;
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
- return '[iterator]';
534
+ throw new Error('iterators are not JSON-serializable');
137
535
  }
138
- if (typeof value === 'object' &&
139
- '__type' in value &&
140
- value.__type === 'callable') {
141
- // Basic callable formatting - full formatting in callable.ts
142
- return '(...) { ... }';
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
- if (Array.isArray(value))
145
- return JSON.stringify(value);
146
- return JSON.stringify(value);
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.size !== b.entries.size)
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 (const [key, aVal] of a.entries) {
177
- const bVal = b.entries.get(key);
178
- if (bVal === undefined || !deepEquals(aVal, bVal))
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);