arc-lang 0.5.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.
package/dist/ir.js ADDED
@@ -0,0 +1,610 @@
1
+ // Arc Intermediate Representation (IR) - SSA Three-Address Code
2
+ import { lex } from "./lexer.js";
3
+ import { parse } from "./parser.js";
4
+ // ---- IR Generator ----
5
+ export class IRGenerator {
6
+ tempCount = 0;
7
+ labelCount = 0;
8
+ functions = [];
9
+ currentInstrs = [];
10
+ temp() {
11
+ return `%${this.tempCount++}`;
12
+ }
13
+ label(prefix = "L") {
14
+ return `${prefix}${this.labelCount++}`;
15
+ }
16
+ emit(instr) {
17
+ this.currentInstrs.push(instr);
18
+ }
19
+ generateIR(program) {
20
+ this.functions = [];
21
+ this.currentInstrs = [];
22
+ this.tempCount = 0;
23
+ this.labelCount = 0;
24
+ for (const stmt of program.stmts) {
25
+ this.lowerStmt(stmt);
26
+ }
27
+ return {
28
+ functions: this.functions,
29
+ main: [{ label: "entry", instrs: this.currentInstrs }],
30
+ };
31
+ }
32
+ // ---- Statement Lowering ----
33
+ lowerStmt(stmt) {
34
+ switch (stmt.kind) {
35
+ case "LetStmt": {
36
+ const val = this.lowerExpr(stmt.value);
37
+ if (typeof stmt.name === "string") {
38
+ this.emit({ op: "store", name: stmt.name, src: val });
39
+ }
40
+ else {
41
+ // Destructuring — store temp then extract fields
42
+ const dt = stmt.name;
43
+ for (let i = 0; i < dt.names.length; i++) {
44
+ const dest = this.temp();
45
+ if (dt.type === "object") {
46
+ this.emit({ op: "field", dest, obj: val, prop: dt.names[i] });
47
+ }
48
+ else {
49
+ const idx = this.temp();
50
+ this.emit({ op: "const", dest: idx, value: i });
51
+ this.emit({ op: "index", dest, obj: val, idx });
52
+ }
53
+ this.emit({ op: "store", name: dt.names[i], src: dest });
54
+ }
55
+ }
56
+ break;
57
+ }
58
+ case "FnStmt": {
59
+ const savedInstrs = this.currentInstrs;
60
+ this.currentInstrs = [];
61
+ // Lower function body
62
+ const result = this.lowerExpr(stmt.body);
63
+ this.emit({ op: "ret", value: result });
64
+ this.functions.push({
65
+ name: stmt.name,
66
+ params: stmt.params,
67
+ blocks: [{ label: "entry", instrs: this.currentInstrs }],
68
+ });
69
+ this.currentInstrs = savedInstrs;
70
+ // Store function reference in main scope
71
+ this.emit({ op: "store", name: stmt.name, src: `@fn:${stmt.name}` });
72
+ break;
73
+ }
74
+ case "ForStmt": {
75
+ const iter = this.lowerExpr(stmt.iterable);
76
+ const lenTemp = this.temp();
77
+ this.emit({ op: "call", dest: lenTemp, fn: "len", args: [iter] });
78
+ const counterName = `__for_i_${this.labelCount}`;
79
+ const zero = this.temp();
80
+ this.emit({ op: "const", dest: zero, value: 0 });
81
+ this.emit({ op: "store", name: counterName, src: zero });
82
+ const loopLabel = this.label("for_loop");
83
+ const bodyLabel = this.label("for_body");
84
+ const endLabel = this.label("for_end");
85
+ this.emit({ op: "label", name: loopLabel });
86
+ const counter = this.temp();
87
+ this.emit({ op: "load", dest: counter, name: counterName });
88
+ const cond = this.temp();
89
+ this.emit({ op: "binop", dest: cond, operator: "<", left: counter, right: lenTemp });
90
+ this.emit({ op: "branch", cond, ifTrue: bodyLabel, ifFalse: endLabel });
91
+ this.emit({ op: "label", name: bodyLabel });
92
+ const elem = this.temp();
93
+ this.emit({ op: "index", dest: elem, obj: iter, idx: counter });
94
+ this.emit({ op: "store", name: stmt.variable, src: elem });
95
+ this.lowerExpr(stmt.body);
96
+ const next = this.temp();
97
+ const one = this.temp();
98
+ this.emit({ op: "const", dest: one, value: 1 });
99
+ this.emit({ op: "load", dest: next, name: counterName });
100
+ const incremented = this.temp();
101
+ this.emit({ op: "binop", dest: incremented, operator: "+", left: next, right: one });
102
+ this.emit({ op: "store", name: counterName, src: incremented });
103
+ this.emit({ op: "jump", target: loopLabel });
104
+ this.emit({ op: "label", name: endLabel });
105
+ break;
106
+ }
107
+ case "DoStmt": {
108
+ const loopLabel = this.label("do_loop");
109
+ const endLabel = this.label("do_end");
110
+ this.emit({ op: "label", name: loopLabel });
111
+ this.lowerExpr(stmt.body);
112
+ const cond = this.lowerExpr(stmt.condition);
113
+ if (stmt.isWhile) {
114
+ this.emit({ op: "branch", cond, ifTrue: loopLabel, ifFalse: endLabel });
115
+ }
116
+ else {
117
+ // until — loop while NOT condition
118
+ this.emit({ op: "branch", cond, ifTrue: endLabel, ifFalse: loopLabel });
119
+ }
120
+ this.emit({ op: "label", name: endLabel });
121
+ break;
122
+ }
123
+ case "AssignStmt": {
124
+ const val = this.lowerExpr(stmt.value);
125
+ this.emit({ op: "store", name: stmt.target, src: val });
126
+ break;
127
+ }
128
+ case "MemberAssignStmt": {
129
+ const obj = this.lowerExpr(stmt.object);
130
+ const val = this.lowerExpr(stmt.value);
131
+ this.emit({ op: "setfield", obj, prop: stmt.property, src: val });
132
+ break;
133
+ }
134
+ case "IndexAssignStmt": {
135
+ const obj = this.lowerExpr(stmt.object);
136
+ const idx = this.lowerExpr(stmt.index);
137
+ const val = this.lowerExpr(stmt.value);
138
+ this.emit({ op: "setindex", obj, idx, src: val });
139
+ break;
140
+ }
141
+ case "ExprStmt": {
142
+ this.lowerExpr(stmt.expr);
143
+ break;
144
+ }
145
+ case "UseStmt": {
146
+ // No-op at IR level — resolved at load time
147
+ break;
148
+ }
149
+ case "TypeStmt": {
150
+ // No-op at IR level — types are compile-time only
151
+ break;
152
+ }
153
+ default:
154
+ // Ignore unknown statement types
155
+ break;
156
+ }
157
+ }
158
+ // ---- Expression Lowering ----
159
+ lowerExpr(expr) {
160
+ switch (expr.kind) {
161
+ case "IntLiteral":
162
+ case "FloatLiteral": {
163
+ const dest = this.temp();
164
+ this.emit({ op: "const", dest, value: expr.value });
165
+ return dest;
166
+ }
167
+ case "BoolLiteral": {
168
+ const dest = this.temp();
169
+ this.emit({ op: "const", dest, value: expr.value });
170
+ return dest;
171
+ }
172
+ case "NilLiteral": {
173
+ const dest = this.temp();
174
+ this.emit({ op: "const", dest, value: null });
175
+ return dest;
176
+ }
177
+ case "StringLiteral": {
178
+ const dest = this.temp();
179
+ this.emit({ op: "const", dest, value: expr.value });
180
+ return dest;
181
+ }
182
+ case "StringInterp": {
183
+ // Lower string interpolation to concat chain
184
+ let result = this.temp();
185
+ this.emit({ op: "const", dest: result, value: "" });
186
+ for (const part of expr.parts) {
187
+ const partVal = typeof part === "string"
188
+ ? (() => { const d = this.temp(); this.emit({ op: "const", dest: d, value: part }); return d; })()
189
+ : (() => {
190
+ const raw = this.lowerExpr(part);
191
+ const d = this.temp();
192
+ this.emit({ op: "call", dest: d, fn: "str", args: [raw] });
193
+ return d;
194
+ })();
195
+ const newResult = this.temp();
196
+ this.emit({ op: "binop", dest: newResult, operator: "++", left: result, right: partVal });
197
+ result = newResult;
198
+ }
199
+ return result;
200
+ }
201
+ case "Identifier": {
202
+ const dest = this.temp();
203
+ this.emit({ op: "load", dest, name: expr.name });
204
+ return dest;
205
+ }
206
+ case "BinaryExpr": {
207
+ const left = this.lowerExpr(expr.left);
208
+ const right = this.lowerExpr(expr.right);
209
+ const dest = this.temp();
210
+ this.emit({ op: "binop", dest, operator: expr.op, left, right });
211
+ return dest;
212
+ }
213
+ case "UnaryExpr": {
214
+ const operand = this.lowerExpr(expr.operand);
215
+ const dest = this.temp();
216
+ this.emit({ op: "unop", dest, operator: expr.op, operand });
217
+ return dest;
218
+ }
219
+ case "CallExpr": {
220
+ const args = expr.args.map(a => this.lowerExpr(a));
221
+ const dest = this.temp();
222
+ if (expr.callee.kind === "Identifier") {
223
+ this.emit({ op: "call", dest, fn: expr.callee.name, args });
224
+ }
225
+ else {
226
+ const fn = this.lowerExpr(expr.callee);
227
+ this.emit({ op: "call", dest, fn, args });
228
+ }
229
+ return dest;
230
+ }
231
+ case "MemberExpr": {
232
+ const obj = this.lowerExpr(expr.object);
233
+ const dest = this.temp();
234
+ this.emit({ op: "field", dest, obj, prop: expr.property });
235
+ return dest;
236
+ }
237
+ case "IndexExpr": {
238
+ const obj = this.lowerExpr(expr.object);
239
+ const idx = this.lowerExpr(expr.index);
240
+ const dest = this.temp();
241
+ this.emit({ op: "index", dest, obj, idx });
242
+ return dest;
243
+ }
244
+ case "PipelineExpr": {
245
+ // a |> f → f(a)
246
+ const left = this.lowerExpr(expr.left);
247
+ const dest = this.temp();
248
+ if (expr.right.kind === "Identifier") {
249
+ this.emit({ op: "call", dest, fn: expr.right.name, args: [left] });
250
+ }
251
+ else if (expr.right.kind === "CallExpr") {
252
+ // a |> f(b) → f(a, b)
253
+ const args = [left, ...expr.right.args.map(a => this.lowerExpr(a))];
254
+ if (expr.right.callee.kind === "Identifier") {
255
+ this.emit({ op: "call", dest, fn: expr.right.callee.name, args });
256
+ }
257
+ else {
258
+ const fn = this.lowerExpr(expr.right.callee);
259
+ this.emit({ op: "call", dest, fn, args });
260
+ }
261
+ }
262
+ else {
263
+ const fn = this.lowerExpr(expr.right);
264
+ this.emit({ op: "call", dest, fn, args: [left] });
265
+ }
266
+ return dest;
267
+ }
268
+ case "IfExpr": {
269
+ const cond = this.lowerExpr(expr.condition);
270
+ const resultName = `__if_result_${this.labelCount}`;
271
+ const thenLabel = this.label("if_then");
272
+ const elseLabel = this.label("if_else");
273
+ const endLabel = this.label("if_end");
274
+ this.emit({ op: "branch", cond, ifTrue: thenLabel, ifFalse: elseLabel });
275
+ this.emit({ op: "label", name: thenLabel });
276
+ const thenVal = this.lowerExpr(expr.then);
277
+ this.emit({ op: "store", name: resultName, src: thenVal });
278
+ this.emit({ op: "jump", target: endLabel });
279
+ this.emit({ op: "label", name: elseLabel });
280
+ if (expr.else_) {
281
+ const elseVal = this.lowerExpr(expr.else_);
282
+ this.emit({ op: "store", name: resultName, src: elseVal });
283
+ }
284
+ else {
285
+ const nil = this.temp();
286
+ this.emit({ op: "const", dest: nil, value: null });
287
+ this.emit({ op: "store", name: resultName, src: nil });
288
+ }
289
+ this.emit({ op: "jump", target: endLabel });
290
+ this.emit({ op: "label", name: endLabel });
291
+ const dest = this.temp();
292
+ this.emit({ op: "load", dest, name: resultName });
293
+ return dest;
294
+ }
295
+ case "MatchExpr": {
296
+ const subject = this.lowerExpr(expr.subject);
297
+ const resultName = `__match_result_${this.labelCount}`;
298
+ const endLabel = this.label("match_end");
299
+ for (let i = 0; i < expr.arms.length; i++) {
300
+ const arm = expr.arms[i];
301
+ const armLabel = this.label("match_arm");
302
+ const nextLabel = i < expr.arms.length - 1 ? this.label("match_next") : endLabel;
303
+ // Lower pattern to condition
304
+ const cond = this.lowerPattern(arm.pattern, subject);
305
+ if (arm.guard) {
306
+ // Pattern match AND guard
307
+ const guardCond = this.lowerExpr(arm.guard);
308
+ const combined = this.temp();
309
+ this.emit({ op: "binop", dest: combined, operator: "and", left: cond, right: guardCond });
310
+ this.emit({ op: "branch", cond: combined, ifTrue: armLabel, ifFalse: nextLabel });
311
+ }
312
+ else {
313
+ this.emit({ op: "branch", cond, ifTrue: armLabel, ifFalse: nextLabel });
314
+ }
315
+ this.emit({ op: "label", name: armLabel });
316
+ // Bind pattern variables
317
+ this.bindPattern(arm.pattern, subject);
318
+ const val = this.lowerExpr(arm.body);
319
+ this.emit({ op: "store", name: resultName, src: val });
320
+ this.emit({ op: "jump", target: endLabel });
321
+ if (i < expr.arms.length - 1) {
322
+ this.emit({ op: "label", name: nextLabel });
323
+ }
324
+ }
325
+ this.emit({ op: "label", name: endLabel });
326
+ const dest = this.temp();
327
+ this.emit({ op: "load", dest, name: resultName });
328
+ return dest;
329
+ }
330
+ case "LambdaExpr": {
331
+ // Lower lambda to anonymous function
332
+ const fnName = `__lambda_${this.labelCount++}`;
333
+ const savedInstrs = this.currentInstrs;
334
+ this.currentInstrs = [];
335
+ const result = this.lowerExpr(expr.body);
336
+ this.emit({ op: "ret", value: result });
337
+ this.functions.push({
338
+ name: fnName,
339
+ params: expr.params,
340
+ blocks: [{ label: "entry", instrs: this.currentInstrs }],
341
+ });
342
+ this.currentInstrs = savedInstrs;
343
+ const dest = this.temp();
344
+ this.emit({ op: "load", dest, name: `@fn:${fnName}` });
345
+ return dest;
346
+ }
347
+ case "ListLiteral": {
348
+ const elements = expr.elements.map(e => this.lowerExpr(e));
349
+ const dest = this.temp();
350
+ this.emit({ op: "list", dest, elements });
351
+ return dest;
352
+ }
353
+ case "MapLiteral": {
354
+ const keys = [];
355
+ const values = [];
356
+ for (const entry of expr.entries) {
357
+ const keyStr = typeof entry.key === "string" ? entry.key : "";
358
+ const k = this.temp();
359
+ this.emit({ op: "const", dest: k, value: keyStr });
360
+ keys.push(k);
361
+ values.push(this.lowerExpr(entry.value));
362
+ }
363
+ const dest = this.temp();
364
+ this.emit({ op: "map", dest, keys, values });
365
+ return dest;
366
+ }
367
+ case "ListComprehension": {
368
+ // Lower to: result = []; for x in iter { if filter { push(result, expr) } }
369
+ const resultName = `__comp_${this.labelCount}`;
370
+ const emptyList = this.temp();
371
+ this.emit({ op: "list", dest: emptyList, elements: [] });
372
+ this.emit({ op: "store", name: resultName, src: emptyList });
373
+ const iter = this.lowerExpr(expr.iterable);
374
+ const lenT = this.temp();
375
+ this.emit({ op: "call", dest: lenT, fn: "len", args: [iter] });
376
+ const counterName = `__comp_i_${this.labelCount}`;
377
+ const zero = this.temp();
378
+ this.emit({ op: "const", dest: zero, value: 0 });
379
+ this.emit({ op: "store", name: counterName, src: zero });
380
+ const loopLabel = this.label("comp_loop");
381
+ const bodyLabel = this.label("comp_body");
382
+ const endLabel = this.label("comp_end");
383
+ this.emit({ op: "label", name: loopLabel });
384
+ const counter = this.temp();
385
+ this.emit({ op: "load", dest: counter, name: counterName });
386
+ const cond = this.temp();
387
+ this.emit({ op: "binop", dest: cond, operator: "<", left: counter, right: lenT });
388
+ this.emit({ op: "branch", cond, ifTrue: bodyLabel, ifFalse: endLabel });
389
+ this.emit({ op: "label", name: bodyLabel });
390
+ const elem = this.temp();
391
+ this.emit({ op: "index", dest: elem, obj: iter, idx: counter });
392
+ this.emit({ op: "store", name: expr.variable, src: elem });
393
+ if (expr.filter) {
394
+ const filterLabel = this.label("comp_filter");
395
+ const skipLabel = this.label("comp_skip");
396
+ const filterCond = this.lowerExpr(expr.filter);
397
+ this.emit({ op: "branch", cond: filterCond, ifTrue: filterLabel, ifFalse: skipLabel });
398
+ this.emit({ op: "label", name: filterLabel });
399
+ const val = this.lowerExpr(expr.expr);
400
+ const list = this.temp();
401
+ this.emit({ op: "load", dest: list, name: resultName });
402
+ this.emit({ op: "call", dest: this.temp(), fn: "push", args: [list, val] });
403
+ this.emit({ op: "jump", target: skipLabel });
404
+ this.emit({ op: "label", name: skipLabel });
405
+ }
406
+ else {
407
+ const val = this.lowerExpr(expr.expr);
408
+ const list = this.temp();
409
+ this.emit({ op: "load", dest: list, name: resultName });
410
+ this.emit({ op: "call", dest: this.temp(), fn: "push", args: [list, val] });
411
+ }
412
+ const next = this.temp();
413
+ this.emit({ op: "load", dest: next, name: counterName });
414
+ const one = this.temp();
415
+ this.emit({ op: "const", dest: one, value: 1 });
416
+ const inc = this.temp();
417
+ this.emit({ op: "binop", dest: inc, operator: "+", left: next, right: one });
418
+ this.emit({ op: "store", name: counterName, src: inc });
419
+ this.emit({ op: "jump", target: loopLabel });
420
+ this.emit({ op: "label", name: endLabel });
421
+ const dest = this.temp();
422
+ this.emit({ op: "load", dest, name: resultName });
423
+ return dest;
424
+ }
425
+ case "ToolCallExpr": {
426
+ const url = this.lowerExpr(expr.arg);
427
+ const dest = this.temp();
428
+ if (expr.body) {
429
+ const body = this.lowerExpr(expr.body);
430
+ this.emit({ op: "toolcall", dest, method: expr.method, url, body });
431
+ }
432
+ else {
433
+ this.emit({ op: "toolcall", dest, method: expr.method, url });
434
+ }
435
+ return dest;
436
+ }
437
+ case "RangeExpr": {
438
+ const start = this.lowerExpr(expr.start);
439
+ const end = this.lowerExpr(expr.end);
440
+ const dest = this.temp();
441
+ this.emit({ op: "range", dest, start, end });
442
+ return dest;
443
+ }
444
+ case "BlockExpr": {
445
+ let last = this.temp();
446
+ this.emit({ op: "const", dest: last, value: null });
447
+ for (const s of expr.stmts) {
448
+ if (s.kind === "ExprStmt") {
449
+ last = this.lowerExpr(s.expr);
450
+ }
451
+ else {
452
+ this.lowerStmt(s);
453
+ }
454
+ }
455
+ return last;
456
+ }
457
+ case "AsyncExpr": {
458
+ // Lower async block as a thunk reference
459
+ const fnName = `__async_${this.labelCount++}`;
460
+ const savedInstrs = this.currentInstrs;
461
+ this.currentInstrs = [];
462
+ const result = this.lowerExpr(expr.body);
463
+ this.emit({ op: "ret", value: result });
464
+ this.functions.push({
465
+ name: fnName,
466
+ params: [],
467
+ blocks: [{ label: "entry", instrs: this.currentInstrs }],
468
+ });
469
+ this.currentInstrs = savedInstrs;
470
+ const dest = this.temp();
471
+ this.emit({ op: "load", dest, name: `@fn:${fnName}` });
472
+ return dest;
473
+ }
474
+ case "AwaitExpr": {
475
+ const val = this.lowerExpr(expr.expr);
476
+ const dest = this.temp();
477
+ this.emit({ op: "call", dest, fn: "__await", args: [val] });
478
+ return dest;
479
+ }
480
+ case "FetchExpr": {
481
+ const targets = expr.targets.map(t => this.lowerExpr(t));
482
+ const dest = this.temp();
483
+ this.emit({ op: "call", dest, fn: "__fetch_parallel", args: targets });
484
+ return dest;
485
+ }
486
+ default:
487
+ const dest = this.temp();
488
+ this.emit({ op: "const", dest, value: null });
489
+ return dest;
490
+ }
491
+ }
492
+ // ---- Pattern Helpers ----
493
+ lowerPattern(pattern, subject) {
494
+ switch (pattern.kind) {
495
+ case "WildcardPattern": {
496
+ const dest = this.temp();
497
+ this.emit({ op: "const", dest, value: true });
498
+ return dest;
499
+ }
500
+ case "LiteralPattern": {
501
+ const val = this.temp();
502
+ this.emit({ op: "const", dest: val, value: pattern.value });
503
+ const dest = this.temp();
504
+ this.emit({ op: "binop", dest, operator: "==", left: subject, right: val });
505
+ return dest;
506
+ }
507
+ case "BindingPattern": {
508
+ // Always matches — bind happens in bindPattern
509
+ const dest = this.temp();
510
+ this.emit({ op: "const", dest, value: true });
511
+ return dest;
512
+ }
513
+ case "ArrayPattern": {
514
+ // Check length matches
515
+ const len = this.temp();
516
+ this.emit({ op: "call", dest: len, fn: "len", args: [subject] });
517
+ const expected = this.temp();
518
+ this.emit({ op: "const", dest: expected, value: pattern.elements.length });
519
+ const dest = this.temp();
520
+ this.emit({ op: "binop", dest, operator: "==", left: len, right: expected });
521
+ return dest;
522
+ }
523
+ case "OrPattern": {
524
+ let result = this.lowerPattern(pattern.patterns[0], subject);
525
+ for (let i = 1; i < pattern.patterns.length; i++) {
526
+ const right = this.lowerPattern(pattern.patterns[i], subject);
527
+ const combined = this.temp();
528
+ this.emit({ op: "binop", dest: combined, operator: "or", left: result, right });
529
+ result = combined;
530
+ }
531
+ return result;
532
+ }
533
+ default: {
534
+ const dest = this.temp();
535
+ this.emit({ op: "const", dest, value: true });
536
+ return dest;
537
+ }
538
+ }
539
+ }
540
+ bindPattern(pattern, subject) {
541
+ if (pattern.kind === "BindingPattern") {
542
+ this.emit({ op: "store", name: pattern.name, src: subject });
543
+ }
544
+ else if (pattern.kind === "ArrayPattern") {
545
+ for (let i = 0; i < pattern.elements.length; i++) {
546
+ const idx = this.temp();
547
+ this.emit({ op: "const", dest: idx, value: i });
548
+ const elem = this.temp();
549
+ this.emit({ op: "index", dest: elem, obj: subject, idx });
550
+ this.bindPattern(pattern.elements[i], elem);
551
+ }
552
+ }
553
+ }
554
+ }
555
+ // ---- IR Printer ----
556
+ export function printIR(module) {
557
+ const lines = [];
558
+ for (const fn of module.functions) {
559
+ lines.push(`fn ${fn.name}(${fn.params.join(", ")}):`);
560
+ for (const block of fn.blocks) {
561
+ lines.push(` ${block.label}:`);
562
+ for (const instr of block.instrs) {
563
+ lines.push(` ${formatInstr(instr)}`);
564
+ }
565
+ }
566
+ lines.push("");
567
+ }
568
+ lines.push("main:");
569
+ for (const block of module.main) {
570
+ lines.push(` ${block.label}:`);
571
+ for (const instr of block.instrs) {
572
+ lines.push(` ${formatInstr(instr)}`);
573
+ }
574
+ }
575
+ return lines.join("\n");
576
+ }
577
+ function formatInstr(instr) {
578
+ switch (instr.op) {
579
+ case "const": return `${instr.dest} = const ${JSON.stringify(instr.value)}`;
580
+ case "load": return `${instr.dest} = load ${instr.name}`;
581
+ case "store": return `store ${instr.name} = ${instr.src}`;
582
+ case "binop": return `${instr.dest} = ${instr.left} ${instr.operator} ${instr.right}`;
583
+ case "unop": return `${instr.dest} = ${instr.operator} ${instr.operand}`;
584
+ case "call": return `${instr.dest} = call ${instr.fn}(${instr.args.join(", ")})`;
585
+ case "toolcall": return `${instr.dest} = @${instr.method} ${instr.url}${instr.body ? ` ${instr.body}` : ""}`;
586
+ case "field": return `${instr.dest} = ${instr.obj}.${instr.prop}`;
587
+ case "index": return `${instr.dest} = ${instr.obj}[${instr.idx}]`;
588
+ case "setfield": return `${instr.obj}.${instr.prop} = ${instr.src}`;
589
+ case "setindex": return `${instr.obj}[${instr.idx}] = ${instr.src}`;
590
+ case "jump": return `jump ${instr.target}`;
591
+ case "branch": return `branch ${instr.cond} ? ${instr.ifTrue} : ${instr.ifFalse}`;
592
+ case "phi": return `${instr.dest} = phi ${instr.sources.map(s => `[${s.block}: ${s.value}]`).join(", ")}`;
593
+ case "ret": return `ret ${instr.value ?? "void"}`;
594
+ case "list": return `${instr.dest} = [${instr.elements.join(", ")}]`;
595
+ case "map": return `${instr.dest} = {${instr.keys.map((k, i) => `${k}: ${instr.values[i]}`).join(", ")}}`;
596
+ case "label": return `${instr.name}:`;
597
+ case "print": return `print ${instr.value}`;
598
+ case "range": return `${instr.dest} = range(${instr.start}, ${instr.end})`;
599
+ case "nop": return "nop";
600
+ }
601
+ }
602
+ // ---- Public API ----
603
+ export function generateIR(program) {
604
+ return new IRGenerator().generateIR(program);
605
+ }
606
+ export function generateIRFromSource(source) {
607
+ const tokens = lex(source);
608
+ const ast = parse(tokens);
609
+ return generateIR(ast);
610
+ }
@@ -0,0 +1,79 @@
1
+ export declare enum TokenType {
2
+ Int = 0,
3
+ Float = 1,
4
+ String = 2,
5
+ StringInterpStart = 3,
6
+ StringInterpPart = 4,
7
+ StringInterpEnd = 5,
8
+ Bool = 6,
9
+ Nil = 7,
10
+ Ident = 8,
11
+ Fn = 9,
12
+ Let = 10,
13
+ Mut = 11,
14
+ Type = 12,
15
+ Use = 13,
16
+ Pub = 14,
17
+ Match = 15,
18
+ If = 16,
19
+ El = 17,
20
+ For = 18,
21
+ In = 19,
22
+ Do = 20,
23
+ While = 21,
24
+ Until = 22,
25
+ Async = 23,
26
+ Await = 24,
27
+ Ret = 25,
28
+ True = 26,
29
+ False = 27,
30
+ NilKw = 28,
31
+ And = 29,
32
+ Or = 30,
33
+ Not = 31,
34
+ Where = 32,
35
+ Matching = 33,
36
+ Fetch = 34,
37
+ Plus = 35,
38
+ Minus = 36,
39
+ Star = 37,
40
+ Slash = 38,
41
+ Percent = 39,
42
+ Power = 40,
43
+ Eq = 41,
44
+ Neq = 42,
45
+ Lt = 43,
46
+ Gt = 44,
47
+ Lte = 45,
48
+ Gte = 46,
49
+ Pipe = 47,
50
+ Bar = 48,
51
+ FatArrow = 49,
52
+ Arrow = 50,
53
+ Question = 51,
54
+ Range = 52,
55
+ Concat = 53,
56
+ At = 54,
57
+ Hash = 55,
58
+ Assign = 56,
59
+ LParen = 57,
60
+ RParen = 58,
61
+ LBrace = 59,
62
+ RBrace = 60,
63
+ LBracket = 61,
64
+ RBracket = 62,
65
+ Comma = 63,
66
+ Colon = 64,
67
+ Dot = 65,
68
+ Semicolon = 66,
69
+ Newline = 67,
70
+ Regex = 68,
71
+ EOF = 69
72
+ }
73
+ export interface Token {
74
+ type: TokenType;
75
+ value: string;
76
+ line: number;
77
+ col: number;
78
+ }
79
+ export declare function lex(source: string): Token[];