functionalscript 0.46.1 → 0.47.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/fjs/basen/cbase32/module.f.mjs +18 -10
- package/fjs/basen/cbase32/proof.f.d.mts +3 -0
- package/fjs/basen/cbase32/proof.f.mjs +24 -2
- package/fjs/basen/proof.f.mjs +8 -16
- package/fjs/bnf/ll1/proof.f.d.mts +2 -0
- package/fjs/bnf/ll1/proof.f.mjs +33 -18
- package/fjs/bnf/module.f.d.mts +2 -2
- package/fjs/bnf/module.f.mjs +2 -2
- package/fjs/cas/proof.f.mjs +4 -26
- package/fjs/ci/common/module.f.mjs +6 -0
- package/fjs/ci/config/module.f.d.mts +7 -7
- package/fjs/ci/config/module.f.mjs +7 -7
- package/fjs/cli/module.f.mjs +14 -10
- package/fjs/crypto/vdf/module.f.d.mts +0 -1
- package/fjs/crypto/vdf/module.f.mjs +3 -14
- package/fjs/dev/module.f.mjs +10 -20
- package/fjs/djs/parser/module.f.d.mts +101 -53
- package/fjs/djs/parser/module.f.mjs +732 -525
- package/fjs/djs/parser/proof.f.d.mts +6 -0
- package/fjs/djs/parser/proof.f.mjs +315 -11
- package/fjs/djs/parser/types.d.ts +29 -2
- package/fjs/djs/tokenizer/module.f.mjs +122 -105
- package/fjs/djs/tokenizer/proof.f.mjs +17 -0
- package/fjs/djs/transpiler/module.f.mjs +4 -5
- package/fjs/djs/transpiler/proof.f.d.mts +2 -0
- package/fjs/djs/transpiler/proof.f.mjs +25 -0
- package/fjs/edag/amnesia/module.f.d.mts +20 -0
- package/fjs/edag/amnesia/module.f.mjs +303 -0
- package/fjs/edag/amnesia/proof.f.d.mts +79 -0
- package/fjs/edag/amnesia/proof.f.mjs +573 -0
- package/fjs/edag/amnesia/types.d.ts +15 -0
- package/fjs/edag/module.f.d.mts +410 -0
- package/fjs/edag/module.f.mjs +559 -0
- package/fjs/edag/proof.f.d.mts +116 -0
- package/fjs/edag/proof.f.mjs +686 -0
- package/fjs/edag/types.d.ts +55 -0
- package/fjs/effects/node/memory/module.d.mts +37 -3
- package/fjs/effects/node/memory/module.mjs +38 -4
- package/fjs/effects/node/memory/proof.d.mts +5 -2
- package/fjs/effects/node/memory/proof.mjs +36 -6
- package/fjs/effects/node/module.d.mts +9 -1
- package/fjs/effects/node/module.f.d.mts +39 -0
- package/fjs/effects/node/module.f.mjs +46 -0
- package/fjs/effects/node/module.mjs +234 -32
- package/fjs/effects/node/types.d.ts +28 -2
- package/fjs/effects/node/virtual/module.f.d.mts +19 -10
- package/fjs/effects/node/virtual/module.f.mjs +246 -32
- package/fjs/effects/node/virtual/proof.f.d.mts +17 -2
- package/fjs/effects/node/virtual/proof.f.mjs +232 -8
- package/fjs/effects/node/virtual/types.d.ts +57 -1
- package/fjs/emergent_testing/browser/proof.d.mts +55 -0
- package/fjs/emergent_testing/browser/proof.mjs +352 -0
- package/fjs/emergent_testing/browser/species.proof.d.mts +4 -0
- package/fjs/emergent_testing/browser/species.proof.mjs +45 -0
- package/fjs/emergent_testing/browser.d.mts +79 -0
- package/fjs/emergent_testing/browser.mjs +432 -0
- package/fjs/emergent_testing/proof.f.d.mts +1 -1
- package/fjs/emergent_testing/proof.f.mjs +2 -2
- package/fjs/js/string_escape/module.f.d.mts +47 -0
- package/fjs/js/string_escape/module.f.mjs +95 -0
- package/fjs/js/string_escape/proof.f.d.mts +8 -0
- package/fjs/js/string_escape/proof.f.mjs +68 -0
- package/fjs/js/tokenizer/module.f.mjs +14 -12
- package/fjs/mcp/cas/module.f.d.mts +1 -7
- package/fjs/mcp/cas/module.f.mjs +24 -15
- package/fjs/mcp/evo/module.f.mjs +9 -24
- package/fjs/mcp/module.f.d.mts +3 -1
- package/fjs/mcp/module.f.mjs +4 -2
- package/fjs/media/json/rtti/module.f.d.mts +2 -2
- package/fjs/media/json/rtti/module.f.mjs +1 -1
- package/fjs/media/json/schema/module.f.d.mts +15 -5
- package/fjs/media/json/schema/module.f.mjs +42 -15
- package/fjs/media/json/schema/proof.f.d.mts +10 -1
- package/fjs/media/json/schema/proof.f.mjs +88 -16
- package/fjs/media/json/serializer/module.f.mjs +14 -26
- package/fjs/media/json/types.d.ts +1 -1
- package/fjs/media/lock/module.f.d.mts +8 -3
- package/fjs/media/lock/module.f.mjs +9 -3
- package/fjs/media/lock/proof.f.d.mts +1 -1
- package/fjs/media/lock/proof.f.mjs +2 -2
- package/fjs/media/lock/types.d.ts +1 -3
- package/fjs/media/module.f.d.mts +20 -13
- package/fjs/media/module.f.mjs +29 -16
- package/fjs/media/note/module.f.d.mts +14 -7
- package/fjs/media/note/module.f.mjs +14 -7
- package/fjs/media/note/proof.f.mjs +1 -1
- package/fjs/media/proof.f.mjs +5 -5
- package/fjs/media/revision/module.f.d.mts +8 -2
- package/fjs/media/revision/module.f.mjs +9 -3
- package/fjs/media/revision/proof.f.mjs +1 -1
- package/fjs/media/revision/types.d.ts +1 -1
- package/fjs/media/type/module.f.d.mts +20 -0
- package/fjs/media/type/module.f.mjs +74 -0
- package/fjs/media/type/proof.f.d.mts +5 -0
- package/fjs/media/type/proof.f.mjs +24 -1
- package/fjs/module.f.mjs +6 -0
- package/fjs/path/module.f.d.mts +60 -3
- package/fjs/path/module.f.mjs +138 -14
- package/fjs/path/proof.f.d.mts +3 -0
- package/fjs/path/proof.f.mjs +298 -2
- package/fjs/protocol/json_rpc/module.f.d.mts +65 -28
- package/fjs/protocol/json_rpc/module.f.mjs +49 -13
- package/fjs/protocol/json_rpc/proof.f.d.mts +4 -0
- package/fjs/protocol/json_rpc/proof.f.mjs +29 -0
- package/fjs/protocol/json_rpc/types.d.ts +12 -2
- package/fjs/protocol/mcp/module.f.d.mts +89 -54
- package/fjs/protocol/mcp/module.f.mjs +110 -56
- package/fjs/protocol/mcp/proof.f.d.mts +13 -0
- package/fjs/protocol/mcp/proof.f.mjs +107 -7
- package/fjs/protocol/mcp/stdio/module.f.mjs +3 -4
- package/fjs/protocol/mcp/stdio/proof.f.mjs +2 -2
- package/fjs/protocol/mcp/types.d.ts +14 -2
- package/fjs/text/utf8/module.f.mjs +27 -1
- package/fjs/text/utf8/proof.f.mjs +37 -0
- package/fjs/types/array/module.f.mjs +18 -11
- package/fjs/types/array/proof.f.mjs +27 -1
- package/fjs/types/bigfloat/module.f.d.mts +48 -9
- package/fjs/types/bigfloat/module.f.mjs +147 -59
- package/fjs/types/bigfloat/proof.f.d.mts +2 -0
- package/fjs/types/bigfloat/proof.f.mjs +136 -4
- package/fjs/types/bigfloat/types.d.ts +17 -0
- package/fjs/types/function/module.f.d.mts +11 -2
- package/fjs/types/function/module.f.mjs +20 -2
- package/fjs/types/function/proof.f.mjs +5 -1
- package/fjs/types/phantom/types.d.ts +21 -0
- package/fjs/types/result/module.f.d.mts +4 -4
- package/fjs/types/result/module.f.mjs +2 -2
- package/fjs/types/result/proof.f.d.mts +6 -2
- package/fjs/types/result/proof.f.mjs +17 -1
- package/fjs/types/rtti/common/module.f.d.mts +78 -5
- package/fjs/types/rtti/common/module.f.mjs +168 -3
- package/fjs/types/rtti/common/proof.f.d.mts +14 -0
- package/fjs/types/rtti/common/proof.f.mjs +61 -2
- package/fjs/types/rtti/common/types.d.ts +23 -1
- package/fjs/types/rtti/data/module.f.d.mts +57 -1
- package/fjs/types/rtti/data/module.f.mjs +241 -47
- package/fjs/types/rtti/data/proof.f.d.mts +10 -0
- package/fjs/types/rtti/data/proof.f.mjs +272 -39
- package/fjs/types/rtti/data/types.d.ts +14 -7
- package/fjs/types/rtti/host.proof.d.mts +33 -0
- package/fjs/types/rtti/host.proof.mjs +170 -0
- package/fjs/types/rtti/module.f.d.mts +51 -6
- package/fjs/types/rtti/module.f.mjs +50 -3
- package/fjs/types/rtti/parse/module.f.d.mts +31 -24
- package/fjs/types/rtti/parse/module.f.mjs +197 -59
- package/fjs/types/rtti/parse/proof.f.d.mts +37 -2
- package/fjs/types/rtti/parse/proof.f.mjs +171 -47
- package/fjs/types/rtti/proof.f.d.mts +6 -0
- package/fjs/types/rtti/proof.f.mjs +42 -1
- package/fjs/types/rtti/ts/module.f.d.mts +9 -3
- package/fjs/types/rtti/ts/module.f.mjs +68 -15
- package/fjs/types/rtti/ts/proof.f.d.mts +42 -2
- package/fjs/types/rtti/ts/proof.f.mjs +139 -6
- package/fjs/types/rtti/ts/types.d.ts +164 -16
- package/fjs/types/rtti/types.d.ts +31 -3
- package/fjs/types/rtti/validate/module.f.d.mts +42 -25
- package/fjs/types/rtti/validate/module.f.mjs +187 -53
- package/fjs/types/rtti/validate/proof.f.d.mts +28 -2
- package/fjs/types/rtti/validate/proof.f.mjs +430 -75
- package/fjs/types/ts/types.d.ts +4 -0
- package/fjs/web/module.f.d.mts +115 -0
- package/fjs/web/module.f.mjs +581 -0
- package/fjs/web/proof.f.d.mts +39 -0
- package/fjs/web/proof.f.mjs +413 -0
- package/fjs/web/types.d.ts +39 -0
- package/fjs/website/browser-prepare.d.mts +5 -0
- package/fjs/website/browser-prepare.mjs +115 -0
- package/fjs/website/browser-source.d.mts +37 -0
- package/fjs/website/browser-source.mjs +202 -0
- package/fjs/website/browser-source.proof.d.mts +31 -0
- package/fjs/website/browser-source.proof.mjs +162 -0
- package/fjs/website/browser.d.mts +5 -0
- package/fjs/website/browser.mjs +8 -0
- package/fjs/website/module.f.d.mts +3 -3
- package/fjs/website/module.f.mjs +62 -9
- package/fjs/website/proof.f.d.mts +4 -0
- package/fjs/website/proof.f.mjs +18 -3
- package/package.json +2 -3
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A tree-walking evaluator for `exp`: `vm(context)(e)` returns a primitive
|
|
3
|
+
* unchanged and dispatches a tagged tuple through one handler per tag.
|
|
4
|
+
*
|
|
5
|
+
* It remembers no node values — every incoming edge evaluates its target
|
|
6
|
+
* again — so it is the Amnesia model of
|
|
7
|
+
* [execution-models.md](../execution-models.md), for proving semantics and
|
|
8
|
+
* not for running FunctionalScript. See [README.md](./README.md).
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*
|
|
12
|
+
* @import { Exp, Op1, Properties } from '../types.ts'
|
|
13
|
+
* @import { OptionLambda, OptionPropertyLambda, PropertyLambda } from '../types.ts'
|
|
14
|
+
* @import { Context, Map, ExpOp, TagMap } from './types.ts'
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { assert } from '../../asserts/module.f.mjs'
|
|
18
|
+
|
|
19
|
+
const o2lazy =
|
|
20
|
+
(/**@type {(a: any, b: () => any) => unknown}*/o) =>
|
|
21
|
+
/**
|
|
22
|
+
* @template {Exp} A
|
|
23
|
+
* @param {Context} c
|
|
24
|
+
* @param {readonly[unknown, A, A]} _2
|
|
25
|
+
*/
|
|
26
|
+
(c, [, a, b]) => {
|
|
27
|
+
const f = vm(c)
|
|
28
|
+
return o(f(a), () => f(b))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Eager pair: `o2lazy` already *is* the handler, so this forces `b` and
|
|
33
|
+
* hands it on — it must not wrap another `(c, e) =>` around it, or every
|
|
34
|
+
* binary op would evaluate to that inner handler instead of to a value.
|
|
35
|
+
*/
|
|
36
|
+
const o2 =
|
|
37
|
+
(/**@type {(a: any, b: any) => unknown}*/o) =>
|
|
38
|
+
o2lazy((a, b) => o(a, b()))
|
|
39
|
+
|
|
40
|
+
/** @typedef {(c: Context, e: Op1) => unknown} _Func1 */
|
|
41
|
+
|
|
42
|
+
const o1 =
|
|
43
|
+
(/**@type {(a: any) => unknown}*/o) =>
|
|
44
|
+
/**@type {_Func1}*/
|
|
45
|
+
(c, [, a]) => o(vm(c)(a))
|
|
46
|
+
|
|
47
|
+
/** @typedef {(_: Exp) => unknown} _Eval */
|
|
48
|
+
|
|
49
|
+
/** Both ways of being nullish, which is what every optional step guards. */
|
|
50
|
+
/** @type {(v: unknown) => boolean} */
|
|
51
|
+
const nullish = v => v === undefined || v === null
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The argument array of a call: one node evaluating to the *complete* array,
|
|
55
|
+
* spread at the call site — `f(a, b)` is `['()', f, ['[]', [a, b]]]`, and `=>`
|
|
56
|
+
* collects with `(...args)`. Passed as a single argument instead, the callee's
|
|
57
|
+
* `['args']` would be `[[a, b]]`.
|
|
58
|
+
*
|
|
59
|
+
* @type {(f: _Eval, e: Exp) => readonly any[]}
|
|
60
|
+
*/
|
|
61
|
+
const argsOf = (f, e) => /**@type {any}*/(f(e))
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Calls a bare value — no receiver.
|
|
65
|
+
*
|
|
66
|
+
* The temporary is load-bearing. Written as a property of whatever holds the
|
|
67
|
+
* callee, this would be a *method* call, so the callee would run with that
|
|
68
|
+
* holder as `this` — a receiver the chain does not have — and a detached host
|
|
69
|
+
* method would then silently succeed on the wrapper instead of throwing:
|
|
70
|
+
* `((a.at)(0))(0)` returned `Array.prototype.at`.
|
|
71
|
+
*
|
|
72
|
+
* @type {(f: _Eval, v: unknown, e: Exp) => unknown}
|
|
73
|
+
*/
|
|
74
|
+
const callValue = (f, v, e) => /**@type {any}*/(v)(...argsOf(f, e))
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Calls `obj[prop]` *on* `obj`. That receiver is the whole reason a property
|
|
78
|
+
* access owns its call rather than handing on a value: `[42].at(0)` is `42`
|
|
79
|
+
* only because `at` is called on the array.
|
|
80
|
+
*
|
|
81
|
+
* **The argument node, not the argument array.** Both helpers take the operand
|
|
82
|
+
* and evaluate it themselves, inside the call expression, so that JavaScript's
|
|
83
|
+
* own order applies: the callee is read first, then the arguments. That order
|
|
84
|
+
* is observable — `a.b(...c)` throws at the access when `a` is nullish with
|
|
85
|
+
* `c` untouched, where `(a?.b)(...c)` reaches the call and so evaluates `c`
|
|
86
|
+
* before throwing — and it is *not* provable here: both readings throw, this
|
|
87
|
+
* language has no mutation for an operand to record itself with, and a `throw`
|
|
88
|
+
* case is pass/fail rather than payload-inspecting (`fjs/AGENTS.md` §1.5). So
|
|
89
|
+
* the structure carries what a proof cannot. Taking an evaluated array instead
|
|
90
|
+
* would put every argument list ahead of the property read, and every test
|
|
91
|
+
* here would still pass.
|
|
92
|
+
*
|
|
93
|
+
* @type {(f: _Eval, obj: any, prop: any, e: Exp) => unknown}
|
|
94
|
+
*/
|
|
95
|
+
const callProperty = (f, obj, prop, e) => obj[prop](...argsOf(f, e))
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The short-circuit. A region whose guard failed produces `undefined` and
|
|
99
|
+
* skips every step of its continuation, operands and all — except `|!()`,
|
|
100
|
+
* which the parentheses put *outside* the region. That step runs anyway, on
|
|
101
|
+
* the `undefined` the region produced, which is why `(u?.b)(...c)` throws
|
|
102
|
+
* where `u?.b(...c)` is `undefined`; its arguments are evaluated first, as a
|
|
103
|
+
* call's always are, and then `undefined` is called.
|
|
104
|
+
*
|
|
105
|
+
* The walk carries no state and does not care which lambda type it is in:
|
|
106
|
+
* every step is `[tag, operand, continuation]`, and a `|!()` is reachable
|
|
107
|
+
* through `|.` steps from either — `(a?.(...b).c)(...d)` is exactly that.
|
|
108
|
+
*
|
|
109
|
+
* @type {(f: _Eval, k: OptionLambda | OptionPropertyLambda) => unknown}
|
|
110
|
+
*/
|
|
111
|
+
const skip = (f, k) => {
|
|
112
|
+
if (k === null) { return undefined }
|
|
113
|
+
return k[0] === '|!()'
|
|
114
|
+
? callValue(f, undefined, k[1])
|
|
115
|
+
: skip(f, k[2])
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* A plain value inside an open region — what `?.()` hands on, and what a call
|
|
120
|
+
* step leaves. Nothing here can short-circuit: the two productions are a call
|
|
121
|
+
* that stays in the region and a property access that hands on a receiver.
|
|
122
|
+
*
|
|
123
|
+
* @type {(f: _Eval, v: unknown, k: OptionLambda) => unknown}
|
|
124
|
+
*/
|
|
125
|
+
const optionLambda = (f, v, k) => {
|
|
126
|
+
if (k === null) { return v }
|
|
127
|
+
const [o, e, cont] = k
|
|
128
|
+
switch (o) {
|
|
129
|
+
case '|()': return optionLambda(f, callValue(f, v, e), cont)
|
|
130
|
+
case '|.': return optionPropertyLambda(f, v, f(e), cont)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* A property access inside an open region — both bits live, so this is the
|
|
136
|
+
* state every step is available in. `|()` inherits the region's guard,
|
|
137
|
+
* `|?.()` adds its own, `|!()` escapes it, and `|.` hands the receiver on
|
|
138
|
+
* within the region, which is the one production that exists to protect a bit
|
|
139
|
+
* other than the one it consumes.
|
|
140
|
+
*
|
|
141
|
+
* `obj[prop]` is read once per step, twice only where the guard has to see
|
|
142
|
+
* the value before the call is made.
|
|
143
|
+
*
|
|
144
|
+
* @type {(f: _Eval, obj: any, prop: any, k: OptionPropertyLambda) => unknown}
|
|
145
|
+
*/
|
|
146
|
+
const optionPropertyLambda = (f, obj, prop, k) => {
|
|
147
|
+
if (k === null) { return obj[prop] }
|
|
148
|
+
const [o, e, cont] = k
|
|
149
|
+
switch (o) {
|
|
150
|
+
case '|.': return optionPropertyLambda(f, obj[prop], f(e), cont)
|
|
151
|
+
case '|()': return optionLambda(f, callProperty(f, obj, prop, e), cont)
|
|
152
|
+
case '|!()': return callProperty(f, obj, prop, e)
|
|
153
|
+
case '|?.()': return nullish(obj[prop])
|
|
154
|
+
? skip(f, cont)
|
|
155
|
+
: optionLambda(f, callProperty(f, obj, prop, e), cont)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* A property access with **no** region around it — the continuation of a `.`
|
|
161
|
+
* node. Only a call can be here, because only a call uses the receiver: `|()`
|
|
162
|
+
* spends it and exits, `|?.()` spends it and opens a region that owns the
|
|
163
|
+
* rest of the chain. With no region open, that guard failing is simply the
|
|
164
|
+
* node's value, since `optionLambda` has no `|!()` of its own — but the walk
|
|
165
|
+
* still goes through `skip`, which reaches one through a `|.`.
|
|
166
|
+
*
|
|
167
|
+
* @type {(f: _Eval, obj: any, prop: any, k: PropertyLambda) => unknown}
|
|
168
|
+
*/
|
|
169
|
+
const propertyLambda = (f, obj, prop, k) => {
|
|
170
|
+
if (k === null) { return obj[prop] }
|
|
171
|
+
const [o, e, cont] = k
|
|
172
|
+
switch (o) {
|
|
173
|
+
case '|()': return callProperty(f, obj, prop, e)
|
|
174
|
+
case '|?.()': return nullish(obj[prop])
|
|
175
|
+
? skip(f, cont)
|
|
176
|
+
: optionLambda(f, callProperty(f, obj, prop, e), cont)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**@type {Map}*/
|
|
181
|
+
const map = {
|
|
182
|
+
'!': o1(a => !a),
|
|
183
|
+
'!==': o2((a, b) => a !== b),
|
|
184
|
+
'%': o2((a, b) => a % b),
|
|
185
|
+
'&': o2((a, b) => a & b),
|
|
186
|
+
'&&': o2lazy((a, b) => a && b()),
|
|
187
|
+
// The call with no receiver and no region: the callee is an ordinary
|
|
188
|
+
// expression, so `(0, a.b)(...c)` is this node over a complete `.` while
|
|
189
|
+
// `a.b(...c)` is that `.` node owning its call. The two differ, and
|
|
190
|
+
// `throw.detachedReceiver` is the difference.
|
|
191
|
+
'()': (x, [, a, b]) => {
|
|
192
|
+
const i = vm(x)
|
|
193
|
+
return callValue(i, i(a), b)
|
|
194
|
+
},
|
|
195
|
+
'*': o2((a, b) => a * b),
|
|
196
|
+
'**': o2((a, b) => a ** b),
|
|
197
|
+
'+': o2((a, b) => a + b),
|
|
198
|
+
',': (x, [, a]) => {
|
|
199
|
+
const f = vm(x)
|
|
200
|
+
return a.reduce((/**@type {unknown}*/_, c) => f(c), undefined)
|
|
201
|
+
},
|
|
202
|
+
'-': o2((a, b) => a - b),
|
|
203
|
+
// Property access, owning whatever its receiver is used for: a `null`
|
|
204
|
+
// continuation drops it, as reading `a.b` for its value does, and the two
|
|
205
|
+
// call steps are the only things that can spend it.
|
|
206
|
+
'.': (x, [, a, k, p]) => {
|
|
207
|
+
const i = vm(x)
|
|
208
|
+
return propertyLambda(i, i(a), i(k), p)
|
|
209
|
+
},
|
|
210
|
+
'/': o2((a, b) => a / b),
|
|
211
|
+
'<': o2((a, b) => a < b),
|
|
212
|
+
'<<': o2((a, b) => a << b),
|
|
213
|
+
'<=': o2((a, b) => a <= b),
|
|
214
|
+
'===': o2((a, b) => a === b),
|
|
215
|
+
'=>': (x, [, frameExp, body]) => {
|
|
216
|
+
const frame = vm(x)(frameExp)
|
|
217
|
+
/**@type {(...arg: readonly unknown[]) => unknown}*/
|
|
218
|
+
return (...args) =>vm({ frame, args })(body)
|
|
219
|
+
},
|
|
220
|
+
'>': o2((a, b) => a > b),
|
|
221
|
+
'>=': o2((a, b) => a >= b),
|
|
222
|
+
'>>': o2((a, b) => a >> b),
|
|
223
|
+
'>>>': o2((a, b) => a >>> b),
|
|
224
|
+
// Optional property access, owning the rest of its optional region. On a
|
|
225
|
+
// nullish input the region short-circuits: the node's own `index` is not
|
|
226
|
+
// evaluated — which is why `a?.[k]` does not evaluate `k` — and neither is
|
|
227
|
+
// any step of the continuation, `|!()` excepted. `skip` is what carries
|
|
228
|
+
// that exception, and it is why `u?.b` and `(u?.b)(d)` part company:
|
|
229
|
+
// the first is `undefined`, the second calls it.
|
|
230
|
+
'?.': (x, [, a, k, p]) => {
|
|
231
|
+
const i = vm(x)
|
|
232
|
+
const obj = i(a)
|
|
233
|
+
return nullish(obj) ? skip(i, p) : optionPropertyLambda(i, obj, i(k), p)
|
|
234
|
+
},
|
|
235
|
+
// Optional call, the region-opening counterpart of `?.`. The callee is an
|
|
236
|
+
// ordinary expression, so this node never carries a receiver — `a.b?.(c)`
|
|
237
|
+
// is a `.` node with a `|?.()` continuation, not this one — and a nullish
|
|
238
|
+
// callee leaves the arguments unevaluated.
|
|
239
|
+
'?.()': (x, [, a, b, k]) => {
|
|
240
|
+
const i = vm(x)
|
|
241
|
+
const f = i(a)
|
|
242
|
+
return nullish(f) ? skip(i, k) : optionLambda(i, callValue(i, f, b), k)
|
|
243
|
+
},
|
|
244
|
+
'??': o2lazy((a, b) => a ?? b()),
|
|
245
|
+
Number: o1(Number),
|
|
246
|
+
String: o1(String),
|
|
247
|
+
'[]': (x, [, a]) => {
|
|
248
|
+
const f = vm(x)
|
|
249
|
+
// A spread operand is iterated, not spliced as one element: `[...'ab']`
|
|
250
|
+
// is `['a', 'b']` and `[...1]` throws, per "array spread" in
|
|
251
|
+
// `../README.md`. `flatMap` alone flattens only real arrays.
|
|
252
|
+
/**@type {(e: Exp) => readonly unknown[]}*/
|
|
253
|
+
const spread = e => [.../**@type {any}*/(f(e))]
|
|
254
|
+
return a.flatMap(e =>
|
|
255
|
+
(e instanceof Array) && e[0] === '...' ? spread(e[1]) : [f(e)])
|
|
256
|
+
},
|
|
257
|
+
'^': o2((a, b) => a ^ b),
|
|
258
|
+
args: ({args}) => args,
|
|
259
|
+
frame: ({frame}) => frame,
|
|
260
|
+
neg: o1(a => -a),
|
|
261
|
+
// The key must *evaluate* to a string — a runtime constraint the
|
|
262
|
+
// shape-only schema cannot express, so the executor upholds it. Without
|
|
263
|
+
// the check JS `ToPropertyKey` would coerce, and `['own', o, 1]` would
|
|
264
|
+
// silently read `o['1']`.
|
|
265
|
+
own: o2((a, b) => {
|
|
266
|
+
assert(typeof b === 'string', ['own: key is not a string', b])
|
|
267
|
+
return Object.getOwnPropertyDescriptor(a, b)?.value
|
|
268
|
+
}),
|
|
269
|
+
undefined: () => undefined,
|
|
270
|
+
'{}': (x, [, a]) => {
|
|
271
|
+
const f = vm(x)
|
|
272
|
+
/**@type {(e: Properties) => readonly[unknown, unknown][]}*/
|
|
273
|
+
const g = e => e[0] === ':'
|
|
274
|
+
? [[f(e[1]), f(e[2])]]
|
|
275
|
+
// `Object(...)` is what makes a nullish operand contribute nothing
|
|
276
|
+
// (`{...null}` is `{}`) while a string still contributes its
|
|
277
|
+
// indices — `Object.entries` alone throws on `null`/`undefined`.
|
|
278
|
+
: Object.entries(Object(f(e[1])))
|
|
279
|
+
const kv = a.flatMap(g)
|
|
280
|
+
return Object.fromEntries(kv)
|
|
281
|
+
},
|
|
282
|
+
'|': o2((a, b) => a | b),
|
|
283
|
+
'||': o2lazy((a, b) => a || b()),
|
|
284
|
+
'~': o1(a => ~a),
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export const vm = (/**@type {Context}*/context) => {
|
|
288
|
+
const compute =
|
|
289
|
+
/**
|
|
290
|
+
* Generic over the tag, not `(e: ExpOp) =>`: with a union-typed `e`,
|
|
291
|
+
* `map[e[0]]` is a union of every handler signature, callable only with
|
|
292
|
+
* the intersection of their tuples — `never`. Through `K` it stays the
|
|
293
|
+
* one signature `(c, r: TagMap[K])` — see `TagMap` in `./types.ts`.
|
|
294
|
+
*
|
|
295
|
+
* @type {<K extends ExpOp[0]>(
|
|
296
|
+
* e: TagMap[K] & readonly [K, ...readonly unknown[]]
|
|
297
|
+
* ) => unknown}
|
|
298
|
+
*/
|
|
299
|
+
e => map[e[0]](context, e)
|
|
300
|
+
return (/**@type{Exp}*/e) => e instanceof Array
|
|
301
|
+
? compute(e)
|
|
302
|
+
: e
|
|
303
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution semantics of `vm` — one section per operand shape, since that is
|
|
3
|
+
* how `map`'s handlers are built (`o1`/`o2`/`o2lazy`), plus the nodes that
|
|
4
|
+
* evaluate their operands themselves (`,`, `[]`, `{}`, and the four chain
|
|
5
|
+
* nodes — `()`, `.`, `?.`, and `?.()`). Nothing is `todo` any more.
|
|
6
|
+
* This is the executing counterpart of `../proof.f.mjs`, which pins what the
|
|
7
|
+
* schema *accepts*; nothing here validates.
|
|
8
|
+
*
|
|
9
|
+
* @import { Exp } from '../types.ts'
|
|
10
|
+
* @import { Context } from './types.ts'
|
|
11
|
+
* @import { Assert } from '../../asserts/types.ts'
|
|
12
|
+
* @import { Equal } from '../../types/ts/types.ts'
|
|
13
|
+
* @import { Array as ExpArray, Call, Dot, Index, Op1, Op2 } from '../types.ts'
|
|
14
|
+
* @import { Get } from './types.ts'
|
|
15
|
+
*/
|
|
16
|
+
import type { Assert } from '../../asserts/types.ts';
|
|
17
|
+
import type { Equal } from '../../types/ts/types.ts';
|
|
18
|
+
import type { Array as ExpArray, Call, Dot, Op1, Op2 } from '../types.ts';
|
|
19
|
+
import type { Get } from './types.ts';
|
|
20
|
+
export type _PlusIsOp2 = Assert<Equal<Get<'+'>, Op2>>;
|
|
21
|
+
export type _NegIsOp1 = Assert<Equal<Get<'neg'>, Op1>>;
|
|
22
|
+
export type _BracketsIsArray = Assert<Equal<Get<'[]'>, ExpArray>>;
|
|
23
|
+
export type _CallIsCall = Assert<Equal<Get<'()'>, Call>>;
|
|
24
|
+
export type _DotIsDot = Assert<Equal<Get<'.'>, Dot>>;
|
|
25
|
+
export declare const proof: {
|
|
26
|
+
primitive: () => void;
|
|
27
|
+
op0: () => void;
|
|
28
|
+
op1: () => void;
|
|
29
|
+
op2: () => void;
|
|
30
|
+
property: () => void;
|
|
31
|
+
lazy: () => void;
|
|
32
|
+
comma: () => void;
|
|
33
|
+
array: () => void;
|
|
34
|
+
object: () => void;
|
|
35
|
+
nested: () => void;
|
|
36
|
+
lambda: () => void;
|
|
37
|
+
call: () => void;
|
|
38
|
+
chain: {
|
|
39
|
+
callStep: () => void;
|
|
40
|
+
optionCallStep: () => void;
|
|
41
|
+
optionCallStepSkips: () => void;
|
|
42
|
+
receiver: () => void;
|
|
43
|
+
throw: {
|
|
44
|
+
detachedReceiver: () => unknown;
|
|
45
|
+
detachedReceiverAfterCall: () => unknown;
|
|
46
|
+
callStepOnNonFunction: () => unknown;
|
|
47
|
+
propertyOnUndefined: () => unknown;
|
|
48
|
+
propertyOnNull: () => unknown;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
optionDot: () => void;
|
|
52
|
+
optionCall: () => void;
|
|
53
|
+
optionRegion: {
|
|
54
|
+
skips: () => void;
|
|
55
|
+
throw: {
|
|
56
|
+
closeStepOnUndefined: () => unknown;
|
|
57
|
+
closeStepOnNull: () => unknown;
|
|
58
|
+
closeStepPastSkippedProperty: () => unknown;
|
|
59
|
+
closeStepAfterOptionCall: () => unknown;
|
|
60
|
+
closeStepAfterPropertyGuard: () => unknown;
|
|
61
|
+
callOfSkippedGuard: () => unknown;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
closure: () => void;
|
|
65
|
+
higherOrder: () => void;
|
|
66
|
+
throw: {
|
|
67
|
+
evaluatedIndex: () => unknown;
|
|
68
|
+
evaluatedArgument: () => unknown;
|
|
69
|
+
optionCallOnNonFunction: () => unknown;
|
|
70
|
+
arraySpreadOfNumber: () => unknown;
|
|
71
|
+
arraySpreadOfNull: () => unknown;
|
|
72
|
+
ownNonStringKey: () => unknown;
|
|
73
|
+
callNonFunction: () => unknown;
|
|
74
|
+
forcedAnd: () => unknown;
|
|
75
|
+
forcedOr: () => unknown;
|
|
76
|
+
forcedCoalesce: () => unknown;
|
|
77
|
+
forcedEager: () => unknown;
|
|
78
|
+
};
|
|
79
|
+
};
|