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.
@@ -0,0 +1,168 @@
1
+ // Arc IR → JavaScript Code Generator
2
+ export function generateJS(module) {
3
+ const lines = [];
4
+ lines.push("// Generated by Arc Compiler");
5
+ lines.push("");
6
+ // Runtime helpers
7
+ lines.push(`// Arc Runtime`);
8
+ lines.push(`const __arc_runtime = {`);
9
+ lines.push(` len(a) { return Array.isArray(a) ? a.length : typeof a === "string" ? a.length : 0; },`);
10
+ lines.push(` str(v) { return String(v); },`);
11
+ lines.push(` push(arr, v) { arr.push(v); return arr; },`);
12
+ lines.push(` print(v) { console.log(typeof v === "object" && v !== null ? JSON.stringify(v) : v); },`);
13
+ lines.push(` head(a) { return a[0]; },`);
14
+ lines.push(` tail(a) { return a.slice(1); },`);
15
+ lines.push(` map(a, f) { return a.map(f); },`);
16
+ lines.push(` filter(a, f) { return a.filter(f); },`);
17
+ lines.push(` fold(a, init, f) { return a.reduce((acc, x) => f(acc, x), init); },`);
18
+ lines.push(` range(start, end) { const r = []; for (let i = start; i < end; i++) r.push(i); return r; },`);
19
+ lines.push(` keys(m) { return m && m.__map ? Array.from(m.entries.keys()) : Object.keys(m || {}); },`);
20
+ lines.push(` values(m) { return m && m.__map ? Array.from(m.entries.values()) : Object.values(m || {}); },`);
21
+ lines.push(` type(v) { if (v === null) return "nil"; if (Array.isArray(v)) return "list"; return typeof v; },`);
22
+ lines.push(` abs(n) { return Math.abs(n); },`);
23
+ lines.push(` max(a, b) { return Math.max(a, b); },`);
24
+ lines.push(` min(a, b) { return Math.min(a, b); },`);
25
+ lines.push(` floor(n) { return Math.floor(n); },`);
26
+ lines.push(` ceil(n) { return Math.ceil(n); },`);
27
+ lines.push(` round(n) { return Math.round(n); },`);
28
+ lines.push(` sort(a) { return [...a].sort((x,y) => x < y ? -1 : x > y ? 1 : 0); },`);
29
+ lines.push(` reverse(a) { return [...a].reverse(); },`);
30
+ lines.push(` contains(a, v) { return Array.isArray(a) ? a.includes(v) : typeof a === "string" ? a.includes(v) : false; },`);
31
+ lines.push(` join(a, sep) { return a.join(sep); },`);
32
+ lines.push(` split(s, sep) { return s.split(sep); },`);
33
+ lines.push(` trim(s) { return s.trim(); },`);
34
+ lines.push(` replace(s, old, nw) { return s.replaceAll(old, nw); },`);
35
+ lines.push(` uppercase(s) { return s.toUpperCase(); },`);
36
+ lines.push(` lowercase(s) { return s.toLowerCase(); },`);
37
+ lines.push(` sum(a) { return a.reduce((s, x) => s + x, 0); },`);
38
+ lines.push(` flat(a) { return a.flat(); },`);
39
+ lines.push(` zip(a, b) { return a.map((x, i) => [x, b[i]]); },`);
40
+ lines.push(` enumerate(a) { return a.map((x, i) => [i, x]); },`);
41
+ lines.push(` slice(a, s, e) { return a.slice(s, e); },`);
42
+ lines.push(` append(a, v) { return [...a, v]; },`);
43
+ lines.push(` concat(a, b) { return [...a, ...b]; },`);
44
+ lines.push(` unique(a) { return [...new Set(a)]; },`);
45
+ lines.push(` int(v) { return parseInt(v, 10); },`);
46
+ lines.push(` float(v) { return parseFloat(v); },`);
47
+ lines.push(` __await(v) { return v && v.__async ? v.thunk() : v; },`);
48
+ lines.push(` __fetch_parallel(...targets) { return targets; },`);
49
+ lines.push(` __make_map(keys, values) { const m = { __map: true, entries: new Map() }; for (let i = 0; i < keys.length; i++) m.entries.set(keys[i], values[i]); return m; },`);
50
+ lines.push(`};`);
51
+ lines.push("");
52
+ // Emit main with hoisted functions
53
+ lines.push("// Main");
54
+ lines.push("(function() {");
55
+ // Hoist function definitions inside the scope
56
+ for (const fn of module.functions) {
57
+ lines.push(emitFunction(fn, " "));
58
+ lines.push("");
59
+ }
60
+ for (const block of module.main) {
61
+ lines.push(...emitBlock(block, " "));
62
+ }
63
+ lines.push("})();");
64
+ return lines.join("\n");
65
+ }
66
+ function emitFunction(fn, baseIndent = "") {
67
+ const lines = [];
68
+ lines.push(`${baseIndent}function ${S(fn.name)}(${fn.params.map(S).join(", ")}) {`);
69
+ for (const block of fn.blocks) {
70
+ lines.push(...emitBlock(block, baseIndent + " "));
71
+ }
72
+ lines.push(`${baseIndent}}`);
73
+ return lines.join("\n");
74
+ }
75
+ function emitBlock(block, indent) {
76
+ const lines = [];
77
+ for (const instr of block.instrs) {
78
+ lines.push(indent + emitInstr(instr));
79
+ }
80
+ return lines;
81
+ }
82
+ function emitInstr(instr) {
83
+ switch (instr.op) {
84
+ case "const":
85
+ return `var ${S(instr.dest)} = ${JSON.stringify(instr.value)};`;
86
+ case "load":
87
+ if (instr.name.startsWith("@fn:")) {
88
+ return `var ${S(instr.dest)} = ${S(instr.name.slice(4))};`;
89
+ }
90
+ return `var ${S(instr.dest)} = ${S(instr.name)};`;
91
+ case "store":
92
+ if (instr.src.startsWith("@fn:")) {
93
+ return `var ${S(instr.name)} = ${S(instr.src.slice(4))};`;
94
+ }
95
+ return `var ${S(instr.name)} = ${S(instr.src)};`;
96
+ case "binop":
97
+ return `var ${S(instr.dest)} = ${emitBinop(instr.operator, S(instr.left), S(instr.right))};`;
98
+ case "unop":
99
+ return `var ${S(instr.dest)} = ${emitUnop(instr.operator, S(instr.operand))};`;
100
+ case "call":
101
+ return `var ${S(instr.dest)} = ${emitCall(instr.fn, instr.args.map(S))};`;
102
+ case "toolcall":
103
+ return `var ${S(instr.dest)} = await fetch(${S(instr.url)}, { method: ${JSON.stringify(instr.method)}${instr.body ? `, body: JSON.stringify(${S(instr.body)})` : ""} }).then(r => r.json());`;
104
+ case "field":
105
+ return `var ${S(instr.dest)} = (${S(instr.obj)} && ${S(instr.obj)}.__map) ? ${S(instr.obj)}.entries.get(${JSON.stringify(instr.prop)}) : ${S(instr.obj)}[${JSON.stringify(instr.prop)}];`;
106
+ case "index":
107
+ return `var ${S(instr.dest)} = ${S(instr.obj)}[${S(instr.idx)}];`;
108
+ case "setfield":
109
+ return `if (${S(instr.obj)} && ${S(instr.obj)}.__map) { ${S(instr.obj)}.entries.set(${JSON.stringify(instr.prop)}, ${S(instr.src)}); } else { ${S(instr.obj)}[${JSON.stringify(instr.prop)}] = ${S(instr.src)}; }`;
110
+ case "setindex":
111
+ return `${S(instr.obj)}[${S(instr.idx)}] = ${S(instr.src)};`;
112
+ case "jump":
113
+ return `/* jump ${instr.target} */`;
114
+ case "branch":
115
+ return `if (${S(instr.cond)}) { /* ${instr.ifTrue} */ } /* branch */`;
116
+ case "phi":
117
+ return `var ${S(instr.dest)} = undefined; /* phi */`;
118
+ case "ret":
119
+ return instr.value ? `return ${S(instr.value)};` : "return;";
120
+ case "list":
121
+ return `var ${S(instr.dest)} = [${instr.elements.map(S).join(", ")}];`;
122
+ case "map":
123
+ return `var ${S(instr.dest)} = __arc_runtime.__make_map([${instr.keys.map(S).join(", ")}], [${instr.values.map(S).join(", ")}]);`;
124
+ case "label":
125
+ return `/* ${instr.name}: */`;
126
+ case "print":
127
+ return `__arc_runtime.print(${S(instr.value)});`;
128
+ case "range":
129
+ return `var ${S(instr.dest)} = __arc_runtime.range(${S(instr.start)}, ${S(instr.end)});`;
130
+ case "nop":
131
+ return "/* nop */";
132
+ }
133
+ }
134
+ function emitBinop(op, left, right) {
135
+ switch (op) {
136
+ case "++": return `String(${left}) + String(${right})`;
137
+ case "and": return `(${left} && ${right})`;
138
+ case "or": return `(${left} || ${right})`;
139
+ case "==": return `(${left} === ${right})`;
140
+ case "!=": return `(${left} !== ${right})`;
141
+ case "**": return `(${left} ** ${right})`;
142
+ case "%": return `(${left} % ${right})`;
143
+ default: return `(${left} ${op} ${right})`;
144
+ }
145
+ }
146
+ function emitUnop(op, operand) {
147
+ switch (op) {
148
+ case "not": return `(!${operand})`;
149
+ default: return `(${op}${operand})`;
150
+ }
151
+ }
152
+ function emitCall(fn, args) {
153
+ const builtins = [
154
+ "len", "str", "push", "print", "head", "tail", "map", "filter", "fold",
155
+ "range", "keys", "values", "type", "abs", "max", "min", "floor", "ceil",
156
+ "round", "sort", "reverse", "contains", "join", "split", "trim", "replace",
157
+ "uppercase", "lowercase", "sum", "flat", "zip", "enumerate", "slice",
158
+ "append", "concat", "unique", "int", "float", "__await", "__fetch_parallel"
159
+ ];
160
+ if (builtins.includes(fn)) {
161
+ return `__arc_runtime.${fn}(${args.join(", ")})`;
162
+ }
163
+ return `${S(fn)}(${args.join(", ")})`;
164
+ }
165
+ function sanitizeName(name) {
166
+ return name.replace(/[^a-zA-Z0-9_$]/g, "_");
167
+ }
168
+ function S(name) { return sanitizeName(name); }
@@ -0,0 +1,2 @@
1
+ import { IRModule } from "./ir.js";
2
+ export declare function generateWAT(module: IRModule): string;
@@ -0,0 +1,364 @@
1
+ // Arc IR → WAT (WebAssembly Text Format) Code Generator
2
+ export function generateWAT(module) {
3
+ const gen = new WATGenerator();
4
+ return gen.generate(module);
5
+ }
6
+ class WATGenerator {
7
+ locals = new Set();
8
+ stringTable = new Map();
9
+ stringOffset = 0;
10
+ dataSegments = [];
11
+ generate(module) {
12
+ const lines = [];
13
+ // First pass: collect all strings and locals
14
+ this.collectStrings(module);
15
+ lines.push("(module");
16
+ // Import runtime functions
17
+ lines.push(' ;; Runtime imports');
18
+ lines.push(' (import "arc" "print_i32" (func $print_i32 (param i32)))');
19
+ lines.push(' (import "arc" "print_f64" (func $print_f64 (param f64)))');
20
+ lines.push(' (import "arc" "print_str" (func $print_str (param i32 i32)))');
21
+ lines.push(' (import "arc" "tool_call" (func $tool_call (param i32 i32 i32 i32) (result i32)))');
22
+ lines.push(' (import "arc" "alloc" (func $alloc (param i32) (result i32)))');
23
+ lines.push(' (import "arc" "list_new" (func $list_new (result i32)))');
24
+ lines.push(' (import "arc" "list_push" (func $list_push (param i32 i32) (result i32)))');
25
+ lines.push(' (import "arc" "list_get" (func $list_get (param i32 i32) (result i32)))');
26
+ lines.push(' (import "arc" "list_len" (func $list_len (param i32) (result i32)))');
27
+ lines.push(' (import "arc" "map_new" (func $map_new (result i32)))');
28
+ lines.push(' (import "arc" "map_set" (func $map_set (param i32 i32 i32) (result i32)))');
29
+ lines.push(' (import "arc" "map_get" (func $map_get (param i32 i32) (result i32)))');
30
+ lines.push("");
31
+ // Memory
32
+ lines.push(" (memory (export \"memory\") 1)");
33
+ lines.push("");
34
+ // Data segments for strings
35
+ if (this.dataSegments.length > 0) {
36
+ lines.push(" ;; String data");
37
+ for (const seg of this.dataSegments) {
38
+ lines.push(` ${seg}`);
39
+ }
40
+ lines.push("");
41
+ }
42
+ // Global variables for locals (simple approach: use globals for main scope)
43
+ lines.push(" ;; Globals");
44
+ lines.push(` (global $__sp (mut i32) (i32.const ${this.stringOffset}))`);
45
+ lines.push("");
46
+ // Emit functions
47
+ for (const fn of module.functions) {
48
+ lines.push(this.emitFunction(fn));
49
+ lines.push("");
50
+ }
51
+ // Emit main as _start
52
+ lines.push(' (func (export "_start")');
53
+ this.locals.clear();
54
+ // Collect locals from main blocks
55
+ for (const block of module.main) {
56
+ this.collectLocals(block);
57
+ }
58
+ for (const local of this.locals) {
59
+ lines.push(` (local $${sanitize(local)} i32)`);
60
+ }
61
+ for (const block of module.main) {
62
+ lines.push(...this.emitBlockInstrs(block, " "));
63
+ }
64
+ lines.push(" )");
65
+ lines.push(")");
66
+ return lines.join("\n");
67
+ }
68
+ collectStrings(module) {
69
+ const visit = (instrs) => {
70
+ for (const instr of instrs) {
71
+ if (instr.op === "const" && typeof instr.value === "string") {
72
+ this.addString(instr.value);
73
+ }
74
+ }
75
+ };
76
+ for (const fn of module.functions) {
77
+ for (const block of fn.blocks)
78
+ visit(block.instrs);
79
+ }
80
+ for (const block of module.main)
81
+ visit(block.instrs);
82
+ }
83
+ addString(s) {
84
+ if (this.stringTable.has(s))
85
+ return this.stringTable.get(s);
86
+ const offset = this.stringOffset;
87
+ const encoded = new TextEncoder().encode(s);
88
+ this.stringTable.set(s, offset);
89
+ // Escape for WAT data segment
90
+ let escaped = "";
91
+ for (const b of encoded) {
92
+ escaped += "\\" + b.toString(16).padStart(2, "0");
93
+ }
94
+ this.dataSegments.push(`(data (i32.const ${offset}) "${escaped}")`);
95
+ this.stringOffset += encoded.length;
96
+ return offset;
97
+ }
98
+ collectLocals(block) {
99
+ for (const instr of block.instrs) {
100
+ if ("dest" in instr && instr.dest)
101
+ this.locals.add(instr.dest);
102
+ if (instr.op === "store")
103
+ this.locals.add(instr.name);
104
+ }
105
+ }
106
+ emitFunction(fn) {
107
+ const lines = [];
108
+ this.locals.clear();
109
+ // Collect locals
110
+ for (const block of fn.blocks)
111
+ this.collectLocals(block);
112
+ const params = fn.params.map(p => `(param $${sanitize(p)} i32)`).join(" ");
113
+ lines.push(` (func $${sanitize(fn.name)} ${params} (result i32)`);
114
+ // Declare locals (excluding params)
115
+ const paramSet = new Set(fn.params);
116
+ for (const local of this.locals) {
117
+ if (!paramSet.has(local)) {
118
+ lines.push(` (local $${sanitize(local)} i32)`);
119
+ }
120
+ }
121
+ for (const block of fn.blocks) {
122
+ lines.push(...this.emitBlockInstrs(block, " "));
123
+ }
124
+ // Default return
125
+ lines.push(" i32.const 0");
126
+ lines.push(" )");
127
+ return lines.join("\n");
128
+ }
129
+ emitBlockInstrs(block, indent) {
130
+ const lines = [];
131
+ lines.push(`${indent};; ${block.label}:`);
132
+ for (const instr of block.instrs) {
133
+ lines.push(...this.emitInstr(instr, indent));
134
+ }
135
+ return lines;
136
+ }
137
+ emitInstr(instr, indent) {
138
+ const lines = [];
139
+ const I = indent;
140
+ switch (instr.op) {
141
+ case "const": {
142
+ if (typeof instr.value === "number") {
143
+ if (Number.isInteger(instr.value)) {
144
+ lines.push(`${I}i32.const ${instr.value}`);
145
+ }
146
+ else {
147
+ // Store float as i32 bits (simplified — real impl would use f64)
148
+ lines.push(`${I}i32.const ${Math.round(instr.value)}`);
149
+ }
150
+ }
151
+ else if (typeof instr.value === "boolean") {
152
+ lines.push(`${I}i32.const ${instr.value ? 1 : 0}`);
153
+ }
154
+ else if (instr.value === null) {
155
+ lines.push(`${I}i32.const 0`);
156
+ }
157
+ else if (typeof instr.value === "string") {
158
+ const offset = this.stringTable.get(instr.value) ?? 0;
159
+ const len = new TextEncoder().encode(instr.value).length;
160
+ // Pack offset and length — simplified: just store offset
161
+ lines.push(`${I}i32.const ${offset} ;; str "${instr.value.slice(0, 20)}"`);
162
+ }
163
+ lines.push(`${I}local.set $${sanitize(instr.dest)}`);
164
+ break;
165
+ }
166
+ case "load": {
167
+ if (instr.name.startsWith("@fn:")) {
168
+ lines.push(`${I}i32.const 0 ;; fn ref ${instr.name}`);
169
+ }
170
+ else {
171
+ lines.push(`${I}local.get $${sanitize(instr.name)}`);
172
+ }
173
+ lines.push(`${I}local.set $${sanitize(instr.dest)}`);
174
+ break;
175
+ }
176
+ case "store": {
177
+ if (instr.src.startsWith("@fn:")) {
178
+ lines.push(`${I}i32.const 0 ;; fn ref ${instr.src}`);
179
+ }
180
+ else {
181
+ lines.push(`${I}local.get $${sanitize(instr.src)}`);
182
+ }
183
+ lines.push(`${I}local.set $${sanitize(instr.name)}`);
184
+ break;
185
+ }
186
+ case "binop": {
187
+ lines.push(`${I}local.get $${sanitize(instr.left)}`);
188
+ lines.push(`${I}local.get $${sanitize(instr.right)}`);
189
+ lines.push(`${I}${watBinop(instr.operator)}`);
190
+ lines.push(`${I}local.set $${sanitize(instr.dest)}`);
191
+ break;
192
+ }
193
+ case "unop": {
194
+ if (instr.operator === "not" || instr.operator === "!") {
195
+ lines.push(`${I}local.get $${sanitize(instr.operand)}`);
196
+ lines.push(`${I}i32.eqz`);
197
+ }
198
+ else if (instr.operator === "-") {
199
+ lines.push(`${I}i32.const 0`);
200
+ lines.push(`${I}local.get $${sanitize(instr.operand)}`);
201
+ lines.push(`${I}i32.sub`);
202
+ }
203
+ else {
204
+ lines.push(`${I}local.get $${sanitize(instr.operand)}`);
205
+ }
206
+ lines.push(`${I}local.set $${sanitize(instr.dest)}`);
207
+ break;
208
+ }
209
+ case "call": {
210
+ // Push args
211
+ for (const arg of instr.args) {
212
+ lines.push(`${I}local.get $${sanitize(arg)}`);
213
+ }
214
+ // Call
215
+ if (isRuntimeBuiltin(instr.fn)) {
216
+ lines.push(`${I}call $__rt_${sanitize(instr.fn)}`);
217
+ }
218
+ else {
219
+ lines.push(`${I}call $${sanitize(instr.fn)}`);
220
+ }
221
+ lines.push(`${I}local.set $${sanitize(instr.dest)}`);
222
+ break;
223
+ }
224
+ case "toolcall": {
225
+ lines.push(`${I}local.get $${sanitize(instr.url)}`);
226
+ lines.push(`${I}i32.const ${this.stringTable.get(instr.method) ?? this.addString(instr.method)}`);
227
+ lines.push(`${I}i32.const ${instr.method.length}`);
228
+ if (instr.body) {
229
+ lines.push(`${I}local.get $${sanitize(instr.body)}`);
230
+ }
231
+ else {
232
+ lines.push(`${I}i32.const 0`);
233
+ }
234
+ lines.push(`${I}call $tool_call`);
235
+ lines.push(`${I}local.set $${sanitize(instr.dest)}`);
236
+ break;
237
+ }
238
+ case "field": {
239
+ lines.push(`${I}local.get $${sanitize(instr.obj)}`);
240
+ const propOffset = this.stringTable.get(instr.prop) ?? this.addString(instr.prop);
241
+ lines.push(`${I}i32.const ${propOffset}`);
242
+ lines.push(`${I}call $map_get`);
243
+ lines.push(`${I}local.set $${sanitize(instr.dest)}`);
244
+ break;
245
+ }
246
+ case "index": {
247
+ lines.push(`${I}local.get $${sanitize(instr.obj)}`);
248
+ lines.push(`${I}local.get $${sanitize(instr.idx)}`);
249
+ lines.push(`${I}call $list_get`);
250
+ lines.push(`${I}local.set $${sanitize(instr.dest)}`);
251
+ break;
252
+ }
253
+ case "setfield": {
254
+ lines.push(`${I}local.get $${sanitize(instr.obj)}`);
255
+ const pOff = this.stringTable.get(instr.prop) ?? this.addString(instr.prop);
256
+ lines.push(`${I}i32.const ${pOff}`);
257
+ lines.push(`${I}local.get $${sanitize(instr.src)}`);
258
+ lines.push(`${I}call $map_set`);
259
+ lines.push(`${I}drop`);
260
+ break;
261
+ }
262
+ case "setindex": {
263
+ lines.push(`${I};; setindex ${instr.obj}[${instr.idx}] = ${instr.src}`);
264
+ break;
265
+ }
266
+ case "list": {
267
+ lines.push(`${I}call $list_new`);
268
+ lines.push(`${I}local.set $${sanitize(instr.dest)}`);
269
+ for (const elem of instr.elements) {
270
+ lines.push(`${I}local.get $${sanitize(instr.dest)}`);
271
+ lines.push(`${I}local.get $${sanitize(elem)}`);
272
+ lines.push(`${I}call $list_push`);
273
+ lines.push(`${I}local.set $${sanitize(instr.dest)}`);
274
+ }
275
+ break;
276
+ }
277
+ case "map": {
278
+ lines.push(`${I}call $map_new`);
279
+ lines.push(`${I}local.set $${sanitize(instr.dest)}`);
280
+ for (let i = 0; i < instr.keys.length; i++) {
281
+ lines.push(`${I}local.get $${sanitize(instr.dest)}`);
282
+ lines.push(`${I}local.get $${sanitize(instr.keys[i])}`);
283
+ lines.push(`${I}local.get $${sanitize(instr.values[i])}`);
284
+ lines.push(`${I}call $map_set`);
285
+ lines.push(`${I}local.set $${sanitize(instr.dest)}`);
286
+ }
287
+ break;
288
+ }
289
+ case "jump": {
290
+ lines.push(`${I};; jump ${instr.target}`);
291
+ break;
292
+ }
293
+ case "branch": {
294
+ lines.push(`${I}local.get $${sanitize(instr.cond)}`);
295
+ lines.push(`${I};; branch -> ${instr.ifTrue} / ${instr.ifFalse}`);
296
+ break;
297
+ }
298
+ case "label": {
299
+ lines.push(`${I};; label ${instr.name}:`);
300
+ break;
301
+ }
302
+ case "print": {
303
+ lines.push(`${I}local.get $${sanitize(instr.value)}`);
304
+ lines.push(`${I}call $print_i32`);
305
+ break;
306
+ }
307
+ case "range": {
308
+ lines.push(`${I};; range(${instr.start}, ${instr.end})`);
309
+ lines.push(`${I}call $list_new`);
310
+ lines.push(`${I}local.set $${sanitize(instr.dest)}`);
311
+ break;
312
+ }
313
+ case "ret": {
314
+ if (instr.value) {
315
+ lines.push(`${I}local.get $${sanitize(instr.value)}`);
316
+ lines.push(`${I}return`);
317
+ }
318
+ else {
319
+ lines.push(`${I}i32.const 0`);
320
+ lines.push(`${I}return`);
321
+ }
322
+ break;
323
+ }
324
+ case "phi": {
325
+ lines.push(`${I};; phi ${instr.dest}`);
326
+ lines.push(`${I}i32.const 0`);
327
+ lines.push(`${I}local.set $${sanitize(instr.dest)}`);
328
+ break;
329
+ }
330
+ case "nop": {
331
+ lines.push(`${I}nop`);
332
+ break;
333
+ }
334
+ }
335
+ return lines;
336
+ }
337
+ }
338
+ function watBinop(op) {
339
+ switch (op) {
340
+ case "+": return "i32.add";
341
+ case "-": return "i32.sub";
342
+ case "*": return "i32.mul";
343
+ case "/": return "i32.div_s";
344
+ case "%": return "i32.rem_s";
345
+ case "==": return "i32.eq";
346
+ case "!=": return "i32.ne";
347
+ case "<": return "i32.lt_s";
348
+ case ">": return "i32.gt_s";
349
+ case "<=": return "i32.le_s";
350
+ case ">=": return "i32.ge_s";
351
+ case "and": return "i32.and";
352
+ case "or": return "i32.or";
353
+ case "**": return "i32.mul ;; TODO: power";
354
+ case "++": return "i32.add ;; TODO: string concat";
355
+ default: return `i32.add ;; TODO: ${op}`;
356
+ }
357
+ }
358
+ function isRuntimeBuiltin(fn) {
359
+ return ["len", "str", "push", "print", "head", "tail", "map", "filter", "fold",
360
+ "range", "keys", "values", "type", "__await", "__fetch_parallel"].includes(fn);
361
+ }
362
+ function sanitize(name) {
363
+ return name.replace(/[^a-zA-Z0-9_]/g, "_");
364
+ }
@@ -0,0 +1,52 @@
1
+ import * as AST from "./ast.js";
2
+ export declare enum ErrorCode {
3
+ UNEXPECTED_TOKEN = "ARC001",
4
+ MISSING_CLOSING_PAREN = "ARC002",
5
+ MISSING_CLOSING_BRACKET = "ARC003",
6
+ MISSING_CLOSING_BRACE = "ARC004",
7
+ EXPECTED_EXPRESSION = "ARC005",
8
+ INVALID_ASSIGNMENT = "ARC006",
9
+ UNTERMINATED_STRING = "ARC007",
10
+ INVALID_NUMBER = "ARC008",
11
+ TYPE_MISMATCH = "ARC100",
12
+ INVALID_OPERATOR = "ARC101",
13
+ NOT_CALLABLE = "ARC102",
14
+ WRONG_ARITY = "ARC103",
15
+ UNDEFINED_VARIABLE = "ARC200",
16
+ IMMUTABLE_REASSIGN = "ARC201",
17
+ INDEX_OUT_OF_BOUNDS = "ARC202",
18
+ DIVISION_BY_ZERO = "ARC203",
19
+ NOT_ITERABLE = "ARC204",
20
+ ASSERTION_FAILED = "ARC205",
21
+ PROPERTY_ACCESS = "ARC206",
22
+ MODULE_NOT_FOUND = "ARC300",
23
+ CIRCULAR_IMPORT = "ARC301",
24
+ SOURCE_TOO_LARGE = "ARC400",
25
+ NESTING_TOO_DEEP = "ARC401",
26
+ EXECUTION_LIMIT = "ARC402",
27
+ RECURSION_LIMIT = "ARC403",
28
+ TOOL_CALL_BLOCKED = "ARC404",
29
+ IMPORT_BLOCKED = "ARC405",
30
+ TIMEOUT = "ARC406"
31
+ }
32
+ export type ErrorCategory = "ParseError" | "TypeError" | "RuntimeError" | "ImportError" | "SecurityError";
33
+ export interface ArcError {
34
+ code: ErrorCode;
35
+ category: ErrorCategory;
36
+ message: string;
37
+ loc?: AST.Loc;
38
+ source?: string;
39
+ suggestion?: string;
40
+ }
41
+ export declare function levenshtein(a: string, b: string): number;
42
+ export declare function findClosestMatch(name: string, candidates: string[], maxDistance?: number): string | null;
43
+ export declare function formatError(error: ArcError, useColor?: boolean): string;
44
+ export declare function undefinedVariableError(name: string, candidates: string[], loc?: AST.Loc, source?: string): ArcError;
45
+ export declare function parseError(message: string, loc?: AST.Loc, source?: string, suggestion?: string): ArcError;
46
+ export declare function typeError(message: string, loc?: AST.Loc, source?: string): ArcError;
47
+ export declare function runtimeError(code: ErrorCode, message: string, loc?: AST.Loc, source?: string, suggestion?: string): ArcError;
48
+ export declare function importError(message: string, loc?: AST.Loc, source?: string): ArcError;
49
+ export declare function securityError(code: ErrorCode, message: string): ArcError;
50
+ export declare function prettyPrintError(err: Error, source?: string, useColor?: boolean): string;
51
+ export declare function setPrettyErrors(enabled: boolean): void;
52
+ export declare function isPrettyErrorsEnabled(): boolean;