jz 0.4.0 → 0.5.1
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 +275 -255
- package/cli.js +8 -51
- package/index.js +166 -31
- package/{src/host.js → interop.js} +291 -88
- package/module/array.js +181 -71
- package/module/collection.js +222 -8
- package/module/console.js +1 -1
- package/module/core.js +277 -118
- package/module/date.js +9 -5
- package/module/function.js +11 -1
- package/module/json.js +361 -55
- package/module/math.js +601 -130
- package/module/number.js +390 -227
- package/module/object.js +252 -34
- package/module/regex.js +123 -16
- package/module/schema.js +15 -1
- package/module/string.js +545 -96
- package/module/timer.js +1 -1
- package/module/typedarray.js +674 -57
- package/package.json +10 -7
- package/src/abi/array.js +89 -0
- package/src/abi/index.js +35 -0
- package/src/abi/number.js +137 -0
- package/src/abi/object.js +57 -0
- package/src/abi/string.js +529 -0
- package/src/analyze.js +1870 -485
- package/src/assemble.js +27 -12
- package/src/autoload.js +13 -1
- package/src/compile.js +446 -179
- package/src/ctx.js +85 -18
- package/src/emit.js +705 -196
- package/src/infer.js +548 -0
- package/src/ir.js +291 -23
- package/src/jzify.js +851 -132
- package/src/narrow.js +433 -251
- package/src/optimize.js +126 -122
- package/src/plan.js +703 -34
- package/src/prepare.js +833 -153
- package/src/resolve.js +85 -0
- package/src/vectorize.js +99 -18
- package/src/ast.js +0 -160
- package/src/auto-config.js +0 -120
- package/src/fuse.js +0 -159
package/src/fuse.js
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AST-level fusion passes.
|
|
3
|
-
*
|
|
4
|
-
* Unlike src/optimize.js (which is a pure WAT IR→IR rewrite, post-emission),
|
|
5
|
-
* these rewrites need the *raw, pre-resolution* AST shape — bindings still named,
|
|
6
|
-
* arrow bodies still inline — so they run inside prepare(), before scope
|
|
7
|
-
* resolution and emit. They mutate the AST in place and always fire (cheap; the
|
|
8
|
-
* shape guards are strict enough that misfires are impossible).
|
|
9
|
-
*
|
|
10
|
-
* @module fuse
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/** Sparse-read .map fusion: rewrite `const b = a.map(arrow); for(...; j<b.length; ...) USE(b[j])`
|
|
14
|
-
* into a fused for-loop that inlines `arrow(a[j])` at the read site, eliminating the materialized
|
|
15
|
-
* intermediate array. Only fires on shapes where every use of `b` is a numeric `b[idx]` read or a
|
|
16
|
-
* `b.length` read, the arrow is pure with a single named param, and `b` is not referenced after the
|
|
17
|
-
* consumer for-loop. Preserves observable behavior because the arrow's pure-expression body has no
|
|
18
|
-
* order-dependent effects. */
|
|
19
|
-
export function fuseSparseMapReads(root) {
|
|
20
|
-
walkSparse(root)
|
|
21
|
-
}
|
|
22
|
-
function walkSparse(node) {
|
|
23
|
-
if (!Array.isArray(node)) return
|
|
24
|
-
for (let i = 1; i < node.length; i++) walkSparse(node[i])
|
|
25
|
-
if (node[0] === ';') tryFuseInBlock(node)
|
|
26
|
-
}
|
|
27
|
-
function tryFuseInBlock(seq) {
|
|
28
|
-
for (let i = 1; i < seq.length - 1; i++) {
|
|
29
|
-
const fused = tryFusePair(seq[i], seq[i + 1], seq, i)
|
|
30
|
-
if (fused) {
|
|
31
|
-
seq.splice(i, 2, ...fused)
|
|
32
|
-
i-- // re-examine same position (chained fusions)
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
function tryFusePair(decl, forNode, seq, declIdx) {
|
|
37
|
-
if (!Array.isArray(decl) || (decl[0] !== 'const' && decl[0] !== 'let')) return null
|
|
38
|
-
if (decl.length !== 2) return null // single binding only
|
|
39
|
-
const bind = decl[1]
|
|
40
|
-
if (!Array.isArray(bind) || bind[0] !== '=' || typeof bind[1] !== 'string') return null
|
|
41
|
-
const NAME = bind[1], rhs = bind[2]
|
|
42
|
-
if (!Array.isArray(rhs) || rhs[0] !== '()') return null
|
|
43
|
-
const callee = rhs[1]
|
|
44
|
-
if (!Array.isArray(callee) || callee[0] !== '.' || callee[2] !== 'map') return null
|
|
45
|
-
const RECV = callee[1]
|
|
46
|
-
if (typeof RECV !== 'string' || RECV === NAME) return null
|
|
47
|
-
const arrow = rhs[2]
|
|
48
|
-
if (!Array.isArray(arrow) || arrow[0] !== '=>') return null
|
|
49
|
-
// Single-name param only: `x => …` or `(x) => …`
|
|
50
|
-
const ap = arrow[1]
|
|
51
|
-
const PARAM = typeof ap === 'string' ? ap :
|
|
52
|
-
(Array.isArray(ap) && ap[0] === '()' && typeof ap[1] === 'string' ? ap[1] : null)
|
|
53
|
-
if (!PARAM || PARAM === NAME || PARAM === RECV) return null
|
|
54
|
-
// Body: single-expression arrow only (block bodies skipped — could extend later).
|
|
55
|
-
const aBody = arrow[2]
|
|
56
|
-
if (Array.isArray(aBody) && aBody[0] === '{}') return null
|
|
57
|
-
if (!isPureSparseArrowBody(aBody, PARAM)) return null
|
|
58
|
-
// For-loop: ['for', [';', initStmt, cond, inc], body]
|
|
59
|
-
if (!Array.isArray(forNode) || forNode[0] !== 'for' || forNode.length !== 3) return null
|
|
60
|
-
const head = forNode[1]
|
|
61
|
-
if (!Array.isArray(head) || head[0] !== ';' || head.length !== 4) return null
|
|
62
|
-
const cond = head[2], forBody = forNode[2]
|
|
63
|
-
// Verify `NAME` is used only as `NAME[idx]` or `NAME.length` inside cond+forBody.
|
|
64
|
-
if (!hasOnlySparseUses(cond, NAME)) return null
|
|
65
|
-
if (!hasOnlySparseUses(forBody, NAME)) return null
|
|
66
|
-
if (!hasAnyIndexedRead(forBody, NAME) && !hasAnyIndexedRead(cond, NAME)) return null
|
|
67
|
-
// `NAME` must not be read after the for-loop in the same block.
|
|
68
|
-
for (let k = declIdx + 2; k < seq.length; k++) {
|
|
69
|
-
if (refsName(seq[k], NAME)) return null
|
|
70
|
-
}
|
|
71
|
-
// RECV must not be reassigned inside the for-loop (would invalidate substitution).
|
|
72
|
-
if (assignsName(forNode, RECV) || assignsName(forNode, NAME)) return null
|
|
73
|
-
// PARAM must not collide with any binding inside forBody (otherwise substitution shadows wrongly).
|
|
74
|
-
if (bindsName(forNode, PARAM)) return null
|
|
75
|
-
// Apply substitution: NAME.length → RECV.length; NAME[idx] → arrowBody[PARAM ← RECV[idx]].
|
|
76
|
-
const newCond = substSparse(cond, NAME, RECV, PARAM, aBody)
|
|
77
|
-
const newBody = substSparse(forBody, NAME, RECV, PARAM, aBody)
|
|
78
|
-
const newHead = [';', head[1], newCond, head[3]]
|
|
79
|
-
return [['for', newHead, newBody]]
|
|
80
|
-
}
|
|
81
|
-
function isPureSparseArrowBody(n, PARAM) {
|
|
82
|
-
if (typeof n === 'string') return true
|
|
83
|
-
if (!Array.isArray(n)) return true
|
|
84
|
-
const op = n[0]
|
|
85
|
-
// Calls / new / assignments / increments are unsafe for repeated-substitution semantics.
|
|
86
|
-
if (op === '()' || op === '?.()' || op === 'new' || op === '++' || op === '--') return false
|
|
87
|
-
if (op === '=>') return false // nested closure is opaque
|
|
88
|
-
if (typeof op === 'string' && op !== '=>' && op !== '===' && op !== '!==' && op !== '==' && op !== '!=' && op !== '<=' && op !== '>=' && op.endsWith('=') && op !== '=') return false
|
|
89
|
-
if (op === '=') return false
|
|
90
|
-
for (let i = 1; i < n.length; i++) if (!isPureSparseArrowBody(n[i], PARAM)) return false
|
|
91
|
-
return true
|
|
92
|
-
}
|
|
93
|
-
function hasOnlySparseUses(n, NAME) {
|
|
94
|
-
if (typeof n === 'string') return n !== NAME
|
|
95
|
-
if (!Array.isArray(n)) return true
|
|
96
|
-
const op = n[0]
|
|
97
|
-
if (op === '[]' && n.length === 3 && n[1] === NAME) return hasOnlySparseUses(n[2], NAME) // NAME[idx] — idx must not reference NAME
|
|
98
|
-
if (op === '.' && n[1] === NAME) {
|
|
99
|
-
if (n[2] === 'length') return true
|
|
100
|
-
return false // any other property access on NAME is opaque
|
|
101
|
-
}
|
|
102
|
-
for (let i = 1; i < n.length; i++) if (!hasOnlySparseUses(n[i], NAME)) return false
|
|
103
|
-
return true
|
|
104
|
-
}
|
|
105
|
-
function hasAnyIndexedRead(n, NAME) {
|
|
106
|
-
if (!Array.isArray(n)) return false
|
|
107
|
-
if (n[0] === '[]' && n.length === 3 && n[1] === NAME) return true
|
|
108
|
-
for (let i = 1; i < n.length; i++) if (hasAnyIndexedRead(n[i], NAME)) return true
|
|
109
|
-
return false
|
|
110
|
-
}
|
|
111
|
-
function refsName(n, NAME) {
|
|
112
|
-
if (typeof n === 'string') return n === NAME
|
|
113
|
-
if (!Array.isArray(n)) return false
|
|
114
|
-
for (let i = 1; i < n.length; i++) if (refsName(n[i], NAME)) return true
|
|
115
|
-
return false
|
|
116
|
-
}
|
|
117
|
-
function assignsName(n, NAME) {
|
|
118
|
-
if (!Array.isArray(n)) return false
|
|
119
|
-
const op = n[0]
|
|
120
|
-
if ((op === '=' || op === '++' || op === '--' ||
|
|
121
|
-
(typeof op === 'string' && op.endsWith('=') && op !== '==' && op !== '===' && op !== '!=' && op !== '!==' && op !== '<=' && op !== '>='))
|
|
122
|
-
&& n[1] === NAME) return true
|
|
123
|
-
for (let i = 1; i < n.length; i++) if (assignsName(n[i], NAME)) return true
|
|
124
|
-
return false
|
|
125
|
-
}
|
|
126
|
-
function bindsName(n, NAME) {
|
|
127
|
-
if (!Array.isArray(n)) return false
|
|
128
|
-
const op = n[0]
|
|
129
|
-
if ((op === 'let' || op === 'const')) {
|
|
130
|
-
for (let i = 1; i < n.length; i++) {
|
|
131
|
-
const bind = n[i]
|
|
132
|
-
if (Array.isArray(bind) && bind[0] === '=' && bind[1] === NAME) return true
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
if (op === '=>') {
|
|
136
|
-
const p = n[1]
|
|
137
|
-
if (p === NAME) return true
|
|
138
|
-
if (Array.isArray(p)) {
|
|
139
|
-
if (p[0] === '()' && p[1] === NAME) return true
|
|
140
|
-
// skip deeper destructuring forms — conservative
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
for (let i = 1; i < n.length; i++) if (bindsName(n[i], NAME)) return true
|
|
144
|
-
return false
|
|
145
|
-
}
|
|
146
|
-
function substSparse(n, NAME, RECV, PARAM, arrowBody) {
|
|
147
|
-
if (typeof n !== 'object' || n === null || !Array.isArray(n)) return n
|
|
148
|
-
if (n[0] === '.' && n[1] === NAME && n[2] === 'length') return ['.', RECV, 'length']
|
|
149
|
-
if (n[0] === '[]' && n.length === 3 && n[1] === NAME) {
|
|
150
|
-
const idx = substSparse(n[2], NAME, RECV, PARAM, arrowBody)
|
|
151
|
-
return cloneAndBind(arrowBody, PARAM, ['[]', RECV, idx])
|
|
152
|
-
}
|
|
153
|
-
return n.map((c, i) => i === 0 ? c : substSparse(c, NAME, RECV, PARAM, arrowBody))
|
|
154
|
-
}
|
|
155
|
-
function cloneAndBind(node, PARAM, replacement) {
|
|
156
|
-
if (node === PARAM) return replacement
|
|
157
|
-
if (!Array.isArray(node)) return node
|
|
158
|
-
return node.map((c, i) => i === 0 ? c : cloneAndBind(c, PARAM, replacement))
|
|
159
|
-
}
|