14 files changed, 428 insertions(+), 95 deletions(-)
CHANGELOG.md+8-4| ... | ... | @@ -1,8 +1,12 @@ |
| 1 | ## 0.1.1-alpha | |
| 1 | ## 0.1.1 | |
| 2 | 2 | |
| 3 | - introduce `Report` and show errors during Tokenize, Parse, and Transform. | |
| 4 | - add flag --dump-ir for enabling ir dumps | |
| 3 | - feat: `var` declarations / runtime memory | |
| 4 | - 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 | |
| 5 | 9 | |
| 6 | ## 0.1.0-alpha | |
| 10 | ## 0.1.0 | |
| 7 | 11 | |
| 8 | 12 | initial demo of compiler pipeline in zig. |
demo.txt deleted-25| ... | ... | @@ -1,25 +0,0 @@ |
| 1 | let print = @js("console").log; | |
| 2 | ||
| 3 | fn 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 | ||
| 23 | print(doThing(1, 6)); | |
| 24 | print(doThing(6, 4)); | |
| 25 | print(doThing(100, 52)); |
src/compiler/Air.Dump.zig+21-3| ... | ... | @@ -77,6 +77,14 @@ const State = struct { |
| 77 | 77 | try s.dumpRef(w, brk.value); |
| 78 | 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 | 88 | inline .add, |
| 81 | 89 | .mul, |
| 82 | 90 | .sub, |
| ... | ... | @@ -92,12 +100,22 @@ const State = struct { |
| 92 | 100 | try s.dumpRef(w, bin.rhs); |
| 93 | 101 | try w.writeAll(")"); |
| 94 | 102 | }, |
| 95 | inline .negate => |comptime_tag| { | |
| 103 | inline .negate, | |
| 104 | .bool_not, | |
| 105 | => |comptime_tag| { | |
| 96 | 106 | const bin: Air.Un = air.instData(comptime_tag, inst); |
| 97 | 107 | try w.writeAll("("); |
| 98 | 108 | try s.dumpRef(w, bin.operand); |
| 99 | 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 | 119 | .field => { |
| 102 | 120 | const field: Air.Field = air.instData(.field, inst); |
| 103 | 121 | try w.writeAll("("); |
| ... | ... | @@ -138,14 +156,14 @@ const State = struct { |
| 138 | 156 | } |
| 139 | 157 | |
| 140 | 158 | fn dumpRef(s: *State, w: anytype, ref: Inst.Ref) !void { |
| 141 | switch (ref.unwrap()) { | |
| 159 | switch (ref.decode()) { | |
| 142 | 160 | .inst => |i| try s.dumpInstRef(w, i), |
| 143 | 161 | .value => |v| try s.dumpValue(w, v), |
| 144 | 162 | } |
| 145 | 163 | } |
| 146 | 164 | |
| 147 | 165 | fn dumpRefRecurse(s: *State, w: anytype, ref: Inst.Ref) !void { |
| 148 | switch (ref.unwrap()) { | |
| 166 | switch (ref.decode()) { | |
| 149 | 167 | .inst => |i| try s.dumpInst(w, i), |
| 150 | 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 | 21 | arg, |
| 22 | 22 | field, |
| 23 | 23 | |
| 24 | memory, | |
| 25 | store, | |
| 26 | ||
| 24 | 27 | // bin |
| 25 | 28 | add, |
| 26 | 29 | sub, |
| ... | ... | @@ -34,6 +37,7 @@ pub const Inst = struct { |
| 34 | 37 | |
| 35 | 38 | // un |
| 36 | 39 | negate, |
| 40 | bool_not, | |
| 37 | 41 | |
| 38 | 42 | pub fn Data(comptime tag: Tag) type { |
| 39 | 43 | return switch (tag) { |
| ... | ... | @@ -43,6 +47,9 @@ pub const Inst = struct { |
| 43 | 47 | .@"if" => If, |
| 44 | 48 | .call => Call, |
| 45 | 49 | .arg => Arg, |
| 50 | .field => Field, | |
| 51 | .memory => Memory, | |
| 52 | .store => Store, | |
| 46 | 53 | .add, |
| 47 | 54 | .sub, |
| 48 | 55 | .mul, |
| ... | ... | @@ -51,10 +58,7 @@ pub const Inst = struct { |
| 51 | 58 | .gt, |
| 52 | 59 | .eq, |
| 53 | 60 | => Bin, |
| 54 | .negate, | |
| 55 | => Un, | |
| 56 | .field, | |
| 57 | => Field, | |
| 61 | .negate, .bool_not => Un, | |
| 58 | 62 | }; |
| 59 | 63 | } |
| 60 | 64 | }; |
| ... | ... | @@ -71,7 +75,7 @@ pub const Inst = struct { |
| 71 | 75 | inst: Index, |
| 72 | 76 | }; |
| 73 | 77 | |
| 74 | pub fn unwrap(ref: Ref) Decoded { | |
| 78 | pub fn decode(ref: Ref) Decoded { | |
| 75 | 79 | return switch (ref.kind) { |
| 76 | 80 | .value => .{ .value = Value.fromInterned(@enumFromInt(ref.data)) }, |
| 77 | 81 | .inst => .{ .inst = @enumFromInt(ref.data) }, |
| ... | ... | @@ -90,6 +94,15 @@ pub const Inst = struct { |
| 90 | 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 | }; |
| 94 | 107 | |
| 95 | 108 | /// reference to another instruction in this air |
| ... | ... | @@ -165,6 +178,11 @@ pub const Bin = struct { |
| 165 | 178 | rhs: Inst.Ref, |
| 166 | 179 | }; |
| 167 | 180 | |
| 181 | pub const Store = struct { | |
| 182 | memory: Inst.Index, | |
| 183 | operand: Inst.Ref, | |
| 184 | }; | |
| 185 | ||
| 168 | 186 | pub const Un = struct { |
| 169 | 187 | operand: Inst.Ref, |
| 170 | 188 | unused: Unused = .zero, |
| ... | ... | @@ -200,6 +218,11 @@ pub const If = struct { |
| 200 | 218 | }; |
| 201 | 219 | }; |
| 202 | 220 | |
| 221 | pub const Memory = struct { | |
| 222 | init: Inst.Ref, | |
| 223 | ty: Value, | |
| 224 | }; | |
| 225 | ||
| 203 | 226 | pub fn deinit(air: *Air, gpa: std.mem.Allocator) void { |
| 204 | 227 | defer air.* = undefined; |
| 205 | 228 | air.insts.deinit(gpa); |
| ... | ... | @@ -227,7 +250,7 @@ pub fn instPayload(air: *const Air, comptime tag: Inst.Tag, index: Inst.Index) t |
| 227 | 250 | } |
| 228 | 251 | |
| 229 | 252 | pub fn typeOf(air: *const Air, ref: Inst.Ref, ip: *InternPool) InternPool.Index { |
| 230 | return switch (ref.unwrap()) { | |
| 253 | return switch (ref.decode()) { | |
| 231 | 254 | .value => |v| v.type(ip).toIntern(), |
| 232 | 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 | 267 | |
| 245 | 268 | inline .add, .sub, .mul, .div => |t| air.typeOf(air.instData(t, index).lhs, ip), |
| 246 | 269 | |
| 247 | .lt, .gt, .eq => .bool_type, | |
| 270 | .lt, .gt, .eq, .bool_not => .bool_type, | |
| 248 | 271 | |
| 249 | 272 | .arg => air.instData(.arg, index).type.toIntern(), |
| 250 | 273 | |
| ... | ... | @@ -261,6 +284,10 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *InternPool) InternPo |
| 261 | 284 | return base_type.toKey(ip).type_function.return_type; |
| 262 | 285 | }, |
| 263 | 286 | |
| 287 | .memory => air.instData(.memory, index).ty.toIntern(), | |
| 288 | ||
| 289 | .store => air.instData(.memory, air.instData(.store, index).memory).ty.toIntern(), | |
| 290 | ||
| 264 | 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 | 202 | return .{ .kind = .value, .data = @intCast(@intFromEnum(index)) }; |
| 203 | 203 | } |
| 204 | 204 | |
| 205 | pub fn toValue(index: Index) @import("Value.zig") { | |
| 206 | return .{ .ip_index = index }; | |
| 207 | } | |
| 208 | ||
| 205 | 209 | pub fn toKey(index: Index, ip: *const InternPool) Key { |
| 206 | 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 | 198 | defer t.end(); |
| 199 | 199 | |
| 200 | 200 | switch (tree.nodeTag(node)) { |
| 201 | else => |tag| std.debug.panic("TODO: printExpr .{s}", .{@tagName(tag)}), | |
| 202 | 201 | .root => unreachable, |
| 203 | 202 | |
| 204 | 203 | .plain_value => { |
| ... | ... | @@ -302,20 +301,28 @@ fn printExpr(link: *JsLink, tree: *const JsTree, node: Node.Index, writer: anyty |
| 302 | 301 | .eq, |
| 303 | 302 | .lt, |
| 304 | 303 | .gt, |
| 304 | .assign_add, | |
| 305 | .assign_sub, | |
| 306 | .assign_mul, | |
| 307 | .assign_div, | |
| 305 | 308 | => |tag| { |
| 306 | 309 | const bin: JsTree.Bin = tree.nodeData(tag, node); |
| 307 | 310 | const op: Operator = @field(Operator, @tagName(tag)); |
| 308 | 311 | try link.printExpr(tree, bin.lhs, writer); |
| 309 | 312 | try writer.writeAll(" "); |
| 310 | try writer.writeAll(op.symbol()); | |
| 313 | try writer.writeAll(comptime op.symbol()); | |
| 311 | 314 | try writer.writeAll(" "); |
| 312 | 315 | try link.printExpr(tree, bin.rhs, writer); |
| 313 | 316 | }, |
| 314 | 317 | |
| 315 | inline .negate => |tag| { | |
| 318 | inline .negate, | |
| 319 | .bool_not, | |
| 320 | .pre_dec, | |
| 321 | .pre_inc, | |
| 322 | => |tag| { | |
| 316 | 323 | const un: JsTree.Un = tree.nodeData(tag, node); |
| 317 | 324 | const op: Operator = @field(Operator, @tagName(tag)); |
| 318 | try writer.writeAll(op.symbol()); | |
| 325 | try writer.writeAll(comptime op.symbol()); | |
| 319 | 326 | try link.printExpr(tree, un.operand, writer); |
| 320 | 327 | }, |
| 321 | 328 | |
| ... | ... | @@ -402,6 +409,10 @@ const Level = enum { |
| 402 | 409 | |
| 403 | 410 | const Operator = enum { |
| 404 | 411 | assign, |
| 412 | assign_add, | |
| 413 | assign_sub, | |
| 414 | assign_mul, | |
| 415 | assign_div, | |
| 405 | 416 | add, |
| 406 | 417 | sub, |
| 407 | 418 | mul, |
| ... | ... | @@ -410,10 +421,17 @@ const Operator = enum { |
| 410 | 421 | lt, |
| 411 | 422 | gt, |
| 412 | 423 | negate, |
| 424 | bool_not, | |
| 425 | pre_inc, | |
| 426 | pre_dec, | |
| 413 | 427 | |
| 414 | 428 | pub fn symbol(op: Operator) []const u8 { |
| 415 | 429 | return switch (op) { |
| 416 | 430 | .assign => "=", |
| 431 | .assign_add => "+=", | |
| 432 | .assign_sub => "-=", | |
| 433 | .assign_mul => "*=", | |
| 434 | .assign_div => "/=", | |
| 417 | 435 | .add => "+", |
| 418 | 436 | .sub => "-", |
| 419 | 437 | .mul => "*", |
| ... | ... | @@ -422,7 +440,11 @@ const Operator = enum { |
| 422 | 440 | .lt => "<", |
| 423 | 441 | .gt => ">", |
| 424 | 442 | |
| 443 | .pre_inc => "++", | |
| 444 | .pre_dec => "++", | |
| 445 | ||
| 425 | 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 | 145 | }; |
| 146 | 146 | }, |
| 147 | 147 | |
| 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 | 165 | inline .gt, |
| 149 | 166 | .lt, |
| 150 | 167 | .eq, |
| ... | ... | @@ -161,7 +178,69 @@ fn codeGenInst(c: *JsNodeGen, inst: Air.Inst.Index) Error!void { |
| 161 | 178 | try c.markExpression(inst, node); |
| 162 | 179 | }, |
| 163 | 180 | |
| 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 | 242 | inline .negate, |
| 243 | .bool_not, | |
| 165 | 244 | => |tag| { |
| 166 | 245 | const data: Air.Un = c.air.instData(tag, inst); |
| 167 | 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 | 410 | } |
| 332 | 411 | } |
| 333 | 412 | |
| 334 | fn fetchExpression(c: *JsNodeGen, inst: Air.Inst.Ref) !Node.Index { | |
| 413 | fn fetchExpression(c: *JsNodeGen, ref: Air.Inst.Ref) !Node.Index { | |
| 335 | 414 | const t = tracer.trace(@src()); |
| 336 | 415 | defer t.end(); |
| 337 | 416 | |
| 338 | return switch (inst.unwrap()) { | |
| 417 | return switch (ref.decode()) { | |
| 339 | 418 | .value => |v| c.valueToExpression(v), |
| 340 | .inst => |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 | }, | |
| 419 | .inst => |index| fetchInstExpression(c, index), | |
| 352 | 420 | }; |
| 353 | 421 | } |
| 354 | 422 | |
| 423 | fn 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 | ||
| 355 | 439 | fn valueToExpression(c: *JsNodeGen, v: Value) !Node.Index { |
| 356 | 440 | const t = tracer.trace(@src()); |
| 357 | 441 | defer t.end(); |
| ... | ... | @@ -405,7 +489,7 @@ fn codeGenBranch(c: *JsNodeGen, inst: Air.Inst.Ref, temporary: ?Node.Index) !?Br |
| 405 | 489 | const this_label = c.next_label; |
| 406 | 490 | |
| 407 | 491 | var requires_label = false; |
| 408 | var items = switch (inst.unwrap()) { | |
| 492 | var items = switch (inst.decode()) { | |
| 409 | 493 | .value => |v| items: { |
| 410 | 494 | if (v.ip_index == .void_value) |
| 411 | 495 | return null; |
| ... | ... | @@ -499,11 +583,15 @@ fn codeGenBlock(c: *JsNodeGen, inst: Air.Inst.Index) !void { |
| 499 | 583 | })); |
| 500 | 584 | } |
| 501 | 585 | |
| 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. | |
| 502 | 590 | fn canLowerToExpression(c: *const JsNodeGen, inst: Air.Inst.Ref) bool { |
| 503 | 591 | const t = tracer.trace(@src()); |
| 504 | 592 | defer t.end(); |
| 505 | 593 | |
| 506 | return switch (inst.unwrap()) { | |
| 594 | return switch (inst.decode()) { | |
| 507 | 595 | .value => true, |
| 508 | 596 | .inst => |index| switch (c.air.instTag(index)) { |
| 509 | 597 | .js_global_ref, |
| ... | ... | @@ -518,12 +606,33 @@ fn canLowerToExpression(c: *const JsNodeGen, inst: Air.Inst.Ref) bool { |
| 518 | 606 | .lt, |
| 519 | 607 | .gt, |
| 520 | 608 | .negate, |
| 609 | .bool_not, | |
| 610 | .store, | |
| 521 | 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 | 618 | .@"break", |
| 523 | 619 | => false, |
| 524 | // TODO: these depend | |
| 525 | .block => false, | |
| 526 | .@"if" => false, | |
| 620 | ||
| 621 | .block => { | |
| 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 | 642 | defer t.end(); |
| 534 | 643 | |
| 535 | 644 | assert(c.canLowerToExpression(ref)); |
| 536 | return switch (ref.unwrap()) { | |
| 645 | return switch (ref.decode()) { | |
| 537 | 646 | .value => |v| try c.valueToExpression(v), |
| 538 | 647 | .inst => |index| { |
| 539 | 648 | try c.codeGenInst(index); |
src/compiler/JsTree.zig+13-1| ... | ... | @@ -26,6 +26,10 @@ pub const Tag = enum { |
| 26 | 26 | decl, |
| 27 | 27 | // bin ops |
| 28 | 28 | assign, |
| 29 | assign_add, | |
| 30 | assign_sub, | |
| 31 | assign_mul, | |
| 32 | assign_div, | |
| 29 | 33 | add, |
| 30 | 34 | sub, |
| 31 | 35 | mul, |
| ... | ... | @@ -37,9 +41,11 @@ pub const Tag = enum { |
| 37 | 41 | call, |
| 38 | 42 | field, |
| 39 | 43 | ternary, |
| 40 | // un ops | |
| 44 | // prefix ops | |
| 41 | 45 | negate, |
| 42 | 46 | bool_not, |
| 47 | pre_inc, | |
| 48 | pre_dec, | |
| 43 | 49 | // stmts |
| 44 | 50 | /// `let ... = ...` |
| 45 | 51 | stmt_declare_local, |
| ... | ... | @@ -60,6 +66,10 @@ pub const Tag = enum { |
| 60 | 66 | .global => GlobalRef, |
| 61 | 67 | .decl => DeclRef, |
| 62 | 68 | .assign, |
| 69 | .assign_add, | |
| 70 | .assign_sub, | |
| 71 | .assign_mul, | |
| 72 | .assign_div, | |
| 63 | 73 | .add, |
| 64 | 74 | .sub, |
| 65 | 75 | .mul, |
| ... | ... | @@ -71,6 +81,8 @@ pub const Tag = enum { |
| 71 | 81 | .ternary => Ternary, |
| 72 | 82 | .negate, |
| 73 | 83 | .bool_not, |
| 84 | .pre_inc, | |
| 85 | .pre_dec, | |
| 74 | 86 | => Un, |
| 75 | 87 | .call => Call, |
| 76 | 88 | .field => Field, |
src/compiler/Sema.zig+137-17| ... | ... | @@ -50,7 +50,7 @@ const InstMap = struct { |
| 50 | 50 | map.* = undefined; |
| 51 | 51 | } |
| 52 | 52 | |
| 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 | 54 | const source_i = @intFromEnum(teg_code); |
| 55 | 55 | const start_i = @intFromEnum(map.start); |
| 56 | 56 | assert(source_i >= start_i and source_i <= (start_i + map.items.len)); |
| ... | ... | @@ -58,7 +58,12 @@ const InstMap = struct { |
| 58 | 58 | } |
| 59 | 59 | |
| 60 | 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 | 67 | const source_i = @intFromEnum(from); |
| 63 | 68 | const start_i = @intFromEnum(map.start); |
| 64 | 69 | map.items[source_i - start_i] = to.toOptional(); |
| ... | ... | @@ -128,7 +133,7 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value { |
| 128 | 133 | const root = (try s.analyzeBlock( |
| 129 | 134 | fn_decl.body.toIndex() orelse |
| 130 | 135 | @panic("TODO: please change this to Index. Ref is not allowed here anymore"), |
| 131 | )).unwrap().inst; | |
| 136 | )).decode().inst; | |
| 132 | 137 | assert(@intFromEnum(root) == 0); |
| 133 | 138 | |
| 134 | 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 | 158 | const t = tracer.trace(@src()); |
| 154 | 159 | defer t.end(); |
| 155 | 160 | |
| 156 | const block = try s.addBlock(); | |
| 157 | ||
| 158 | try s.inst_map.add(in, block.block_index.toRef()); | |
| 159 | ||
| 160 | 161 | const scratch_top = s.scratch.items.len; |
| 161 | 162 | defer s.scratch.shrinkRetainingCapacity(scratch_top); |
| 162 | 163 | |
| 164 | const block = try s.addBlock(); | |
| 165 | try s.inst_map.add(in, block.block_index.toRef()); | |
| 166 | ||
| 163 | 167 | var last: Air.Inst.Ref = undefined; |
| 164 | 168 | const instructions = s.code.blockInstructions(in); |
| 165 | 169 | assert(instructions.len != 0); |
| ... | ... | @@ -194,8 +198,13 @@ fn analyzeCode(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 194 | 198 | defer t.end(); |
| 195 | 199 | |
| 196 | 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 | 206 | try s.inst_map.add(in, out); |
| 207 | } | |
| 199 | 208 | |
| 200 | 209 | return out; |
| 201 | 210 | } |
| ... | ... | @@ -269,7 +278,11 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 269 | 278 | const rhs = try s.resolveRefRuntime(bin.rhs); |
| 270 | 279 | |
| 271 | 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 | } |
| 274 | 287 | |
| 275 | 288 | const left_type = s.typeOfRef(lhs); |
| ... | ... | @@ -284,7 +297,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 284 | 297 | .rhs = rhs, |
| 285 | 298 | }); |
| 286 | 299 | }, |
| 287 | inline .negate => |tag| { | |
| 300 | inline .negate, .bool_not => |tag| { | |
| 288 | 301 | const un: Teg.Inst.RefNode = s.code.instData(tag, in); |
| 289 | 302 | |
| 290 | 303 | const operand = try s.resolveRefRuntime(un.operand); |
| ... | ... | @@ -301,7 +314,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 301 | 314 | .@"break" => { |
| 302 | 315 | const brk: Teg.Inst.Break = s.code.instPayload(.@"break", in); |
| 303 | 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 teg | |
| 317 | const block_ref = s.inst_map.get(brk.block).?.decode().inst; // block ref is resolved by teg | |
| 305 | 318 | return s.addInstRef(.@"break", .{ |
| 306 | 319 | .block_ref = block_ref, |
| 307 | 320 | .value = operand, |
| ... | ... | @@ -398,12 +411,41 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 398 | 411 | }, |
| 399 | 412 | .block => { |
| 400 | 413 | const block = try s.analyzeBlock(in); |
| 401 | switch (block.unwrap()) { | |
| 402 | .inst => |t| try s.scratch.append(s.gpa, @intFromEnum(t)), | |
| 414 | switch (block.decode()) { | |
| 415 | .inst => |t| if (s.tempAir().instTag(t) == .block) { | |
| 416 | try s.scratch.append(s.gpa, @intFromEnum(t)); | |
| 417 | }, | |
| 403 | 418 | else => {}, |
| 404 | 419 | } |
| 405 | 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 | 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 | 462 | const scratch_top = s.scratch.items.len; |
| 421 | 463 | defer s.scratch.shrinkRetainingCapacity(scratch_top); |
| 422 | 464 | |
| 465 | std.debug.print("{d} {d}\n", .{ scratch_top, s.scratch.items.len }); | |
| 423 | 466 | const visited = try s.resolveRefRuntime(in); |
| 467 | std.debug.print("{d} {d}\n", .{ scratch_top, s.scratch.items.len }); | |
| 424 | 468 | |
| 425 | if (scratch_top == s.scratch.items.len) { | |
| 469 | if (scratch_top == s.scratch.items.len and visited.kind == .value) { | |
| 426 | 470 | return visited; |
| 427 | 471 | } |
| 428 | 472 | |
| 429 | 473 | 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 | 476 | return visited; |
| 433 | 477 | } |
| 434 | 478 | |
| 435 | 479 | const block = try s.addBlock(); |
| 480 | _ = try s.addInstRef(.@"break", .{ | |
| 481 | .block_ref = block.block_index, | |
| 482 | .value = visited, | |
| 483 | }); | |
| 436 | 484 | return (try block.finish(s, scratch_top)).toRef(); |
| 437 | 485 | } |
| 438 | 486 | |
| ... | ... | @@ -443,7 +491,7 @@ fn resolveRefRuntimeExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) anyerror!Air. |
| 443 | 491 | |
| 444 | 492 | if (s.inst_map.get(index)) |air| { |
| 445 | 493 | if (mark) { |
| 446 | switch (air.unwrap()) { | |
| 494 | switch (air.decode()) { | |
| 447 | 495 | .inst => |i| s.markInst(i), |
| 448 | 496 | else => {}, |
| 449 | 497 | } |
| ... | ... | @@ -464,7 +512,7 @@ fn resolveRefComptime(s: *Sema, ref: Teg.Inst.Ref) !Value { |
| 464 | 512 | }; |
| 465 | 513 | |
| 466 | 514 | if (s.inst_map.get(in)) |air| { |
| 467 | switch (air.unwrap()) { | |
| 515 | switch (air.decode()) { | |
| 468 | 516 | .value => |value| return value, |
| 469 | 517 | else => {}, |
| 470 | 518 | } |
| ... | ... | @@ -545,6 +593,70 @@ fn unionType(s: *Sema, a: Value, b: Value) Value { |
| 545 | 593 | // return s.ip.get(s.gpa, .{ .@"union" = &.{ a, b } }); |
| 546 | 594 | } |
| 547 | 595 | |
| 596 | const BinaryOp = enum { | |
| 597 | add, | |
| 598 | sub, | |
| 599 | mul, | |
| 600 | div, | |
| 601 | lt, | |
| 602 | gt, | |
| 603 | eq, | |
| 604 | }; | |
| 605 | ||
| 606 | fn 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 | 660 | /// --- helper --- |
| 549 | 661 | const IncompleteBlock = struct { |
| 550 | 662 | block_index: Air.Inst.Index, |
| ... | ... | @@ -655,6 +767,14 @@ const Allocator = std.mem.Allocator; |
| 655 | 767 | const assert = std.debug.assert; |
| 656 | 768 | const log = std.log.scoped(.sema); |
| 657 | 769 | |
| 770 | inline 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 | ||
| 658 | 778 | const Comp = @import("Comp.zig"); |
| 659 | 779 | const File = Comp.File; |
| 660 | 780 | const Decl = Comp.Decl; |
src/compiler/Teg.Dump.zig+8-1| ... | ... | @@ -93,7 +93,6 @@ const State = struct { |
| 93 | 93 | try tty.setColor(writer, .reset); |
| 94 | 94 | }, |
| 95 | 95 | inline .field, |
| 96 | .store, | |
| 97 | 96 | .add, |
| 98 | 97 | .sub, |
| 99 | 98 | .mul, |
| ... | ... | @@ -129,6 +128,14 @@ const State = struct { |
| 129 | 128 | try s.dumpRefMarker(writer, data.operand); |
| 130 | 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 | 139 | .string => { |
| 133 | 140 | const data: Teg.SlicedString = teg.instData(.string, inst).extra_index.get(teg); |
| 134 | 141 | try tty.setColor(writer, .green); |
src/compiler/Teg.zig+6-1| ... | ... | @@ -91,6 +91,7 @@ pub const Inst = struct { |
| 91 | 91 | .@"if" => PlNode(If), |
| 92 | 92 | .load_decl => StrNode, |
| 93 | 93 | .param => DataNode, |
| 94 | .store => PlNode(Store), | |
| 94 | 95 | |
| 95 | 96 | // Single instruction operand |
| 96 | 97 | .memory, |
| ... | ... | @@ -108,7 +109,6 @@ pub const Inst = struct { |
| 108 | 109 | |
| 109 | 110 | // Two instruction operands |
| 110 | 111 | .field, |
| 111 | .store, | |
| 112 | 112 | .add, |
| 113 | 113 | .sub, |
| 114 | 114 | .mul, |
| ... | ... | @@ -258,6 +258,11 @@ pub const Inst = struct { |
| 258 | 258 | rhs: Inst.Ref, |
| 259 | 259 | }; |
| 260 | 260 | |
| 261 | pub const Store = struct { | |
| 262 | memory: Inst.Index, | |
| 263 | operand: Inst.Ref, | |
| 264 | }; | |
| 265 | ||
| 261 | 266 | pub const As = struct { |
| 262 | 267 | type: Inst.Ref, |
| 263 | 268 | operand: Inst.Ref, |
src/compiler/Transform.zig+6-6| ... | ... | @@ -656,9 +656,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 656 | 656 | .tag = .store, |
| 657 | 657 | .data = EncodedData.encode(.store, .{ |
| 658 | 658 | .src_node = decl.nodeOffset(node), |
| 659 | .extra_index = try t.extra.append(t.gpa, Inst.Bin, .{ | |
| 660 | .lhs = lhs, | |
| 661 | .rhs = rhs, | |
| 659 | .extra_index = try t.extra.append(t.gpa, Inst.Store, .{ | |
| 660 | .memory = lhs.toIndex() orelse @panic("TODO: convert this into Ref"), | |
| 661 | .operand = rhs, | |
| 662 | 662 | }), |
| 663 | 663 | }), |
| 664 | 664 | }); |
| ... | ... | @@ -728,9 +728,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 728 | 728 | .tag = .store, |
| 729 | 729 | .data = EncodedData.encode(.store, .{ |
| 730 | 730 | .src_node = decl.nodeOffset(node), |
| 731 | .extra_index = try t.extra.append(t.gpa, Inst.Bin, .{ | |
| 732 | .lhs = lhs, | |
| 733 | .rhs = compute_inst, | |
| 731 | .extra_index = try t.extra.append(t.gpa, Inst.Store, .{ | |
| 732 | .memory = lhs.toIndex() orelse @panic("TODO: convert this into Ref"), | |
| 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 | 22 | }; |
| 23 | 23 | } |
| 24 | 24 | |
| 25 | pub fn asString(val: Value, ip: *InternPool) InternPool.String.WithLen { | |
| 25 | pub fn asString(val: Value, ip: *const InternPool) InternPool.String.WithLen { | |
| 26 | 26 | const index = val.toIntern(); |
| 27 | 27 | return ip.items.items(.data)[@intFromEnum(index)].decode( |
| 28 | 28 | .string, |
| ... | ... | @@ -31,7 +31,7 @@ pub fn asString(val: Value, ip: *InternPool) InternPool.String.WithLen { |
| 31 | 31 | .get(ip); |
| 32 | 32 | } |
| 33 | 33 | |
| 34 | pub fn @"type"(val: Value, ip: *InternPool) Value { | |
| 34 | pub fn @"type"(val: Value, ip: *const InternPool) Value { | |
| 35 | 35 | const v: InternPool.Index = switch (val.toKey(ip)) { |
| 36 | 36 | .simple_type, .type_function => .type_type, |
| 37 | 37 | |
| ... | ... | @@ -47,8 +47,16 @@ pub fn @"type"(val: Value, ip: *InternPool) Value { |
| 47 | 47 | return Value.fromInterned(v); |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | pub 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 | 58 | /// used for validating ensure_void instructions |
| 51 | pub fn isEnsureVoid(val: Value, ip: *InternPool) bool { | |
| 59 | pub fn isEnsureVoid(val: Value, ip: *const InternPool) bool { | |
| 52 | 60 | const intern = val.toIntern(); |
| 53 | 61 | const i = @intFromEnum(intern); |
| 54 | 62 |
src/compiler/main.zig+25-3| ... | ... | @@ -43,6 +43,7 @@ pub fn main() !void { |
| 43 | 43 | const cwd = try std.posix.getcwd(&cwd_buf); |
| 44 | 44 | |
| 45 | 45 | var exe_name: []const u8 = undefined; |
| 46 | var dump_ir = false; | |
| 46 | 47 | |
| 47 | 48 | { |
| 48 | 49 | var args = try std.process.argsWithAllocator(alloc); |
| ... | ... | @@ -52,8 +53,24 @@ pub fn main() !void { |
| 52 | 53 | errdefer alloc.free(exe_name); |
| 53 | 54 | |
| 54 | 55 | while (args.next()) |arg| { |
| 55 | maybe_file_path = try std.fs.path.resolve(alloc, &.{ cwd, arg }); | |
| 56 | break; | |
| 56 | if (std.mem.startsWith(u8, arg, "-")) { | |
| 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 | 76 | defer alloc.free(exe_name); |
| ... | ... | @@ -70,7 +87,12 @@ pub fn main() !void { |
| 70 | 87 | std.process.exit(1); |
| 71 | 88 | }; |
| 72 | 89 | |
| 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 | 96 | try comp.init(); |
| 75 | 97 | defer comp.deinit(); |
| 76 | 98 | const main_file = try comp.getFile(file_path); |