@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,167 @@
1
+ // ============================================================
2
+ // Flux Self-Hosted Bundler
3
+ // src/self/bundler.flux — written in Flux, compiled by stage-0
4
+ //
5
+ // Bundles multiple Flux modules into a single JS file.
6
+ // ============================================================
7
+
8
+ import Fs from "fs"
9
+ import Path from "path"
10
+ import { lexerize } from './lexer'
11
+ import { makeParser } from './parser'
12
+ import { makeCodeGen } from './codegen'
13
+
14
+ fn toModuleId(absPath):
15
+ val base = Path.basename(absPath, '.flux')
16
+ return '_flux_' + base.replace(/[^a-zA-Z0-9]/g, '_')
17
+
18
+ fn extractModuleInfo(ast, fromFile):
19
+ val imports = []
20
+ val exports = []
21
+ val body = []
22
+ val dir = Path.dirname(fromFile)
23
+
24
+ for node in ast.body:
25
+ if node.type == 'ImportDecl':
26
+ var src = node.source
27
+ if not src.endsWith('.flux'): src = src + '.flux'
28
+ val absPath = Path.resolve(dir, src)
29
+ imports.push({ names: node.names, source: node.source, absPath })
30
+
31
+ else if node.type == 'ExportDecl':
32
+ val inner = node.decl
33
+ if inner.type == 'FnDecl': exports.push(inner.name)
34
+ if inner.type == 'ClassDecl': exports.push(inner.name)
35
+ if inner.type == 'VarDecl': exports.push(inner.name)
36
+ body.push(inner)
37
+
38
+ else:
39
+ body.push(node)
40
+
41
+ return { cleanAst: { type: 'Program', body }, imports, exports }
42
+
43
+ fn codegenModule(ast):
44
+ val cg = makeCodeGen({ indent: ' ' })
45
+ val result = cg.generate(ast)
46
+ val lines = result.code.split('\n')
47
+ val start = lines.findIndex(l -> l.trim() != '' and not l.includes('Generated by Flux') and not l.includes('"use strict"'))
48
+ return lines.slice(start).join('\n')
49
+
50
+ export class Bundler:
51
+ entryFile: string
52
+ options: any
53
+ modules: any
54
+ order: any[]
55
+ visited: any
56
+ inStack: any
57
+
58
+ fn bundle():
59
+ self.collect(self.entryFile)
60
+ return self.link()
61
+
62
+ fn collect(absPath):
63
+ if self.visited.has(absPath): return
64
+ if self.inStack.has(absPath):
65
+ throw new Error("[" + Path.basename(absPath) + "] Circular dependency detected: " + absPath)
66
+ if not Fs.existsSync(absPath):
67
+ throw new Error("[" + Path.basename(absPath) + "] File not found: " + absPath)
68
+
69
+ self.inStack.add(absPath)
70
+
71
+ val source = Fs.readFileSync(absPath, 'utf8')
72
+
73
+ var ast = null
74
+ try:
75
+ val tokens = lexerize(source).tokenize()
76
+ ast = makeParser(tokens).parse()
77
+ catch(e):
78
+ throw new Error("[" + Path.basename(absPath) + "] Parse error: " + e.message)
79
+
80
+ val info = extractModuleInfo(ast, absPath)
81
+ self.modules.set(absPath, { cleanAst: info.cleanAst, imports: info.imports, exports: info.exports, source, absPath })
82
+
83
+ for imp in info.imports:
84
+ self.collect(imp.absPath)
85
+
86
+ self.inStack.delete(absPath)
87
+ self.visited.add(absPath)
88
+ self.order.push(absPath)
89
+
90
+ fn link():
91
+ val lines = []
92
+ val banner = self.options.banner != false
93
+
94
+ if banner:
95
+ lines.push('// Generated by Flux Bundler (self-hosted)')
96
+ lines.push('// Entry: ' + Path.basename(self.entryFile))
97
+ lines.push('// Modules: ' + self.order.length)
98
+ lines.push('"use strict";')
99
+ lines.push('')
100
+
101
+ lines.push('(function() {')
102
+ lines.push('')
103
+
104
+ for absPath in self.order:
105
+ val mod = self.modules.get(absPath)
106
+ val isEntry = absPath == self.entryFile
107
+ val modId = toModuleId(absPath)
108
+ val relName = Path.relative(process.cwd(), absPath)
109
+
110
+ lines.push(' // ' + '─'.repeat(60))
111
+ lines.push(' // Module: ' + relName)
112
+ lines.push(' // ' + '─'.repeat(60))
113
+
114
+ if not isEntry:
115
+ lines.push(' var ' + modId + ' = (function() {')
116
+ lines.push(' var _exports = {};')
117
+ lines.push('')
118
+
119
+ for imp in mod.imports:
120
+ val srcId = toModuleId(imp.absPath)
121
+ if imp.names.length == 1:
122
+ lines.push(' var ' + imp.names[0] + ' = ' + srcId + ';')
123
+ else:
124
+ for name in imp.names:
125
+ lines.push(' var ' + name + ' = ' + srcId + '._exports ? ' + srcId + '._exports.' + name + ' : ' + srcId + '.' + name + ';')
126
+
127
+ if mod.imports.length > 0: lines.push('')
128
+
129
+ val moduleCode = codegenModule(mod.cleanAst)
130
+ lines.push(moduleCode)
131
+
132
+ if not isEntry:
133
+ if mod.exports.length > 0:
134
+ lines.push('')
135
+ for expName in mod.exports:
136
+ lines.push(' _exports.' + expName + ' = ' + expName + ';')
137
+ lines.push('')
138
+ lines.push(' return _exports;')
139
+ lines.push(' })()')
140
+ lines.push(' ' + modId + '._exports = ' + modId + ';')
141
+
142
+ lines.push('')
143
+
144
+ lines.push('})();')
145
+ return { code: lines.join('\n'), modules: self.order.length }
146
+
147
+ export fn makeBundler(entryFile, options):
148
+ return new Bundler(
149
+ Path.resolve(entryFile),
150
+ options ?? {},
151
+ new Map(),
152
+ [],
153
+ new Set(),
154
+ new Set()
155
+ )
156
+
157
+ export fn bundle(entryFile, options):
158
+ val result = { success: false, code: '', modules: 0, errors: [] }
159
+ try:
160
+ val b = makeBundler(entryFile, options)
161
+ val out = b.bundle()
162
+ result.code = out.code
163
+ result.modules = out.modules
164
+ result.success = true
165
+ catch(e):
166
+ result.errors.push({ message: e.message, name: e.name, file: null })
167
+ return result
@@ -0,0 +1,187 @@
1
+ // ── Flux stdlib ──
2
+
3
+ function findIndex(arr, fn) { return arr.findIndex(fn); }
4
+
5
+ function join(arr, sep) { return arr.join(sep != null ? sep : ','); }
6
+
7
+ function includes(arr, val) { return arr.includes(val); }
8
+ // ── end stdlib ──
9
+
10
+ // Generated by Flux Transpiler v3.1.0
11
+ "use strict";
12
+
13
+ const Fs = require("fs");
14
+ const Path = require("path");
15
+ const { lexerize } = require("./lexer");
16
+ const { makeParser } = require("./parser");
17
+ const { makeCodeGen } = require("./codegen");
18
+ function toModuleId(absPath) {
19
+ const base = Path.basename(absPath, ".flux");
20
+ return ("_flux_" + base.replace(/[^a-zA-Z0-9]/g, "_"));
21
+ }
22
+ function extractModuleInfo(ast, fromFile) {
23
+ const imports = [];
24
+ const exports = [];
25
+ const body = [];
26
+ const dir = Path.dirname(fromFile);
27
+ for (const node of ast.body) {
28
+ if ((node.type == "ImportDecl")) {
29
+ let src = node.source;
30
+ if (!src.endsWith(".flux")) {
31
+ src = (src + ".flux");
32
+ }
33
+ const absPath = Path.resolve(dir, src);
34
+ imports.push({ names: node.names, source: node.source, absPath });
35
+ }
36
+ else if ((node.type == "ExportDecl")) {
37
+ const inner = node.decl;
38
+ if ((inner.type == "FnDecl")) {
39
+ exports.push(inner.name);
40
+ }
41
+ if ((inner.type == "ClassDecl")) {
42
+ exports.push(inner.name);
43
+ }
44
+ if ((inner.type == "VarDecl")) {
45
+ exports.push(inner.name);
46
+ }
47
+ body.push(inner);
48
+ }
49
+ else {
50
+ body.push(node);
51
+ }
52
+ }
53
+ return { cleanAst: { type: "Program", body }, imports, exports };
54
+ }
55
+ function codegenModule(ast) {
56
+ const cg = makeCodeGen({ indent: " " });
57
+ const result = cg.generate(ast);
58
+ const lines = result.code.split("\n");
59
+ const start = lines.findIndex((l) => (((l.trim() != "") && !l.includes("Generated by Flux")) && !l.includes("\"use strict\"")));
60
+ return lines.slice(start).join("\n");
61
+ }
62
+ class Bundler {
63
+ constructor(entryFile, options, modules, order, visited, inStack) {
64
+ this.entryFile = entryFile;
65
+ this.options = options;
66
+ this.modules = modules;
67
+ this.order = order;
68
+ this.visited = visited;
69
+ this.inStack = inStack;
70
+ }
71
+
72
+ bundle() {
73
+ this.collect(this.entryFile);
74
+ return this.link();
75
+ }
76
+
77
+ collect(absPath) {
78
+ if (this.visited.has(absPath)) {
79
+ return;
80
+ }
81
+ if (this.inStack.has(absPath)) {
82
+ throw new Error(((("[" + Path.basename(absPath)) + "] Circular dependency detected: ") + absPath));
83
+ }
84
+ if (!Fs.existsSync(absPath)) {
85
+ throw new Error(((("[" + Path.basename(absPath)) + "] File not found: ") + absPath));
86
+ }
87
+ this.inStack.add(absPath);
88
+ const source = Fs.readFileSync(absPath, "utf8");
89
+ let ast = null;
90
+ try {
91
+ const tokens = lexerize(source).tokenize();
92
+ ast = makeParser(tokens).parse();
93
+ }
94
+ catch (e) {
95
+ throw new Error(((("[" + Path.basename(absPath)) + "] Parse error: ") + e.message));
96
+ }
97
+ const info = extractModuleInfo(ast, absPath);
98
+ this.modules.set(absPath, { cleanAst: info.cleanAst, imports: info.imports, exports: info.exports, source, absPath });
99
+ for (const imp of info.imports) {
100
+ this.collect(imp.absPath);
101
+ }
102
+ this.inStack.delete(absPath);
103
+ this.visited.add(absPath);
104
+ this.order.push(absPath);
105
+ }
106
+
107
+ link() {
108
+ const lines = [];
109
+ const banner = (this.options.banner != false);
110
+ if (banner) {
111
+ lines.push("// Generated by Flux Bundler (self-hosted)");
112
+ lines.push(("// Entry: " + Path.basename(this.entryFile)));
113
+ lines.push(("// Modules: " + this.order.length));
114
+ lines.push("\"use strict\";");
115
+ lines.push("");
116
+ }
117
+ lines.push("(function() {");
118
+ lines.push("");
119
+ for (const absPath of this.order) {
120
+ const mod = this.modules.get(absPath);
121
+ const isEntry = (absPath == this.entryFile);
122
+ const modId = toModuleId(absPath);
123
+ const relName = Path.relative(process.cwd(), absPath);
124
+ lines.push((" // " + "─".repeat(60)));
125
+ lines.push((" // Module: " + relName));
126
+ lines.push((" // " + "─".repeat(60)));
127
+ if (!isEntry) {
128
+ lines.push(((" var " + modId) + " = (function() {"));
129
+ lines.push(" var _exports = {};");
130
+ lines.push("");
131
+ }
132
+ for (const imp of mod.imports) {
133
+ const srcId = toModuleId(imp.absPath);
134
+ if ((imp.names.length == 1)) {
135
+ lines.push(((((" var " + imp.names[0]) + " = ") + srcId) + ";"));
136
+ }
137
+ else {
138
+ for (const name of imp.names) {
139
+ lines.push(((((((((((((" var " + name) + " = ") + srcId) + "._exports ? ") + srcId) + "._exports.") + name) + " : ") + srcId) + ".") + name) + ";"));
140
+ }
141
+ }
142
+ }
143
+ if ((mod.imports.length > 0)) {
144
+ lines.push("");
145
+ }
146
+ const moduleCode = codegenModule(mod.cleanAst);
147
+ lines.push(moduleCode);
148
+ if (!isEntry) {
149
+ if ((mod.exports.length > 0)) {
150
+ lines.push("");
151
+ for (const expName of mod.exports) {
152
+ lines.push(((((" _exports." + expName) + " = ") + expName) + ";"));
153
+ }
154
+ }
155
+ lines.push("");
156
+ lines.push(" return _exports;");
157
+ lines.push(" })()");
158
+ lines.push(((((" " + modId) + "._exports = ") + modId) + ";"));
159
+ }
160
+ lines.push("");
161
+ }
162
+ lines.push("})();");
163
+ return { code: lines.join("\n"), modules: this.order.length };
164
+ }
165
+
166
+ }
167
+
168
+ module.exports.Bundler = Bundler;
169
+ function makeBundler(entryFile, options) {
170
+ return new Bundler(Path.resolve(entryFile), (options ?? { }), new Map(), [], new Set(), new Set());
171
+ }
172
+ module.exports.makeBundler = makeBundler;
173
+ function bundle(entryFile, options) {
174
+ const result = { success: false, code: "", modules: 0, errors: [] };
175
+ try {
176
+ const b = makeBundler(entryFile, options);
177
+ const out = b.bundle();
178
+ result.code = out.code;
179
+ result.modules = out.modules;
180
+ result.success = true;
181
+ }
182
+ catch (e) {
183
+ result.errors.push({ message: e.message, name: e.name, file: null });
184
+ }
185
+ return result;
186
+ }
187
+ module.exports.bundle = bundle;
@@ -0,0 +1,249 @@
1
+ // ============================================================
2
+ // Flux Self-Hosted Static Checker
3
+ // src/self/checker.flux — written in Flux, compiled by stage-0
4
+ //
5
+ // Checks val immutability and basic scope correctness.
6
+ // ============================================================
7
+
8
+ export class CheckError:
9
+ message: any
10
+ loc: any
11
+ hint: any
12
+ name: any
13
+ line: any
14
+ col: any
15
+
16
+ export class Scope:
17
+ parent: any
18
+ vars: any
19
+
20
+ fn define(name, kind, loc):
21
+ self.vars.set(name, { kind, loc })
22
+
23
+ fn lookup(name):
24
+ var scope = self
25
+ while scope:
26
+ if scope.vars.has(name): return scope.vars.get(name)
27
+ scope = scope.parent
28
+ return null
29
+
30
+ fn isVal(name):
31
+ val entry = self.lookup(name)
32
+ return entry and entry.kind == "val"
33
+
34
+ export class Checker:
35
+ errors: any[]
36
+ warnings: any[]
37
+
38
+ fn error(msg, loc, hint):
39
+ val err = new CheckError()
40
+ err.message = msg
41
+ err.loc = loc
42
+ err.hint = hint ?? null
43
+ err.name = "CheckError"
44
+ if loc:
45
+ err.line = loc.line
46
+ err.col = loc.col
47
+ self.errors.push(err)
48
+
49
+ fn warn(msg, loc, hint):
50
+ val w = new CheckError()
51
+ w.message = msg
52
+ w.loc = loc
53
+ w.hint = hint ?? null
54
+ w.name = "CheckError"
55
+ if loc:
56
+ w.line = loc.line
57
+ w.col = loc.col
58
+ self.warnings.push(w)
59
+
60
+ fn check(ast):
61
+ self.errors = []
62
+ self.warnings = []
63
+ val scope = new Scope(null, new Map())
64
+ self.walkStmts(ast.body, scope)
65
+ return { errors: self.errors, warnings: self.warnings }
66
+
67
+ fn walkStmts(stmts, scope):
68
+ for node in stmts: self.walkStmt(node, scope)
69
+
70
+ fn walkStmt(node, scope):
71
+ if not node: return
72
+
73
+ if node.type == "VarDecl":
74
+ if node.init: self.walkExpr(node.init, scope)
75
+ scope.define(node.name, node.kind, node.loc)
76
+
77
+ else if node.type == "DestructureDecl":
78
+ if node.init: self.walkExpr(node.init, scope)
79
+ if node.patternType == "object":
80
+ for p in node.pattern: scope.define(p.alias, node.kind, node.loc)
81
+ else:
82
+ for p in node.pattern:
83
+ if p: scope.define(p.name, node.kind, node.loc)
84
+
85
+ else if node.type == "FnDecl":
86
+ val inner = new Scope(scope, new Map())
87
+ for p in node.params: inner.define(p.name, "val", node.loc)
88
+ if node.name: scope.define(node.name, "val", node.loc)
89
+ if node.inline:
90
+ self.walkExpr(node.body, inner)
91
+ else:
92
+ self.walkStmts(node.body, inner)
93
+
94
+ else if node.type == "ClassDecl":
95
+ scope.define(node.name, "val", node.loc)
96
+ val cls = new Scope(scope, new Map())
97
+ for f in node.fields: cls.define(f.name, "var", node.loc)
98
+ for m in node.methods: self.walkStmt(m, cls)
99
+
100
+ else if node.type == "TypeDecl":
101
+ for v in node.variants: scope.define(v.name, "val", node.loc)
102
+
103
+ else if node.type == "InterfaceDecl":
104
+ scope.define(node.name, "val", node.loc)
105
+
106
+ else if node.type == "EnumDecl":
107
+ scope.define(node.name, "val", node.loc)
108
+
109
+ else if node.type == "IfStmt":
110
+ self.walkExpr(node.cond, scope)
111
+ self.walkStmts(node.then, new Scope(scope, new Map()))
112
+ for ei in node.elseifs:
113
+ self.walkExpr(ei.cond, scope)
114
+ self.walkStmts(ei.body, new Scope(scope, new Map()))
115
+ if node.else_: self.walkStmts(node.else_, new Scope(scope, new Map()))
116
+
117
+ else if node.type == "ForInStmt":
118
+ self.walkExpr(node.iter, scope)
119
+ val inner = new Scope(scope, new Map())
120
+ inner.define(node.var, "val", node.loc)
121
+ self.walkStmts(node.body, inner)
122
+
123
+ else if node.type == "WhileStmt":
124
+ self.walkExpr(node.cond, scope)
125
+ self.walkStmts(node.body, new Scope(scope, new Map()))
126
+
127
+ else if node.type == "DoWhileStmt":
128
+ self.walkStmts(node.body, new Scope(scope, new Map()))
129
+ self.walkExpr(node.cond, scope)
130
+
131
+ else if node.type == "MatchStmt":
132
+ self.walkExpr(node.subject, scope)
133
+ for arm in node.arms:
134
+ val inner = new Scope(scope, new Map())
135
+ if arm.pattern.type == "VariantPat":
136
+ for b in arm.pattern.bindings: inner.define(b, "val", node.loc)
137
+ if arm.guard: self.walkExpr(arm.guard, inner)
138
+ self.walkStmts(arm.body, inner)
139
+
140
+ else if node.type == "ReturnStmt":
141
+ if node.value: self.walkExpr(node.value, scope)
142
+
143
+ else if node.type == "ThrowStmt":
144
+ self.walkExpr(node.value, scope)
145
+
146
+ else if node.type == "TryCatchStmt":
147
+ self.walkStmts(node.tryBody, new Scope(scope, new Map()))
148
+ if node.catchBody:
149
+ val inner = new Scope(scope, new Map())
150
+ if node.catchParam: inner.define(node.catchParam, "val", node.loc)
151
+ self.walkStmts(node.catchBody, inner)
152
+ if node.finallyBody: self.walkStmts(node.finallyBody, new Scope(scope, new Map()))
153
+
154
+ else if node.type == "ImportDecl":
155
+ if node.defaultName: scope.define(node.defaultName, "val", node.loc)
156
+ if node.namespaceName: scope.define(node.namespaceName, "val", node.loc)
157
+ for n in node.names:
158
+ val nm = typeof n == "string" ? n : n.alias
159
+ scope.define(nm, "val", node.loc)
160
+
161
+ else if node.type == "ExportDecl":
162
+ if node.isDefault:
163
+ self.walkExpr(node.decl, scope)
164
+ else:
165
+ self.walkStmt(node.decl, scope)
166
+
167
+ else if node.type == "ExprStmt":
168
+ self.walkExpr(node.expr, scope)
169
+
170
+ fn walkExpr(node, scope):
171
+ if not node: return
172
+
173
+ if node.type == "AssignExpr":
174
+ val target = node.target
175
+ if target.type == "Identifier":
176
+ if scope.isVal(target.name):
177
+ self.error(
178
+ "Cannot reassign 'val " + target.name + "' — val is immutable",
179
+ target.loc,
180
+ "Use 'var " + target.name + "' if you need a mutable variable"
181
+ )
182
+ self.walkExpr(node.target, scope)
183
+ self.walkExpr(node.value, scope)
184
+
185
+ else if node.type == "UpdateExpr":
186
+ if node.operand.type == "Identifier" and scope.isVal(node.operand.name):
187
+ self.error(
188
+ "Cannot apply '" + node.op + "' to 'val " + node.operand.name + "' — val is immutable",
189
+ node.operand.loc,
190
+ "Use 'var " + node.operand.name + "' if you need a mutable variable"
191
+ )
192
+ self.walkExpr(node.operand, scope)
193
+
194
+ else if node.type == "BinaryExpr" or node.type == "PipeExpr":
195
+ self.walkExpr(node.left, scope)
196
+ self.walkExpr(node.right, scope)
197
+
198
+ else if node.type == "UnaryExpr" or node.type == "AwaitExpr" or node.type == "TypeofExpr":
199
+ self.walkExpr(node.operand, scope)
200
+
201
+ else if node.type == "TernaryExpr":
202
+ self.walkExpr(node.cond, scope)
203
+ self.walkExpr(node.then, scope)
204
+ self.walkExpr(node.else_, scope)
205
+
206
+ else if node.type == "CallExpr" or node.type == "OptCallExpr":
207
+ self.walkExpr(node.callee, scope)
208
+ for a in node.args: self.walkExpr(a, scope)
209
+
210
+ else if node.type == "MemberExpr" or node.type == "OptMemberExpr":
211
+ self.walkExpr(node.obj, scope)
212
+
213
+ else if node.type == "IndexExpr" or node.type == "OptIndexExpr":
214
+ self.walkExpr(node.obj, scope)
215
+ self.walkExpr(node.idx, scope)
216
+
217
+ else if node.type == "NewExpr":
218
+ for a in node.args: self.walkExpr(a, scope)
219
+
220
+ else if node.type == "ArrayExpr":
221
+ for item in node.items:
222
+ if item: self.walkExpr(item, scope)
223
+
224
+ else if node.type == "ObjectExpr":
225
+ for pair in node.pairs:
226
+ if pair.value: self.walkExpr(pair.value, scope)
227
+
228
+ else if node.type == "SpreadExpr":
229
+ self.walkExpr(node.expr, scope)
230
+
231
+ else if node.type == "LambdaExpr":
232
+ val inner = new Scope(scope, new Map())
233
+ for p in node.params: inner.define(p.name, "val", null)
234
+ self.walkExpr(node.body, inner)
235
+
236
+ else if node.type == "FnDecl":
237
+ val inner = new Scope(scope, new Map())
238
+ for p in node.params: inner.define(p.name, "val", null)
239
+ if node.inline:
240
+ self.walkExpr(node.body, inner)
241
+ else:
242
+ self.walkStmts(node.body, inner)
243
+
244
+ else if node.type == "CastExpr" or node.type == "AsConstExpr" or node.type == "SatisfiesExpr" or node.type == "IsExpr" or node.type == "NonNullExpr":
245
+ self.walkExpr(node.expr, scope)
246
+
247
+ else if node.type == "RangeExpr":
248
+ self.walkExpr(node.start, scope)
249
+ self.walkExpr(node.end, scope)