authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-05-20 17:28:35-07:00
committergravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-05-20 17:28:35-07:00
log460a9789a37e51de9c6f7067f26c4dd4a680d3ce
tree4cd1da5a62cf6ab15fd036ad4fc23e3d1c91b3b4
parentadc3371e964920ff856589de0621665f99f87580
signature Commit is signed but in an unrecognized format.

add var decls and comptime math


14 files changed, 428 insertions(+), 95 deletions(-)

CHANGELOG.md+8-4
...@@ -1,8 +1,12 @@...@@ -1,8 +1,12 @@
1## 0.1.1-alpha1## 0.1.1
22
3- introduce `Report` and show errors during Tokenize, Parse, and Transform.3- feat: `var` declarations / runtime memory
4- add flag --dump-ir for enabling ir dumps4- feat: comptime binary operators
5- feat: introduce `Report` and show error reports
6- feat: show warnings for unused vars/lets/fns/params
7- feat: add flag --dump-ir for enabling ir dumps
8- fix: fix AIR generation for a block with a variable name
59
6## 0.1.0-alpha10## 0.1.0
711
8initial demo of compiler pipeline in zig.12initial demo of compiler pipeline in zig.
demo.txt deleted-25
...@@ -1,25 +0,0 @@
1let print = @js("console").log;
2
3fn doThing(x: float, y: float) -> float {
4 let math = if x < y {
5 x - y
6 } else b: {
7 let sum = x + y;
8 if sum > 100 {
9 break :b sum;
10 }
11 if x == y {
12 return 5;
13 }
14 sum * -sum
15 };
16 if x == y {
17 return 5;
18 }
19 let divisor = if x > 10 { 2 } else { 3 };
20 math / divisor
21}
22
23print(doThing(1, 6));
24print(doThing(6, 4));
25print(doThing(100, 52));
src/compiler/Air.Dump.zig+21-3
...@@ -77,6 +77,14 @@ const State = struct {...@@ -77,6 +77,14 @@ const State = struct {
77 try s.dumpRef(w, brk.value);77 try s.dumpRef(w, brk.value);
78 try w.writeAll(")");78 try w.writeAll(")");
79 },79 },
80 .store => {
81 const store: Air.Store = air.instData(.store, inst);
82 try w.writeAll("(");
83 try s.dumpInstRef(w, store.memory);
84 try w.writeAll(", ");
85 try s.dumpRef(w, store.operand);
86 try w.writeAll(")");
87 },
80 inline .add,88 inline .add,
81 .mul,89 .mul,
82 .sub,90 .sub,
...@@ -92,12 +100,22 @@ const State = struct {...@@ -92,12 +100,22 @@ const State = struct {
92 try s.dumpRef(w, bin.rhs);100 try s.dumpRef(w, bin.rhs);
93 try w.writeAll(")");101 try w.writeAll(")");
94 },102 },
95 inline .negate => |comptime_tag| {103 inline .negate,
104 .bool_not,
105 => |comptime_tag| {
96 const bin: Air.Un = air.instData(comptime_tag, inst);106 const bin: Air.Un = air.instData(comptime_tag, inst);
97 try w.writeAll("(");107 try w.writeAll("(");
98 try s.dumpRef(w, bin.operand);108 try s.dumpRef(w, bin.operand);
99 try w.writeAll(")");109 try w.writeAll(")");
100 },110 },
111 .memory => {
112 const memory: Air.Memory = air.instData(.memory, inst);
113 try w.writeAll("(");
114 try s.dumpRef(w, memory.init);
115 try w.writeAll(", type: ");
116 try s.dumpValue(w, memory.ty);
117 try w.writeAll(")");
118 },
101 .field => {119 .field => {
102 const field: Air.Field = air.instData(.field, inst);120 const field: Air.Field = air.instData(.field, inst);
103 try w.writeAll("(");121 try w.writeAll("(");
...@@ -138,14 +156,14 @@ const State = struct {...@@ -138,14 +156,14 @@ const State = struct {
138 }156 }
139157
140 fn dumpRef(s: *State, w: anytype, ref: Inst.Ref) !void {158 fn dumpRef(s: *State, w: anytype, ref: Inst.Ref) !void {
141 switch (ref.unwrap()) {159 switch (ref.decode()) {
142 .inst => |i| try s.dumpInstRef(w, i),160 .inst => |i| try s.dumpInstRef(w, i),
143 .value => |v| try s.dumpValue(w, v),161 .value => |v| try s.dumpValue(w, v),
144 }162 }
145 }163 }
146164
147 fn dumpRefRecurse(s: *State, w: anytype, ref: Inst.Ref) !void {165 fn dumpRefRecurse(s: *State, w: anytype, ref: Inst.Ref) !void {
148 switch (ref.unwrap()) {166 switch (ref.decode()) {
149 .inst => |i| try s.dumpInst(w, i),167 .inst => |i| try s.dumpInst(w, i),
150 .value => |v| try s.dumpValue(w, v),168 .value => |v| try s.dumpValue(w, v),
151 }169 }
src/compiler/Air.zig+34-7
...@@ -21,6 +21,9 @@ pub const Inst = struct {...@@ -21,6 +21,9 @@ pub const Inst = struct {
21 arg,21 arg,
22 field,22 field,
2323
24 memory,
25 store,
26
24 // bin27 // bin
25 add,28 add,
26 sub,29 sub,
...@@ -34,6 +37,7 @@ pub const Inst = struct {...@@ -34,6 +37,7 @@ pub const Inst = struct {
3437
35 // un38 // un
36 negate,39 negate,
40 bool_not,
3741
38 pub fn Data(comptime tag: Tag) type {42 pub fn Data(comptime tag: Tag) type {
39 return switch (tag) {43 return switch (tag) {
...@@ -43,6 +47,9 @@ pub const Inst = struct {...@@ -43,6 +47,9 @@ pub const Inst = struct {
43 .@"if" => If,47 .@"if" => If,
44 .call => Call,48 .call => Call,
45 .arg => Arg,49 .arg => Arg,
50 .field => Field,
51 .memory => Memory,
52 .store => Store,
46 .add,53 .add,
47 .sub,54 .sub,
48 .mul,55 .mul,
...@@ -51,10 +58,7 @@ pub const Inst = struct {...@@ -51,10 +58,7 @@ pub const Inst = struct {
51 .gt,58 .gt,
52 .eq,59 .eq,
53 => Bin,60 => Bin,
54 .negate,61 .negate, .bool_not => Un,
55 => Un,
56 .field,
57 => Field,
58 };62 };
59 }63 }
60 };64 };
...@@ -71,7 +75,7 @@ pub const Inst = struct {...@@ -71,7 +75,7 @@ pub const Inst = struct {
71 inst: Index,75 inst: Index,
72 };76 };
7377
74 pub fn unwrap(ref: Ref) Decoded {78 pub fn decode(ref: Ref) Decoded {
75 return switch (ref.kind) {79 return switch (ref.kind) {
76 .value => .{ .value = Value.fromInterned(@enumFromInt(ref.data)) },80 .value => .{ .value = Value.fromInterned(@enumFromInt(ref.data)) },
77 .inst => .{ .inst = @enumFromInt(ref.data) },81 .inst => .{ .inst = @enumFromInt(ref.data) },
...@@ -90,6 +94,15 @@ pub const Inst = struct {...@@ -90,6 +94,15 @@ pub const Inst = struct {
90 return if (opt != .none) @bitCast(@intFromEnum(opt)) else null;94 return if (opt != .none) @bitCast(@intFromEnum(opt)) else null;
91 }95 }
92 };96 };
97
98 pub fn isVoid(ref: Ref) bool {
99 return ref.kind == .value and
100 ref.decode().value.toIntern() == .void_value;
101 }
102
103 pub fn eql(ref: Ref, other: Ref) bool {
104 return @as(u32, @bitCast(ref)) == @as(u32, @bitCast(other));
105 }
93 };106 };
94107
95 /// reference to another instruction in this air108 /// reference to another instruction in this air
...@@ -165,6 +178,11 @@ pub const Bin = struct {...@@ -165,6 +178,11 @@ pub const Bin = struct {
165 rhs: Inst.Ref,178 rhs: Inst.Ref,
166};179};
167180
181pub const Store = struct {
182 memory: Inst.Index,
183 operand: Inst.Ref,
184};
185
168pub const Un = struct {186pub const Un = struct {
169 operand: Inst.Ref,187 operand: Inst.Ref,
170 unused: Unused = .zero,188 unused: Unused = .zero,
...@@ -200,6 +218,11 @@ pub const If = struct {...@@ -200,6 +218,11 @@ pub const If = struct {
200 };218 };
201};219};
202220
221pub const Memory = struct {
222 init: Inst.Ref,
223 ty: Value,
224};
225
203pub fn deinit(air: *Air, gpa: std.mem.Allocator) void {226pub fn deinit(air: *Air, gpa: std.mem.Allocator) void {
204 defer air.* = undefined;227 defer air.* = undefined;
205 air.insts.deinit(gpa);228 air.insts.deinit(gpa);
...@@ -227,7 +250,7 @@ pub fn instPayload(air: *const Air, comptime tag: Inst.Tag, index: Inst.Index) t...@@ -227,7 +250,7 @@ pub fn instPayload(air: *const Air, comptime tag: Inst.Tag, index: Inst.Index) t
227}250}
228251
229pub fn typeOf(air: *const Air, ref: Inst.Ref, ip: *InternPool) InternPool.Index {252pub fn typeOf(air: *const Air, ref: Inst.Ref, ip: *InternPool) InternPool.Index {
230 return switch (ref.unwrap()) {253 return switch (ref.decode()) {
231 .value => |v| v.type(ip).toIntern(),254 .value => |v| v.type(ip).toIntern(),
232 .inst => |i| air.typeOfIndex(i, ip),255 .inst => |i| air.typeOfIndex(i, ip),
233 };256 };
...@@ -244,7 +267,7 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *InternPool) InternPo...@@ -244,7 +267,7 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *InternPool) InternPo
244267
245 inline .add, .sub, .mul, .div => |t| air.typeOf(air.instData(t, index).lhs, ip),268 inline .add, .sub, .mul, .div => |t| air.typeOf(air.instData(t, index).lhs, ip),
246269
247 .lt, .gt, .eq => .bool_type,270 .lt, .gt, .eq, .bool_not => .bool_type,
248271
249 .arg => air.instData(.arg, index).type.toIntern(),272 .arg => air.instData(.arg, index).type.toIntern(),
250273
...@@ -261,6 +284,10 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *InternPool) InternPo...@@ -261,6 +284,10 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *InternPool) InternPo
261 return base_type.toKey(ip).type_function.return_type;284 return base_type.toKey(ip).type_function.return_type;
262 },285 },
263286
287 .memory => air.instData(.memory, index).ty.toIntern(),
288
289 .store => air.instData(.memory, air.instData(.store, index).memory).ty.toIntern(),
290
264 .@"if" => air.instData(.@"if", index).data.get(air).union_ty.toIntern(),291 .@"if" => air.instData(.@"if", index).data.get(air).union_ty.toIntern(),
265 };292 };
266}293}
src/compiler/InternPool.zig+4
...@@ -202,6 +202,10 @@ pub const Index = enum(u32) {...@@ -202,6 +202,10 @@ pub const Index = enum(u32) {
202 return .{ .kind = .value, .data = @intCast(@intFromEnum(index)) };202 return .{ .kind = .value, .data = @intCast(@intFromEnum(index)) };
203 }203 }
204204
205 pub fn toValue(index: Index) @import("Value.zig") {
206 return .{ .ip_index = index };
207 }
208
205 pub fn toKey(index: Index, ip: *const InternPool) Key {209 pub fn toKey(index: Index, ip: *const InternPool) Key {
206 return ip.indexToKey(index);210 return ip.indexToKey(index);
207 }211 }
src/compiler/JsLink.zig+26-4
...@@ -198,7 +198,6 @@ fn printExpr(link: *JsLink, tree: *const JsTree, node: Node.Index, writer: anyty...@@ -198,7 +198,6 @@ fn printExpr(link: *JsLink, tree: *const JsTree, node: Node.Index, writer: anyty
198 defer t.end();198 defer t.end();
199199
200 switch (tree.nodeTag(node)) {200 switch (tree.nodeTag(node)) {
201 else => |tag| std.debug.panic("TODO: printExpr .{s}", .{@tagName(tag)}),
202 .root => unreachable,201 .root => unreachable,
203202
204 .plain_value => {203 .plain_value => {
...@@ -302,20 +301,28 @@ fn printExpr(link: *JsLink, tree: *const JsTree, node: Node.Index, writer: anyty...@@ -302,20 +301,28 @@ fn printExpr(link: *JsLink, tree: *const JsTree, node: Node.Index, writer: anyty
302 .eq,301 .eq,
303 .lt,302 .lt,
304 .gt,303 .gt,
304 .assign_add,
305 .assign_sub,
306 .assign_mul,
307 .assign_div,
305 => |tag| {308 => |tag| {
306 const bin: JsTree.Bin = tree.nodeData(tag, node);309 const bin: JsTree.Bin = tree.nodeData(tag, node);
307 const op: Operator = @field(Operator, @tagName(tag));310 const op: Operator = @field(Operator, @tagName(tag));
308 try link.printExpr(tree, bin.lhs, writer);311 try link.printExpr(tree, bin.lhs, writer);
309 try writer.writeAll(" ");312 try writer.writeAll(" ");
310 try writer.writeAll(op.symbol());313 try writer.writeAll(comptime op.symbol());
311 try writer.writeAll(" ");314 try writer.writeAll(" ");
312 try link.printExpr(tree, bin.rhs, writer);315 try link.printExpr(tree, bin.rhs, writer);
313 },316 },
314317
315 inline .negate => |tag| {318 inline .negate,
319 .bool_not,
320 .pre_dec,
321 .pre_inc,
322 => |tag| {
316 const un: JsTree.Un = tree.nodeData(tag, node);323 const un: JsTree.Un = tree.nodeData(tag, node);
317 const op: Operator = @field(Operator, @tagName(tag));324 const op: Operator = @field(Operator, @tagName(tag));
318 try writer.writeAll(op.symbol());325 try writer.writeAll(comptime op.symbol());
319 try link.printExpr(tree, un.operand, writer);326 try link.printExpr(tree, un.operand, writer);
320 },327 },
321328
...@@ -402,6 +409,10 @@ const Level = enum {...@@ -402,6 +409,10 @@ const Level = enum {
402409
403const Operator = enum {410const Operator = enum {
404 assign,411 assign,
412 assign_add,
413 assign_sub,
414 assign_mul,
415 assign_div,
405 add,416 add,
406 sub,417 sub,
407 mul,418 mul,
...@@ -410,10 +421,17 @@ const Operator = enum {...@@ -410,10 +421,17 @@ const Operator = enum {
410 lt,421 lt,
411 gt,422 gt,
412 negate,423 negate,
424 bool_not,
425 pre_inc,
426 pre_dec,
413427
414 pub fn symbol(op: Operator) []const u8 {428 pub fn symbol(op: Operator) []const u8 {
415 return switch (op) {429 return switch (op) {
416 .assign => "=",430 .assign => "=",
431 .assign_add => "+=",
432 .assign_sub => "-=",
433 .assign_mul => "*=",
434 .assign_div => "/=",
417 .add => "+",435 .add => "+",
418 .sub => "-",436 .sub => "-",
419 .mul => "*",437 .mul => "*",
...@@ -422,7 +440,11 @@ const Operator = enum {...@@ -422,7 +440,11 @@ const Operator = enum {
422 .lt => "<",440 .lt => "<",
423 .gt => ">",441 .gt => ">",
424442
443 .pre_inc => "++",
444 .pre_dec => "++",
445
425 .negate => "-",446 .negate => "-",
447 .bool_not => "!",
426 };448 };
427 }449 }
428};450};
src/compiler/JsNodeGen.zig+129-20
...@@ -145,6 +145,23 @@ fn codeGenInst(c: *JsNodeGen, inst: Air.Inst.Index) Error!void {...@@ -145,6 +145,23 @@ fn codeGenInst(c: *JsNodeGen, inst: Air.Inst.Index) Error!void {
145 };145 };
146 },146 },
147147
148 .memory => {
149 const i = @intFromEnum(inst);
150 const data: Air.Memory = c.air.instData(.memory, inst);
151 const value: JsTree.Node.Index.Optional = if (data.init.isVoid())
152 .none
153 else
154 (try c.fetchExpression(data.init)).toOptional();
155
156 try c.addStmt(try c.add(.stmt_declare_local, .{
157 .identifier_index = c.next_id,
158 .init = value,
159 }));
160 const id = try c.add(.local, .{ .id = c.next_id });
161 c.next_id += 1;
162 c.air_node_map[i] = .{ .node = id };
163 },
164
148 inline .gt,165 inline .gt,
149 .lt,166 .lt,
150 .eq,167 .eq,
...@@ -161,7 +178,69 @@ fn codeGenInst(c: *JsNodeGen, inst: Air.Inst.Index) Error!void {...@@ -161,7 +178,69 @@ fn codeGenInst(c: *JsNodeGen, inst: Air.Inst.Index) Error!void {
161 try c.markExpression(inst, node);178 try c.markExpression(inst, node);
162 },179 },
163180
181 .store => {
182 const data: Air.Store = c.air.instData(.store, inst);
183
184 const target = try c.fetchInstExpression(data.memory);
185
186 if (data.operand.kind == .inst) {
187 const index = data.operand.decode().inst;
188 switch (c.air.instTag(index)) {
189 inline .add,
190 .sub,
191 .mul,
192 .div,
193 => |tag| {
194 const inner_data: Air.Bin = c.air.instData(tag, index);
195 if (inner_data.lhs.eql(data.memory.toRef())) {
196 if ((tag == .add or tag == .sub) and
197 inner_data.rhs.kind == .value)
198 {
199 if (inner_data.rhs.decode().value.toFloat(c.ip)) |f| {
200 if (f == 1) {
201 // convert this into ++ or --
202 const node = try c.add(
203 if (tag == .add)
204 .pre_inc
205 else
206 .pre_dec,
207 .{
208 .operand = target,
209 },
210 );
211 try c.markExpression(inst, node);
212
213 return;
214 }
215 }
216 }
217
218 // convert into a compound assignment
219 const node = try c.add(
220 @field(JsTree.Tag, "assign_" ++ @tagName(tag)),
221 .{
222 .lhs = target,
223 .rhs = try c.fetchExpression(inner_data.rhs),
224 },
225 );
226 try c.markExpression(inst, node);
227
228 return;
229 }
230 },
231 else => {},
232 }
233 }
234
235 const node = try c.add(.assign, .{
236 .lhs = target,
237 .rhs = try c.fetchExpression(data.operand),
238 });
239 try c.markExpression(inst, node);
240 },
241
164 inline .negate,242 inline .negate,
243 .bool_not,
165 => |tag| {244 => |tag| {
166 const data: Air.Un = c.air.instData(tag, inst);245 const data: Air.Un = c.air.instData(tag, inst);
167 const node = try c.add(@field(JsTree.Tag, @tagName(tag)), .{246 const node = try c.add(@field(JsTree.Tag, @tagName(tag)), .{
...@@ -331,27 +410,32 @@ fn markExpression(c: *JsNodeGen, inst: Air.Inst.Index, node: Node.Index) !void {...@@ -331,27 +410,32 @@ fn markExpression(c: *JsNodeGen, inst: Air.Inst.Index, node: Node.Index) !void {
331 }410 }
332}411}
333412
334fn fetchExpression(c: *JsNodeGen, inst: Air.Inst.Ref) !Node.Index {413fn fetchExpression(c: *JsNodeGen, ref: Air.Inst.Ref) !Node.Index {
335 const t = tracer.trace(@src());414 const t = tracer.trace(@src());
336 defer t.end();415 defer t.end();
337416
338 return switch (inst.unwrap()) {417 return switch (ref.decode()) {
339 .value => |v| c.valueToExpression(v),418 .value => |v| c.valueToExpression(v),
340 .inst => |index| {419 .inst => |index| fetchInstExpression(c, index),
341 const data = c.air_node_map[@intFromEnum(index)];
342 if (std.debug.runtime_safety) {
343 const flags: Air.Flags = c.air.instFlags(index);
344 switch (flags.uses) {
345 .none => unreachable, // incorrectly marked instruction as unused,
346 .single => {},
347 .multi => assert(c.nodes.items(.tag)[@intFromEnum(data.node)] == .local),
348 }
349 }
350 return data.node;
351 },
352 };420 };
353}421}
354422
423fn fetchInstExpression(c: *JsNodeGen, index: Air.Inst.Index) !Node.Index {
424 const t = tracer.trace(@src());
425 defer t.end();
426
427 const data = c.air_node_map[@intFromEnum(index)];
428 if (std.debug.runtime_safety) {
429 const flags: Air.Flags = c.air.instFlags(index);
430 switch (flags.uses) {
431 .none => unreachable, // incorrectly marked instruction as unused,
432 .single => {},
433 .multi => assert(c.nodes.items(.tag)[@intFromEnum(data.node)] == .local),
434 }
435 }
436 return data.node;
437}
438
355fn valueToExpression(c: *JsNodeGen, v: Value) !Node.Index {439fn valueToExpression(c: *JsNodeGen, v: Value) !Node.Index {
356 const t = tracer.trace(@src());440 const t = tracer.trace(@src());
357 defer t.end();441 defer t.end();
...@@ -405,7 +489,7 @@ fn codeGenBranch(c: *JsNodeGen, inst: Air.Inst.Ref, temporary: ?Node.Index) !?Br...@@ -405,7 +489,7 @@ fn codeGenBranch(c: *JsNodeGen, inst: Air.Inst.Ref, temporary: ?Node.Index) !?Br
405 const this_label = c.next_label;489 const this_label = c.next_label;
406490
407 var requires_label = false;491 var requires_label = false;
408 var items = switch (inst.unwrap()) {492 var items = switch (inst.decode()) {
409 .value => |v| items: {493 .value => |v| items: {
410 if (v.ip_index == .void_value)494 if (v.ip_index == .void_value)
411 return null;495 return null;
...@@ -499,11 +583,15 @@ fn codeGenBlock(c: *JsNodeGen, inst: Air.Inst.Index) !void {...@@ -499,11 +583,15 @@ fn codeGenBlock(c: *JsNodeGen, inst: Air.Inst.Index) !void {
499 }));583 }));
500}584}
501585
586// Ideally, we convert everything to an expression. This allows for much
587// more compact code. When that isnt possible (`return`, `while`, etc),
588// we detect that here and ensure to lower `if` to actual if statements,
589// and so on.
502fn canLowerToExpression(c: *const JsNodeGen, inst: Air.Inst.Ref) bool {590fn canLowerToExpression(c: *const JsNodeGen, inst: Air.Inst.Ref) bool {
503 const t = tracer.trace(@src());591 const t = tracer.trace(@src());
504 defer t.end();592 defer t.end();
505593
506 return switch (inst.unwrap()) {594 return switch (inst.decode()) {
507 .value => true,595 .value => true,
508 .inst => |index| switch (c.air.instTag(index)) {596 .inst => |index| switch (c.air.instTag(index)) {
509 .js_global_ref,597 .js_global_ref,
...@@ -518,12 +606,33 @@ fn canLowerToExpression(c: *const JsNodeGen, inst: Air.Inst.Ref) bool {...@@ -518,12 +606,33 @@ fn canLowerToExpression(c: *const JsNodeGen, inst: Air.Inst.Ref) bool {
518 .lt,606 .lt,
519 .gt,607 .gt,
520 .negate,608 .negate,
609 .bool_not,
610 .store,
521 => true,611 => true,
612
613 // JavaScript variable declarations require statements.
614 // Later, this could be made true if we can hoist temporaries up.
615 .memory => false,
616
617 // break/return must be a js statement
522 .@"break",618 .@"break",
523 => false,619 => false,
524 // TODO: these depend620
525 .block => false,621 .block => {
526 .@"if" => false,622 // TODO: blocks that are
623 // - all statements except last are lowerable
624 // - the last statement is lowerable OR a break of this block
625 return false;
626 },
627
628 .@"if" => {
629 const call = c.air.instData(.@"if", index);
630 const data = call.data.get(c.air);
631
632 return c.canLowerToExpression(call.cond) and
633 c.canLowerToExpression(data.yes) and
634 c.canLowerToExpression(data.no);
635 },
527 },636 },
528 };637 };
529}638}
...@@ -533,7 +642,7 @@ fn lowerToExpression(c: *JsNodeGen, ref: Air.Inst.Ref) !Node.Index {...@@ -533,7 +642,7 @@ fn lowerToExpression(c: *JsNodeGen, ref: Air.Inst.Ref) !Node.Index {
533 defer t.end();642 defer t.end();
534643
535 assert(c.canLowerToExpression(ref));644 assert(c.canLowerToExpression(ref));
536 return switch (ref.unwrap()) {645 return switch (ref.decode()) {
537 .value => |v| try c.valueToExpression(v),646 .value => |v| try c.valueToExpression(v),
538 .inst => |index| {647 .inst => |index| {
539 try c.codeGenInst(index);648 try c.codeGenInst(index);
src/compiler/JsTree.zig+13-1
...@@ -26,6 +26,10 @@ pub const Tag = enum {...@@ -26,6 +26,10 @@ pub const Tag = enum {
26 decl,26 decl,
27 // bin ops27 // bin ops
28 assign,28 assign,
29 assign_add,
30 assign_sub,
31 assign_mul,
32 assign_div,
29 add,33 add,
30 sub,34 sub,
31 mul,35 mul,
...@@ -37,9 +41,11 @@ pub const Tag = enum {...@@ -37,9 +41,11 @@ pub const Tag = enum {
37 call,41 call,
38 field,42 field,
39 ternary,43 ternary,
40 // un ops44 // prefix ops
41 negate,45 negate,
42 bool_not,46 bool_not,
47 pre_inc,
48 pre_dec,
43 // stmts49 // stmts
44 /// `let ... = ...`50 /// `let ... = ...`
45 stmt_declare_local,51 stmt_declare_local,
...@@ -60,6 +66,10 @@ pub const Tag = enum {...@@ -60,6 +66,10 @@ pub const Tag = enum {
60 .global => GlobalRef,66 .global => GlobalRef,
61 .decl => DeclRef,67 .decl => DeclRef,
62 .assign,68 .assign,
69 .assign_add,
70 .assign_sub,
71 .assign_mul,
72 .assign_div,
63 .add,73 .add,
64 .sub,74 .sub,
65 .mul,75 .mul,
...@@ -71,6 +81,8 @@ pub const Tag = enum {...@@ -71,6 +81,8 @@ pub const Tag = enum {
71 .ternary => Ternary,81 .ternary => Ternary,
72 .negate,82 .negate,
73 .bool_not,83 .bool_not,
84 .pre_inc,
85 .pre_dec,
74 => Un,86 => Un,
75 .call => Call,87 .call => Call,
76 .field => Field,88 .field => Field,
src/compiler/Sema.zig+137-17
...@@ -50,7 +50,7 @@ const InstMap = struct {...@@ -50,7 +50,7 @@ const InstMap = struct {
50 map.* = undefined;50 map.* = undefined;
51 }51 }
5252
53 pub fn get(map: *InstMap, teg_code: Teg.Inst.Index) ?Air.Inst.Ref {53 pub fn get(map: *const InstMap, teg_code: Teg.Inst.Index) ?Air.Inst.Ref {
54 const source_i = @intFromEnum(teg_code);54 const source_i = @intFromEnum(teg_code);
55 const start_i = @intFromEnum(map.start);55 const start_i = @intFromEnum(map.start);
56 assert(source_i >= start_i and source_i <= (start_i + map.items.len));56 assert(source_i >= start_i and source_i <= (start_i + map.items.len));
...@@ -58,7 +58,12 @@ const InstMap = struct {...@@ -58,7 +58,12 @@ const InstMap = struct {
58 }58 }
5959
60 pub fn add(map: *InstMap, from: Teg.Inst.Index, to: Air.Inst.Ref) !void {60 pub fn add(map: *InstMap, from: Teg.Inst.Index, to: Air.Inst.Ref) !void {
61 assert(map.get(from) == null);61 if (assertWithMessage(map.get(from) == null)) {
62 std.debug.panic("%{d} is already mapped (to #{d})", .{
63 @intFromEnum(from),
64 @intFromEnum(map.get(from).?.decode().inst),
65 });
66 }
62 const source_i = @intFromEnum(from);67 const source_i = @intFromEnum(from);
63 const start_i = @intFromEnum(map.start);68 const start_i = @intFromEnum(map.start);
64 map.items[source_i - start_i] = to.toOptional();69 map.items[source_i - start_i] = to.toOptional();
...@@ -128,7 +133,7 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value {...@@ -128,7 +133,7 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value {
128 const root = (try s.analyzeBlock(133 const root = (try s.analyzeBlock(
129 fn_decl.body.toIndex() orelse134 fn_decl.body.toIndex() orelse
130 @panic("TODO: please change this to Index. Ref is not allowed here anymore"),135 @panic("TODO: please change this to Index. Ref is not allowed here anymore"),
131 )).unwrap().inst;136 )).decode().inst;
132 assert(@intFromEnum(root) == 0);137 assert(@intFromEnum(root) == 0);
133138
134 const func_prototype = try s.ip.get(s.gpa, .{139 const func_prototype = try s.ip.get(s.gpa, .{
...@@ -153,13 +158,12 @@ pub fn analyzeBlock(s: *Sema, in: Teg.Inst.Index) anyerror!Air.Inst.Ref {...@@ -153,13 +158,12 @@ pub fn analyzeBlock(s: *Sema, in: Teg.Inst.Index) anyerror!Air.Inst.Ref {
153 const t = tracer.trace(@src());158 const t = tracer.trace(@src());
154 defer t.end();159 defer t.end();
155160
156 const block = try s.addBlock();
157
158 try s.inst_map.add(in, block.block_index.toRef());
159
160 const scratch_top = s.scratch.items.len;161 const scratch_top = s.scratch.items.len;
161 defer s.scratch.shrinkRetainingCapacity(scratch_top);162 defer s.scratch.shrinkRetainingCapacity(scratch_top);
162163
164 const block = try s.addBlock();
165 try s.inst_map.add(in, block.block_index.toRef());
166
163 var last: Air.Inst.Ref = undefined;167 var last: Air.Inst.Ref = undefined;
164 const instructions = s.code.blockInstructions(in);168 const instructions = s.code.blockInstructions(in);
165 assert(instructions.len != 0);169 assert(instructions.len != 0);
...@@ -194,8 +198,13 @@ fn analyzeCode(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {...@@ -194,8 +198,13 @@ fn analyzeCode(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
194 defer t.end();198 defer t.end();
195199
196 const out = try s.analyzeCodeInner(in);200 const out = try s.analyzeCodeInner(in);
197 if (s.code.instTag(in) != .block)201
202 // Block refs are added automatically
203 if (s.code.instTag(in) == .block) {
204 assert(s.inst_map.get(in) != null);
205 } else {
198 try s.inst_map.add(in, out);206 try s.inst_map.add(in, out);
207 }
199208
200 return out;209 return out;
201}210}
...@@ -269,7 +278,11 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {...@@ -269,7 +278,11 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
269 const rhs = try s.resolveRefRuntime(bin.rhs);278 const rhs = try s.resolveRefRuntime(bin.rhs);
270279
271 if (lhs.kind == .value and rhs.kind == .value) {280 if (lhs.kind == .value and rhs.kind == .value) {
272 @panic("TODO: comptime math operation");281 return (try s.comptimeBin(
282 @field(BinaryOp, @tagName(tag)),
283 lhs.decode().value,
284 rhs.decode().value,
285 ));
273 }286 }
274287
275 const left_type = s.typeOfRef(lhs);288 const left_type = s.typeOfRef(lhs);
...@@ -284,7 +297,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {...@@ -284,7 +297,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
284 .rhs = rhs,297 .rhs = rhs,
285 });298 });
286 },299 },
287 inline .negate => |tag| {300 inline .negate, .bool_not => |tag| {
288 const un: Teg.Inst.RefNode = s.code.instData(tag, in);301 const un: Teg.Inst.RefNode = s.code.instData(tag, in);
289302
290 const operand = try s.resolveRefRuntime(un.operand);303 const operand = try s.resolveRefRuntime(un.operand);
...@@ -301,7 +314,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {...@@ -301,7 +314,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
301 .@"break" => {314 .@"break" => {
302 const brk: Teg.Inst.Break = s.code.instPayload(.@"break", in);315 const brk: Teg.Inst.Break = s.code.instPayload(.@"break", in);
303 const operand = try s.resolveRefRuntime(brk.operand);316 const operand = try s.resolveRefRuntime(brk.operand);
304 const block_ref = s.inst_map.get(brk.block).?.unwrap().inst; // block ref is resolved by teg317 const block_ref = s.inst_map.get(brk.block).?.decode().inst; // block ref is resolved by teg
305 return s.addInstRef(.@"break", .{318 return s.addInstRef(.@"break", .{
306 .block_ref = block_ref,319 .block_ref = block_ref,
307 .value = operand,320 .value = operand,
...@@ -398,12 +411,41 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {...@@ -398,12 +411,41 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
398 },411 },
399 .block => {412 .block => {
400 const block = try s.analyzeBlock(in);413 const block = try s.analyzeBlock(in);
401 switch (block.unwrap()) {414 switch (block.decode()) {
402 .inst => |t| try s.scratch.append(s.gpa, @intFromEnum(t)),415 .inst => |t| if (s.tempAir().instTag(t) == .block) {
416 try s.scratch.append(s.gpa, @intFromEnum(t));
417 },
403 else => {},418 else => {},
404 }419 }
405 return block;420 return block;
406 },421 },
422 .memory => {
423 const data = s.code.instData(.memory, in);
424
425 const initial_value = try s.resolveRefRuntime(data.operand);
426
427 return try s.addInstRef(.memory, .{
428 .init = initial_value,
429 .ty = s.typeOfRef(initial_value).toValue(),
430 });
431 },
432 .store => {
433 const data: Teg.Inst.Store = s.code.instPayload(.store, in);
434 assert(s.code.instTag(data.memory) == .memory); // corrupt store instruction
435
436 const location = s.mapTeg(data.memory.toRef()).decode().inst;
437 const operand = try s.resolveRefRuntime(data.operand);
438
439 try s.validateType(
440 s.typeOfRef(operand).toValue(),
441 s.tempAir().instData(.memory, location).ty.ip_index,
442 );
443
444 return try s.addInstRef(.store, .{
445 .memory = location,
446 .operand = operand,
447 });
448 },
407 else => |tag| std.debug.panic("TODO: analyze of .{s}", .{@tagName(tag)}),449 else => |tag| std.debug.panic("TODO: analyze of .{s}", .{@tagName(tag)}),
408 }450 }
409}451}
...@@ -420,19 +462,25 @@ fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) anyerror!Air.Inst.Ref {...@@ -420,19 +462,25 @@ fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) anyerror!Air.Inst.Ref {
420 const scratch_top = s.scratch.items.len;462 const scratch_top = s.scratch.items.len;
421 defer s.scratch.shrinkRetainingCapacity(scratch_top);463 defer s.scratch.shrinkRetainingCapacity(scratch_top);
422464
465 std.debug.print("{d} {d}\n", .{ scratch_top, s.scratch.items.len });
423 const visited = try s.resolveRefRuntime(in);466 const visited = try s.resolveRefRuntime(in);
467 std.debug.print("{d} {d}\n", .{ scratch_top, s.scratch.items.len });
424468
425 if (scratch_top == s.scratch.items.len) {469 if (scratch_top == s.scratch.items.len and visited.kind == .value) {
426 return visited;470 return visited;
427 }471 }
428472
429 if (visited.kind == .inst and473 if (visited.kind == .inst and
430 s.air_instructions.items(.tag)[@intFromEnum(visited.unwrap().inst)] == .block)474 s.air_instructions.items(.tag)[@intFromEnum(visited.decode().inst)] == .block)
431 {475 {
432 return visited;476 return visited;
433 }477 }
434478
435 const block = try s.addBlock();479 const block = try s.addBlock();
480 _ = try s.addInstRef(.@"break", .{
481 .block_ref = block.block_index,
482 .value = visited,
483 });
436 return (try block.finish(s, scratch_top)).toRef();484 return (try block.finish(s, scratch_top)).toRef();
437}485}
438486
...@@ -443,7 +491,7 @@ fn resolveRefRuntimeExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) anyerror!Air....@@ -443,7 +491,7 @@ fn resolveRefRuntimeExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) anyerror!Air.
443491
444 if (s.inst_map.get(index)) |air| {492 if (s.inst_map.get(index)) |air| {
445 if (mark) {493 if (mark) {
446 switch (air.unwrap()) {494 switch (air.decode()) {
447 .inst => |i| s.markInst(i),495 .inst => |i| s.markInst(i),
448 else => {},496 else => {},
449 }497 }
...@@ -464,7 +512,7 @@ fn resolveRefComptime(s: *Sema, ref: Teg.Inst.Ref) !Value {...@@ -464,7 +512,7 @@ fn resolveRefComptime(s: *Sema, ref: Teg.Inst.Ref) !Value {
464 };512 };
465513
466 if (s.inst_map.get(in)) |air| {514 if (s.inst_map.get(in)) |air| {
467 switch (air.unwrap()) {515 switch (air.decode()) {
468 .value => |value| return value,516 .value => |value| return value,
469 else => {},517 else => {},
470 }518 }
...@@ -545,6 +593,70 @@ fn unionType(s: *Sema, a: Value, b: Value) Value {...@@ -545,6 +593,70 @@ fn unionType(s: *Sema, a: Value, b: Value) Value {
545 // return s.ip.get(s.gpa, .{ .@"union" = &.{ a, b } });593 // return s.ip.get(s.gpa, .{ .@"union" = &.{ a, b } });
546}594}
547595
596const BinaryOp = enum {
597 add,
598 sub,
599 mul,
600 div,
601 lt,
602 gt,
603 eq,
604};
605
606fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Air.Inst.Ref {
607 switch (op) {
608 .add => {
609 const l = lhs.toFloat(s.ip) orelse @panic("TODO: typecheck / string add");
610 const r = rhs.toFloat(s.ip) orelse @panic("TODO: typecheck / string add");
611 return (try s.ip.get(s.gpa, .{
612 .float = l + r,
613 })).toAir();
614 },
615 inline .sub, .mul, .div => |tag| {
616 const l = lhs.toFloat(s.ip) orelse @panic("TODO: typecheck");
617 const r = rhs.toFloat(s.ip) orelse @panic("TODO: typecheck");
618
619 const result = switch (tag) {
620 .sub => l - r,
621 .mul => l * r,
622 .div => l / r,
623 else => comptime unreachable,
624 };
625
626 return (try s.ip.get(s.gpa, .{
627 .float = result,
628 })).toAir();
629 },
630
631 inline .lt,
632 .gt,
633 => |tag| {
634 const l = lhs.toFloat(s.ip) orelse @panic("TODO: typecheck");
635 const r = rhs.toFloat(s.ip) orelse @panic("TODO: typecheck");
636 const result = switch (tag) {
637 .lt => l < r,
638 .gt => l > r,
639 else => comptime unreachable,
640 };
641 return @as(
642 InternPool.Index,
643 if (result) .bool_true else .bool_false,
644 ).toAir();
645 },
646
647 .eq,
648 => {
649 const l = lhs.toFloat(s.ip) orelse @panic("TODO: typecheck / non-numeric eq");
650 const r = rhs.toFloat(s.ip) orelse @panic("TODO: typecheck / non-numeric eq");
651 const result = l == r;
652 return @as(
653 InternPool.Index,
654 if (result) .bool_true else .bool_false,
655 ).toAir();
656 },
657 }
658}
659
548/// --- helper ---660/// --- helper ---
549const IncompleteBlock = struct {661const IncompleteBlock = struct {
550 block_index: Air.Inst.Index,662 block_index: Air.Inst.Index,
...@@ -655,6 +767,14 @@ const Allocator = std.mem.Allocator;...@@ -655,6 +767,14 @@ const Allocator = std.mem.Allocator;
655const assert = std.debug.assert;767const assert = std.debug.assert;
656const log = std.log.scoped(.sema);768const log = std.log.scoped(.sema);
657769
770inline fn assertWithMessage(cond: bool) bool {
771 if (std.debug.runtime_safety) {
772 return !cond;
773 } else if (!cond) {
774 unreachable; // assertion failure
775 }
776}
777
658const Comp = @import("Comp.zig");778const Comp = @import("Comp.zig");
659const File = Comp.File;779const File = Comp.File;
660const Decl = Comp.Decl;780const Decl = Comp.Decl;
src/compiler/Teg.Dump.zig+8-1
...@@ -93,7 +93,6 @@ const State = struct {...@@ -93,7 +93,6 @@ const State = struct {
93 try tty.setColor(writer, .reset);93 try tty.setColor(writer, .reset);
94 },94 },
95 inline .field,95 inline .field,
96 .store,
97 .add,96 .add,
98 .sub,97 .sub,
99 .mul,98 .mul,
...@@ -129,6 +128,14 @@ const State = struct {...@@ -129,6 +128,14 @@ const State = struct {
129 try s.dumpRefMarker(writer, data.operand);128 try s.dumpRefMarker(writer, data.operand);
130 try writer.writeAll(")");129 try writer.writeAll(")");
131 },130 },
131 .store => {
132 const data: Teg.Inst.Store = teg.instData(.store, inst).extra_index.get(teg);
133 try writer.writeAll("(");
134 try s.dumpRefMarker(writer, data.memory.toRef());
135 try writer.writeAll(", ");
136 try s.dumpRefMarker(writer, data.operand);
137 try writer.writeAll(")");
138 },
132 .string => {139 .string => {
133 const data: Teg.SlicedString = teg.instData(.string, inst).extra_index.get(teg);140 const data: Teg.SlicedString = teg.instData(.string, inst).extra_index.get(teg);
134 try tty.setColor(writer, .green);141 try tty.setColor(writer, .green);
src/compiler/Teg.zig+6-1
...@@ -91,6 +91,7 @@ pub const Inst = struct {...@@ -91,6 +91,7 @@ pub const Inst = struct {
91 .@"if" => PlNode(If),91 .@"if" => PlNode(If),
92 .load_decl => StrNode,92 .load_decl => StrNode,
93 .param => DataNode,93 .param => DataNode,
94 .store => PlNode(Store),
9495
95 // Single instruction operand96 // Single instruction operand
96 .memory,97 .memory,
...@@ -108,7 +109,6 @@ pub const Inst = struct {...@@ -108,7 +109,6 @@ pub const Inst = struct {
108109
109 // Two instruction operands110 // Two instruction operands
110 .field,111 .field,
111 .store,
112 .add,112 .add,
113 .sub,113 .sub,
114 .mul,114 .mul,
...@@ -258,6 +258,11 @@ pub const Inst = struct {...@@ -258,6 +258,11 @@ pub const Inst = struct {
258 rhs: Inst.Ref,258 rhs: Inst.Ref,
259 };259 };
260260
261 pub const Store = struct {
262 memory: Inst.Index,
263 operand: Inst.Ref,
264 };
265
261 pub const As = struct {266 pub const As = struct {
262 type: Inst.Ref,267 type: Inst.Ref,
263 operand: Inst.Ref,268 operand: Inst.Ref,
src/compiler/Transform.zig+6-6
...@@ -656,9 +656,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -656,9 +656,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
656 .tag = .store,656 .tag = .store,
657 .data = EncodedData.encode(.store, .{657 .data = EncodedData.encode(.store, .{
658 .src_node = decl.nodeOffset(node),658 .src_node = decl.nodeOffset(node),
659 .extra_index = try t.extra.append(t.gpa, Inst.Bin, .{659 .extra_index = try t.extra.append(t.gpa, Inst.Store, .{
660 .lhs = lhs,660 .memory = lhs.toIndex() orelse @panic("TODO: convert this into Ref"),
661 .rhs = rhs,661 .operand = rhs,
662 }),662 }),
663 }),663 }),
664 });664 });
...@@ -728,9 +728,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -728,9 +728,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
728 .tag = .store,728 .tag = .store,
729 .data = EncodedData.encode(.store, .{729 .data = EncodedData.encode(.store, .{
730 .src_node = decl.nodeOffset(node),730 .src_node = decl.nodeOffset(node),
731 .extra_index = try t.extra.append(t.gpa, Inst.Bin, .{731 .extra_index = try t.extra.append(t.gpa, Inst.Store, .{
732 .lhs = lhs,732 .memory = lhs.toIndex() orelse @panic("TODO: convert this into Ref"),
733 .rhs = compute_inst,733 .operand = compute_inst,
734 }),734 }),
735 }),735 }),
736 });736 });
src/compiler/Value.zig+11-3
...@@ -22,7 +22,7 @@ pub fn asBool(val: Value) i32 {...@@ -22,7 +22,7 @@ pub fn asBool(val: Value) i32 {
22 };22 };
23}23}
2424
25pub fn asString(val: Value, ip: *InternPool) InternPool.String.WithLen {25pub fn asString(val: Value, ip: *const InternPool) InternPool.String.WithLen {
26 const index = val.toIntern();26 const index = val.toIntern();
27 return ip.items.items(.data)[@intFromEnum(index)].decode(27 return ip.items.items(.data)[@intFromEnum(index)].decode(
28 .string,28 .string,
...@@ -31,7 +31,7 @@ pub fn asString(val: Value, ip: *InternPool) InternPool.String.WithLen {...@@ -31,7 +31,7 @@ pub fn asString(val: Value, ip: *InternPool) InternPool.String.WithLen {
31 .get(ip);31 .get(ip);
32}32}
3333
34pub fn @"type"(val: Value, ip: *InternPool) Value {34pub fn @"type"(val: Value, ip: *const InternPool) Value {
35 const v: InternPool.Index = switch (val.toKey(ip)) {35 const v: InternPool.Index = switch (val.toKey(ip)) {
36 .simple_type, .type_function => .type_type,36 .simple_type, .type_function => .type_type,
3737
...@@ -47,8 +47,16 @@ pub fn @"type"(val: Value, ip: *InternPool) Value {...@@ -47,8 +47,16 @@ pub fn @"type"(val: Value, ip: *InternPool) Value {
47 return Value.fromInterned(v);47 return Value.fromInterned(v);
48}48}
4949
50pub fn toFloat(val: Value, ip: *const InternPool) ?f64 {
51 return switch (val.toKey(ip)) {
52 .int32 => |i| @floatFromInt(i),
53 .float => |f| f,
54 else => null,
55 };
56}
57
50/// used for validating ensure_void instructions58/// used for validating ensure_void instructions
51pub fn isEnsureVoid(val: Value, ip: *InternPool) bool {59pub fn isEnsureVoid(val: Value, ip: *const InternPool) bool {
52 const intern = val.toIntern();60 const intern = val.toIntern();
53 const i = @intFromEnum(intern);61 const i = @intFromEnum(intern);
5462
src/compiler/main.zig+25-3
...@@ -43,6 +43,7 @@ pub fn main() !void {...@@ -43,6 +43,7 @@ pub fn main() !void {
43 const cwd = try std.posix.getcwd(&cwd_buf);43 const cwd = try std.posix.getcwd(&cwd_buf);
4444
45 var exe_name: []const u8 = undefined;45 var exe_name: []const u8 = undefined;
46 var dump_ir = false;
4647
47 {48 {
48 var args = try std.process.argsWithAllocator(alloc);49 var args = try std.process.argsWithAllocator(alloc);
...@@ -52,8 +53,24 @@ pub fn main() !void {...@@ -52,8 +53,24 @@ pub fn main() !void {
52 errdefer alloc.free(exe_name);53 errdefer alloc.free(exe_name);
5354
54 while (args.next()) |arg| {55 while (args.next()) |arg| {
55 maybe_file_path = try std.fs.path.resolve(alloc, &.{ cwd, arg });56 if (std.mem.startsWith(u8, arg, "-")) {
56 break;57 if (std.mem.eql(u8, arg, "--dump-ir")) {
58 dump_ir = true;
59 } else {
60 std.debug.print("error: unknown flag '{s}'\n", .{arg});
61 maybe_file_path = null;
62 break;
63 }
64 continue;
65 }
66
67 if (maybe_file_path == null) {
68 maybe_file_path = try std.fs.path.resolve(alloc, &.{ cwd, arg });
69 } else {
70 std.debug.print("error: multiple files specified\n", .{});
71 maybe_file_path = null;
72 break;
73 }
57 }74 }
58 }75 }
59 defer alloc.free(exe_name);76 defer alloc.free(exe_name);
...@@ -70,7 +87,12 @@ pub fn main() !void {...@@ -70,7 +87,12 @@ pub fn main() !void {
70 std.process.exit(1);87 std.process.exit(1);
71 };88 };
7289
73 var comp: Comp = .{ .gpa = tracer.tracingAllocator(&alloc) };90 var comp: Comp = .{
91 .gpa = tracer.tracingAllocator(&alloc),
92 .options = .{
93 .dump_ir = dump_ir,
94 },
95 };
74 try comp.init();96 try comp.init();
75 defer comp.deinit();97 defer comp.deinit();
76 const main_file = try comp.getFile(file_path);98 const main_file = try comp.getFile(file_path);