@xnoxs/flux-lang 3.1.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.md +1089 -0
  3. package/bin/flux.js +1397 -0
  4. package/dist/flux.cjs.js +6664 -0
  5. package/dist/flux.esm.js +6674 -0
  6. package/dist/flux.min.js +263 -0
  7. package/index.d.ts +202 -0
  8. package/index.js +26 -0
  9. package/package.json +77 -0
  10. package/scripts/build.js +76 -0
  11. package/src/bundler.js +216 -0
  12. package/src/checker.js +322 -0
  13. package/src/codegen.js +785 -0
  14. package/src/css-preprocessor.js +399 -0
  15. package/src/formatter.js +140 -0
  16. package/src/jsx.js +480 -0
  17. package/src/lexer.js +518 -0
  18. package/src/linter.js +758 -0
  19. package/src/mangler.js +280 -0
  20. package/src/parser.js +1671 -0
  21. package/src/self/bundler.flux +167 -0
  22. package/src/self/bundler.js +187 -0
  23. package/src/self/checker.flux +249 -0
  24. package/src/self/checker.js +338 -0
  25. package/src/self/codegen.flux +555 -0
  26. package/src/self/codegen.js +784 -0
  27. package/src/self/css-preprocessor.flux +373 -0
  28. package/src/self/css-preprocessor.js +387 -0
  29. package/src/self/formatter.flux +93 -0
  30. package/src/self/formatter.js +114 -0
  31. package/src/self/jsx.flux +430 -0
  32. package/src/self/jsx.js +396 -0
  33. package/src/self/lexer.flux +529 -0
  34. package/src/self/lexer.js +709 -0
  35. package/src/self/lexer.stage2.js +700 -0
  36. package/src/self/linter.flux +515 -0
  37. package/src/self/linter.js +804 -0
  38. package/src/self/mangler.flux +253 -0
  39. package/src/self/mangler.js +348 -0
  40. package/src/self/parser.flux +1146 -0
  41. package/src/self/parser.js +1571 -0
  42. package/src/self/sourcemap.flux +66 -0
  43. package/src/self/sourcemap.js +72 -0
  44. package/src/self/stdlib.flux +356 -0
  45. package/src/self/stdlib.js +396 -0
  46. package/src/self/test-runner.flux +201 -0
  47. package/src/self/test-runner.js +132 -0
  48. package/src/self/transpiler.flux +123 -0
  49. package/src/self/transpiler.js +83 -0
  50. package/src/self/type-checker.flux +821 -0
  51. package/src/self/type-checker.js +1106 -0
  52. package/src/sourcemap.js +82 -0
  53. package/src/stdlib.js +436 -0
  54. package/src/test-runner.js +239 -0
  55. package/src/transpiler.js +172 -0
  56. package/src/type-checker.js +1206 -0
@@ -0,0 +1,555 @@
1
+ // ============================================================
2
+ // Flux Self-Hosted Code Generator
3
+ // src/self/codegen.flux — written in Flux, compiled by stage-0
4
+ // ============================================================
5
+
6
+ import { Lexer, lexerize, T } from './lexer'
7
+ import { Parser, makeParser } from './parser'
8
+
9
+ fn extractFormatSpec(raw):
10
+ val ci = raw.lastIndexOf(':')
11
+ if ci < 1: return null
12
+ val spec = raw.slice(ci + 1).trim()
13
+ if spec.length > 0 and /[.<>^,dbeEfFgGoOxXs%bcn]/.test(spec) and /^([.<>^0\-+ #,]*[0-9]*\.?[0-9]*[dbeEfFgGoOxXs%bcn]?[,]?)$/.test(spec):
14
+ return { expr: raw.slice(0, ci).trim(), fmt: spec }
15
+ return null
16
+
17
+ val FMT_HELPER = `
18
+ function _fmt(v, s) {
19
+ if (s === ',') return (+v).toLocaleString();
20
+ if (s === '%') return ((+v)*100).toFixed(0)+'%';
21
+ var m = s.match(/^([0-9,]*)?\.([0-9]+)([fdgGeEb%x])$/);
22
+ if (m) {
23
+ var d=+m[2], t=m[3], comma=s[0]===',';
24
+ if (t==='f'||t==='d') return comma ? (+v).toLocaleString(void 0,{minimumFractionDigits:d,maximumFractionDigits:d}) : (+v).toFixed(d);
25
+ if (t==='e'||t==='E') return (+v).toExponential(d);
26
+ if (t==='%') return ((+v)*100).toFixed(d)+'%';
27
+ if (t==='b') return Math.round(+v).toString(2);
28
+ if (t==='x') return Math.round(+v).toString(16);
29
+ }
30
+ var m2 = s.match(/^([0-9]*)d$/); if (m2) return Math.round(+v).toString();
31
+ return String(v);
32
+ }`
33
+
34
+ fn buildClassRegistry(ast):
35
+ val reg = {}
36
+ for node in ast.body:
37
+ val n = node.type == 'ExportDecl' ? node.decl : node
38
+ if n.type == 'ClassDecl':
39
+ reg[n.name] = { fields: n.fields, superClass: n.superClass }
40
+ return reg
41
+
42
+ fn getAllFields(name, reg, visited):
43
+ val vis = visited ?? new Set([])
44
+ if not name or not reg[name] or vis.has(name): return []
45
+ vis.add(name)
46
+ val parent = getAllFields(reg[name].superClass, reg, vis)
47
+ return [...parent, ...reg[name].fields]
48
+
49
+ export class CodeGenerator:
50
+ ind: string
51
+ level: int
52
+ lines: any[]
53
+ clsReg: any
54
+ smBuilder: any
55
+ _needsFmt: bool
56
+ _loopDepth: int
57
+
58
+ fn i():
59
+ var s = ''
60
+ var n = self.level
61
+ while n > 0:
62
+ s += self.ind
63
+ n = n - 1
64
+ return s
65
+
66
+ fn emit(s):
67
+ self.lines.push(self.i() + s)
68
+
69
+ fn emitRaw(s):
70
+ self.lines.push(s)
71
+
72
+ fn blank():
73
+ self.lines.push('')
74
+
75
+ fn indIn():
76
+ self.level = self.level + 1
77
+
78
+ fn indOut():
79
+ if self.level > 0: self.level = self.level - 1
80
+
81
+ fn generate(ast):
82
+ self.clsReg = buildClassRegistry(ast)
83
+ self.lines = []
84
+ self.level = 0
85
+ self._needsFmt = false
86
+ self.emit('// Generated by Flux Transpiler v3.1.0 (self-hosted)')
87
+ self.emit('"use strict";')
88
+ self.blank()
89
+ for node in ast.body: self.genStmt(node)
90
+ if self._needsFmt:
91
+ self.lines.splice(2, 0, FMT_HELPER)
92
+ return { code: self.lines.join('\n'), smBuilder: self.smBuilder }
93
+
94
+ fn genStmt(node):
95
+ if node.type == 'VarDecl': self.genVar(node)
96
+ else if node.type == 'DestructureDecl': self.genDestructure(node)
97
+ else if node.type == 'FnDecl': self.genFn(node, '')
98
+ else if node.type == 'ClassDecl': self.genClass(node)
99
+ else if node.type == 'IfStmt': self.genIf(node)
100
+ else if node.type == 'ForInStmt': self.genFor(node)
101
+ else if node.type == 'WhileStmt': self.genWhile(node)
102
+ else if node.type == 'MatchStmt': self.genMatch(node)
103
+ else if node.type == 'ReturnStmt': self.genReturn(node)
104
+ else if node.type == 'TryCatchStmt': self.genTryCatch(node)
105
+ else if node.type == 'ThrowStmt': self.genThrow(node)
106
+ else if node.type == 'DoWhileStmt': self.genDoWhile(node)
107
+ else if node.type == 'BreakStmt': self.emit('break;')
108
+ else if node.type == 'ContinueStmt': self.emit('continue;')
109
+ else if node.type == 'ImportDecl': self.genImport(node)
110
+ else if node.type == 'ExportDecl': self.genExport(node)
111
+ else if node.type == 'TypeDecl': self.genTypeDecl(node)
112
+ else if node.type == 'InterfaceDecl': self.genInterfaceDecl(node)
113
+ else if node.type == 'EnumDecl': self.genEnumDecl(node)
114
+ else if node.type == 'ExprStmt': self.emit(self.genExpr(node.expr) + ';')
115
+ else:
116
+ throw new Error("Unknown statement: {node.type}")
117
+
118
+ fn genVar(node):
119
+ val kw = node.kind == 'val' ? 'const' : 'let'
120
+ val init = node.init ? ' = ' + self.genExpr(node.init) : ''
121
+ self.emit(kw + ' ' + node.name + init + ';')
122
+
123
+ fn genObjPair(p):
124
+ if p.spread: return '...' + self.genExpr(p.value)
125
+ val isIdent = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(p.key)
126
+ val keyStr = isIdent ? p.key : '"' + p.key + '"'
127
+ if isIdent and p.value and p.value.type == 'Identifier' and p.value.name == p.key:
128
+ return p.key
129
+ return keyStr + ': ' + self.genExpr(p.value)
130
+
131
+ fn genDestructProp(p):
132
+ if p.rest: return '...' + p.key
133
+ var s = p.alias != p.key ? p.key + ': ' + p.alias : p.key
134
+ if p.defaultVal: s = s + ' = ' + self.genExpr(p.defaultVal)
135
+ return s
136
+
137
+ fn genDestructItem(p):
138
+ if not p: return ''
139
+ if p.rest: return '...' + p.name
140
+ var s = p.name
141
+ if p.defaultVal: s = s + ' = ' + self.genExpr(p.defaultVal)
142
+ return s
143
+
144
+ fn genDestructure(node):
145
+ val kw = node.kind == 'val' ? 'const' : 'let'
146
+ val init = self.genExpr(node.init)
147
+ if node.patternType == 'object':
148
+ val props = node.pattern.map(p -> self.genDestructProp(p)).join(', ')
149
+ self.emit(kw + ' { ' + props + ' } = ' + init + ';')
150
+ else:
151
+ val items = node.pattern.map(p -> self.genDestructItem(p)).join(', ')
152
+ self.emit(kw + ' [' + items + '] = ' + init + ';')
153
+
154
+ fn genFn(node, prefix):
155
+ val asyncKw = node.async ? 'async ' : ''
156
+ val name = node.name ?? ''
157
+ val params = node.params.map(p ->
158
+ p.rest ? '...' + p.name :
159
+ p.defaultVal ? p.name + ' = ' + self.genExpr(p.defaultVal) :
160
+ p.name
161
+ ).join(', ')
162
+ if node.inline:
163
+ self.emit(prefix + asyncKw + 'function ' + name + '(' + params + ') { return ' + self.genExpr(node.body) + '; }')
164
+ else:
165
+ self.emit(prefix + asyncKw + 'function ' + name + '(' + params + ') {')
166
+ self.indIn()
167
+ for s in node.body: self.genStmt(s)
168
+ self.indOut()
169
+ self.emit('}')
170
+
171
+ fn genClass(node):
172
+ val ext = node.superClass ? ' extends ' + node.superClass : ''
173
+ self.emit('class ' + node.name + ext + ' {')
174
+ self.indIn()
175
+ val allFields = getAllFields(node.name, self.clsReg, null)
176
+ if allFields.length > 0:
177
+ val params = allFields.map(f -> f.name).join(', ')
178
+ self.emit('constructor(' + params + ') {')
179
+ self.indIn()
180
+ if node.superClass and self.clsReg[node.superClass]:
181
+ val parentFields = getAllFields(node.superClass, self.clsReg, null)
182
+ if parentFields.length > 0:
183
+ self.emit('super(' + parentFields.map(f -> f.name).join(', ') + ');')
184
+ for f in node.fields: self.emit('this.' + f.name + ' = ' + f.name + ';')
185
+ self.indOut()
186
+ self.emit('}')
187
+ self.blank()
188
+ for m in node.methods:
189
+ val asyncKw = m.async ? 'async ' : ''
190
+ val staticKw = (m.modifiers and m.modifiers.has('static')) ? 'static ' : ''
191
+ val params = m.params.map(p ->
192
+ p.rest ? '...' + p.name :
193
+ p.defaultVal ? p.name + ' = ' + self.genExpr(p.defaultVal) :
194
+ p.name
195
+ ).join(', ')
196
+ if m.inline:
197
+ self.emit(staticKw + asyncKw + m.name + '(' + params + ') { return ' + self.genExpr(m.body) + '; }')
198
+ else:
199
+ self.emit(staticKw + asyncKw + m.name + '(' + params + ') {')
200
+ self.indIn()
201
+ for s in m.body: self.genStmt(s)
202
+ self.indOut()
203
+ self.emit('}')
204
+ self.blank()
205
+ self.indOut()
206
+ self.emit('}')
207
+ self.blank()
208
+
209
+ fn genIf(node):
210
+ self.emit('if (' + self.genExpr(node.cond) + ') {')
211
+ self.indIn()
212
+ for s in node.then: self.genStmt(s)
213
+ self.indOut()
214
+ self.emit('}')
215
+ for ei in node.elseifs:
216
+ self.emitRaw(self.i() + 'else if (' + self.genExpr(ei.cond) + ') {')
217
+ self.indIn()
218
+ for s in ei.body: self.genStmt(s)
219
+ self.indOut()
220
+ self.emit('}')
221
+ if node.else_:
222
+ self.emitRaw(self.i() + 'else {')
223
+ self.indIn()
224
+ for s in node.else_: self.genStmt(s)
225
+ self.indOut()
226
+ self.emit('}')
227
+
228
+ fn genFor(node):
229
+ val varName = node["var"]
230
+ val iter = node.iter
231
+ if iter.type == 'RangeExpr':
232
+ val s = self.genExpr(iter.start)
233
+ val e = self.genExpr(iter.end)
234
+ self.emit('for (let ' + varName + ' = ' + s + '; ' + varName + ' < ' + e + '; ' + varName + '++) {')
235
+ else if node.isAwait:
236
+ self.emit('for await (const ' + varName + ' of ' + self.genExpr(iter) + ') {')
237
+ else:
238
+ self.emit('for (const ' + varName + ' of ' + self.genExpr(iter) + ') {')
239
+ self._loopDepth = self._loopDepth + 1
240
+ self.indIn()
241
+ for s in node.body: self.genStmt(s)
242
+ self.indOut()
243
+ self._loopDepth = self._loopDepth - 1
244
+ self.emit('}')
245
+
246
+ fn genWhile(node):
247
+ self.emit('while (' + self.genExpr(node.cond) + ') {')
248
+ self._loopDepth = self._loopDepth + 1
249
+ self.indIn()
250
+ for s in node.body: self.genStmt(s)
251
+ self.indOut()
252
+ self._loopDepth = self._loopDepth - 1
253
+ self.emit('}')
254
+
255
+ fn genMatch(node):
256
+ val subj = self.genExpr(node.subject)
257
+ val arms = node.arms
258
+ var hasOpenIf = false
259
+ for arm in arms:
260
+ val pat = arm.pattern
261
+ var cond = ""
262
+ val bindings = []
263
+ if pat.type == 'WildcardPat':
264
+ cond = ""
265
+ else if pat.type == 'RangePat':
266
+ val lo = self.genExpr(pat.start)
267
+ val hi = self.genExpr(pat.end)
268
+ cond = subj + ' >= ' + lo + ' && ' + subj + ' <= ' + hi
269
+ else if pat.type == 'VariantPat':
270
+ cond = subj + '?.__type === "' + pat.variant + '"'
271
+ var bi = 0
272
+ for b in pat.bindings:
273
+ bindings.push('const ' + b + ' = ' + subj + '.__args[' + bi + '];')
274
+ bi = bi + 1
275
+ else:
276
+ val patExpr = self.genExpr(pat.value)
277
+ if pat.value.type == 'Identifier' and /^[A-Z]/.test(pat.value.name):
278
+ cond = '(' + subj + ' === ' + patExpr + ' || ' + subj + '?.__type === "' + pat.value.name + '")'
279
+ else:
280
+ cond = subj + ' === ' + patExpr
281
+ if arm.guard:
282
+ val guardSrc = self.genExpr(arm.guard)
283
+ if bindings.length > 0:
284
+ val rebindings = bindings.map(bx -> bx.replace('const ', 'var ')).join(' ')
285
+ val iife = '(function(){ ' + rebindings + ' return (' + guardSrc + '); }())'
286
+ cond = cond ? '(' + cond + ') && ' + iife : iife
287
+ else:
288
+ cond = cond ? '(' + cond + ') && (' + guardSrc + ')' : guardSrc
289
+ if not hasOpenIf and cond:
290
+ self.emit('if (' + cond + ') {')
291
+ hasOpenIf = true
292
+ else if not hasOpenIf and not cond:
293
+ self.emit('if (true) {')
294
+ hasOpenIf = true
295
+ else if hasOpenIf and cond:
296
+ self.emitRaw(self.i() + 'else if (' + cond + ') {')
297
+ else:
298
+ self.emitRaw(self.i() + 'else {')
299
+ self.indIn()
300
+ for b in bindings: self.emit(b)
301
+ if arm.inline:
302
+ val exprSrc = self.genExpr(arm.body[0].expr)
303
+ if self._loopDepth > 0:
304
+ self.emit(exprSrc + ';')
305
+ else:
306
+ self.emit('return ' + exprSrc + ';')
307
+ else:
308
+ for s in arm.body: self.genStmt(s)
309
+ self.indOut()
310
+ self.emit('}')
311
+
312
+ fn genInterfaceDecl(node):
313
+ self.blank()
314
+ self.emit('// interface ' + node.name)
315
+ self.blank()
316
+
317
+ fn genEnumDecl(node):
318
+ self.blank()
319
+ val pairs = node.members.map(m -> m.name + ': ' + self.genExpr(m.value)).join(', ')
320
+ self.emit('const ' + node.name + ' = Object.freeze({ ' + pairs + ' });')
321
+ self.blank()
322
+
323
+ fn genTypeDecl(node):
324
+ self.blank()
325
+ self.emit('// ADT type: ' + node.name)
326
+ for v in node.variants:
327
+ if v.fields.length == 0:
328
+ self.emit('const ' + v.name + ' = Object.freeze({ __type: "' + v.name + '", __args: [] });')
329
+ else:
330
+ val params = v.fields.join(', ')
331
+ val argsArr = '[' + v.fields.join(', ') + ']'
332
+ val fieldsObj = v.fields.map(f -> f + ': ' + f).join(', ')
333
+ self.emit('function ' + v.name + '(' + params + ') { return Object.freeze({ __type: "' + v.name + '", __args: ' + argsArr + ', ' + fieldsObj + ' }); }')
334
+ self.blank()
335
+
336
+ fn genReturn(node):
337
+ if node.value: self.emit('return ' + self.genExpr(node.value) + ';')
338
+ else: self.emit('return;')
339
+
340
+ fn genTryCatch(node):
341
+ self.emit('try {')
342
+ self.indIn()
343
+ for s in node.tryBody: self.genStmt(s)
344
+ self.indOut()
345
+ self.emit('}')
346
+ if node.catchBody:
347
+ val param = node.catchParam ? '(' + node.catchParam + ')' : '(_err)'
348
+ self.emitRaw(self.i() + 'catch ' + param + ' {')
349
+ self.indIn()
350
+ for s in node.catchBody: self.genStmt(s)
351
+ self.indOut()
352
+ self.emit('}')
353
+ if node.finallyBody:
354
+ self.emitRaw(self.i() + 'finally {')
355
+ self.indIn()
356
+ for s in node.finallyBody: self.genStmt(s)
357
+ self.indOut()
358
+ self.emit('}')
359
+
360
+ fn genThrow(node):
361
+ self.emit('throw ' + self.genExpr(node.value) + ';')
362
+
363
+ fn genDoWhile(node):
364
+ self.emit('do {')
365
+ self.indIn()
366
+ for s in node.body: self.genStmt(s)
367
+ self.indOut()
368
+ self.emit('} while (' + self.genExpr(node.cond) + ');')
369
+
370
+ fn genImport(node):
371
+ val src = String(node.source)
372
+ if node.namespaceName:
373
+ self.emit('const ' + node.namespaceName + ' = require("' + src + '");')
374
+ else if node.defaultName:
375
+ self.emit('const ' + node.defaultName + ' = require("' + src + '");')
376
+ else if node.names.length:
377
+ val parts = node.names.map(n -> (typeof n == 'string' ? n : (n.name != n.alias ? n.name + ': ' + n.alias : n.name))).join(', ')
378
+ self.emit('const { ' + parts + ' } = require("' + src + '");')
379
+
380
+ fn genExport(node):
381
+ if node.isDefault:
382
+ self.emit('module.exports = ' + self.genExpr(node.decl) + ';')
383
+ return
384
+ val decl = node.decl
385
+ if decl.type == 'FnDecl':
386
+ val asyncKw = decl.async ? 'async ' : ''
387
+ val params = decl.params.map(p ->
388
+ p.rest ? '...' + p.name :
389
+ p.defaultVal ? p.name + ' = ' + self.genExpr(p.defaultVal) :
390
+ p.name
391
+ ).join(', ')
392
+ if decl.inline:
393
+ self.emit(asyncKw + 'function ' + decl.name + '(' + params + ') { return ' + self.genExpr(decl.body) + '; }')
394
+ else:
395
+ self.emit(asyncKw + 'function ' + decl.name + '(' + params + ') {')
396
+ self.indIn()
397
+ for s in decl.body: self.genStmt(s)
398
+ self.indOut()
399
+ self.emit('}')
400
+ self.emit('module.exports.' + decl.name + ' = ' + decl.name + ';')
401
+ else if decl.type == 'ClassDecl':
402
+ self.genClass(decl)
403
+ self.emit('module.exports.' + decl.name + ' = ' + decl.name + ';')
404
+ else if decl.type == 'TypeDecl':
405
+ self.genTypeDecl(decl)
406
+ for v in decl.variants: self.emit('module.exports.' + v.name + ' = ' + v.name + ';')
407
+ else if decl.type == 'InterfaceDecl':
408
+ self.genInterfaceDecl(decl)
409
+ else if decl.type == 'EnumDecl':
410
+ self.genEnumDecl(decl)
411
+ self.emit('module.exports.' + decl.name + ' = ' + decl.name + ';')
412
+ else if decl.type == 'VarDecl':
413
+ val kw = decl.kind == 'val' ? 'const' : 'let'
414
+ self.emit(kw + ' ' + decl.name + ' = ' + self.genExpr(decl.init) + ';')
415
+ self.emit('module.exports.' + decl.name + ' = ' + decl.name + ';')
416
+ else:
417
+ self.genStmt(decl)
418
+
419
+ fn genExpr(node):
420
+ if not node: return 'undefined'
421
+ if node.type == 'NumberLit': return String(node.value)
422
+ if node.type == 'BoolLit': return String(node.value)
423
+ if node.type == 'NullLit': return 'null'
424
+ if node.type == 'SelfExpr': return 'this'
425
+ if node.type == 'Identifier': return node.name == 'print' ? 'console.log' : node.name
426
+ if node.type == 'StringLit': return JSON.stringify(node.value)
427
+ if node.type == 'RegexLit': return '/' + node.value.pattern + '/' + node.value.flags
428
+ if node.type == 'TemplateLit': return self.genTemplate(node.parts)
429
+ if node.type == 'BinaryExpr':
430
+ return '(' + self.genExpr(node.left) + ' ' + node.op + ' ' + self.genExpr(node.right) + ')'
431
+ if node.type == 'UnaryExpr':
432
+ return node.op + self.genExpr(node.operand)
433
+ if node.type == 'UpdateExpr':
434
+ if node.prefix: return node.op + self.genExpr(node.operand)
435
+ return self.genExpr(node.operand) + node.op
436
+ if node.type == 'TernaryExpr':
437
+ return '(' + self.genExpr(node.cond) + ' ? ' + self.genExpr(node.then) + ' : ' + self.genExpr(node.else_) + ')'
438
+ if node.type == 'AssignExpr':
439
+ return self.genExpr(node.target) + ' ' + node.op + ' ' + self.genExpr(node.value)
440
+ if node.type == 'AwaitExpr':
441
+ return 'await ' + self.genExpr(node.operand)
442
+ if node.type == 'TypeofExpr':
443
+ return 'typeof ' + self.genExpr(node.operand)
444
+ if node.type == 'SpreadExpr':
445
+ return '...' + self.genExpr(node.expr)
446
+ if node.type == 'CallExpr':
447
+ val callee = self.genExpr(node.callee)
448
+ val args = node.args.map(a -> self.genExpr(a)).join(', ')
449
+ return callee + '(' + args + ')'
450
+ if node.type == 'MemberExpr':
451
+ val objSrc = self.genExpr(node.obj)
452
+ val wrapped = node.obj.type == 'NumberLit' ? '(' + objSrc + ')' : objSrc
453
+ return wrapped + '.' + node.prop
454
+ if node.type == 'OptMemberExpr':
455
+ return self.genExpr(node.obj) + '?.' + node.prop
456
+ if node.type == 'OptIndexExpr':
457
+ return self.genExpr(node.obj) + '?.[' + self.genExpr(node.idx) + ']'
458
+ if node.type == 'OptCallExpr':
459
+ val args = node.args.map(a -> self.genExpr(a)).join(', ')
460
+ return self.genExpr(node.callee) + '?.(' + args + ')'
461
+ if node.type == 'IndexExpr':
462
+ return self.genExpr(node.obj) + '[' + self.genExpr(node.idx) + ']'
463
+ if node.type == 'NewExpr':
464
+ val args = node.args.map(a -> self.genExpr(a)).join(', ')
465
+ return 'new ' + node.callee + '(' + args + ')'
466
+ if node.type == 'LambdaExpr':
467
+ val params = node.params.map(p -> p.rest ? '...' + p.name : p.name).join(', ')
468
+ return '(' + params + ') => ' + self.genExpr(node.body)
469
+ if node.type == 'FnDecl':
470
+ val asyncKw = node.async ? 'async ' : ''
471
+ val params = node.params.map(p ->
472
+ p.rest ? '...' + p.name :
473
+ p.defaultVal ? p.name + ' = ' + self.genExpr(p.defaultVal) :
474
+ p.name
475
+ ).join(', ')
476
+ if node.inline:
477
+ return asyncKw + 'function(' + params + ') { return ' + self.genExpr(node.body) + '; }'
478
+ val saved = self.lines.length
479
+ val savedLevel = self.level
480
+ self.emit(asyncKw + 'function(' + params + ') {')
481
+ self.indIn()
482
+ for s in node.body: self.genStmt(s)
483
+ self.indOut()
484
+ self.emit('}')
485
+ val block = self.lines.splice(saved).map(l -> l.trimStart()).join(' ')
486
+ self.level = savedLevel
487
+ return block
488
+ if node.type == 'MatchStmt':
489
+ val saved = self.lines.length
490
+ val savedLevel = self.level
491
+ val savedLoop = self._loopDepth
492
+ self._loopDepth = 0
493
+ self.emit('(() => {')
494
+ self.indIn()
495
+ self.genMatch(node)
496
+ self.indOut()
497
+ self.emit('})()')
498
+ self._loopDepth = savedLoop
499
+ val block = self.lines.splice(saved).map(l -> l.trimStart()).join(' ')
500
+ self.level = savedLevel
501
+ return block
502
+ if node.type == 'ArrayExpr':
503
+ return '[' + node.items.map(i -> self.genExpr(i)).join(', ') + ']'
504
+ if node.type == 'ObjectExpr':
505
+ val pairs = node.pairs.map(p -> self.genObjPair(p)).join(', ')
506
+ return '{ ' + pairs + ' }'
507
+ if node.type == 'RangeExpr':
508
+ return 'Array.from({ length: ' + self.genExpr(node.end) + ' - ' + self.genExpr(node.start) + ' }, (_, i) => i + ' + self.genExpr(node.start) + ')'
509
+ if node.type == 'PipeExpr':
510
+ return self.genPipe(node)
511
+ if node.type == 'CastExpr': return self.genExpr(node.expr)
512
+ if node.type == 'AsConstExpr': return 'Object.freeze(' + self.genExpr(node.expr) + ')'
513
+ if node.type == 'SatisfiesExpr': return self.genExpr(node.expr)
514
+ if node.type == 'IsExpr': return self.genExpr(node.expr)
515
+ if node.type == 'NonNullExpr': return self.genExpr(node.expr)
516
+ throw new Error("Unknown expression: {node.type}")
517
+
518
+ fn genTemplate(parts):
519
+ var result = '`'
520
+ for p in parts:
521
+ if p.type == 'text':
522
+ result += p.value.replace(/`/g, '\\`').replace(/\$/g, '\\$')
523
+ else:
524
+ val fmtInfo = extractFormatSpec(p.value)
525
+ val exprSrc = fmtInfo ? fmtInfo.expr : p.value
526
+ val fmt = fmtInfo ? fmtInfo.fmt : null
527
+ try:
528
+ val tokens = lexerize(exprSrc).tokenize()
529
+ val expr = makeParser(tokens).parseExpr()
530
+ val gen = self.genExpr(expr)
531
+ if fmt:
532
+ self._needsFmt = true
533
+ result += '$' + '{_fmt(' + gen + ', ' + JSON.stringify(fmt) + ')}'
534
+ else:
535
+ result += '$' + '{' + gen + '}'
536
+ catch(err):
537
+ result += '$' + '{' + p.value + '}'
538
+ result += '`'
539
+ return result
540
+
541
+ fn genPipe(node):
542
+ val left = self.genExpr(node.left)
543
+ val right = node.right
544
+ if right.type == 'Identifier':
545
+ return self.genExpr(right) + '(' + left + ')'
546
+ if right.type == 'CallExpr':
547
+ val fn_ = self.genExpr(right.callee)
548
+ val rest = right.args.map(a -> self.genExpr(a)).join(', ')
549
+ return rest ? fn_ + '(' + left + ', ' + rest + ')' : fn_ + '(' + left + ')'
550
+ return '(' + self.genExpr(right) + ')(' + left + ')'
551
+
552
+ export fn makeCodeGen(opts):
553
+ val ind = opts?.indent ?? ' '
554
+ val smb = opts?.smBuilder ?? null
555
+ return new CodeGenerator(ind, 0, [], {}, smb, false, 0)