@rcrsr/rill 0.15.0 → 0.17.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 +2 -13
- package/dist/ast-nodes.js +0 -1
- package/dist/ast-unions.d.ts +0 -1
- package/dist/ast-unions.js +0 -1
- package/dist/constants.d.ts +0 -1
- package/dist/constants.js +0 -1
- package/dist/error-classes.d.ts +0 -1
- package/dist/error-classes.js +0 -1
- package/dist/error-formatter.d.ts +0 -1
- package/dist/error-formatter.js +0 -1
- package/dist/error-registry.d.ts +0 -1
- package/dist/error-registry.js +32 -1
- package/dist/ext/crypto/index.d.ts +3 -4
- package/dist/ext/crypto/index.js +66 -64
- package/dist/ext/exec/index.d.ts +3 -4
- package/dist/ext/exec/index.js +17 -12
- package/dist/ext/exec/runner.d.ts +0 -1
- package/dist/ext/exec/runner.js +0 -1
- package/dist/ext/fetch/index.d.ts +3 -4
- package/dist/ext/fetch/index.js +23 -49
- package/dist/ext/fetch/request.d.ts +0 -1
- package/dist/ext/fetch/request.js +0 -1
- package/dist/ext/fs/index.d.ts +3 -4
- package/dist/ext/fs/index.js +268 -266
- package/dist/ext/fs/sandbox.d.ts +0 -1
- package/dist/ext/fs/sandbox.js +0 -1
- package/dist/ext/kv/index.d.ts +3 -4
- package/dist/ext/kv/index.js +216 -215
- package/dist/ext/kv/store.d.ts +0 -1
- package/dist/ext/kv/store.js +2 -2
- 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 -2
- package/dist/generated/introspection-data.js +385 -297
- package/dist/generated/version-data.d.ts +1 -2
- package/dist/generated/version-data.js +2 -3
- package/dist/highlight-map.d.ts +0 -1
- package/dist/highlight-map.js +0 -1
- package/dist/index.d.ts +15 -5
- package/dist/index.js +14 -6
- package/dist/lexer/errors.d.ts +0 -1
- package/dist/lexer/errors.js +0 -1
- package/dist/lexer/helpers.d.ts +0 -1
- package/dist/lexer/helpers.js +0 -1
- package/dist/lexer/index.d.ts +0 -1
- package/dist/lexer/index.js +0 -1
- package/dist/lexer/operators.d.ts +0 -1
- package/dist/lexer/operators.js +0 -1
- package/dist/lexer/readers.d.ts +0 -1
- package/dist/lexer/readers.js +0 -1
- package/dist/lexer/state.d.ts +0 -1
- package/dist/lexer/state.js +0 -1
- package/dist/lexer/tokenizer.d.ts +0 -1
- package/dist/lexer/tokenizer.js +0 -1
- package/dist/parser/helpers.d.ts +0 -1
- package/dist/parser/helpers.js +0 -1
- package/dist/parser/index.d.ts +0 -1
- package/dist/parser/index.js +0 -1
- package/dist/parser/parser-collect.d.ts +0 -1
- package/dist/parser/parser-collect.js +0 -1
- package/dist/parser/parser-control.d.ts +0 -1
- package/dist/parser/parser-control.js +0 -1
- package/dist/parser/parser-expr.d.ts +0 -1
- package/dist/parser/parser-expr.js +0 -1
- package/dist/parser/parser-extract.d.ts +0 -1
- package/dist/parser/parser-extract.js +0 -1
- package/dist/parser/parser-functions.d.ts +0 -1
- package/dist/parser/parser-functions.js +0 -1
- package/dist/parser/parser-literals.d.ts +0 -1
- package/dist/parser/parser-literals.js +4 -2
- package/dist/parser/parser-script.d.ts +0 -1
- package/dist/parser/parser-script.js +0 -1
- package/dist/parser/parser-shape.d.ts +2 -3
- package/dist/parser/parser-shape.js +8 -52
- package/dist/parser/parser-types.d.ts +28 -2
- package/dist/parser/parser-types.js +76 -13
- package/dist/parser/parser-use.d.ts +0 -1
- package/dist/parser/parser-use.js +7 -2
- package/dist/parser/parser-variables.d.ts +0 -1
- package/dist/parser/parser-variables.js +0 -1
- package/dist/parser/parser.d.ts +0 -1
- package/dist/parser/parser.js +0 -1
- package/dist/parser/state.d.ts +0 -1
- package/dist/parser/state.js +0 -1
- package/dist/runtime/core/callable.d.ts +59 -20
- package/dist/runtime/core/callable.js +188 -39
- package/dist/runtime/core/context.d.ts +0 -12
- package/dist/runtime/core/context.js +77 -77
- package/dist/runtime/core/equals.d.ts +0 -1
- package/dist/runtime/core/equals.js +35 -3
- package/dist/runtime/core/eval/base.d.ts +0 -1
- package/dist/runtime/core/eval/base.js +0 -1
- package/dist/runtime/core/eval/evaluator.d.ts +0 -1
- package/dist/runtime/core/eval/evaluator.js +0 -1
- package/dist/runtime/core/eval/index.d.ts +2 -3
- package/dist/runtime/core/eval/index.js +11 -1
- package/dist/runtime/core/eval/mixins/annotations.d.ts +0 -1
- package/dist/runtime/core/eval/mixins/annotations.js +0 -1
- package/dist/runtime/core/eval/mixins/closures.d.ts +0 -1
- package/dist/runtime/core/eval/mixins/closures.js +92 -70
- package/dist/runtime/core/eval/mixins/collections.d.ts +0 -1
- package/dist/runtime/core/eval/mixins/collections.js +9 -4
- package/dist/runtime/core/eval/mixins/control-flow.d.ts +0 -1
- package/dist/runtime/core/eval/mixins/control-flow.js +0 -1
- package/dist/runtime/core/eval/mixins/conversion.d.ts +0 -1
- package/dist/runtime/core/eval/mixins/conversion.js +196 -188
- package/dist/runtime/core/eval/mixins/core.d.ts +0 -1
- package/dist/runtime/core/eval/mixins/core.js +2 -3
- package/dist/runtime/core/eval/mixins/expressions.d.ts +0 -1
- package/dist/runtime/core/eval/mixins/expressions.js +35 -28
- package/dist/runtime/core/eval/mixins/extraction.d.ts +0 -1
- package/dist/runtime/core/eval/mixins/extraction.js +8 -9
- package/dist/runtime/core/eval/mixins/list-dispatch.d.ts +0 -1
- package/dist/runtime/core/eval/mixins/list-dispatch.js +0 -1
- package/dist/runtime/core/eval/mixins/literals.d.ts +0 -1
- package/dist/runtime/core/eval/mixins/literals.js +6 -10
- package/dist/runtime/core/eval/mixins/types.d.ts +2 -1
- package/dist/runtime/core/eval/mixins/types.js +231 -261
- package/dist/runtime/core/eval/mixins/use.d.ts +0 -1
- package/dist/runtime/core/eval/mixins/use.js +0 -1
- package/dist/runtime/core/eval/mixins/variables.d.ts +0 -1
- package/dist/runtime/core/eval/mixins/variables.js +16 -15
- package/dist/runtime/core/eval/types.d.ts +0 -1
- package/dist/runtime/core/eval/types.js +0 -1
- package/dist/runtime/core/execute.d.ts +0 -1
- package/dist/runtime/core/execute.js +0 -1
- package/dist/runtime/core/field-descriptor.d.ts +4 -5
- package/dist/runtime/core/field-descriptor.js +2 -2
- package/dist/runtime/core/introspection.d.ts +0 -1
- package/dist/runtime/core/introspection.js +6 -7
- package/dist/runtime/core/markers.d.ts +12 -0
- package/dist/runtime/core/markers.js +7 -0
- package/dist/runtime/core/resolvers.d.ts +0 -1
- package/dist/runtime/core/resolvers.js +0 -1
- package/dist/runtime/core/signals.d.ts +0 -1
- package/dist/runtime/core/signals.js +0 -1
- package/dist/runtime/core/type-registrations.d.ts +136 -0
- package/dist/runtime/core/type-registrations.js +749 -0
- package/dist/runtime/core/type-structures.d.ts +128 -0
- package/dist/runtime/core/type-structures.js +12 -0
- package/dist/runtime/core/types.d.ts +15 -4
- package/dist/runtime/core/types.js +0 -1
- package/dist/runtime/core/values.d.ts +88 -146
- package/dist/runtime/core/values.js +466 -470
- package/dist/runtime/ext/builtins.d.ts +0 -1
- package/dist/runtime/ext/builtins.js +125 -80
- package/dist/runtime/ext/extensions.d.ts +30 -125
- package/dist/runtime/ext/extensions.js +0 -94
- package/dist/runtime/ext/test-context.d.ts +28 -0
- package/dist/runtime/ext/test-context.js +154 -0
- package/dist/runtime/index.d.ts +22 -9
- package/dist/runtime/index.js +18 -5
- package/dist/signature-parser.d.ts +2 -3
- package/dist/signature-parser.js +19 -17
- package/dist/source-location.d.ts +0 -1
- package/dist/source-location.js +0 -1
- package/dist/token-types.d.ts +0 -1
- package/dist/token-types.js +0 -1
- package/dist/types.d.ts +0 -1
- package/dist/types.js +0 -1
- package/dist/value-types.d.ts +15 -12
- package/dist/value-types.js +0 -1
- package/package.json +2 -1
- package/dist/ast-nodes.d.ts.map +0 -1
- package/dist/ast-nodes.js.map +0 -1
- package/dist/ast-unions.d.ts.map +0 -1
- package/dist/ast-unions.js.map +0 -1
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js.map +0 -1
- package/dist/error-classes.d.ts.map +0 -1
- package/dist/error-classes.js.map +0 -1
- package/dist/error-formatter.d.ts.map +0 -1
- package/dist/error-formatter.js.map +0 -1
- package/dist/error-registry.d.ts.map +0 -1
- package/dist/error-registry.js.map +0 -1
- package/dist/ext/crypto/index.d.ts.map +0 -1
- package/dist/ext/crypto/index.js.map +0 -1
- package/dist/ext/exec/index.d.ts.map +0 -1
- package/dist/ext/exec/index.js.map +0 -1
- package/dist/ext/exec/runner.d.ts.map +0 -1
- package/dist/ext/exec/runner.js.map +0 -1
- package/dist/ext/fetch/index.d.ts.map +0 -1
- package/dist/ext/fetch/index.js.map +0 -1
- package/dist/ext/fetch/request.d.ts.map +0 -1
- package/dist/ext/fetch/request.js.map +0 -1
- package/dist/ext/fs/index.d.ts.map +0 -1
- package/dist/ext/fs/index.js.map +0 -1
- package/dist/ext/fs/sandbox.d.ts.map +0 -1
- package/dist/ext/fs/sandbox.js.map +0 -1
- package/dist/ext/kv/index.d.ts.map +0 -1
- package/dist/ext/kv/index.js.map +0 -1
- package/dist/ext/kv/store.d.ts.map +0 -1
- package/dist/ext/kv/store.js.map +0 -1
- package/dist/generated/introspection-data.d.ts.map +0 -1
- package/dist/generated/introspection-data.js.map +0 -1
- package/dist/generated/version-data.d.ts.map +0 -1
- package/dist/generated/version-data.js.map +0 -1
- package/dist/highlight-map.d.ts.map +0 -1
- package/dist/highlight-map.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lexer/errors.d.ts.map +0 -1
- package/dist/lexer/errors.js.map +0 -1
- package/dist/lexer/helpers.d.ts.map +0 -1
- package/dist/lexer/helpers.js.map +0 -1
- package/dist/lexer/index.d.ts.map +0 -1
- package/dist/lexer/index.js.map +0 -1
- package/dist/lexer/operators.d.ts.map +0 -1
- package/dist/lexer/operators.js.map +0 -1
- package/dist/lexer/readers.d.ts.map +0 -1
- package/dist/lexer/readers.js.map +0 -1
- package/dist/lexer/state.d.ts.map +0 -1
- package/dist/lexer/state.js.map +0 -1
- package/dist/lexer/tokenizer.d.ts.map +0 -1
- package/dist/lexer/tokenizer.js.map +0 -1
- package/dist/parser/helpers.d.ts.map +0 -1
- package/dist/parser/helpers.js.map +0 -1
- package/dist/parser/index.d.ts.map +0 -1
- package/dist/parser/index.js.map +0 -1
- package/dist/parser/parser-collect.d.ts.map +0 -1
- package/dist/parser/parser-collect.js.map +0 -1
- package/dist/parser/parser-control.d.ts.map +0 -1
- package/dist/parser/parser-control.js.map +0 -1
- package/dist/parser/parser-expr.d.ts.map +0 -1
- package/dist/parser/parser-expr.js.map +0 -1
- package/dist/parser/parser-extract.d.ts.map +0 -1
- package/dist/parser/parser-extract.js.map +0 -1
- package/dist/parser/parser-functions.d.ts.map +0 -1
- package/dist/parser/parser-functions.js.map +0 -1
- package/dist/parser/parser-literals.d.ts.map +0 -1
- package/dist/parser/parser-literals.js.map +0 -1
- package/dist/parser/parser-script.d.ts.map +0 -1
- package/dist/parser/parser-script.js.map +0 -1
- package/dist/parser/parser-shape.d.ts.map +0 -1
- package/dist/parser/parser-shape.js.map +0 -1
- package/dist/parser/parser-types.d.ts.map +0 -1
- package/dist/parser/parser-types.js.map +0 -1
- package/dist/parser/parser-use.d.ts.map +0 -1
- package/dist/parser/parser-use.js.map +0 -1
- package/dist/parser/parser-variables.d.ts.map +0 -1
- package/dist/parser/parser-variables.js.map +0 -1
- package/dist/parser/parser.d.ts.map +0 -1
- package/dist/parser/parser.js.map +0 -1
- package/dist/parser/state.d.ts.map +0 -1
- package/dist/parser/state.js.map +0 -1
- package/dist/runtime/core/callable.d.ts.map +0 -1
- package/dist/runtime/core/callable.js.map +0 -1
- package/dist/runtime/core/context.d.ts.map +0 -1
- package/dist/runtime/core/context.js.map +0 -1
- package/dist/runtime/core/equals.d.ts.map +0 -1
- package/dist/runtime/core/equals.js.map +0 -1
- package/dist/runtime/core/eval/base.d.ts.map +0 -1
- package/dist/runtime/core/eval/base.js.map +0 -1
- package/dist/runtime/core/eval/evaluator.d.ts.map +0 -1
- package/dist/runtime/core/eval/evaluator.js.map +0 -1
- package/dist/runtime/core/eval/index.d.ts.map +0 -1
- package/dist/runtime/core/eval/index.js.map +0 -1
- package/dist/runtime/core/eval/mixins/annotations.d.ts.map +0 -1
- package/dist/runtime/core/eval/mixins/annotations.js.map +0 -1
- package/dist/runtime/core/eval/mixins/closures.d.ts.map +0 -1
- package/dist/runtime/core/eval/mixins/closures.js.map +0 -1
- package/dist/runtime/core/eval/mixins/collections.d.ts.map +0 -1
- package/dist/runtime/core/eval/mixins/collections.js.map +0 -1
- package/dist/runtime/core/eval/mixins/control-flow.d.ts.map +0 -1
- package/dist/runtime/core/eval/mixins/control-flow.js.map +0 -1
- package/dist/runtime/core/eval/mixins/conversion.d.ts.map +0 -1
- package/dist/runtime/core/eval/mixins/conversion.js.map +0 -1
- package/dist/runtime/core/eval/mixins/core.d.ts.map +0 -1
- package/dist/runtime/core/eval/mixins/core.js.map +0 -1
- package/dist/runtime/core/eval/mixins/expressions.d.ts.map +0 -1
- package/dist/runtime/core/eval/mixins/expressions.js.map +0 -1
- package/dist/runtime/core/eval/mixins/extraction.d.ts.map +0 -1
- package/dist/runtime/core/eval/mixins/extraction.js.map +0 -1
- package/dist/runtime/core/eval/mixins/list-dispatch.d.ts.map +0 -1
- package/dist/runtime/core/eval/mixins/list-dispatch.js.map +0 -1
- package/dist/runtime/core/eval/mixins/literals.d.ts.map +0 -1
- package/dist/runtime/core/eval/mixins/literals.js.map +0 -1
- package/dist/runtime/core/eval/mixins/types.d.ts.map +0 -1
- package/dist/runtime/core/eval/mixins/types.js.map +0 -1
- package/dist/runtime/core/eval/mixins/use.d.ts.map +0 -1
- package/dist/runtime/core/eval/mixins/use.js.map +0 -1
- package/dist/runtime/core/eval/mixins/variables.d.ts.map +0 -1
- package/dist/runtime/core/eval/mixins/variables.js.map +0 -1
- package/dist/runtime/core/eval/types.d.ts.map +0 -1
- package/dist/runtime/core/eval/types.js.map +0 -1
- package/dist/runtime/core/execute.d.ts.map +0 -1
- package/dist/runtime/core/execute.js.map +0 -1
- package/dist/runtime/core/field-descriptor.d.ts.map +0 -1
- package/dist/runtime/core/field-descriptor.js.map +0 -1
- package/dist/runtime/core/introspection.d.ts.map +0 -1
- package/dist/runtime/core/introspection.js.map +0 -1
- package/dist/runtime/core/resolvers.d.ts.map +0 -1
- package/dist/runtime/core/resolvers.js.map +0 -1
- package/dist/runtime/core/signals.d.ts.map +0 -1
- package/dist/runtime/core/signals.js.map +0 -1
- package/dist/runtime/core/types.d.ts.map +0 -1
- package/dist/runtime/core/types.js.map +0 -1
- package/dist/runtime/core/values.d.ts.map +0 -1
- package/dist/runtime/core/values.js.map +0 -1
- package/dist/runtime/ext/builtins.d.ts.map +0 -1
- package/dist/runtime/ext/builtins.js.map +0 -1
- package/dist/runtime/ext/extensions.d.ts.map +0 -1
- package/dist/runtime/ext/extensions.js.map +0 -1
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/index.js.map +0 -1
- package/dist/signature-parser.d.ts.map +0 -1
- package/dist/signature-parser.js.map +0 -1
- package/dist/source-location.d.ts.map +0 -1
- package/dist/source-location.js.map +0 -1
- package/dist/token-types.d.ts.map +0 -1
- package/dist/token-types.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/value-types.d.ts.map +0 -1
- package/dist/value-types.js.map +0 -1
|
@@ -3,18 +3,29 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Core value types that flow through Rill programs.
|
|
5
5
|
* Public API for host applications.
|
|
6
|
+
*
|
|
7
|
+
* Dispatch functions (inferType, formatValue, deepEquals, serializeValue,
|
|
8
|
+
* copyValue) re-export from type-registrations.ts protocol implementations.
|
|
6
9
|
*/
|
|
7
10
|
import { RuntimeError } from '../../types.js';
|
|
8
11
|
import { VALID_TYPE_NAMES } from '../../constants.js';
|
|
9
|
-
import {
|
|
12
|
+
import { isCallable, isDict } from './callable.js';
|
|
13
|
+
import { inferType as registryInferType, formatValue as registryFormatValue, deepEquals as registryDeepEquals, serializeValue as registrySerializeValue, copyValue as registryCopyValue, } from './type-registrations.js';
|
|
10
14
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
15
|
+
* Normalize a TypeStructure that may use the legacy `.type` discriminator.
|
|
16
|
+
* During migration, some code (builtins.ts, ext/) constructs objects with
|
|
17
|
+
* `{ type: 'string' }` instead of `{ kind: 'string' }`. This function
|
|
18
|
+
* converts legacy format to current format on the fly.
|
|
13
19
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
function normalizeStructure(ts) {
|
|
21
|
+
if (ts.kind !== undefined)
|
|
22
|
+
return ts;
|
|
23
|
+
// Legacy format: { type: 'string' } → { kind: 'string' }
|
|
24
|
+
const legacy = ts;
|
|
25
|
+
if (typeof legacy['type'] === 'string') {
|
|
26
|
+
return { ...legacy, kind: legacy['type'] };
|
|
27
|
+
}
|
|
28
|
+
return ts;
|
|
18
29
|
}
|
|
19
30
|
/** Type guard for RillTuple (spread args) */
|
|
20
31
|
export function isTuple(value) {
|
|
@@ -44,7 +55,11 @@ export function isTypeValue(value) {
|
|
|
44
55
|
'__rill_type' in value &&
|
|
45
56
|
value.__rill_type === true);
|
|
46
57
|
}
|
|
47
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Create ordered from entries array (named, preserves insertion order).
|
|
60
|
+
* Entries may be 2-element [name, value] or 3-element [name, value, default]
|
|
61
|
+
* tuples; the third element carries a default value for `.^input` reflection.
|
|
62
|
+
*/
|
|
48
63
|
export function createOrdered(entries) {
|
|
49
64
|
return Object.freeze({ __rill_ordered: true, entries: [...entries] });
|
|
50
65
|
}
|
|
@@ -62,98 +77,185 @@ export function createVector(data, model) {
|
|
|
62
77
|
}
|
|
63
78
|
return { __rill_vector: true, data, model };
|
|
64
79
|
}
|
|
65
|
-
/** Infer the Rill type from a runtime value */
|
|
66
|
-
export
|
|
67
|
-
if (value === null)
|
|
68
|
-
return 'string'; // null treated as empty string
|
|
69
|
-
if (typeof value === 'string')
|
|
70
|
-
return 'string';
|
|
71
|
-
if (typeof value === 'number')
|
|
72
|
-
return 'number';
|
|
73
|
-
if (typeof value === 'boolean')
|
|
74
|
-
return 'bool';
|
|
75
|
-
if (isTuple(value))
|
|
76
|
-
return 'tuple';
|
|
77
|
-
if (isOrdered(value))
|
|
78
|
-
return 'ordered';
|
|
79
|
-
if (isVector(value))
|
|
80
|
-
return 'vector';
|
|
81
|
-
if (Array.isArray(value))
|
|
82
|
-
return 'list';
|
|
83
|
-
if (isTypeValue(value))
|
|
84
|
-
return 'type';
|
|
85
|
-
if (isRillIterator(value))
|
|
86
|
-
return 'iterator';
|
|
87
|
-
if (typeof value === 'object' &&
|
|
88
|
-
'__type' in value &&
|
|
89
|
-
value.__type === 'callable') {
|
|
90
|
-
return 'closure';
|
|
91
|
-
}
|
|
92
|
-
if (typeof value === 'object')
|
|
93
|
-
return 'dict';
|
|
94
|
-
return 'string'; // fallback
|
|
95
|
-
}
|
|
80
|
+
/** Infer the Rill type from a runtime value. Delegates to type-registrations. */
|
|
81
|
+
export const inferType = registryInferType;
|
|
96
82
|
/**
|
|
97
83
|
* Infer the element type for a homogeneous list.
|
|
98
|
-
* Empty arrays return {
|
|
84
|
+
* Empty arrays return { kind: 'any' }.
|
|
99
85
|
* Mixed types throw RILL-R002.
|
|
100
86
|
*/
|
|
101
87
|
export function inferElementType(elements) {
|
|
102
88
|
if (elements.length === 0)
|
|
103
|
-
return {
|
|
89
|
+
return { kind: 'any' };
|
|
104
90
|
const firstElem = elements[0];
|
|
105
|
-
|
|
91
|
+
let accType = inferStructure(firstElem);
|
|
106
92
|
for (let i = 1; i < elements.length; i++) {
|
|
107
93
|
const elem = elements[i];
|
|
108
|
-
const elemType =
|
|
109
|
-
|
|
110
|
-
|
|
94
|
+
const elemType = inferStructure(elem);
|
|
95
|
+
const merged = commonType(accType, elemType);
|
|
96
|
+
if (merged === null) {
|
|
97
|
+
throw new RuntimeError('RILL-R002', `List elements must be the same type: expected ${formatStructure(accType)}, got ${formatStructure(elemType)} at index ${i}`);
|
|
98
|
+
}
|
|
99
|
+
accType = merged;
|
|
100
|
+
}
|
|
101
|
+
return accType;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Merge uniform value types from two sides of the same compound type.
|
|
105
|
+
* Sub-case A: both carry valueType -> recurse commonType.
|
|
106
|
+
* Sub-case B: both carry structural fields -> extract value types, merge all.
|
|
107
|
+
* Returns the merged TypeStructure on success, undefined when no uniform merge applies.
|
|
108
|
+
*/
|
|
109
|
+
function mergeUniformValueType(aValue, bValue, aFields, bFields) {
|
|
110
|
+
// Sub-case A: both carry valueType
|
|
111
|
+
if (aValue !== undefined && bValue !== undefined) {
|
|
112
|
+
const merged = commonType(aValue, bValue);
|
|
113
|
+
if (merged !== null)
|
|
114
|
+
return merged;
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
// Sub-case B: both carry structural fields
|
|
118
|
+
if (aFields !== undefined && bFields !== undefined) {
|
|
119
|
+
const allTypes = [
|
|
120
|
+
...aFields.map((f) => f.type),
|
|
121
|
+
...bFields.map((f) => f.type),
|
|
122
|
+
];
|
|
123
|
+
if (allTypes.length === 0)
|
|
124
|
+
return undefined;
|
|
125
|
+
let merged = allTypes[0];
|
|
126
|
+
for (let i = 1; i < allTypes.length; i++) {
|
|
127
|
+
const next = commonType(merged, allTypes[i]);
|
|
128
|
+
if (next === null)
|
|
129
|
+
return undefined;
|
|
130
|
+
merged = next;
|
|
111
131
|
}
|
|
132
|
+
return merged;
|
|
112
133
|
}
|
|
113
|
-
return
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Return the most specific shared type for two TypeStructure values.
|
|
138
|
+
* Returns null when types are incompatible at the top level.
|
|
139
|
+
*
|
|
140
|
+
* Cascade priority:
|
|
141
|
+
* 1. Any-narrowing: if either side is `any`, return the other
|
|
142
|
+
* 2. Structural match: delegate to structureEquals; on true, return a
|
|
143
|
+
* 3. Recursive list: merge inner element types
|
|
144
|
+
* 3b. Uniform valueType: merge dict/tuple/ordered value types
|
|
145
|
+
* 4. Bare type fallback: same compound type but structural mismatch
|
|
146
|
+
* 5. Incompatible: different top-level types return null
|
|
147
|
+
*/
|
|
148
|
+
export function commonType(a, b) {
|
|
149
|
+
// 1. Any-narrowing
|
|
150
|
+
if (a.kind === 'any')
|
|
151
|
+
return b;
|
|
152
|
+
if (b.kind === 'any')
|
|
153
|
+
return a;
|
|
154
|
+
// 5. Incompatible top-level types (checked early to short-circuit)
|
|
155
|
+
if (a.kind !== b.kind)
|
|
156
|
+
return null;
|
|
157
|
+
// 2. Structural match
|
|
158
|
+
if (structureEquals(a, b))
|
|
159
|
+
return a;
|
|
160
|
+
// 3. Recursive list element merging
|
|
161
|
+
if (a.kind === 'list' && b.kind === 'list') {
|
|
162
|
+
const aList = a;
|
|
163
|
+
const bList = b;
|
|
164
|
+
if (aList.element !== undefined && bList.element !== undefined) {
|
|
165
|
+
const inner = commonType(aList.element, bList.element);
|
|
166
|
+
if (inner !== null)
|
|
167
|
+
return { kind: 'list', element: inner };
|
|
168
|
+
}
|
|
169
|
+
return { kind: 'list' };
|
|
170
|
+
}
|
|
171
|
+
// 3b. Uniform valueType merging for dict/tuple/ordered
|
|
172
|
+
if (a.kind === 'dict' && b.kind === 'dict') {
|
|
173
|
+
const aDict = a;
|
|
174
|
+
const bDict = b;
|
|
175
|
+
const merged = mergeUniformValueType(aDict.valueType, bDict.valueType, aDict.fields ? Object.values(aDict.fields) : undefined, bDict.fields ? Object.values(bDict.fields) : undefined);
|
|
176
|
+
if (merged !== undefined)
|
|
177
|
+
return { kind: 'dict', valueType: merged };
|
|
178
|
+
}
|
|
179
|
+
if (a.kind === 'tuple' && b.kind === 'tuple') {
|
|
180
|
+
const aTuple = a;
|
|
181
|
+
const bTuple = b;
|
|
182
|
+
const merged = mergeUniformValueType(aTuple.valueType, bTuple.valueType, aTuple.elements, bTuple.elements);
|
|
183
|
+
if (merged !== undefined)
|
|
184
|
+
return { kind: 'tuple', valueType: merged };
|
|
185
|
+
}
|
|
186
|
+
if (a.kind === 'ordered' && b.kind === 'ordered') {
|
|
187
|
+
const aOrd = a;
|
|
188
|
+
const bOrd = b;
|
|
189
|
+
const merged = mergeUniformValueType(aOrd.valueType, bOrd.valueType, aOrd.fields, bOrd.fields);
|
|
190
|
+
if (merged !== undefined)
|
|
191
|
+
return { kind: 'ordered', valueType: merged };
|
|
192
|
+
}
|
|
193
|
+
// 4. Bare type fallback for compound types.
|
|
194
|
+
// The cast is safe for closure/dict/tuple/ordered (all sub-fields optional).
|
|
195
|
+
// For union, members is required by TypeStructure but omitted here intentionally:
|
|
196
|
+
// bare union signals structural incompatibility without enumerating members.
|
|
197
|
+
if (a.kind === 'closure' ||
|
|
198
|
+
a.kind === 'dict' ||
|
|
199
|
+
a.kind === 'tuple' ||
|
|
200
|
+
a.kind === 'ordered' ||
|
|
201
|
+
a.kind === 'union') {
|
|
202
|
+
return { kind: a.kind };
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
114
205
|
}
|
|
115
206
|
/** Compare two structural types for equality. */
|
|
116
|
-
export function
|
|
117
|
-
if (a.
|
|
207
|
+
export function structureEquals(a, b) {
|
|
208
|
+
if (a.kind !== b.kind)
|
|
118
209
|
return false;
|
|
119
|
-
// Leaf variants compare by
|
|
120
|
-
if (a.
|
|
121
|
-
a.
|
|
122
|
-
a.
|
|
123
|
-
a.
|
|
124
|
-
a.
|
|
125
|
-
a.
|
|
210
|
+
// Leaf variants compare by kind alone
|
|
211
|
+
if (a.kind === 'number' ||
|
|
212
|
+
a.kind === 'string' ||
|
|
213
|
+
a.kind === 'bool' ||
|
|
214
|
+
a.kind === 'vector' ||
|
|
215
|
+
a.kind === 'type' ||
|
|
216
|
+
a.kind === 'any') {
|
|
126
217
|
return true;
|
|
127
218
|
}
|
|
128
|
-
if (a.
|
|
129
|
-
|
|
219
|
+
if (a.kind === 'list' && b.kind === 'list') {
|
|
220
|
+
const aList = a;
|
|
221
|
+
const bList = b;
|
|
222
|
+
if (aList.element === undefined && bList.element === undefined)
|
|
130
223
|
return true;
|
|
131
|
-
if (
|
|
224
|
+
if (aList.element === undefined || bList.element === undefined)
|
|
132
225
|
return false;
|
|
133
|
-
return
|
|
134
|
-
}
|
|
135
|
-
if (a.
|
|
136
|
-
|
|
226
|
+
return structureEquals(aList.element, bList.element);
|
|
227
|
+
}
|
|
228
|
+
if (a.kind === 'dict' && b.kind === 'dict') {
|
|
229
|
+
const aDict = a;
|
|
230
|
+
const bDict = b;
|
|
231
|
+
// Uniform valueType comparison
|
|
232
|
+
const aHasValue = aDict.valueType !== undefined;
|
|
233
|
+
const bHasValue = bDict.valueType !== undefined;
|
|
234
|
+
if (aHasValue || bHasValue) {
|
|
235
|
+
if (!aHasValue || !bHasValue)
|
|
236
|
+
return false;
|
|
237
|
+
return structureEquals(aDict.valueType, bDict.valueType);
|
|
238
|
+
}
|
|
239
|
+
// Structural fields comparison
|
|
240
|
+
if (aDict.fields === undefined && bDict.fields === undefined)
|
|
137
241
|
return true;
|
|
138
|
-
if (
|
|
242
|
+
if (aDict.fields === undefined || bDict.fields === undefined)
|
|
139
243
|
return false;
|
|
140
|
-
const aKeys = Object.keys(
|
|
141
|
-
const bKeys = Object.keys(
|
|
244
|
+
const aKeys = Object.keys(aDict.fields).sort();
|
|
245
|
+
const bKeys = Object.keys(bDict.fields).sort();
|
|
142
246
|
if (aKeys.length !== bKeys.length)
|
|
143
247
|
return false;
|
|
144
248
|
for (let i = 0; i < aKeys.length; i++) {
|
|
145
249
|
const key = aKeys[i];
|
|
146
250
|
if (key !== bKeys[i])
|
|
147
251
|
return false;
|
|
148
|
-
const aField =
|
|
149
|
-
const bField =
|
|
150
|
-
const aHasDefault =
|
|
151
|
-
const bHasDefault =
|
|
252
|
+
const aField = aDict.fields[key];
|
|
253
|
+
const bField = bDict.fields[key];
|
|
254
|
+
const aHasDefault = aField.defaultValue !== undefined;
|
|
255
|
+
const bHasDefault = bField.defaultValue !== undefined;
|
|
152
256
|
if (aHasDefault !== bHasDefault)
|
|
153
257
|
return false;
|
|
154
|
-
|
|
155
|
-
const bType = bHasDefault ? bField.type : bField;
|
|
156
|
-
if (!structuralTypeEquals(aType, bType))
|
|
258
|
+
if (!structureEquals(aField.type, bField.type))
|
|
157
259
|
return false;
|
|
158
260
|
if (aHasDefault && bHasDefault) {
|
|
159
261
|
if (!deepEquals(aField.defaultValue, bField.defaultValue))
|
|
@@ -162,20 +264,31 @@ export function structuralTypeEquals(a, b) {
|
|
|
162
264
|
}
|
|
163
265
|
return true;
|
|
164
266
|
}
|
|
165
|
-
if (a.
|
|
166
|
-
|
|
267
|
+
if (a.kind === 'tuple' && b.kind === 'tuple') {
|
|
268
|
+
const aTuple = a;
|
|
269
|
+
const bTuple = b;
|
|
270
|
+
// Uniform valueType comparison
|
|
271
|
+
const aHasValue = aTuple.valueType !== undefined;
|
|
272
|
+
const bHasValue = bTuple.valueType !== undefined;
|
|
273
|
+
if (aHasValue || bHasValue) {
|
|
274
|
+
if (!aHasValue || !bHasValue)
|
|
275
|
+
return false;
|
|
276
|
+
return structureEquals(aTuple.valueType, bTuple.valueType);
|
|
277
|
+
}
|
|
278
|
+
// Structural elements comparison
|
|
279
|
+
if (aTuple.elements === undefined && bTuple.elements === undefined)
|
|
167
280
|
return true;
|
|
168
|
-
if (
|
|
281
|
+
if (aTuple.elements === undefined || bTuple.elements === undefined)
|
|
169
282
|
return false;
|
|
170
|
-
if (
|
|
283
|
+
if (aTuple.elements.length !== bTuple.elements.length)
|
|
171
284
|
return false;
|
|
172
|
-
for (let i = 0; i <
|
|
173
|
-
const aElem =
|
|
174
|
-
const bElem =
|
|
175
|
-
if (!
|
|
285
|
+
for (let i = 0; i < aTuple.elements.length; i++) {
|
|
286
|
+
const aElem = aTuple.elements[i];
|
|
287
|
+
const bElem = bTuple.elements[i];
|
|
288
|
+
if (!structureEquals(aElem.type, bElem.type))
|
|
176
289
|
return false;
|
|
177
|
-
const aDefault = aElem
|
|
178
|
-
const bDefault = bElem
|
|
290
|
+
const aDefault = aElem.defaultValue;
|
|
291
|
+
const bDefault = bElem.defaultValue;
|
|
179
292
|
if (aDefault === undefined && bDefault === undefined)
|
|
180
293
|
continue;
|
|
181
294
|
if (aDefault === undefined || bDefault === undefined)
|
|
@@ -185,22 +298,33 @@ export function structuralTypeEquals(a, b) {
|
|
|
185
298
|
}
|
|
186
299
|
return true;
|
|
187
300
|
}
|
|
188
|
-
if (a.
|
|
189
|
-
|
|
301
|
+
if (a.kind === 'ordered' && b.kind === 'ordered') {
|
|
302
|
+
const aOrd = a;
|
|
303
|
+
const bOrd = b;
|
|
304
|
+
// Uniform valueType comparison
|
|
305
|
+
const aHasValue = aOrd.valueType !== undefined;
|
|
306
|
+
const bHasValue = bOrd.valueType !== undefined;
|
|
307
|
+
if (aHasValue || bHasValue) {
|
|
308
|
+
if (!aHasValue || !bHasValue)
|
|
309
|
+
return false;
|
|
310
|
+
return structureEquals(aOrd.valueType, bOrd.valueType);
|
|
311
|
+
}
|
|
312
|
+
// Structural fields comparison
|
|
313
|
+
if (aOrd.fields === undefined && bOrd.fields === undefined)
|
|
190
314
|
return true;
|
|
191
|
-
if (
|
|
315
|
+
if (aOrd.fields === undefined || bOrd.fields === undefined)
|
|
192
316
|
return false;
|
|
193
|
-
if (
|
|
317
|
+
if (aOrd.fields.length !== bOrd.fields.length)
|
|
194
318
|
return false;
|
|
195
|
-
for (let i = 0; i <
|
|
196
|
-
const aField =
|
|
197
|
-
const bField =
|
|
198
|
-
if (aField
|
|
319
|
+
for (let i = 0; i < aOrd.fields.length; i++) {
|
|
320
|
+
const aField = aOrd.fields[i];
|
|
321
|
+
const bField = bOrd.fields[i];
|
|
322
|
+
if (aField.name !== bField.name)
|
|
199
323
|
return false;
|
|
200
|
-
if (!
|
|
324
|
+
if (!structureEquals(aField.type, bField.type))
|
|
201
325
|
return false;
|
|
202
|
-
const aDefault = aField
|
|
203
|
-
const bDefault = bField
|
|
326
|
+
const aDefault = aField.defaultValue;
|
|
327
|
+
const bDefault = bField.defaultValue;
|
|
204
328
|
if (aDefault === undefined && bDefault === undefined)
|
|
205
329
|
continue;
|
|
206
330
|
if (aDefault === undefined || bDefault === undefined)
|
|
@@ -210,34 +334,38 @@ export function structuralTypeEquals(a, b) {
|
|
|
210
334
|
}
|
|
211
335
|
return true;
|
|
212
336
|
}
|
|
213
|
-
if (a.
|
|
214
|
-
|
|
337
|
+
if (a.kind === 'union' && b.kind === 'union') {
|
|
338
|
+
const aUnion = a;
|
|
339
|
+
const bUnion = b;
|
|
340
|
+
if (aUnion.members.length !== bUnion.members.length)
|
|
215
341
|
return false;
|
|
216
|
-
for (let i = 0; i <
|
|
217
|
-
if (!
|
|
342
|
+
for (let i = 0; i < aUnion.members.length; i++) {
|
|
343
|
+
if (!structureEquals(aUnion.members[i], bUnion.members[i]))
|
|
218
344
|
return false;
|
|
219
345
|
}
|
|
220
346
|
return true;
|
|
221
347
|
}
|
|
222
|
-
if (a.
|
|
223
|
-
|
|
348
|
+
if (a.kind === 'closure' && b.kind === 'closure') {
|
|
349
|
+
const aCls = a;
|
|
350
|
+
const bCls = b;
|
|
351
|
+
if (aCls.params === undefined && bCls.params === undefined) {
|
|
224
352
|
// Both absent: compare ret
|
|
225
353
|
}
|
|
226
|
-
else if (
|
|
354
|
+
else if (aCls.params === undefined || bCls.params === undefined) {
|
|
227
355
|
return false;
|
|
228
356
|
}
|
|
229
357
|
else {
|
|
230
|
-
if (
|
|
358
|
+
if (aCls.params.length !== bCls.params.length)
|
|
231
359
|
return false;
|
|
232
|
-
for (let i = 0; i <
|
|
233
|
-
const aParam =
|
|
234
|
-
const bParam =
|
|
235
|
-
if (aParam
|
|
360
|
+
for (let i = 0; i < aCls.params.length; i++) {
|
|
361
|
+
const aParam = aCls.params[i];
|
|
362
|
+
const bParam = bCls.params[i];
|
|
363
|
+
if (aParam.name !== bParam.name)
|
|
236
364
|
return false;
|
|
237
|
-
if (!
|
|
365
|
+
if (!structureEquals(aParam.type, bParam.type))
|
|
238
366
|
return false;
|
|
239
|
-
const aDefault = aParam
|
|
240
|
-
const bDefault = bParam
|
|
367
|
+
const aDefault = aParam.defaultValue;
|
|
368
|
+
const bDefault = bParam.defaultValue;
|
|
241
369
|
if (aDefault === undefined && bDefault === undefined)
|
|
242
370
|
continue;
|
|
243
371
|
if (aDefault === undefined || bDefault === undefined)
|
|
@@ -246,184 +374,219 @@ export function structuralTypeEquals(a, b) {
|
|
|
246
374
|
return false;
|
|
247
375
|
}
|
|
248
376
|
}
|
|
249
|
-
if (
|
|
377
|
+
if (aCls.ret === undefined && bCls.ret === undefined)
|
|
250
378
|
return true;
|
|
251
|
-
if (
|
|
379
|
+
if (aCls.ret === undefined || bCls.ret === undefined)
|
|
252
380
|
return false;
|
|
253
|
-
return
|
|
381
|
+
return structureEquals(aCls.ret, bCls.ret);
|
|
254
382
|
}
|
|
255
383
|
return false;
|
|
256
384
|
}
|
|
385
|
+
/** @deprecated Use structureEquals instead. */
|
|
386
|
+
export const structuralTypeEquals = structureEquals;
|
|
257
387
|
/** Infer the structural type descriptor for any Rill value. */
|
|
258
|
-
export function
|
|
388
|
+
export function inferStructure(value) {
|
|
259
389
|
if (value === null || typeof value === 'string') {
|
|
260
|
-
return {
|
|
390
|
+
return { kind: 'string' };
|
|
261
391
|
}
|
|
262
392
|
if (typeof value === 'number') {
|
|
263
|
-
return {
|
|
393
|
+
return { kind: 'number' };
|
|
264
394
|
}
|
|
265
395
|
if (typeof value === 'boolean') {
|
|
266
|
-
return {
|
|
396
|
+
return { kind: 'bool' };
|
|
267
397
|
}
|
|
268
398
|
if (isTypeValue(value)) {
|
|
269
|
-
return {
|
|
399
|
+
return { kind: 'type' };
|
|
270
400
|
}
|
|
271
401
|
if (Array.isArray(value)) {
|
|
272
|
-
return {
|
|
402
|
+
return { kind: 'list', element: inferElementType(value) };
|
|
273
403
|
}
|
|
274
404
|
if (isTuple(value)) {
|
|
275
405
|
return {
|
|
276
|
-
|
|
277
|
-
elements: value.entries.map((e) =>
|
|
278
|
-
|
|
279
|
-
|
|
406
|
+
kind: 'tuple',
|
|
407
|
+
elements: value.entries.map((e) => ({
|
|
408
|
+
type: inferStructure(e),
|
|
409
|
+
})),
|
|
280
410
|
};
|
|
281
411
|
}
|
|
282
412
|
if (isOrdered(value)) {
|
|
283
413
|
return {
|
|
284
|
-
|
|
285
|
-
fields: value.entries.map(([k, v]) =>
|
|
286
|
-
k,
|
|
287
|
-
|
|
288
|
-
|
|
414
|
+
kind: 'ordered',
|
|
415
|
+
fields: value.entries.map(([k, v]) => ({
|
|
416
|
+
name: k,
|
|
417
|
+
type: inferStructure(v),
|
|
418
|
+
})),
|
|
289
419
|
};
|
|
290
420
|
}
|
|
291
421
|
if (isVector(value)) {
|
|
292
|
-
return {
|
|
422
|
+
return { kind: 'vector' };
|
|
293
423
|
}
|
|
294
424
|
if (isCallable(value)) {
|
|
295
|
-
const params = (value.params ?? []).map((p) =>
|
|
425
|
+
const params = (value.params ?? []).map((p) => paramToFieldDef(p.name, p.type ?? { kind: 'any' }, p.defaultValue));
|
|
296
426
|
const ret = value.returnType.structure;
|
|
297
|
-
return {
|
|
427
|
+
return { kind: 'closure', params, ret };
|
|
298
428
|
}
|
|
299
429
|
if (typeof value === 'object') {
|
|
300
430
|
const dict = value;
|
|
301
431
|
const fields = {};
|
|
302
432
|
for (const [k, v] of Object.entries(dict)) {
|
|
303
|
-
fields[k] =
|
|
433
|
+
fields[k] = { type: inferStructure(v) };
|
|
304
434
|
}
|
|
305
|
-
return {
|
|
435
|
+
return { kind: 'dict', fields };
|
|
306
436
|
}
|
|
307
437
|
throw new RuntimeError('RILL-R004', `Cannot infer structural type for ${formatValue(value)}`);
|
|
308
438
|
}
|
|
439
|
+
/** @deprecated Use inferStructure instead. */
|
|
440
|
+
export const inferStructuralType = inferStructure;
|
|
309
441
|
/**
|
|
310
442
|
* Check if a value matches a structural type descriptor.
|
|
311
443
|
* Used for runtime type checking (`:?` operator).
|
|
312
444
|
*/
|
|
313
|
-
export function
|
|
445
|
+
export function structureMatches(value, type) {
|
|
446
|
+
type = normalizeStructure(type);
|
|
314
447
|
if (typeof value === 'undefined') {
|
|
315
448
|
throw new RuntimeError('RILL-R004', 'Cannot type-check non-value');
|
|
316
449
|
}
|
|
317
|
-
if (type.
|
|
450
|
+
if (type.kind === 'any')
|
|
318
451
|
return true;
|
|
319
452
|
// Leaf primitive variants: match by inferred type name
|
|
320
|
-
if (type.
|
|
321
|
-
type.
|
|
322
|
-
type.
|
|
323
|
-
type.
|
|
324
|
-
type.
|
|
325
|
-
return inferType(value) === type.
|
|
326
|
-
}
|
|
327
|
-
if (type.
|
|
453
|
+
if (type.kind === 'number' ||
|
|
454
|
+
type.kind === 'string' ||
|
|
455
|
+
type.kind === 'bool' ||
|
|
456
|
+
type.kind === 'vector' ||
|
|
457
|
+
type.kind === 'type') {
|
|
458
|
+
return inferType(value) === type.kind;
|
|
459
|
+
}
|
|
460
|
+
if (type.kind === 'list') {
|
|
461
|
+
const t = type;
|
|
328
462
|
if (!Array.isArray(value))
|
|
329
463
|
return false;
|
|
330
|
-
|
|
331
|
-
if (type.element === undefined)
|
|
464
|
+
if (t.element === undefined)
|
|
332
465
|
return true;
|
|
333
|
-
if (
|
|
466
|
+
if (t.element.kind === 'any')
|
|
334
467
|
return true;
|
|
335
|
-
return value.every((elem) =>
|
|
468
|
+
return value.every((elem) => structureMatches(elem, t.element));
|
|
336
469
|
}
|
|
337
|
-
if (type.
|
|
470
|
+
if (type.kind === 'dict') {
|
|
471
|
+
const t = type;
|
|
338
472
|
if (!isDict(value))
|
|
339
473
|
return false;
|
|
340
|
-
|
|
341
|
-
|
|
474
|
+
if (t.valueType !== undefined) {
|
|
475
|
+
const vals = Object.values(value);
|
|
476
|
+
return vals.every((v) => structureMatches(v, t.valueType));
|
|
477
|
+
}
|
|
478
|
+
if (t.fields === undefined)
|
|
342
479
|
return true;
|
|
343
|
-
const dictKeys = Object.keys(
|
|
344
|
-
// Empty fields object matches any dict
|
|
480
|
+
const dictKeys = Object.keys(t.fields);
|
|
345
481
|
if (dictKeys.length === 0)
|
|
346
482
|
return true;
|
|
347
483
|
const dict = value;
|
|
348
484
|
for (const key of dictKeys) {
|
|
349
|
-
if (!(key in dict))
|
|
485
|
+
if (!(key in dict)) {
|
|
486
|
+
const field = t.fields[key];
|
|
487
|
+
if (field.defaultValue !== undefined)
|
|
488
|
+
continue;
|
|
350
489
|
return false;
|
|
351
|
-
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
: fieldType;
|
|
355
|
-
if (!structuralTypeMatches(dict[key], resolvedType))
|
|
490
|
+
}
|
|
491
|
+
const field = t.fields[key];
|
|
492
|
+
if (!structureMatches(dict[key], field.type))
|
|
356
493
|
return false;
|
|
357
494
|
}
|
|
358
495
|
return true;
|
|
359
496
|
}
|
|
360
|
-
if (type.
|
|
497
|
+
if (type.kind === 'tuple') {
|
|
498
|
+
const t = type;
|
|
361
499
|
if (!isTuple(value))
|
|
362
500
|
return false;
|
|
363
|
-
|
|
364
|
-
|
|
501
|
+
if (t.valueType !== undefined) {
|
|
502
|
+
return value.entries.every((v) => structureMatches(v, t.valueType));
|
|
503
|
+
}
|
|
504
|
+
if (t.elements === undefined)
|
|
365
505
|
return true;
|
|
366
|
-
if (
|
|
506
|
+
if (t.elements.length === 0)
|
|
367
507
|
return value.entries.length === 0;
|
|
368
|
-
if (value.entries.length
|
|
508
|
+
if (value.entries.length > t.elements.length)
|
|
369
509
|
return false;
|
|
370
|
-
|
|
371
|
-
|
|
510
|
+
if (value.entries.length < t.elements.length) {
|
|
511
|
+
for (let i = value.entries.length; i < t.elements.length; i++) {
|
|
512
|
+
const field = t.elements[i];
|
|
513
|
+
if (field.defaultValue === undefined)
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
for (let i = 0; i < value.entries.length; i++) {
|
|
518
|
+
if (!structureMatches(value.entries[i], t.elements[i].type))
|
|
372
519
|
return false;
|
|
373
520
|
}
|
|
374
521
|
return true;
|
|
375
522
|
}
|
|
376
|
-
if (type.
|
|
523
|
+
if (type.kind === 'ordered') {
|
|
524
|
+
const t = type;
|
|
377
525
|
if (!isOrdered(value))
|
|
378
526
|
return false;
|
|
379
|
-
|
|
380
|
-
|
|
527
|
+
if (t.valueType !== undefined) {
|
|
528
|
+
return value.entries.every(([, v]) => structureMatches(v, t.valueType));
|
|
529
|
+
}
|
|
530
|
+
if (t.fields === undefined)
|
|
381
531
|
return true;
|
|
382
|
-
if (
|
|
532
|
+
if (t.fields.length === 0)
|
|
383
533
|
return value.entries.length === 0;
|
|
384
|
-
if (value.entries.length
|
|
534
|
+
if (value.entries.length > t.fields.length)
|
|
385
535
|
return false;
|
|
386
|
-
|
|
387
|
-
|
|
536
|
+
if (value.entries.length < t.fields.length) {
|
|
537
|
+
for (let i = value.entries.length; i < t.fields.length; i++) {
|
|
538
|
+
const field = t.fields[i];
|
|
539
|
+
if (field.defaultValue === undefined)
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
for (let i = 0; i < value.entries.length; i++) {
|
|
544
|
+
const field = t.fields[i];
|
|
388
545
|
const [actualName, actualValue] = value.entries[i];
|
|
389
|
-
if (actualName !==
|
|
546
|
+
if (actualName !== field.name)
|
|
390
547
|
return false;
|
|
391
|
-
if (!
|
|
548
|
+
if (!structureMatches(actualValue, field.type))
|
|
392
549
|
return false;
|
|
393
550
|
}
|
|
394
551
|
return true;
|
|
395
552
|
}
|
|
396
|
-
if (type.
|
|
553
|
+
if (type.kind === 'closure') {
|
|
554
|
+
const t = type;
|
|
397
555
|
if (!isCallable(value))
|
|
398
556
|
return false;
|
|
399
|
-
|
|
400
|
-
if (type.params === undefined)
|
|
557
|
+
if (t.params === undefined)
|
|
401
558
|
return true;
|
|
402
559
|
const valueParams = value.params ?? [];
|
|
403
|
-
if (valueParams.length !==
|
|
560
|
+
if (valueParams.length !== t.params.length)
|
|
404
561
|
return false;
|
|
405
|
-
for (let i = 0; i <
|
|
406
|
-
const
|
|
562
|
+
for (let i = 0; i < t.params.length; i++) {
|
|
563
|
+
const field = t.params[i];
|
|
407
564
|
const param = valueParams[i];
|
|
408
|
-
if (param.name !==
|
|
565
|
+
if (param.name !== field.name)
|
|
409
566
|
return false;
|
|
410
|
-
const paramType = param.type ?? {
|
|
411
|
-
if (!
|
|
567
|
+
const paramType = param.type ?? { kind: 'any' };
|
|
568
|
+
if (!structureEquals(paramType, field.type))
|
|
412
569
|
return false;
|
|
413
570
|
}
|
|
414
571
|
const retType = value.returnType.structure;
|
|
415
|
-
if (
|
|
572
|
+
if (t.ret === undefined)
|
|
416
573
|
return true;
|
|
417
|
-
return
|
|
574
|
+
return structureEquals(retType, t.ret);
|
|
418
575
|
}
|
|
419
|
-
if (type.
|
|
420
|
-
|
|
576
|
+
if (type.kind === 'union') {
|
|
577
|
+
const t = type;
|
|
578
|
+
return t.members.some((member) => structureMatches(value, member));
|
|
421
579
|
}
|
|
422
580
|
return false;
|
|
423
581
|
}
|
|
424
|
-
/**
|
|
425
|
-
export
|
|
426
|
-
|
|
582
|
+
/** @deprecated Use structureMatches instead. */
|
|
583
|
+
export const structuralTypeMatches = structureMatches;
|
|
584
|
+
/** Build a closure param field definition from name, type, and optional default. */
|
|
585
|
+
export function paramToFieldDef(name, type, defaultValue) {
|
|
586
|
+
const field = { name, type };
|
|
587
|
+
if (defaultValue !== undefined)
|
|
588
|
+
field.defaultValue = defaultValue;
|
|
589
|
+
return field;
|
|
427
590
|
}
|
|
428
591
|
/** Format a RillValue as a rill literal for use in type signatures. */
|
|
429
592
|
function formatRillLiteral(value) {
|
|
@@ -440,75 +603,92 @@ function formatRillLiteral(value) {
|
|
|
440
603
|
return formatValue(value);
|
|
441
604
|
}
|
|
442
605
|
/** Format a structural type descriptor as a human-readable string. */
|
|
443
|
-
export function
|
|
444
|
-
if (type.
|
|
445
|
-
type.
|
|
446
|
-
type.
|
|
447
|
-
type.
|
|
448
|
-
type.
|
|
449
|
-
type.
|
|
450
|
-
return type.
|
|
451
|
-
}
|
|
452
|
-
if (type.
|
|
453
|
-
|
|
606
|
+
export function formatStructure(type) {
|
|
607
|
+
if (type.kind === 'any' ||
|
|
608
|
+
type.kind === 'number' ||
|
|
609
|
+
type.kind === 'string' ||
|
|
610
|
+
type.kind === 'bool' ||
|
|
611
|
+
type.kind === 'vector' ||
|
|
612
|
+
type.kind === 'type') {
|
|
613
|
+
return type.kind;
|
|
614
|
+
}
|
|
615
|
+
if (type.kind === 'list') {
|
|
616
|
+
const t = type;
|
|
617
|
+
if (t.element === undefined)
|
|
454
618
|
return 'list';
|
|
455
|
-
return `list(${
|
|
619
|
+
return `list(${formatStructure(t.element)})`;
|
|
456
620
|
}
|
|
457
|
-
if (type.
|
|
458
|
-
|
|
621
|
+
if (type.kind === 'dict') {
|
|
622
|
+
const t = type;
|
|
623
|
+
if (t.valueType !== undefined && t.fields === undefined) {
|
|
624
|
+
return `dict(${formatStructure(t.valueType)})`;
|
|
625
|
+
}
|
|
626
|
+
if (t.fields === undefined)
|
|
459
627
|
return 'dict';
|
|
460
|
-
const parts = Object.keys(
|
|
628
|
+
const parts = Object.keys(t.fields)
|
|
461
629
|
.sort()
|
|
462
630
|
.map((k) => {
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
return `${
|
|
631
|
+
const field = t.fields[k];
|
|
632
|
+
const base = `${k}: ${formatStructure(field.type)}`;
|
|
633
|
+
if (field.defaultValue === undefined)
|
|
634
|
+
return base;
|
|
635
|
+
return `${base} = ${formatRillLiteral(field.defaultValue)}`;
|
|
468
636
|
});
|
|
469
637
|
return `dict(${parts.join(', ')})`;
|
|
470
638
|
}
|
|
471
|
-
if (type.
|
|
472
|
-
|
|
639
|
+
if (type.kind === 'tuple') {
|
|
640
|
+
const t = type;
|
|
641
|
+
if (t.valueType !== undefined && t.elements === undefined) {
|
|
642
|
+
return `tuple(${formatStructure(t.valueType)})`;
|
|
643
|
+
}
|
|
644
|
+
if (t.elements === undefined)
|
|
473
645
|
return 'tuple';
|
|
474
|
-
const parts =
|
|
475
|
-
const base =
|
|
476
|
-
if (
|
|
646
|
+
const parts = t.elements.map((field) => {
|
|
647
|
+
const base = formatStructure(field.type);
|
|
648
|
+
if (field.defaultValue === undefined)
|
|
477
649
|
return base;
|
|
478
|
-
return `${base} = ${formatRillLiteral(
|
|
650
|
+
return `${base} = ${formatRillLiteral(field.defaultValue)}`;
|
|
479
651
|
});
|
|
480
652
|
return `tuple(${parts.join(', ')})`;
|
|
481
653
|
}
|
|
482
|
-
if (type.
|
|
483
|
-
|
|
654
|
+
if (type.kind === 'ordered') {
|
|
655
|
+
const t = type;
|
|
656
|
+
if (t.valueType !== undefined && t.fields === undefined) {
|
|
657
|
+
return `ordered(${formatStructure(t.valueType)})`;
|
|
658
|
+
}
|
|
659
|
+
if (t.fields === undefined)
|
|
484
660
|
return 'ordered';
|
|
485
|
-
const parts =
|
|
486
|
-
const base = `${
|
|
487
|
-
if (
|
|
661
|
+
const parts = t.fields.map((field) => {
|
|
662
|
+
const base = `${field.name}: ${formatStructure(field.type)}`;
|
|
663
|
+
if (field.defaultValue === undefined)
|
|
488
664
|
return base;
|
|
489
|
-
return `${base} = ${formatRillLiteral(
|
|
665
|
+
return `${base} = ${formatRillLiteral(field.defaultValue)}`;
|
|
490
666
|
});
|
|
491
667
|
return `ordered(${parts.join(', ')})`;
|
|
492
668
|
}
|
|
493
|
-
if (type.
|
|
494
|
-
|
|
669
|
+
if (type.kind === 'closure') {
|
|
670
|
+
const t = type;
|
|
671
|
+
if (t.params === undefined)
|
|
495
672
|
return 'closure';
|
|
496
|
-
const params =
|
|
497
|
-
.map((
|
|
498
|
-
const base = `${name}: ${
|
|
499
|
-
if (
|
|
673
|
+
const params = t.params
|
|
674
|
+
.map((field) => {
|
|
675
|
+
const base = `${field.name}: ${formatStructure(field.type)}`;
|
|
676
|
+
if (field.defaultValue === undefined)
|
|
500
677
|
return base;
|
|
501
|
-
return `${base} = ${formatRillLiteral(
|
|
678
|
+
return `${base} = ${formatRillLiteral(field.defaultValue)}`;
|
|
502
679
|
})
|
|
503
680
|
.join(', ');
|
|
504
|
-
const ret =
|
|
681
|
+
const ret = t.ret !== undefined ? formatStructure(t.ret) : 'any';
|
|
505
682
|
return `|${params}| :${ret}`;
|
|
506
683
|
}
|
|
507
|
-
if (type.
|
|
508
|
-
|
|
684
|
+
if (type.kind === 'union') {
|
|
685
|
+
const t = type;
|
|
686
|
+
return t.members.map(formatStructure).join('|');
|
|
509
687
|
}
|
|
510
688
|
return 'any';
|
|
511
689
|
}
|
|
690
|
+
/** @deprecated Use formatStructure instead. */
|
|
691
|
+
export const formatStructuralType = formatStructure;
|
|
512
692
|
/**
|
|
513
693
|
* Check if a value is of the expected type.
|
|
514
694
|
* Returns true if the value matches the expected type, false otherwise.
|
|
@@ -545,88 +725,12 @@ export function isTruthy(value) {
|
|
|
545
725
|
export function isEmpty(value) {
|
|
546
726
|
return !isTruthy(value);
|
|
547
727
|
}
|
|
548
|
-
/** Format a value for display */
|
|
549
|
-
export
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
if (typeof value === 'number')
|
|
555
|
-
return String(value);
|
|
556
|
-
if (typeof value === 'boolean')
|
|
557
|
-
return value ? 'true' : 'false';
|
|
558
|
-
if (isCallable(value)) {
|
|
559
|
-
return 'type(closure)';
|
|
560
|
-
}
|
|
561
|
-
if (isTuple(value)) {
|
|
562
|
-
return `tuple[${value.entries.map(formatValue).join(', ')}]`;
|
|
563
|
-
}
|
|
564
|
-
if (isOrdered(value)) {
|
|
565
|
-
const parts = value.entries.map(([k, v]) => `${k}: ${formatValue(v)}`);
|
|
566
|
-
return `ordered[${parts.join(', ')}]`;
|
|
567
|
-
}
|
|
568
|
-
if (isRillIterator(value)) {
|
|
569
|
-
return 'type(iterator)';
|
|
570
|
-
}
|
|
571
|
-
if (Array.isArray(value)) {
|
|
572
|
-
return `list[${value.map(formatValue).join(', ')}]`;
|
|
573
|
-
}
|
|
574
|
-
if (isVector(value)) {
|
|
575
|
-
return `vector(${value.model}, ${value.data.length}d)`;
|
|
576
|
-
}
|
|
577
|
-
if (isTypeValue(value)) {
|
|
578
|
-
return formatStructuralType(value.structure);
|
|
579
|
-
}
|
|
580
|
-
// Plain dict
|
|
581
|
-
if (typeof value === 'object') {
|
|
582
|
-
const dict = value;
|
|
583
|
-
const parts = Object.entries(dict).map(([k, v]) => `${k}: ${formatValue(v)}`);
|
|
584
|
-
return `dict[${parts.join(', ')}]`;
|
|
585
|
-
}
|
|
586
|
-
return String(value);
|
|
587
|
-
}
|
|
588
|
-
/**
|
|
589
|
-
* Convert a RillValue to a JSON-serializable value.
|
|
590
|
-
* @throws {Error} plain Error (not RuntimeError) for non-serializable types
|
|
591
|
-
*/
|
|
592
|
-
export function valueToJSON(value) {
|
|
593
|
-
if (value === null)
|
|
594
|
-
return null;
|
|
595
|
-
if (typeof value === 'string')
|
|
596
|
-
return value;
|
|
597
|
-
if (typeof value === 'number')
|
|
598
|
-
return value;
|
|
599
|
-
if (typeof value === 'boolean')
|
|
600
|
-
return value;
|
|
601
|
-
if (Array.isArray(value)) {
|
|
602
|
-
return value.map(valueToJSON);
|
|
603
|
-
}
|
|
604
|
-
if (isCallable(value)) {
|
|
605
|
-
throw new Error('closures are not JSON-serializable');
|
|
606
|
-
}
|
|
607
|
-
if (isTuple(value)) {
|
|
608
|
-
throw new Error('tuples are not JSON-serializable');
|
|
609
|
-
}
|
|
610
|
-
if (isOrdered(value)) {
|
|
611
|
-
throw new Error('ordered values are not JSON-serializable');
|
|
612
|
-
}
|
|
613
|
-
if (isVector(value)) {
|
|
614
|
-
throw new Error('vectors are not JSON-serializable');
|
|
615
|
-
}
|
|
616
|
-
if (isTypeValue(value)) {
|
|
617
|
-
throw new Error('type values are not JSON-serializable');
|
|
618
|
-
}
|
|
619
|
-
if (isRillIterator(value)) {
|
|
620
|
-
throw new Error('iterators are not JSON-serializable');
|
|
621
|
-
}
|
|
622
|
-
// Plain dict
|
|
623
|
-
const dict = value;
|
|
624
|
-
const result = {};
|
|
625
|
-
for (const [k, v] of Object.entries(dict)) {
|
|
626
|
-
result[k] = valueToJSON(v);
|
|
627
|
-
}
|
|
628
|
-
return result;
|
|
629
|
-
}
|
|
728
|
+
/** Format a value for display. Delegates to type-registrations. */
|
|
729
|
+
export const formatValue = registryFormatValue;
|
|
730
|
+
/** Serialize a Rill value for JSON transport. Delegates to type-registrations. */
|
|
731
|
+
export const serializeValue = registrySerializeValue;
|
|
732
|
+
/** @deprecated Use serializeValue instead. */
|
|
733
|
+
export const valueToJSON = serializeValue;
|
|
630
734
|
/**
|
|
631
735
|
* Convert a RillValue to a NativeResult for host consumption.
|
|
632
736
|
* Non-representable types (closures, vectors, type values, iterators) produce descriptor objects.
|
|
@@ -634,7 +738,7 @@ export function valueToJSON(value) {
|
|
|
634
738
|
*/
|
|
635
739
|
export function toNative(value) {
|
|
636
740
|
const rillTypeName = inferType(value);
|
|
637
|
-
const rillTypeSignature =
|
|
741
|
+
const rillTypeSignature = formatStructure(inferStructure(value));
|
|
638
742
|
const nativeValue = toNativeValue(value);
|
|
639
743
|
return { rillTypeName, rillTypeSignature, value: nativeValue };
|
|
640
744
|
}
|
|
@@ -651,7 +755,7 @@ function toNativeValue(value) {
|
|
|
651
755
|
return value.map(toNativeValue);
|
|
652
756
|
}
|
|
653
757
|
if (isCallable(value)) {
|
|
654
|
-
return { signature:
|
|
758
|
+
return { signature: formatStructure(inferStructure(value)) };
|
|
655
759
|
}
|
|
656
760
|
if (isTuple(value)) {
|
|
657
761
|
return value.entries.map(toNativeValue);
|
|
@@ -669,10 +773,10 @@ function toNativeValue(value) {
|
|
|
669
773
|
if (isTypeValue(value)) {
|
|
670
774
|
return {
|
|
671
775
|
name: value.typeName,
|
|
672
|
-
signature:
|
|
776
|
+
signature: formatStructure(value.structure),
|
|
673
777
|
};
|
|
674
778
|
}
|
|
675
|
-
if (
|
|
779
|
+
if (isIterator(value)) {
|
|
676
780
|
return { done: value.done };
|
|
677
781
|
}
|
|
678
782
|
// Plain dict
|
|
@@ -683,152 +787,8 @@ function toNativeValue(value) {
|
|
|
683
787
|
}
|
|
684
788
|
return result;
|
|
685
789
|
}
|
|
686
|
-
/**
|
|
687
|
-
|
|
688
|
-
* - Primitives: value equality
|
|
689
|
-
* - Tuples: length + recursive element equality
|
|
690
|
-
* - Dicts: same keys + recursive value equality (order-independent)
|
|
691
|
-
*/
|
|
692
|
-
export function deepEquals(a, b) {
|
|
693
|
-
// Handle primitives and null
|
|
694
|
-
if (a === b)
|
|
695
|
-
return true;
|
|
696
|
-
if (a === null || b === null)
|
|
697
|
-
return false;
|
|
698
|
-
if (typeof a !== typeof b)
|
|
699
|
-
return false;
|
|
700
|
-
// Primitives (string, number, boolean) - covered by === above
|
|
701
|
-
if (typeof a !== 'object' || typeof b !== 'object')
|
|
702
|
-
return false;
|
|
703
|
-
// Both are non-null objects at this point
|
|
704
|
-
const aObj = a;
|
|
705
|
-
const bObj = b;
|
|
706
|
-
// Check for tuples (positional spread args)
|
|
707
|
-
const aIsTuple = isTuple(a);
|
|
708
|
-
const bIsTuple = isTuple(b);
|
|
709
|
-
if (aIsTuple !== bIsTuple)
|
|
710
|
-
return false;
|
|
711
|
-
if (aIsTuple && bIsTuple) {
|
|
712
|
-
if (a.entries.length !== b.entries.length)
|
|
713
|
-
return false;
|
|
714
|
-
for (let i = 0; i < a.entries.length; i++) {
|
|
715
|
-
const aVal = a.entries[i];
|
|
716
|
-
const bVal = b.entries[i];
|
|
717
|
-
if (aVal === undefined || bVal === undefined) {
|
|
718
|
-
if (aVal !== bVal)
|
|
719
|
-
return false;
|
|
720
|
-
}
|
|
721
|
-
else if (!deepEquals(aVal, bVal)) {
|
|
722
|
-
return false;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
return true;
|
|
726
|
-
}
|
|
727
|
-
// Check for ordered (named spread args)
|
|
728
|
-
const aIsOrdered = isOrdered(a);
|
|
729
|
-
const bIsOrdered = isOrdered(b);
|
|
730
|
-
if (aIsOrdered !== bIsOrdered)
|
|
731
|
-
return false;
|
|
732
|
-
if (aIsOrdered && bIsOrdered) {
|
|
733
|
-
if (a.entries.length !== b.entries.length)
|
|
734
|
-
return false;
|
|
735
|
-
for (let i = 0; i < a.entries.length; i++) {
|
|
736
|
-
const aEntry = a.entries[i];
|
|
737
|
-
const bEntry = b.entries[i];
|
|
738
|
-
if (aEntry === undefined || bEntry === undefined)
|
|
739
|
-
return false;
|
|
740
|
-
if (aEntry[0] !== bEntry[0])
|
|
741
|
-
return false;
|
|
742
|
-
if (!deepEquals(aEntry[1], bEntry[1]))
|
|
743
|
-
return false;
|
|
744
|
-
}
|
|
745
|
-
return true;
|
|
746
|
-
}
|
|
747
|
-
// Check for vectors
|
|
748
|
-
const aIsVector = isVector(a);
|
|
749
|
-
const bIsVector = isVector(b);
|
|
750
|
-
if (aIsVector !== bIsVector)
|
|
751
|
-
return false;
|
|
752
|
-
if (aIsVector && bIsVector) {
|
|
753
|
-
// Vectors equal when model matches AND all float elements match
|
|
754
|
-
if (a.model !== b.model)
|
|
755
|
-
return false;
|
|
756
|
-
if (a.data.length !== b.data.length)
|
|
757
|
-
return false;
|
|
758
|
-
for (let i = 0; i < a.data.length; i++) {
|
|
759
|
-
const aVal = a.data[i];
|
|
760
|
-
const bVal = b.data[i];
|
|
761
|
-
if (aVal !== bVal)
|
|
762
|
-
return false;
|
|
763
|
-
}
|
|
764
|
-
return true;
|
|
765
|
-
}
|
|
766
|
-
// Check for type values (first-class type names)
|
|
767
|
-
const aIsTypeValue = isTypeValue(a);
|
|
768
|
-
const bIsTypeValue = isTypeValue(b);
|
|
769
|
-
if (aIsTypeValue !== bIsTypeValue)
|
|
770
|
-
return false;
|
|
771
|
-
if (aIsTypeValue && bIsTypeValue) {
|
|
772
|
-
return structuralTypeEquals(a.structure, b.structure);
|
|
773
|
-
}
|
|
774
|
-
// Check for arrays (lists)
|
|
775
|
-
const aIsArray = Array.isArray(a);
|
|
776
|
-
const bIsArray = Array.isArray(b);
|
|
777
|
-
if (aIsArray !== bIsArray)
|
|
778
|
-
return false;
|
|
779
|
-
if (aIsArray && bIsArray) {
|
|
780
|
-
if (a.length !== b.length)
|
|
781
|
-
return false;
|
|
782
|
-
for (let i = 0; i < a.length; i++) {
|
|
783
|
-
const aElem = a[i];
|
|
784
|
-
const bElem = b[i];
|
|
785
|
-
if (aElem === undefined || bElem === undefined) {
|
|
786
|
-
if (aElem !== bElem)
|
|
787
|
-
return false;
|
|
788
|
-
}
|
|
789
|
-
else if (!deepEquals(aElem, bElem)) {
|
|
790
|
-
return false;
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
return true;
|
|
794
|
-
}
|
|
795
|
-
// Both are dicts (plain objects) or callables
|
|
796
|
-
// For script callables, use structural equality (params + body AST + captured values)
|
|
797
|
-
// For runtime/application callables, use reference equality
|
|
798
|
-
if ('__type' in aObj || '__type' in bObj) {
|
|
799
|
-
// Both must be callables to be equal
|
|
800
|
-
if (!('__type' in aObj) || !('__type' in bObj))
|
|
801
|
-
return false;
|
|
802
|
-
if (aObj.__type !== 'callable' || bObj.__type !== 'callable')
|
|
803
|
-
return false;
|
|
804
|
-
// Script callables: structural equality
|
|
805
|
-
if (isScriptCallable(a) && isScriptCallable(b)) {
|
|
806
|
-
return callableEquals(a, b, deepEquals);
|
|
807
|
-
}
|
|
808
|
-
// Runtime/application callables: reference equality
|
|
809
|
-
return a === b;
|
|
810
|
-
}
|
|
811
|
-
const aDict = a;
|
|
812
|
-
const bDict = b;
|
|
813
|
-
const aKeys = Object.keys(aDict);
|
|
814
|
-
const bKeys = Object.keys(bDict);
|
|
815
|
-
if (aKeys.length !== bKeys.length)
|
|
816
|
-
return false;
|
|
817
|
-
for (const key of aKeys) {
|
|
818
|
-
if (!(key in bDict))
|
|
819
|
-
return false;
|
|
820
|
-
const aVal = aDict[key];
|
|
821
|
-
const bVal = bDict[key];
|
|
822
|
-
if (aVal === undefined || bVal === undefined) {
|
|
823
|
-
if (aVal !== bVal)
|
|
824
|
-
return false;
|
|
825
|
-
}
|
|
826
|
-
else if (!deepEquals(aVal, bVal)) {
|
|
827
|
-
return false;
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
return true;
|
|
831
|
-
}
|
|
790
|
+
/** Deep structural equality for all Rill values. Delegates to type-registrations. */
|
|
791
|
+
export const deepEquals = registryDeepEquals;
|
|
832
792
|
/** Reserved dict method names that cannot be overridden */
|
|
833
793
|
export const RESERVED_DICT_METHODS = ['keys', 'values', 'entries'];
|
|
834
794
|
/**
|
|
@@ -838,23 +798,54 @@ export const RESERVED_DICT_METHODS = ['keys', 'values', 'entries'];
|
|
|
838
798
|
export const anyTypeValue = Object.freeze({
|
|
839
799
|
__rill_type: true,
|
|
840
800
|
typeName: 'any',
|
|
841
|
-
structure: {
|
|
801
|
+
structure: { kind: 'any' },
|
|
842
802
|
});
|
|
843
803
|
/**
|
|
844
|
-
* Convert a
|
|
845
|
-
* Uses the
|
|
804
|
+
* Convert a TypeStructure descriptor to a RillTypeValue.
|
|
805
|
+
* Uses the TypeStructure's `kind` field as the `typeName`.
|
|
846
806
|
* Falls back to 'any' for compound types that lack a direct RillTypeName mapping.
|
|
847
807
|
*/
|
|
848
|
-
export function
|
|
808
|
+
export function structureToTypeValue(type) {
|
|
849
809
|
const validNames = VALID_TYPE_NAMES;
|
|
850
810
|
return Object.freeze({
|
|
851
811
|
__rill_type: true,
|
|
852
|
-
typeName: (validNames.includes(type.
|
|
853
|
-
? type.
|
|
812
|
+
typeName: (validNames.includes(type.kind)
|
|
813
|
+
? type.kind
|
|
854
814
|
: 'any'),
|
|
855
815
|
structure: type,
|
|
856
816
|
});
|
|
857
817
|
}
|
|
818
|
+
/** @deprecated Use structureToTypeValue instead. */
|
|
819
|
+
export const rillTypeToTypeValue = structureToTypeValue;
|
|
820
|
+
/**
|
|
821
|
+
* Check if a type is a collection (dict, ordered, tuple) with defined
|
|
822
|
+
* fields or elements. Used to decide if an empty collection can be
|
|
823
|
+
* synthesized and hydrated.
|
|
824
|
+
*/
|
|
825
|
+
export function hasCollectionFields(type) {
|
|
826
|
+
return ((type.kind === 'dict' &&
|
|
827
|
+
(!!type.fields ||
|
|
828
|
+
!!type.valueType)) ||
|
|
829
|
+
(type.kind === 'ordered' &&
|
|
830
|
+
(!!type.fields ||
|
|
831
|
+
!!type.valueType)) ||
|
|
832
|
+
(type.kind === 'tuple' &&
|
|
833
|
+
(!!type.elements ||
|
|
834
|
+
!!type.valueType)));
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Create an empty collection value matching the given TypeStructure.
|
|
838
|
+
* Assumes the type is dict, ordered, or tuple.
|
|
839
|
+
*/
|
|
840
|
+
export function emptyForType(type) {
|
|
841
|
+
if (type.kind === 'dict')
|
|
842
|
+
return {};
|
|
843
|
+
if (type.kind === 'ordered')
|
|
844
|
+
return createOrdered([]);
|
|
845
|
+
if (type.kind === 'tuple')
|
|
846
|
+
return createTuple([]);
|
|
847
|
+
return {};
|
|
848
|
+
}
|
|
858
849
|
/** Check if a key name is reserved */
|
|
859
850
|
export function isReservedMethod(name) {
|
|
860
851
|
return RESERVED_DICT_METHODS.includes(name);
|
|
@@ -866,7 +857,7 @@ export function isReservedMethod(name) {
|
|
|
866
857
|
* - next: callable - function to get next iterator
|
|
867
858
|
* - value: any (only required when not done) - current element
|
|
868
859
|
*/
|
|
869
|
-
export function
|
|
860
|
+
export function isIterator(value) {
|
|
870
861
|
if (!isDict(value))
|
|
871
862
|
return false;
|
|
872
863
|
const dict = value;
|
|
@@ -879,4 +870,9 @@ export function isRillIterator(value) {
|
|
|
879
870
|
return false;
|
|
880
871
|
return true;
|
|
881
872
|
}
|
|
882
|
-
|
|
873
|
+
/** @deprecated Use isIterator instead. */
|
|
874
|
+
export const isRillIterator = isIterator;
|
|
875
|
+
/** Copy a RillValue. Delegates to type-registrations. */
|
|
876
|
+
export const copyValue = registryCopyValue;
|
|
877
|
+
/** @deprecated Use copyValue instead. */
|
|
878
|
+
export const deepCopyRillValue = copyValue;
|