xanascript 2.0.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.
@@ -0,0 +1,352 @@
1
+ export function generateOpt(node, types = {}, opts = {}) {
2
+ const unroll = opts.unroll !== false;
3
+
4
+ switch (node.type) {
5
+
6
+ case "Program": {
7
+ const body = node.body.filter(s => s.type !== "ExportStmt");
8
+ return body.map(n => {
9
+ sm(n.loc?.start?.line);
10
+ return generateOpt(n, types, opts);
11
+ }).join("\n");
12
+ }
13
+
14
+ case "Block":
15
+ return `{\n${node.body.filter(s => s.type !== "ExportStmt").map(n => " " + generateOpt(n, types, opts)).join("\n")}\n}`;
16
+
17
+ case "VarDecl": {
18
+ const init = generateOpt(node.init, types, opts);
19
+
20
+ if (node.init?.type === "ArrayExpr" && node.init.items.every(i => i.type === "Num")) {
21
+ const items = node.init.items.map(i => generateOpt(i, types, opts)).join(",");
22
+ return `let ${node.id}=new Float64Array([${items}])`;
23
+ }
24
+
25
+ return `let ${node.id}=${init}`;
26
+ }
27
+
28
+ case "Assign": {
29
+ if (node.left.type === "Member") {
30
+ return `${generateOpt(node.left.obj, types, opts)}.${node.left.prop}=${generateOpt(node.right, types, opts)}`;
31
+ }
32
+ if (node.left.type === "IndexExpr") {
33
+ return `${generateOpt(node.left.obj, types, opts)}[${generateOpt(node.left.index, types, opts)}]=${generateOpt(node.right, types, opts)}`;
34
+ }
35
+ return `${node.left.name}=${generateOpt(node.right, types, opts)}`;
36
+ }
37
+
38
+ case "IfStmt": {
39
+ const t = generateOpt(node.test, types, opts);
40
+ return `if(${t})${generateOpt(node.cons, types, opts)}${node.alt ? `else${generateOpt(node.alt, types, opts)}` : ""}`;
41
+ }
42
+
43
+ case "ForStmt": {
44
+ const test = generateOpt(node.test, types, opts);
45
+ const update = node.update ? generateOpt(node.update, types, opts) : "";
46
+
47
+ if (unroll && canUnroll(node)) {
48
+ return unrollLoop(node, types, opts);
49
+ }
50
+
51
+ if (node.init?.type === "VarDecl") {
52
+ return `for(let ${node.init.id}=${generateOpt(node.init.init, types, opts)};${test};${update})${generateOpt(node.body, types, opts)}`;
53
+ }
54
+ return `for(${node.init ? generateOpt(node.init, types, opts) : ""};${test};${update})${generateOpt(node.body, types, opts)}`;
55
+ }
56
+
57
+ case "WhileStmt":
58
+ return `while(${generateOpt(node.test, types, opts)})${generateOpt(node.body, types, opts)}`;
59
+
60
+ case "FunctionDecl":
61
+ return `function ${node.name}(${node.params.join(",")})${generateOpt(node.body, types, opts)}`;
62
+
63
+ case "ReturnStmt":
64
+ return node.arg ? `return ${generateOpt(node.arg, types, opts)}` : "return";
65
+
66
+ case "Call": {
67
+ const args = node.args.map(a => generateOpt(a, types, opts)).join(",");
68
+ const callee = generateOpt(node.callee, types, opts);
69
+
70
+ if (callee === "SOLTA_O_GRITO") return `console.log(${args})`;
71
+ if (callee === "FALA_BAIXO") return `console.warn(${args})`;
72
+ if (callee === "AGORA_VAI") return `(await __http(${args}))`;
73
+ if (callee === "ESPERA_AI") return `(await __sleep(${args}))`;
74
+ if (callee === "SORTEIA") return `__randInt(${args})`;
75
+ if (callee === "PARSEIA") return `JSON.parse(${args})`;
76
+ if (callee === "OUVE_AQUI") return `__env(${args})`;
77
+ if (callee === "TAMANHO") return `${args}.length`;
78
+ if (callee === "DIVIDE_TEXTO") return `${args[0]}.split(${args[1]})`;
79
+ if (callee === "ENCONTRA") return `${args[0]}.match(new RegExp(${args[1]}))`;
80
+ if (callee === "DECODIFICA_URL") return `decodeURIComponent(${args[0]})`;
81
+ if (callee === "JUNTAR") return `${args[0]}.join(${args[1]})`;
82
+ if (callee === "AGORA") return `Date.now()`;
83
+ if (callee === "CRIA_SERVIDOR") return `__createServer(${args[0]},${args[1]})`;
84
+ if (callee === "PARA_SERVIDOR") return `__stopServer(${args[0]})`;
85
+
86
+ return `${callee}(${args})`;
87
+ }
88
+
89
+ case "Member":
90
+ return `${generateOpt(node.obj, types, opts)}.${node.prop}`;
91
+
92
+ case "IndexExpr":
93
+ return `${generateOpt(node.obj, types, opts)}[${generateOpt(node.index, types, opts)}]`;
94
+
95
+ case "Binary": {
96
+ const l = generateOpt(node.left, types, opts);
97
+ const r = generateOpt(node.right, types, opts);
98
+
99
+ const op = { "~=": "~=" }[node.op] || node.op;
100
+
101
+ const lt = typeOf(node.left, types);
102
+ const rt = typeOf(node.right, types);
103
+
104
+ if (lt === "NUM" && rt === "NUM" && ["+", "-", "*"].includes(op)) {
105
+ return `((${l}${op}${r})|0)`;
106
+ }
107
+
108
+ if (op === "~=") return `(new RegExp(${r}).test(${l}))`;
109
+
110
+ return `(${l}${op}${r})`;
111
+ }
112
+
113
+ case "Unary":
114
+ return `${node.op}${generateOpt(node.arg, types, opts)}`;
115
+
116
+ case "Ternary":
117
+ return `(${generateOpt(node.test, types, opts)}?${generateOpt(node.cons, types, opts)}:${generateOpt(node.alt, types, opts)})`;
118
+
119
+ case "Ident":
120
+ return node.name;
121
+
122
+ case "Num":
123
+ return String(node.value);
124
+
125
+ case "Str":
126
+ return JSON.stringify(node.value);
127
+
128
+ case "Bool":
129
+ return node.value ? "1" : "0";
130
+
131
+ case "Nil":
132
+ return "null";
133
+
134
+ case "ArrayExpr":
135
+ return `[${node.items.map(i => generateOpt(i, types, opts)).join(",")}]`;
136
+
137
+ case "ObjectExpr":
138
+ return `{${node.props.map(p => `${p.key}:${generateOpt(p.value, types, opts)}`).join(",")}}`;
139
+
140
+ case "ImportExpr":
141
+ return `(await __require(${JSON.stringify(node.path)}))`;
142
+ case "ImportStmt":
143
+ return `await __require(${JSON.stringify(node.path)})`;
144
+
145
+ case "TryCatchStmt":
146
+ return `try${generateOpt(node.tryBlock, types, opts)}catch(${node.catchParam})${generateOpt(node.catchBlock, types, opts)}`;
147
+
148
+ case "BreakStmt":
149
+ return "break";
150
+ case "ContinueStmt":
151
+ return "continue";
152
+
153
+ case "ClassDecl": {
154
+ const parent = node.superClass ? ` extends ${node.superClass}` : "";
155
+ const methods = node.methods.map(m => {
156
+ if (m.isConstructor) {
157
+ return `constructor(${m.params.join(",")})${generateOpt(m.body, types, opts)}`;
158
+ }
159
+ return `${m.name}(${m.params.join(",")})${generateOpt(m.body, types, opts)}`;
160
+ }).join("\n");
161
+ return `class ${node.name}${parent}{\n${methods}\n}`;
162
+ }
163
+
164
+ case "ThisExpr":
165
+ return "this";
166
+
167
+ case "NewExpr": {
168
+ const args = node.args.map(a => generateOpt(a, types, opts)).join(",");
169
+ return `new ${generateOpt(node.callee, types, opts)}(${args})`;
170
+ }
171
+
172
+ case "SwitchStmt": {
173
+ const cases = node.cases.map(c => {
174
+ if (c.test === null) return `default:\n ${generateOpt(c.body, types, opts)}`;
175
+ return `case ${generateOpt(c.test, types, opts)}:\n ${generateOpt(c.body, types, opts)}`;
176
+ }).join("\n");
177
+ return `switch(${generateOpt(node.test, types, opts)}){\n${cases}\n}`;
178
+ }
179
+
180
+ case "MatchExpr": {
181
+ const cases = node.cases.map(c => {
182
+ if (c.pattern === null) return `default:${generateOpt(c.body, types, opts)}`;
183
+ return `case ${patternToJS(c.pattern, generateOpt(node.test, types, opts))}:\n ${generateOpt(c.body, types, opts)}`;
184
+ }).join("\n");
185
+ return `switch(true){\n${cases}\n}`;
186
+ }
187
+
188
+ default:
189
+ return ``;
190
+ }
191
+ }
192
+
193
+ function patternToJS(pattern, valueExpr) {
194
+ switch (pattern.type) {
195
+ case "PatternLiteral":
196
+ return `(${valueExpr}===${JSON.stringify(pattern.value)})`;
197
+ case "PatternIdent":
198
+ if (pattern.name === "_") return `true`;
199
+ return `(${valueExpr}!==undefined&&true)`;
200
+ case "PatternArray":
201
+ return `(Array.isArray(${valueExpr})&&${pattern.elements.map((el, i) => {
202
+ if (el.type === "PatternRest") return `${valueExpr}.length>=${i}`;
203
+ return patternToJS(el, `${valueExpr}[${i}]`);
204
+ }).join("&&")})`;
205
+ case "PatternObject":
206
+ return `(typeof ${valueExpr}==="object"&&${valueExpr}!==null&&${pattern.props.map(p =>
207
+ `(${p.key} in ${valueExpr})&&${patternToJS(p.pattern, `${valueExpr}.${p.key}`)}`
208
+ ).join("&&")})`;
209
+ default:
210
+ return `true`;
211
+ }
212
+ }
213
+
214
+ function canUnroll(forNode) {
215
+ if (!forNode.test || forNode.test.type !== "Binary") return false;
216
+ if (forNode.test.left?.type !== "Ident") return false;
217
+ if (forNode.test.right?.type !== "Num") return false;
218
+ const limit = forNode.test.right.value;
219
+ return typeof limit === "number" && limit >= 1 && limit <= 8;
220
+ }
221
+
222
+ function unrollLoop(forNode, types, opts) {
223
+ const varName = forNode.test.left.name;
224
+ const limit = forNode.test.right.value;
225
+ const body = forNode.body;
226
+
227
+ const stmts = [];
228
+ for (let i = 0; i < limit; i++) {
229
+ const iterStmts = [];
230
+ for (const stmt of body.body) {
231
+ iterStmts.push(expandStmt(stmt, varName, i, types, opts));
232
+ }
233
+
234
+ stmts.push("{\n" + iterStmts.map(s => " " + s).join("\n") + "\n }");
235
+ }
236
+ return `{\n${stmts.map(s => " " + s).join("\n")}\n}`;
237
+ }
238
+
239
+ function expandStmt(node, varName, value, types, opts) {
240
+ const replace = (n) => {
241
+ if (!n || typeof n !== "object") return n;
242
+ if (n.type === "Ident" && n.name === varName) {
243
+ return { type: "Num", value };
244
+ }
245
+ const clone = { ...n };
246
+ for (const k of Object.keys(clone)) {
247
+ if (k === "type") continue;
248
+ if (Array.isArray(clone[k])) {
249
+ clone[k] = clone[k].map(ell => replace(ell));
250
+ } else if (typeof clone[k] === "object" && clone[k] !== null) {
251
+ clone[k] = replace(clone[k]);
252
+ }
253
+ }
254
+ return clone;
255
+ };
256
+ return generateOpt(replace(node), types, opts) + ";";
257
+ }
258
+
259
+ function countNodes(node) {
260
+ if (!node || typeof node !== "object") return 0;
261
+ let count = 1;
262
+ for (const k of Object.keys(node)) {
263
+ if (k === "type") continue;
264
+ if (Array.isArray(node[k])) {
265
+ for (const el of node[k]) count += countNodes(el);
266
+ } else if (typeof node[k] === "object" && node[k] !== null) {
267
+ count += countNodes(node[k]);
268
+ }
269
+ }
270
+ return count;
271
+ }
272
+
273
+ export function inferTypes(ast) {
274
+ const types = {};
275
+ walkTypes(ast, types);
276
+ return types;
277
+ }
278
+
279
+ function walkTypes(node, types) {
280
+ if (!node || typeof node !== "object") return;
281
+ if (Array.isArray(node)) { node.forEach(n => walkTypes(n, types)); return; }
282
+
283
+ if (node.type === "VarDecl") {
284
+ if (node.typeHint) {
285
+ types[node.id] = node.typeHint;
286
+ } else if (node.init) {
287
+ const t = typeOf(node.init, types);
288
+ if (t) types[node.id] = t;
289
+ }
290
+ }
291
+
292
+ if (node.type === "Assign") {
293
+ if (node.left.type === "Ident") {
294
+ const t = typeOf(node.right, types);
295
+ if (t) types[node.left.name] = t;
296
+ }
297
+ }
298
+
299
+ for (const key of Object.keys(node)) {
300
+ if (key === "type") continue;
301
+ walkTypes(node[key], types);
302
+ }
303
+ }
304
+
305
+ function typeOf(node, types) {
306
+ switch (node.type) {
307
+ case "Num": return "NUM";
308
+ case "Str": return "STR";
309
+ case "Bool": return "BOOL";
310
+ case "Nil": return "NIL";
311
+ case "ArrayExpr": return "ARR";
312
+ case "ObjectExpr": return "OBJ";
313
+ case "Ident":
314
+ if (node.name in types) return types[node.name];
315
+ return null;
316
+ case "Binary": {
317
+ const lt = typeOf(node.left, types);
318
+ const rt = typeOf(node.right, types);
319
+ if (lt === "NUM" && rt === "NUM") return "NUM";
320
+ if (node.op === "+" && (lt === "STR" || rt === "STR")) return "STR";
321
+ if (["==", "!=", "~=", ">", "<", ">=", "<=", "&&", "||"].includes(node.op)) return "BOOL";
322
+ return null;
323
+ }
324
+ case "Unary": {
325
+ if (node.op === "!") return "BOOL";
326
+ if (node.op === "-") return "NUM";
327
+ return null;
328
+ }
329
+ case "Call": {
330
+ if (node.callee.type === "Ident") {
331
+ if (node.callee.name === "SORTEIA") return "NUM";
332
+ if (node.callee.name === "PARSEIA") return "OBJ";
333
+ if (node.callee.name === "AGORA_VAI") return "OBJ";
334
+ if (node.callee.name === "OUVE_AQUI") return "STR";
335
+ if (node.callee.name === "TAMANHO") return "NUM";
336
+ if (node.callee.name === "ENCONTRA") return "OBJ";
337
+ if (node.callee.name === "JUNTAR") return "STR";
338
+ if (node.callee.name === "AGORA") return "NUM";
339
+ }
340
+ return null;
341
+ }
342
+ case "Ternary": {
343
+ const ct = typeOf(node.cons, types);
344
+ const at = typeOf(node.alt, types);
345
+ if (ct && ct === at) return ct;
346
+ return null;
347
+ }
348
+ case "Member": return "OBJ";
349
+ case "IndexExpr": return "OBJ";
350
+ default: return null;
351
+ }
352
+ }
@@ -0,0 +1,306 @@
1
+ export function generateWasm(ast) {
2
+ const ctx = {
3
+ vars: new Map(),
4
+ varCount: 0,
5
+ functions: new Map(),
6
+ funcCount: 0,
7
+ imports: new Map(),
8
+ importCount: 0,
9
+ stringPool: [],
10
+ wasm: [],
11
+ _mainEmitted: false,
12
+ };
13
+
14
+ collectDeclarations(ast, ctx);
15
+
16
+ ctx.wasm.push(`(module`);
17
+
18
+ for (const [name, imp] of ctx.imports) {
19
+ ctx.wasm.push(` (import "env" "${imp.jsName}" (func $${name} ${imp.params} ${imp.result}))`);
20
+ }
21
+
22
+ ctx.wasm.push(` (memory (export "memory") 1)`);
23
+ ctx.wasm.push(` (global $__sp (mut i32) (i32.const 0))`);
24
+
25
+ if (ctx.stringPool.length > 0) {
26
+ ctx.wasm.push(` (data (i32.const 0) "${ctx.stringPool.join('')}")`);
27
+ }
28
+
29
+ emitFunctions(ast, ctx);
30
+
31
+ if (!ctx._mainEmitted) {
32
+ emitWasmFunction("main", [], "i32", ast, ctx);
33
+ ctx._mainEmitted = true;
34
+ }
35
+
36
+ ctx.wasm.push(`)`);
37
+
38
+ return ctx.wasm.join("\n");
39
+ }
40
+
41
+ function collectDeclarations(node, ctx) {
42
+ if (!node || typeof node !== "object") return;
43
+ if (Array.isArray(node)) { node.forEach(n => collectDeclarations(n, ctx)); return; }
44
+
45
+ if (node.type === "VarDecl") {
46
+ if (!ctx.vars.has(node.id)) {
47
+ ctx.vars.set(node.id, { index: ctx.varCount++, type: "i32" });
48
+ }
49
+ }
50
+
51
+ if (node.type === "FunctionDecl") {
52
+ ctx.functions.set(node.name, { params: node.params || [], hasBody: true });
53
+ }
54
+
55
+ if (node.type === "Call" && node.callee?.type === "Ident") {
56
+ const name = node.callee.name;
57
+ if (["SOLTA_O_GRITO", "FALA_BAIXO", "SORTEIA", "PARSEIA"].includes(name)) {
58
+ if (!ctx.imports.has(name)) {
59
+ const jsMap = {
60
+ SOLTA_O_GRITO: { jsName: "log", params: "(param i32)", result: "(result i32)" },
61
+ FALA_BAIXO: { jsName: "warn", params: "(param i32)", result: "(result i32)" },
62
+ SORTEIA: { jsName: "randInt", params: "(param i32)(param i32)", result: "(result i32)" },
63
+ PARSEIA: { jsName: "parseInt", params: "(param i32)", result: "(result i32)" },
64
+ };
65
+ ctx.imports.set(name, jsMap[name]);
66
+ }
67
+ }
68
+ }
69
+
70
+ for (const k of Object.keys(node)) {
71
+ if (k === "type") continue;
72
+ collectDeclarations(node[k], ctx);
73
+ }
74
+ }
75
+
76
+ function emitFunctions(node, ctx) {
77
+ if (!node || typeof node !== "object") return;
78
+ if (Array.isArray(node)) { node.forEach(n => emitFunctions(n, ctx)); return; }
79
+
80
+ if (node.type === "FunctionDecl") {
81
+ const params = node.params.map(() => "i32").join(" ");
82
+ emitWasmFunction(node.name, node.params, "i32", node.body, ctx);
83
+ }
84
+
85
+ if (node.type === "Program") {
86
+
87
+ if (!ctx._mainEmitted) {
88
+ emitWasmFunction("main", [], "i32", node, ctx);
89
+ ctx._mainEmitted = true;
90
+ }
91
+ }
92
+
93
+ for (const k of Object.keys(node)) {
94
+ if (k === "type" || k === "body") continue;
95
+ emitFunctions(node[k], ctx);
96
+ }
97
+
98
+ if (node.type === "Block" || node.type === "Program") {
99
+
100
+ }
101
+ }
102
+
103
+ function emitWasmFunction(name, params, result, body, ctx) {
104
+ ctx.wasm.push(` (func $${name} (export "${name}")${params.length ? " (param " + params.map(() => "i32").join(" ") + ")" : ""} (result ${result})`);
105
+ ctx.wasm.push(` (local $__ret i32)`);
106
+
107
+ for (const [vname, v] of ctx.vars) {
108
+ ctx.wasm.push(` (local $${vname} i32)`);
109
+ }
110
+
111
+ ctx.wasm.push(` (block $__exit`);
112
+ emitWasmCode(body, ctx);
113
+ ctx.wasm.push(` )`);
114
+
115
+ ctx.wasm.push(` local.get $__ret`);
116
+
117
+ ctx.wasm.push(` )`);
118
+ }
119
+
120
+ function emitWasmCode(node, ctx) {
121
+ if (!node || typeof node !== "object") return;
122
+
123
+ switch (node.type) {
124
+
125
+ case "Program":
126
+ node.body.forEach(n => emitWasmCode(n, ctx));
127
+ break;
128
+
129
+ case "Block":
130
+ node.body.forEach(n => emitWasmCode(n, ctx));
131
+ break;
132
+
133
+ case "VarDecl": {
134
+ emitWasmCode(node.init, ctx);
135
+ ctx.wasm.push(` local.set $${node.id}`);
136
+ break;
137
+ }
138
+
139
+ case "Assign": {
140
+ if (node.left?.type === "Ident") {
141
+ emitWasmCode(node.right, ctx);
142
+ ctx.wasm.push(` local.set $${node.left.name}`);
143
+ }
144
+ break;
145
+ }
146
+
147
+ case "Num": {
148
+ ctx.wasm.push(` i32.const ${node.value | 0}`);
149
+ break;
150
+ }
151
+
152
+ case "Ident": {
153
+ if (ctx.vars.has(node.name)) {
154
+ ctx.wasm.push(` local.get $${node.name}`);
155
+ } else {
156
+ ctx.wasm.push(` i32.const 0`);
157
+ }
158
+ break;
159
+ }
160
+
161
+ case "Binary": {
162
+ emitWasmCode(node.left, ctx);
163
+ emitWasmCode(node.right, ctx);
164
+
165
+ const opMap = {
166
+ "+": "i32.add",
167
+ "-": "i32.sub",
168
+ "*": "i32.mul",
169
+ "/": "i32.div_s",
170
+ "%": "i32.rem_s",
171
+ "==": "i32.eq",
172
+ "!=": "i32.ne",
173
+ ">": "i32.gt_s",
174
+ "<": "i32.lt_s",
175
+ ">=": "i32.ge_s",
176
+ "<=": "i32.le_s",
177
+ };
178
+
179
+ if (opMap[node.op]) {
180
+ ctx.wasm.push(` ${opMap[node.op]}`);
181
+ }
182
+ break;
183
+ }
184
+
185
+ case "Unary": {
186
+ emitWasmCode(node.arg, ctx);
187
+ if (node.op === "-") {
188
+ ctx.wasm.push(` i32.const -1`);
189
+ ctx.wasm.push(` i32.mul`);
190
+ }
191
+ if (node.op === "!") {
192
+ ctx.wasm.push(` i32.eqz`);
193
+ }
194
+ break;
195
+ }
196
+
197
+ case "IfStmt": {
198
+ emitWasmCode(node.test, ctx);
199
+ ctx.wasm.push(` if`);
200
+ emitWasmCode(node.cons, ctx);
201
+ if (node.alt) {
202
+ ctx.wasm.push(` else`);
203
+ emitWasmCode(node.alt, ctx);
204
+ }
205
+ ctx.wasm.push(` end`);
206
+ break;
207
+ }
208
+
209
+ case "ForStmt": {
210
+ if (node.init) emitWasmCode(node.init, ctx);
211
+ ctx.wasm.push(` block $__break`);
212
+ ctx.wasm.push(` loop $__continue`);
213
+ emitWasmCode(node.test, ctx);
214
+ ctx.wasm.push(` i32.eqz`);
215
+ ctx.wasm.push(` br_if $__break`);
216
+ emitWasmCode(node.body, ctx);
217
+ if (node.update) emitWasmCode(node.update, ctx);
218
+ ctx.wasm.push(` br $__continue`);
219
+ ctx.wasm.push(` end`);
220
+ ctx.wasm.push(` end`);
221
+ break;
222
+ }
223
+
224
+ case "WhileStmt": {
225
+ ctx.wasm.push(` block $__break`);
226
+ ctx.wasm.push(` loop $__continue`);
227
+ emitWasmCode(node.test, ctx);
228
+ ctx.wasm.push(` i32.eqz`);
229
+ ctx.wasm.push(` br_if $__break`);
230
+ emitWasmCode(node.body, ctx);
231
+ ctx.wasm.push(` br $__continue`);
232
+ ctx.wasm.push(` end`);
233
+ ctx.wasm.push(` end`);
234
+ break;
235
+ }
236
+
237
+ case "ReturnStmt": {
238
+ if (node.arg) {
239
+ emitWasmCode(node.arg, ctx);
240
+ ctx.wasm.push(` local.set $__ret`);
241
+ }
242
+ ctx.wasm.push(` br $__exit`);
243
+ break;
244
+ }
245
+
246
+ case "BreakStmt":
247
+ ctx.wasm.push(` br $__break`);
248
+ break;
249
+
250
+ case "ContinueStmt":
251
+ ctx.wasm.push(` br $__continue`);
252
+ break;
253
+
254
+ case "Call": {
255
+ if (node.callee?.type === "Ident") {
256
+ const name = node.callee.name;
257
+
258
+ if (ctx.imports.has(name)) {
259
+ for (const arg of node.args) emitWasmCode(arg, ctx);
260
+ ctx.wasm.push(` call $${name}`);
261
+ break;
262
+ }
263
+
264
+ if (ctx.functions.has(name)) {
265
+ for (const arg of node.args) emitWasmCode(arg, ctx);
266
+ ctx.wasm.push(` call $${name}`);
267
+ break;
268
+ }
269
+ }
270
+ break;
271
+ }
272
+
273
+ case "Ternary": {
274
+ emitWasmCode(node.test, ctx);
275
+ ctx.wasm.push(` if (result i32)`);
276
+ emitWasmCode(node.cons, ctx);
277
+ ctx.wasm.push(` else`);
278
+ emitWasmCode(node.alt, ctx);
279
+ ctx.wasm.push(` end`);
280
+ break;
281
+ }
282
+
283
+ case "Bool":
284
+ ctx.wasm.push(` i32.const ${node.value ? 1 : 0}`);
285
+ break;
286
+
287
+ case "Nil":
288
+ ctx.wasm.push(` i32.const 0`);
289
+ break;
290
+ }
291
+ }
292
+
293
+ export function getWasmRuntime() {
294
+ return {
295
+ log: (n) => { console.log(n); return n; },
296
+ warn: (n) => { console.warn(n); return n; },
297
+ randInt: (a, b) => Math.floor(Math.random() * (b - a + 1)) + a,
298
+ parseInt: (s) => parseInt(s) || 0,
299
+ };
300
+ }
301
+
302
+ export function getDefaultExports() {
303
+ return {
304
+ env: getWasmRuntime()
305
+ };
306
+ }