jz 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +315 -318
  2. package/bench/README.md +369 -0
  3. package/bench/bench.svg +102 -0
  4. package/cli.js +104 -30
  5. package/dist/interop.js +1 -0
  6. package/dist/jz.js +6024 -0
  7. package/index.d.ts +126 -0
  8. package/index.js +362 -84
  9. package/interop.js +159 -186
  10. package/jz.svg +5 -0
  11. package/jzify/arguments.js +97 -0
  12. package/jzify/bundler.js +382 -0
  13. package/jzify/classes.js +328 -0
  14. package/jzify/hoist-vars.js +177 -0
  15. package/jzify/index.js +51 -0
  16. package/jzify/names.js +37 -0
  17. package/jzify/switch.js +106 -0
  18. package/jzify/transform.js +349 -0
  19. package/layout.js +184 -0
  20. package/module/array.js +377 -176
  21. package/module/collection.js +639 -145
  22. package/module/console.js +55 -43
  23. package/module/core.js +264 -153
  24. package/module/date.js +15 -3
  25. package/module/function.js +73 -6
  26. package/module/index.js +2 -1
  27. package/module/json.js +226 -61
  28. package/module/math.js +551 -187
  29. package/module/number.js +327 -60
  30. package/module/object.js +474 -184
  31. package/module/regex.js +255 -25
  32. package/module/schema.js +24 -6
  33. package/module/simd.js +117 -0
  34. package/module/string.js +621 -226
  35. package/module/symbol.js +1 -1
  36. package/module/timer.js +9 -14
  37. package/module/typedarray.js +459 -73
  38. package/package.json +58 -14
  39. package/src/abi/index.js +39 -23
  40. package/src/abi/string.js +38 -41
  41. package/src/ast.js +460 -0
  42. package/src/autoload.js +29 -24
  43. package/src/bridge.js +111 -0
  44. package/src/compile/analyze-scans.js +824 -0
  45. package/src/compile/analyze.js +1600 -0
  46. package/src/compile/emit-assign.js +411 -0
  47. package/src/compile/emit.js +3512 -0
  48. package/src/compile/flow-types.js +103 -0
  49. package/src/{compile.js → compile/index.js} +778 -128
  50. package/src/{infer.js → compile/infer.js} +62 -98
  51. package/src/compile/loop-divmod.js +155 -0
  52. package/src/{narrow.js → compile/narrow.js} +409 -106
  53. package/src/compile/peel-stencil.js +261 -0
  54. package/src/compile/plan/advise.js +370 -0
  55. package/src/compile/plan/common.js +150 -0
  56. package/src/compile/plan/index.js +122 -0
  57. package/src/compile/plan/inline.js +682 -0
  58. package/src/compile/plan/literals.js +1199 -0
  59. package/src/compile/plan/loops.js +472 -0
  60. package/src/compile/plan/scope.js +649 -0
  61. package/src/compile/program-facts.js +423 -0
  62. package/src/ctx.js +220 -64
  63. package/src/ir.js +589 -172
  64. package/src/kind-traits.js +132 -0
  65. package/src/kind.js +524 -0
  66. package/src/op-policy.js +57 -0
  67. package/src/{optimize.js → optimize/index.js} +1432 -473
  68. package/src/optimize/vectorize.js +4660 -0
  69. package/src/param-reps.js +65 -0
  70. package/src/parse.js +44 -0
  71. package/src/{prepare.js → prepare/index.js} +649 -205
  72. package/src/prepare/lift-iife.js +149 -0
  73. package/src/reps.js +116 -0
  74. package/src/resolve.js +12 -3
  75. package/src/static.js +208 -0
  76. package/src/type.js +651 -0
  77. package/src/{assemble.js → wat/assemble.js} +275 -55
  78. package/src/wat/optimize.js +3938 -0
  79. package/transform.js +21 -0
  80. package/wasi.js +47 -5
  81. package/src/analyze.js +0 -3818
  82. package/src/emit.js +0 -3040
  83. package/src/jzify.js +0 -1580
  84. package/src/plan.js +0 -2132
  85. package/src/vectorize.js +0 -1088
  86. /package/src/{codegen.js → wat/codegen.js} +0 -0
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Class and object-method `this` lowering.
3
+ * @module jzify/classes
4
+ */
5
+
6
+ import { extractParams as paramList, objectLiteralEntries } from '../src/ast.js'
7
+ import { err } from '../src/ctx.js'
8
+
9
+ export function createClassLowering({ transform, names, JC }) {
10
+ // === class lowering ===
11
+ //
12
+ // A class is lowered to a factory arrow. Instance state is a plain object;
13
+ // methods are per-instance arrows capturing it (so `obj.m()` keeps working
14
+ // without a separate `this` argument); `this` is renamed to that object;
15
+ // `new C(a)` is already turned into `C(a)` by the `new` handler.
16
+ //
17
+ // class Point { x = 0; y; constructor(a,b){ this.x = a; this.y = b }
18
+ // dist(){ return Math.hypot(this.x, this.y) } }
19
+ // →
20
+ // let Point = (a, b) => {
21
+ // let selfN = { x: undefined, y: undefined,
22
+ // dist: () => Math.hypot(selfN.x, selfN.y) }
23
+ // selfN.x = 0 // field initializers, in declaration order
24
+ // selfN.x = a // then the constructor body
25
+ // selfN.y = b
26
+ // return selfN
27
+ // }
28
+ //
29
+ // Simple inheritance is lowered too: `class D extends B` builds the instance
30
+ // from `B`'s factory — forwarding `super(...)` args, or the derived ctor params
31
+ // when the derived constructor is implicit — then applies D's own fields and
32
+ // methods over it.
33
+ //
34
+ // Out of scope (rejected with a clear message): full `super.foo` property
35
+ // semantics, getters/setters, non-constant computed member names. Private
36
+ // `#name` members are kept as the literal key string `#name` (jz allows it).
37
+ const DEFAULT_DERIVED_CTOR_ARITY = 8
38
+
39
+ const arrowParams = params => Array.isArray(params) && params[0] === '()' ? params : ['()', params]
40
+ const block = b => Array.isArray(b) && b[0] === '{}' ? b : ['{}', b]
41
+
42
+ const classBodyItems = (body) =>
43
+ body == null ? [] : Array.isArray(body) && body[0] === ';' ? body.slice(1) : [body]
44
+
45
+ // Rename `this` → `to`, not crossing into a nested `function`/`class` (those
46
+ // rebind `this`); arrows inherit `this`, so they are crossed. Property *names*
47
+ // (`obj.this`, `{this: …}` value-side only) are left alone.
48
+ function renameThis(node, to) {
49
+ if (node === 'this') return to
50
+ if (!Array.isArray(node)) return node
51
+ if (node[0] === 'function' || node[0] === 'class') return node
52
+ if (node[0] === '.' || node[0] === '?.') return [node[0], renameThis(node[1], to), node[2]]
53
+ if (node[0] === ':') return [node[0], node[1], renameThis(node[2], to)]
54
+ return node.map(n => renameThis(n, to))
55
+ }
56
+
57
+ function usesThis(node) {
58
+ if (node === 'this') return true
59
+ if (!Array.isArray(node)) return false
60
+ if (node[0] === 'function' || node[0] === 'class') return false
61
+ if (node[0] === '.' || node[0] === '?.') return usesThis(node[1])
62
+ if (node[0] === ':') return usesThis(node[2])
63
+ return node.some(usesThis)
64
+ }
65
+
66
+ function hasSuperProp(node) {
67
+ if (!Array.isArray(node)) return false
68
+ if ((node[0] === '.' || node[0] === '?.') && node[1] === 'super') return true
69
+ if (node[0] === '[]' && node[1] === 'super') return true
70
+ return node.some(hasSuperProp)
71
+ }
72
+
73
+ function isSuperCall(node) {
74
+ return Array.isArray(node) && node[0] === '()' && node[1] === 'super'
75
+ }
76
+
77
+ function literalStringKey(node) {
78
+ return Array.isArray(node) && node[0] == null && typeof node[1] === 'string' ? node[1] : null
79
+ }
80
+
81
+ function constStringKey(node) {
82
+ if (typeof node === 'string') return node
83
+ const lit = literalStringKey(node)
84
+ if (lit != null) return lit
85
+ if (Array.isArray(node) && node[0] === '[]') return literalStringKey(node[1])
86
+ return null
87
+ }
88
+
89
+ function superMethodName(callee) {
90
+ if (!Array.isArray(callee)) return null
91
+ if ((callee[0] === '.' || callee[0] === '?.') && callee[1] === 'super') return callee[2]
92
+ if (callee[0] === '[]' && callee[1] === 'super') return literalStringKey(callee[2])
93
+ return null
94
+ }
95
+
96
+ function collectSuperMethodCalls(node, out = new Set()) {
97
+ if (!Array.isArray(node)) return out
98
+ if (node[0] === 'function' || node[0] === 'class') return out
99
+ if (node[0] === '()') {
100
+ const name = superMethodName(node[1])
101
+ if (name) out.add(name)
102
+ }
103
+ for (const n of node) collectSuperMethodCalls(n, out)
104
+ return out
105
+ }
106
+
107
+ function rewriteSuperMethodCalls(node, baseMethodVars) {
108
+ if (!Array.isArray(node)) return node
109
+ if (node[0] === 'function' || node[0] === 'class') return node
110
+ if (node[0] === '()') {
111
+ const name = superMethodName(node[1])
112
+ if (name) {
113
+ const fn = baseMethodVars.get(name)
114
+ if (!fn) jzifyError(`super.${name} is not available on the base class`)
115
+ return ['()', fn, ...node.slice(2).map(n => rewriteSuperMethodCalls(n, baseMethodVars))]
116
+ }
117
+ }
118
+ return node.map(n => rewriteSuperMethodCalls(n, baseMethodVars))
119
+ }
120
+
121
+ function splitCtorSuper(body) {
122
+ if (body == null) return { args: null, body }
123
+ if (isSuperCall(body)) return { args: body.slice(2), body: null }
124
+ if (Array.isArray(body) && body[0] === '{}') {
125
+ const inner = splitCtorSuper(body[1])
126
+ return { args: inner.args, body: ['{}', inner.body] }
127
+ }
128
+ if (Array.isArray(body) && body[0] === ';') {
129
+ const out = [';']
130
+ let args = null
131
+ for (const stmt of body.slice(1)) {
132
+ if (args == null && isSuperCall(stmt)) { args = stmt.slice(2); continue }
133
+ out.push(stmt)
134
+ }
135
+ return { args, body: out.length === 1 ? null : out.length === 2 ? out[1] : out }
136
+ }
137
+ return { args: null, body }
138
+ }
139
+
140
+ // Object shorthand methods and arrow-valued properties both parse as `=>`.
141
+ // Stay conservative: only statement-shaped bodies are receiver methods here;
142
+ // expression-bodied arrows keep their lexical `this` and remain unsupported.
143
+ const OBJ_METHOD_BODY_OPS = new Set([';', 'return', 'if', 'for', 'for-in', 'for-of',
144
+ 'while', 'do', 'switch', 'throw', 'try', 'break', 'continue'])
145
+
146
+ function isStatementBody(body) {
147
+ return Array.isArray(body) && OBJ_METHOD_BODY_OPS.has(body[0])
148
+ }
149
+
150
+ function objectMethodUsesThis(prop) {
151
+ if (!Array.isArray(prop) || prop[0] !== ':' || typeof prop[1] !== 'string') return false
152
+ const value = prop[2]
153
+ if (!Array.isArray(value)) return false
154
+ if (value[0] === '=>' && isStatementBody(value[2])) return usesThis(value[2])
155
+ return false
156
+ }
157
+
158
+ function lowerObjectLiteralThis(args) {
159
+ const props = objectLiteralEntries(args)
160
+ if (props.length === 0 || !props.some(objectMethodUsesThis)) return null
161
+ if (!props.every(p => Array.isArray(p) && p[0] === ':' && typeof p[1] === 'string')) return null
162
+
163
+ const self = names.objThis()
164
+ const litProps = props.map(p => {
165
+ const value = p[2]
166
+ if (objectMethodUsesThis(p)) {
167
+ return [':', p[1], transform(['=>', value[1], block(renameThis(value[2], self))])]
168
+ }
169
+ return [':', p[1], transform(value)]
170
+ })
171
+ const lit = ['{}', litProps.length === 1 ? litProps[0] : [',', ...litProps]]
172
+ return ['()', ['()', ['=>', null, ['{}', [';',
173
+ ['let', ['=', self, lit]],
174
+ ['return', self]
175
+ ]]]], null]
176
+ }
177
+
178
+ // Route through the shared compiler error channel (uniform Error shape + stack
179
+ // cleanup for dependents). jzify runs pre-emit, so err()'s location/function
180
+ // enrichment guards simply no-op; the `jzify:` prefix marks the phase.
181
+ function jzifyError(msg) { err(`jzify: ${msg}`) }
182
+
183
+ function lowerClass(name, heritage, body) {
184
+ let ctorParams = null, ctorBody = null
185
+ const methods = [], fields = [], statics = []
186
+ for (const it of classBodyItems(body)) {
187
+ if (typeof it === 'string') { fields.push([it, null]); continue } // bare `x;`
188
+ if (!Array.isArray(it)) continue
189
+ const bareFieldName = constStringKey(it)
190
+ if (bareFieldName != null) { fields.push([bareFieldName, null]); continue }
191
+ if (it[0] === ':' && Array.isArray(it[2]) && it[2][0] === '=>') {
192
+ const key = constStringKey(it[1])
193
+ if (key == null) jzifyError(JC.computedMember)
194
+ if (key === 'constructor') { ctorParams = it[2][1]; ctorBody = it[2][2] }
195
+ else methods.push([key, it[2][1], it[2][2]])
196
+ continue
197
+ }
198
+ if (it[0] === '=') {
199
+ const lhs = it[1]
200
+ if (Array.isArray(lhs) && lhs[0] === 'static') {
201
+ const key = constStringKey(lhs[1])
202
+ if (key == null) jzifyError(JC.computedStaticField)
203
+ statics.push([key, it[2]])
204
+ continue
205
+ }
206
+ const key = constStringKey(lhs)
207
+ if (key == null) jzifyError(JC.computedField)
208
+ fields.push([key, it[2]])
209
+ continue
210
+ }
211
+ if (it[0] === 'static') {
212
+ const key = constStringKey(it[1])
213
+ if (key != null) {
214
+ statics.push([key, null])
215
+ continue
216
+ }
217
+ }
218
+ if (it[0] === 'static' && typeof it[1] === 'string') {
219
+ statics.push([it[1], null])
220
+ continue
221
+ }
222
+ if (it[0] === 'static' && Array.isArray(it[1]) && it[1][0] === ':' && Array.isArray(it[1][2]) && it[1][2][0] === '=>') {
223
+ const key = constStringKey(it[1][1])
224
+ if (key == null) jzifyError(JC.computedStaticMember)
225
+ statics.push([key, it[1][2], true])
226
+ continue
227
+ }
228
+ if (it[0] === 'get' || it[0] === 'set') jzifyError(JC.accessor)
229
+ if (it[0] === 'static') jzifyError(JC.staticMember)
230
+ jzifyError(`unsupported class member ${JSON.stringify(it).slice(0, 60)}`)
231
+ }
232
+ const superMethods = heritage == null ? new Set() : new Set([
233
+ ...collectSuperMethodCalls(ctorBody),
234
+ ...fields.flatMap(([, init]) => init == null ? [] : [...collectSuperMethodCalls(init)]),
235
+ ...methods.flatMap(([, , mbody]) => [...collectSuperMethodCalls(mbody)])
236
+ ])
237
+ if (heritage != null) {
238
+ const dummySuperVars = new Map([...superMethods].map((k, i) => [k, names.classSuper(i)]))
239
+ const unsupportedSuperProp = node => node != null && hasSuperProp(rewriteSuperMethodCalls(node, dummySuperVars))
240
+ if (
241
+ unsupportedSuperProp(ctorBody) ||
242
+ fields.some(([, init]) => unsupportedSuperProp(init)) ||
243
+ methods.some(([, , mbody]) => unsupportedSuperProp(mbody))
244
+ )
245
+ jzifyError(JC.superProp)
246
+ }
247
+ const self = names.classSelf()
248
+ const UNDEF = [] // jessie's node for `undefined`
249
+ // Object literal: every declared field (its initializer inline when it doesn't
250
+ // touch `this`, else `undefined` and assigned below), every method as its
251
+ // self-capturing arrow. Declaring all fields up front fixes the object shape.
252
+ const litProps = [], deferred = []
253
+ for (const [fname, init] of fields) {
254
+ if (init != null && !usesThis(init)) litProps.push([':', fname, transform(init)])
255
+ else { litProps.push([':', fname, UNDEF]); if (init != null) deferred.push([fname, init]) }
256
+ }
257
+ for (const [mname, mparams, mbody] of methods)
258
+ litProps.push([':', mname, transform(['=>', mparams ?? ['()', null], block(renameThis(mbody, self))])])
259
+ const lit = ['{}', litProps.length === 0 ? null : litProps.length === 1 ? litProps[0] : [',', ...litProps]]
260
+ let params = ctorParams ?? ['()', null]
261
+ const dynamicBase = heritage != null && typeof heritage !== 'string'
262
+ const baseRef = heritage == null ? null : dynamicBase ? names.classBase() : heritage
263
+ const stmts = []
264
+ if (heritage != null) {
265
+ const split = splitCtorSuper(ctorBody)
266
+ ctorBody = split.body
267
+ const defaultArgs = ctorParams == null
268
+ ? Array.from({ length: DEFAULT_DERIVED_CTOR_ARITY }, (_, i) => names.classSuperArg(i))
269
+ : null
270
+ const baseArgs = split.args ?? (defaultArgs ? [defaultArgs.length === 1 ? defaultArgs[0] : [',', ...defaultArgs]] : paramList(ctorParams))
271
+ stmts.push(['let', ['=', self, ['()', baseRef, ...baseArgs.map(transform)]]])
272
+ const superMethodVars = new Map()
273
+ let superIdx = 0
274
+ for (const mname of superMethods) {
275
+ const v = names.classSuper(superIdx++)
276
+ superMethodVars.set(mname, v)
277
+ stmts.push(['let', ['=', v, ['.', self, mname]]])
278
+ }
279
+ for (const [fname, init] of fields)
280
+ stmts.push(['=', ['.', self, fname], init != null ? transform(renameThis(rewriteSuperMethodCalls(init, superMethodVars), self)) : UNDEF])
281
+ for (const [mname, mparams, mbody] of methods)
282
+ stmts.push(['=', ['.', self, mname], transform(['=>', mparams ?? ['()', null], block(renameThis(rewriteSuperMethodCalls(mbody, superMethodVars), self))])])
283
+ ctorBody = rewriteSuperMethodCalls(ctorBody, superMethodVars)
284
+ if (defaultArgs) params = ['()', defaultArgs.length === 1 ? defaultArgs[0] : [',', ...defaultArgs]]
285
+ } else {
286
+ stmts.push(['let', ['=', self, lit]])
287
+ }
288
+ // `this`-dependent field initializers run, in declaration order, before the ctor.
289
+ if (heritage == null) {
290
+ for (const [fname, init] of deferred)
291
+ stmts.push(['=', ['.', self, fname], transform(renameThis(init, self))])
292
+ }
293
+ if (ctorBody != null) {
294
+ let cb = transform(renameThis(ctorBody, self))
295
+ if (Array.isArray(cb) && cb[0] === '{}') cb = cb[1]
296
+ if (Array.isArray(cb) && cb[0] === ';') stmts.push(...cb.slice(1).filter(s => s != null))
297
+ else if (cb != null) stmts.push(cb)
298
+ }
299
+ stmts.push(['return', self])
300
+ const factory = ['=>', arrowParams(params), ['{}', [';', ...stmts]]]
301
+ if (!dynamicBase && statics.length === 0) return factory
302
+
303
+ const cls = name || names.classStatic()
304
+ const staticStmts = []
305
+ if (dynamicBase) staticStmts.push(['let', ['=', baseRef, transform(heritage)]])
306
+ staticStmts.push(['let', ['=', cls, factory]])
307
+ for (const [sname, value, isMethod] of statics) {
308
+ const rhs = isMethod
309
+ ? transform(['=>', value[1], block(renameThis(value[2], cls))])
310
+ : value == null ? UNDEF : transform(renameThis(value, cls))
311
+ staticStmts.push(['=', ['.', cls, sname], rhs])
312
+ }
313
+ staticStmts.push(['return', cls])
314
+ return ['()', ['()', ['=>', null, ['{}', [';', ...staticStmts]]]], null]
315
+ }
316
+
317
+ // Array(a, b, …) / new Array(a, b, …) → array literal [a, b, …]; Array() → [].
318
+ // The single-argument Array(n) is a length constructor (n holes), not a
319
+ // literal — return null there so the caller keeps it as a constructor call.
320
+ function lowerArrayConstructor(arg) {
321
+ if (arg == null) return ['[]', null]
322
+ if (Array.isArray(arg) && arg[0] === ',' && arg.length > 2)
323
+ return ['[]', [',', ...arg.slice(1).map(transform)]]
324
+ return null
325
+ }
326
+
327
+ return { lowerClass, lowerObjectLiteralThis, lowerArrayConstructor }
328
+ }
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Var hoisting — bubble `var` to scope top, normalize for-heads, destructure patterns.
3
+ * @module jzify/hoist-vars
4
+ */
5
+
6
+ import { JZ_BLOCK_OPS } from '../src/ast.js'
7
+
8
+ export const isDestructurePat = p =>
9
+ Array.isArray(p) && (p[0] === '[]' || p[0] === '{}' || (p[0] === '=' && isDestructurePat(p[1])))
10
+
11
+ export function hoistVars(node, names) {
12
+ if (node == null || !Array.isArray(node)) return node
13
+ const op = node[0]
14
+ if (op === 'function') {
15
+ const inner = new Set()
16
+ let body = hoistVars(node[3], inner)
17
+ if (inner.size) body = prependDecls(body, inner)
18
+ return ['function', node[1], node[2], body]
19
+ }
20
+ if (op === '=>') {
21
+ const inner = new Set()
22
+ let body = hoistVars(node[2], inner)
23
+ if (inner.size) body = prependDecls(body, inner)
24
+ return ['=>', node[1], body]
25
+ }
26
+ if (op === 'in' || op === 'of') {
27
+ let lhs = node[1]
28
+ if (Array.isArray(lhs) && lhs[0] === 'var' && typeof lhs[1] === 'string' && lhs.length === 2) {
29
+ names.add(lhs[1])
30
+ lhs = lhs[1]
31
+ } else {
32
+ lhs = hoistVars(lhs, names)
33
+ }
34
+ return [op, lhs, hoistVars(node[2], names)]
35
+ }
36
+ if (op === ':' && typeof node[1] === 'string') {
37
+ return [':', node[1], hoistVars(node[2], names)]
38
+ }
39
+ if (op === '=' && Array.isArray(node[1]) && node[1][0] === 'var' && typeof node[1][1] === 'string' && node[1].length === 2) {
40
+ names.add(node[1][1])
41
+ return ['=', node[1][1], hoistVars(node[2], names)]
42
+ }
43
+ if (op === '=' && isDestructurePat(node[1])) {
44
+ return ['=', hoistPattern(node[1], names), hoistVars(node[2], names)]
45
+ }
46
+ if (op === 'for') {
47
+ const head = node[1]
48
+ let h2
49
+ const normalizedHead = normalizeForDeclHead(head, names) || normalizeForCommaHead(head, names)
50
+ if (normalizedHead) {
51
+ h2 = normalizedHead
52
+ } else if (Array.isArray(head) && head[0] === 'var' && Array.isArray(head[1]) &&
53
+ (head[1][0] === 'in' || head[1][0] === 'of') && typeof head[1][1] === 'string') {
54
+ names.add(head[1][1])
55
+ h2 = [head[1][0], head[1][1], hoistVars(head[1][2], names)]
56
+ } else if (Array.isArray(head) && head[0] === ';') {
57
+ h2 = [';']
58
+ for (let i = 1; i < head.length; i++) h2.push(hoistVars(head[i], names))
59
+ } else {
60
+ h2 = hoistVars(head, names)
61
+ }
62
+ return ['for', h2, hoistVars(node[2], names)]
63
+ }
64
+ if (op === 'var') {
65
+ const decls = []
66
+ for (let i = 1; i < node.length; i++) {
67
+ const d = node[i]
68
+ if (typeof d === 'string') { names.add(d); continue }
69
+ if (Array.isArray(d) && d[0] === '=' && typeof d[1] === 'string') {
70
+ names.add(d[1])
71
+ decls.push(['=', d[1], hoistVars(d[2], names)])
72
+ }
73
+ }
74
+ if (decls.length === 0) return null
75
+ if (decls.length === 1) return decls[0]
76
+ return [',', ...decls]
77
+ }
78
+ if (op === 'let' || op === 'const') {
79
+ const decls = [op]
80
+ for (let i = 1; i < node.length; i++) {
81
+ const d = node[i]
82
+ if (Array.isArray(d) && d[0] === '=' && isDestructurePat(d[1])) {
83
+ decls.push(['=', hoistPattern(d[1], names), hoistVars(d[2], names)])
84
+ } else {
85
+ decls.push(hoistVars(d, names))
86
+ }
87
+ }
88
+ return decls
89
+ }
90
+ if (op === ';') {
91
+ const out = [op]
92
+ for (let i = 1; i < node.length; i++) {
93
+ const child = node[i]
94
+ if (Array.isArray(child) && child[0] === 'var' && child.length === 2 &&
95
+ Array.isArray(child[1]) && child[1][0] === '=' && typeof child[1][1] === 'string' &&
96
+ Array.isArray(child[1][2]) && child[1][2][0] === '=>') {
97
+ out.push(['let', ['=', child[1][1], hoistVars(child[1][2], names)]])
98
+ continue
99
+ }
100
+ const c = hoistVars(child, names)
101
+ if (c != null) out.push(c)
102
+ }
103
+ if (out.length === 1) return null
104
+ if (out.length === 2) return out[1]
105
+ return out
106
+ }
107
+ if (op === '{}' && node.length === 2) {
108
+ const inner = node[1]
109
+ const wasBlock = inner != null && Array.isArray(inner) && JZ_BLOCK_OPS.has(inner[0])
110
+ const t = hoistVars(inner, names)
111
+ if (!wasBlock || t == null) return ['{}', t]
112
+ const stayed = Array.isArray(t) && JZ_BLOCK_OPS.has(t[0])
113
+ return ['{}', stayed ? t : [';', t]]
114
+ }
115
+ const out = new Array(node.length)
116
+ out[0] = op
117
+ for (let i = 1; i < node.length; i++) out[i] = hoistVars(node[i], names)
118
+ return out
119
+ }
120
+
121
+ export function hoistPattern(node, names) {
122
+ if (node == null || !Array.isArray(node)) return node
123
+ const op = node[0]
124
+ if (op === '=') return ['=', hoistPattern(node[1], names), hoistVars(node[2], names)]
125
+ if (op === ':') return [':', hoistVars(node[1], names), hoistPattern(node[2], names)]
126
+ if (op === '...') return ['...', hoistPattern(node[1], names)]
127
+ if (op === '[]' || op === '{}' || op === ',') return [op, ...node.slice(1).map(n => hoistPattern(n, names))]
128
+ return hoistVars(node, names)
129
+ }
130
+
131
+ export function prependDecls(body, names) {
132
+ const decl = ['let', ...names]
133
+ if (Array.isArray(body) && body[0] === ';') return [';', decl, ...body.slice(1)]
134
+ if (Array.isArray(body) && body[0] === '{}') {
135
+ const inner = body[1]
136
+ if (Array.isArray(inner) && inner[0] === ';') return ['{}', [';', decl, ...inner.slice(1)]]
137
+ if (inner == null) return ['{}', decl]
138
+ return ['{}', [';', decl, inner]]
139
+ }
140
+ return body == null ? decl : [';', decl, body]
141
+ }
142
+
143
+ function normalizeForDeclHead(head, names) {
144
+ if (!Array.isArray(head) || (head[0] !== 'var' && head[0] !== 'let' && head[0] !== 'const')) return null
145
+ const kind = head[0]
146
+ if (head.length === 2) {
147
+ const expr = head[1]
148
+ if (!Array.isArray(expr)) return null
149
+ if (expr.length >= 3 && Array.isArray(expr[1]) &&
150
+ (expr[1][0] === 'in' || expr[1][0] === 'of') && typeof expr[1][1] === 'string') {
151
+ const iter = expr[1]
152
+ return [iter[0], normalizeForDecl(kind, iter[1], names), hoistVars([expr[0], iter[2], ...expr.slice(2)], names)]
153
+ }
154
+ return null
155
+ }
156
+ if (head.length > 2 && Array.isArray(head[1]) &&
157
+ (head[1][0] === 'in' || head[1][0] === 'of') && typeof head[1][1] === 'string') {
158
+ const iter = head[1]
159
+ return [iter[0], normalizeForDecl(kind, iter[1], names), hoistVars([',', iter[2], ...head.slice(2)], names)]
160
+ }
161
+ return null
162
+ }
163
+
164
+ function normalizeForCommaHead(head, names) {
165
+ if (!Array.isArray(head) || head[0] !== ',' || head.length < 3) return null
166
+ const iter = head[1]
167
+ if (!Array.isArray(iter) || (iter[0] !== 'in' && iter[0] !== 'of') || typeof iter[1] !== 'string') return null
168
+ return [iter[0], iter[1], hoistVars([',', iter[2], ...head.slice(2)], names)]
169
+ }
170
+
171
+ function normalizeForDecl(kind, name, names) {
172
+ if (kind === 'var') {
173
+ names.add(name)
174
+ return name
175
+ }
176
+ return [kind, name]
177
+ }
package/jzify/index.js ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * jzify — Transform JS AST into jz-compatible form.
3
+ *
4
+ * Crockford-aligned: eliminates bad parts, enforces good practices.
5
+ * Runs before prepare() as an AST→AST pass.
6
+ *
7
+ * @module jzify
8
+ */
9
+
10
+ import { JZIFY_CLASS_ERRORS as JC } from '../src/op-policy.js'
11
+ import { createNames } from './names.js'
12
+ import { foldStaticExportHelpers, foldStaticBundlerHelpers, canonicalizeObjectIdioms } from './bundler.js'
13
+ import { createSwitchLowering, normalizeCaseBody } from './switch.js'
14
+ import { createClassLowering } from './classes.js'
15
+ import { hoistVars, prependDecls } from './hoist-vars.js'
16
+ import { createArgumentsLowering } from './arguments.js'
17
+ import { createTransform } from './transform.js'
18
+
19
+ const names = createNames()
20
+ const { lowerArguments, transformPattern, bindTransform } = createArgumentsLowering(names)
21
+
22
+ let lowerClass, lowerObjectLiteralThis, lowerArrayConstructor, transformSwitch
23
+ let transform, transformScope
24
+
25
+ ;({ transform, transformScope } = createTransform({
26
+ names,
27
+ lowerArguments,
28
+ transformPattern,
29
+ normalizeCaseBody,
30
+ transformSwitch: (...a) => transformSwitch(...a),
31
+ lowerClass: () => lowerClass,
32
+ lowerObjectLiteralThis: () => lowerObjectLiteralThis,
33
+ lowerArrayConstructor: () => lowerArrayConstructor,
34
+ }))
35
+ bindTransform(transform)
36
+
37
+ ;({ lowerClass, lowerObjectLiteralThis, lowerArrayConstructor } = createClassLowering({ transform, names, JC }))
38
+ transformSwitch = createSwitchLowering(transform, names)
39
+
40
+ /**
41
+ * Transform AST in-place. Returns transformed AST.
42
+ * @param {Array} ast - subscript/jessie parsed AST
43
+ * @returns {Array} Transformed AST
44
+ */
45
+ export default function jzify(ast) {
46
+ names.reset()
47
+ const hoisted = new Set()
48
+ ast = hoistVars(ast, hoisted)
49
+ if (hoisted.size) ast = prependDecls(ast, hoisted)
50
+ return foldStaticBundlerHelpers(foldStaticExportHelpers(canonicalizeObjectIdioms(transformScope(ast))))
51
+ }
package/jzify/names.js ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Synthetic temp-name factory for jzify lowering passes.
3
+ *
4
+ * Namespaces use private-use Unicode prefixes so they never collide with user code.
5
+ *
6
+ * @module jzify/names
7
+ */
8
+
9
+ import { T } from '../src/ast.js'
10
+
11
+ /** @returns fresh name counters, all reset together at jzify entry. */
12
+ export function createNames() {
13
+ let swIdx = 0
14
+ let argsIdx = 0
15
+ let doIdx = 0
16
+ let classIdx = 0
17
+ let objThisIdx = 0
18
+ let staticClassIdx = 0
19
+ let classBaseIdx = 0
20
+
21
+ return {
22
+ reset() {
23
+ swIdx = argsIdx = doIdx = classIdx = objThisIdx = staticClassIdx = classBaseIdx = 0
24
+ },
25
+ switchDisc: () => `${T}sw${swIdx++}`,
26
+ switchStart: () => `${T}swst${swIdx++}`,
27
+ switchBreak: () => `${T}swbrk${swIdx++}`,
28
+ arg: () => `\uE001arg${argsIdx++}`,
29
+ doFlag: () => `\uE002do${doIdx++}`,
30
+ objThis: () => `\uE003obj${objThisIdx++}`,
31
+ classSelf: () => `\uE003self${classIdx++}`,
32
+ classBase: () => `\uE003base${classBaseIdx++}`,
33
+ classStatic: () => `\uE003class${staticClassIdx++}`,
34
+ classSuperArg: i => `\uE003superArg${classIdx}_${i}`,
35
+ classSuper: i => `\uE003super${classIdx}_${i}`,
36
+ }
37
+ }