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/LICENSE +21 -0
- package/README.md +148 -0
- package/dist/ast.d.ts +298 -0
- package/dist/ast.js +2 -0
- package/dist/build.d.ts +7 -0
- package/dist/build.js +138 -0
- package/dist/codegen-js.d.ts +2 -0
- package/dist/codegen-js.js +168 -0
- package/dist/codegen.d.ts +2 -0
- package/dist/codegen.js +364 -0
- package/dist/errors.d.ts +52 -0
- package/dist/errors.js +229 -0
- package/dist/formatter.d.ts +5 -0
- package/dist/formatter.js +361 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +165 -0
- package/dist/interpreter.d.ts +39 -0
- package/dist/interpreter.js +668 -0
- package/dist/ir.d.ts +126 -0
- package/dist/ir.js +610 -0
- package/dist/lexer.d.ts +79 -0
- package/dist/lexer.js +335 -0
- package/dist/linter.d.ts +15 -0
- package/dist/linter.js +382 -0
- package/dist/lsp.d.ts +1 -0
- package/dist/lsp.js +253 -0
- package/dist/modules.d.ts +24 -0
- package/dist/modules.js +115 -0
- package/dist/optimizer.d.ts +17 -0
- package/dist/optimizer.js +481 -0
- package/dist/package-manager.d.ts +31 -0
- package/dist/package-manager.js +180 -0
- package/dist/parser.d.ts +42 -0
- package/dist/parser.js +779 -0
- package/dist/repl.d.ts +1 -0
- package/dist/repl.js +120 -0
- package/dist/security.d.ts +48 -0
- package/dist/security.js +198 -0
- package/dist/semantic.d.ts +7 -0
- package/dist/semantic.js +327 -0
- package/dist/typechecker.d.ts +7 -0
- package/dist/typechecker.js +132 -0
- package/dist/version.d.ts +26 -0
- package/dist/version.js +71 -0
- package/package.json +51 -0
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
|
+
}
|
package/dist/lexer.d.ts
ADDED
|
@@ -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[];
|