diff --git a/src/AstGen.zig b/src/AstGen.zig index 1326f01..38389e8 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -12,6 +12,8 @@ tree: *const Ast, string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .empty, string_bytes: std.ArrayListUnmanaged(u8) = .empty, instructions: std.ArrayListUnmanaged(Ir.Inst) = .empty, +globals: std.ArrayListUnmanaged(Ir.Global) = .empty, +global_ref_table: std.AutoHashMapUnmanaged(Ir.NullTerminatedString, usize) = .empty, extra: std.ArrayListUnmanaged(u32) = .empty, errors: std.ArrayListUnmanaged(Ast.Error) = .empty, @@ -22,6 +24,17 @@ pub const InnerError = error{ Overflow, }; +pub fn deinit(astgen: *AstGen) void { + const gpa = astgen.gpa; + astgen.string_table.deinit(gpa); + astgen.string_bytes.deinit(gpa); + astgen.globals.deinit(gpa); + astgen.global_ref_table.deinit(gpa); + astgen.instructions.deinit(gpa); + astgen.extra.deinit(gpa); + astgen.errors.deinit(gpa); +} + const Scope = struct { parent: ?*Scope, astgen: *AstGen, @@ -238,32 +251,41 @@ const GenIr = struct { } }; -pub fn deinit(astgen: *AstGen) void { - const gpa = astgen.gpa; - astgen.string_table.deinit(gpa); - astgen.string_bytes.deinit(gpa); - astgen.instructions.deinit(gpa); - astgen.errors.deinit(gpa); -} - fn setDeclaration( decl_index: Ir.Inst.Index, args: struct { name: Ir.NullTerminatedString, + tag: Ir.Global.Tag, ref: Ir.Inst.Index, + decl_node: *const Ast.Node, body_gi: *GenIr, + is_constant: bool = true, }, ) !void { const astgen = args.body_gi.astgen; const gpa = astgen.gpa; const extra_len = @typeInfo(Ir.Inst.Declaration).@"struct".fields.len; + const global_index = astgen.globals.items.len; + try astgen.extra.ensureUnusedCapacity(gpa, extra_len); + try astgen.globals.ensureUnusedCapacity(gpa, 1); + try astgen.global_ref_table.ensureUnusedCapacity(gpa, 1); const inst_data = &astgen.instructions.items[@intFromEnum(decl_index)].data; inst_data.payload.payload_index = astgen.addExtraAssumeCapacity( Ir.Inst.Declaration{ .name = args.name, .value = args.ref }, ); + if (astgen.global_ref_table.get(args.name)) |_| { + return astgen.fail(.redefined_identifier, args.decl_node); + } + + astgen.globals.appendAssumeCapacity(.{ + .tag = args.tag, + .name = args.name, + .is_constant = args.is_constant, + }); + astgen.global_ref_table.putAssumeCapacity(args.name, global_index); args.body_gi.unstack(); } @@ -423,10 +445,13 @@ fn stringExpr(gen: *GenIr, expr_node: *const Ast.Node) InnerError!Ir.Inst.Index return stringLiteral(gen, first_node); } -fn identifier(gen: *GenIr, _: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Index { - const name_str = try gen.astgen.stringFromNode(node); - return gen.addDeclRef(name_str); - //return gen.fail(.unknown_identifier, node); +fn identifier(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Index { + const astgen = gi.astgen; + const str = try astgen.stringFromNode(node); + if (scope.lookup(str)) |decl| { + return gi.addUnaryNode(.load_local, decl.inst_index); + } + return gi.addDeclRef(str); } fn expr(gen: *GenIr, scope: *Scope, optional_expr: ?*const Ast.Node) InnerError!Ir.Inst.Index { @@ -736,30 +761,18 @@ fn contentStmt(gen: *GenIr, scope: *Scope, stmt_node: *const Ast.Node) InnerErro return gen.addUnaryNode(.content, expr_ref); } -fn assignStmt(gen: *GenIr, scope: *Scope, stmt_node: *const Ast.Node) InnerError!void { +fn assignStmt(gi: *GenIr, scope: *Scope, stmt_node: *const Ast.Node) InnerError!void { + const astgen = gi.astgen; const identifier_node = stmt_node.data.bin.lhs orelse unreachable; const expr_node = stmt_node.data.bin.rhs orelse unreachable; - const name_ref = try gen.astgen.stringFromNode(identifier_node); + const name_ref = try astgen.stringFromNode(identifier_node); - if (scope.lookup(name_ref)) |symbol| switch (symbol) { - .global => |data| { - if (data.is_constant) { - return gen.fail(.assignment_to_const, identifier_node); - } - try expr(gen, scope, expr_node); - try gen.emitConstInst(.store_global, data.constant_slot); - try gen.emitSimpleInst(.pop); - return; - }, - .local => |data| { - try expr(gen, scope, expr_node); - try gen.emitConstInst(.store, data.stack_slot); - try gen.emitSimpleInst(.pop); - return; - }, - else => unreachable, - }; - return gen.fail(.unknown_identifier, identifier_node); + if (scope.lookup(name_ref)) |decl| { + const expr_result = try expr(gi, scope, expr_node); + _ = try gi.addBinaryNode(.store_local, decl.inst_index, expr_result); + return; + } + return gi.fail(.unknown_identifier, identifier_node); } fn choiceStmt(gen: *GenIr, scope: *Scope, stmt_node: *const Ast.Node) InnerError!void { @@ -836,8 +849,7 @@ fn choiceStmt(gen: *GenIr, scope: *Scope, stmt_node: *const Ast.Node) InnerError } } -fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void { - const gpa = gi.astgen.gpa; +fn tempDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void { const identifier_node = decl_node.data.bin.lhs.?; const expr_node = decl_node.data.bin.rhs.?; const name_ref = try gi.astgen.stringFromNode(identifier_node); @@ -846,6 +858,21 @@ fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void { return gi.fail(.redefined_identifier, decl_node); } + const alloc_inst = try gi.add(.{ .tag = .alloc_local, .data = undefined }); + const expr_result = try expr(gi, scope, expr_node); + _ = try gi.addBinaryNode(.store_local, alloc_inst, expr_result); + + return scope.insert(name_ref, .{ + .decl_node = decl_node, + .inst_index = alloc_inst, + }); +} + +fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void { + const astgen = gi.astgen; + const gpa = astgen.gpa; + const identifier_node = decl_node.data.bin.lhs.?; + const expr_node = decl_node.data.bin.rhs.?; const decl_inst = try gi.makeDeclaration(); try gi.instructions.append(gpa, decl_inst); @@ -853,15 +880,14 @@ fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void { defer decl_block.unstack(); _ = try expr(&decl_block, scope, expr_node); - try scope.insert(name_ref, .{ - .decl_node = decl_node, - }); - const var_inst = try decl_block.addVar(); try setDeclaration(decl_inst, .{ - .name = name_ref, + .tag = .variable, + .name = try astgen.stringFromNode(identifier_node), .ref = var_inst, + .decl_node = decl_node, .body_gi = &decl_block, + .is_constant = decl_node.tag == .const_decl, }); } @@ -873,8 +899,8 @@ fn blockInner(gi: *GenIr, parent_scope: *Scope, stmt_list: []*Ast.Node) !void { _ = switch (inner_node.tag) { .var_decl => try varDecl(gi, &child_scope, inner_node), .const_decl => try varDecl(gi, &child_scope, inner_node), - //.temp_decl => try varDecl(gen, scope, inner_node), - //.assign_stmt => try assignStmt(gen, scope, inner_node), + .temp_decl => try tempDecl(gi, &child_scope, inner_node), + .assign_stmt => try assignStmt(gi, &child_scope, inner_node), .content_stmt => try contentStmt(gi, &child_scope, inner_node), //.choice_stmt => try choiceStmt(gen, scope, inner_node), .expr_stmt => try exprStmt(gi, &child_scope, inner_node), @@ -922,6 +948,8 @@ fn defaultBlock( const knot_inst = try decl_scope.addKnot(); try setDeclaration(decl_inst, .{ + .tag = .knot, + .decl_node = body_node, .name = try astgen.stringFromBytes("$__main__$"), .ref = knot_inst, .body_gi = &decl_scope, @@ -992,6 +1020,7 @@ fn file(root_gi: *GenIr, scope: *Scope, file_node: *const Ast.Node) InnerError!v pub const Decl = struct { decl_node: *const Ast.Node, + inst_index: Ir.Inst.Index, }; /// Perform code generation via tree-walk. @@ -1021,12 +1050,16 @@ pub fn generate(gpa: std.mem.Allocator, tree: *const Ast) !Ir { defer gen.unstack(); // TODO: Make sure this is never null. - const root_node = tree.root orelse unreachable; - try file(&gen, &file_scope, root_node); + const root_node = tree.root.?; + file(&gen, &file_scope, root_node) catch |err| switch (err) { + error.SemanticError => {}, + else => |e| return e, + }; return .{ .string_bytes = try astgen.string_bytes.toOwnedSlice(gpa), .instructions = try astgen.instructions.toOwnedSlice(gpa), + .globals = try astgen.globals.toOwnedSlice(gpa), .extra = try astgen.extra.toOwnedSlice(gpa), .errors = try astgen.errors.toOwnedSlice(gpa), }; diff --git a/src/Ir.zig b/src/Ir.zig index 2a41afb..840e3ae 100644 --- a/src/Ir.zig +++ b/src/Ir.zig @@ -4,6 +4,7 @@ const Ir = @This(); string_bytes: []u8, instructions: []Inst, +globals: []Global, extra: []u32, errors: []Ast.Error, @@ -23,6 +24,9 @@ pub const Inst = struct { decl_var, decl_ref, block, + alloc_local, + load_local, + store_local, add, sub, mul, @@ -78,6 +82,17 @@ pub const Inst = struct { }; }; +pub const Global = struct { + tag: Tag, + name: Ir.NullTerminatedString, + is_constant: bool, + + pub const Tag = enum { + knot, + variable, + }; +}; + pub const NullTerminatedString = enum(u32) { empty, _, @@ -91,6 +106,7 @@ pub const IndexSlice = struct { pub fn deinit(ir: *Ir, gpa: std.mem.Allocator) void { gpa.free(ir.string_bytes); gpa.free(ir.instructions); + gpa.free(ir.globals); gpa.free(ir.extra); gpa.free(ir.errors); ir.* = undefined; @@ -129,12 +145,17 @@ const Render = struct { WriteFailed, }; - fn renderUnaryInst(r: *Render, inst: Inst) Error!void { + fn renderSimple(r: *Render, inst: Inst) Error!void { + const io_w = r.writer; + return io_w.print("{s}(?)", .{@tagName(inst.tag)}); + } + + fn renderUnary(r: *Render, inst: Inst) Error!void { const io_w = r.writer; return io_w.print("{s}(%{d})", .{ @tagName(inst.tag), inst.data.un.lhs }); } - fn renderBinaryInst(r: *Render, inst: Inst) Error!void { + fn renderBinary(r: *Render, inst: Inst) Error!void { const io_w = r.writer; return io_w.print("{s}(%{d}, %{d})", .{ @tagName(inst.tag), inst.data.bin.lhs, inst.data.bin.rhs }); } @@ -152,7 +173,7 @@ const Render = struct { try io_w.writeAll("}"); } - fn renderBlockInst(r: *Render, ir: Ir, inst: Inst) Error!void { + fn renderBlock(r: *Render, ir: Ir, inst: Inst) Error!void { const io_w = r.writer; const extra = ir.extraData(Inst.Block, inst.data.payload.payload_index); const body_slice = ir.bodySlice(extra.end, extra.data.body_len); @@ -182,16 +203,6 @@ const Render = struct { try io_w.writeAll(")"); } - fn renderFileInst(r: *Render, ir: Ir, inst: Inst) Error!void { - const io_w = r.writer; - const extra = ir.extraData(Inst.Block, inst.data.payload.payload_index); - const body_slice = ir.bodySlice(extra.end, extra.data.body_len); - - try io_w.print("{s}(", .{@tagName(inst.tag)}); - try renderBodyInner(r, ir, body_slice); - return io_w.writeAll(")"); - } - fn renderDeclaration(r: *Render, ir: Ir, inst: Inst) Error!void { const io_w = r.writer; const extra = ir.extraData(Inst.Declaration, inst.data.payload.payload_index); @@ -207,6 +218,16 @@ const Render = struct { return io_w.writeAll(")"); } + fn renderFile(r: *Render, ir: Ir, inst: Inst) Error!void { + const io_w = r.writer; + const extra = ir.extraData(Inst.Block, inst.data.payload.payload_index); + const body_slice = ir.bodySlice(extra.end, extra.data.body_len); + + try io_w.print("{s}(", .{@tagName(inst.tag)}); + try renderBodyInner(r, ir, body_slice); + return io_w.writeAll(")"); + } + fn renderInst(r: *Render, ir: Ir, index: Inst.Index) Error!void { const io_w = r.writer; const inst_index: u32 = @intFromEnum(index); @@ -215,7 +236,7 @@ const Render = struct { try r.prefix.writeIndent(io_w); try io_w.print("%{d} = ", .{inst_index}); switch (inst.tag) { - .file => try r.renderFileInst(ir, inst), + .file => try r.renderFile(ir, inst), .declaration => try r.renderDeclaration(ir, inst), .decl_knot => try r.renderKnotDecl(ir, inst), .decl_var => try r.renderVarDecl(ir, inst), @@ -223,13 +244,16 @@ const Render = struct { const str_bytes = inst.data.string.get(ir); try io_w.print("{s}(\"{s}\")", .{ @tagName(inst.tag), str_bytes }); }, - .block => try r.renderBlockInst(ir, inst), - .add => try r.renderBinaryInst(inst), - .sub => try r.renderBinaryInst(inst), - .mul => try r.renderBinaryInst(inst), - .div => try r.renderBinaryInst(inst), - .mod => try r.renderBinaryInst(inst), - .neg => try r.renderUnaryInst(inst), + .alloc_local => try r.renderSimple(inst), + .load_local => try r.renderUnary(inst), + .store_local => try r.renderBinary(inst), + .block => try r.renderBlock(ir, inst), + .add => try r.renderBinary(inst), + .sub => try r.renderBinary(inst), + .mul => try r.renderBinary(inst), + .div => try r.renderBinary(inst), + .mod => try r.renderBinary(inst), + .neg => try r.renderUnary(inst), .true_literal => { try io_w.print("{s}", .{@tagName(inst.tag)}); }, @@ -244,7 +268,7 @@ const Render = struct { const str_bytes = inst.data.string.get(ir); try io_w.print("{s}(\"{s}\")", .{ @tagName(inst.tag), str_bytes }); }, - .content => try r.renderUnaryInst(inst), + .content => try r.renderUnary(inst), } try io_w.writeAll("\n"); } @@ -259,18 +283,23 @@ pub fn render(ir: Ir, gpa: std.mem.Allocator, writer: *std.Io.Writer) !void { return writer.flush(); } -pub fn dumpStringsWithHex(cu: *const Ir) void { - const bytes = cu.string_bytes; +pub fn dumpInfo(ir: Ir, writer: *std.Io.Writer) !void { + const bytes = ir.string_bytes; + var start: usize = 0; while (start < bytes.len) { const end = std.mem.indexOfScalarPos(u8, bytes, start, 0) orelse break; - const s = bytes[start..end]; + const str = bytes[start..end]; - std.debug.print("[{d:04}] ", .{start}); - for (s) |b| std.debug.print("{x:02} ", .{b}); - std.debug.print("00: {s}\n", .{s}); + try writer.print("[{d:04}] ", .{start}); + for (str) |b| try writer.print("{x:02} ", .{b}); + try writer.print("00: {s}\n", .{str}); start = end + 1; } + for (ir.globals) |global| { + try writer.print("{any}\n", .{global}); + } + return writer.flush(); } fn ExtraData(comptime T: type) type { diff --git a/src/Sema.zig b/src/Sema.zig index f47919b..c42f7c5 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2,6 +2,7 @@ const std = @import("std"); const Ir = @import("Ir.zig"); const Story = @import("Story.zig"); const Object = Story.Object; +const assert = std.debug.assert; const Sema = @This(); gpa: std.mem.Allocator, @@ -23,6 +24,10 @@ fn deinit(sema: *Sema) void { sema.* = undefined; } +fn fail(_: *Sema, message: []const u8) InnerError { + @panic(message); +} + fn resolveIndex(sema: *Sema, index: Ir.Inst.Index) Ir.Inst { return sema.ir.instructions[@intFromEnum(index)]; } @@ -36,12 +41,12 @@ fn emitByteOp(sema: *Sema, op: Story.Opcode) !void { return emitByte(sema, @intFromEnum(op)); } -fn emitConstOp(sema: *Sema, op: Story.Opcode, arg: usize) !void { +fn emitConstOp(sema: *Sema, op: Story.Opcode, arg: u8) !void { const gpa = sema.gpa; if (arg >= std.math.maxInt(u8)) return error.TooManyConstants; try sema.bytecode.ensureUnusedCapacity(gpa, 2); sema.bytecode.appendAssumeCapacity(@intFromEnum(op)); - sema.bytecode.appendAssumeCapacity(@intCast(arg)); + sema.bytecode.appendAssumeCapacity(arg); } fn emitJumpOp(sema: *Sema, op: Story.Opcode) error{OutOfMemory}!usize { @@ -60,6 +65,27 @@ fn makeConstant(sema: *Sema, data: CompiledStory.Constant) !usize { return const_index; } +const Chunk = struct { + name: Ir.NullTerminatedString, + arity: u32, + stack_size: u32, + stack_map: std.AutoHashMapUnmanaged(Ir.Inst.Index, u32), +}; + +fn integerInst(sema: *Sema, inst: Ir.Inst) InnerError!void { + const int_const = try sema.makeConstant(.{ + .integer = inst.data.integer.value, + }); + return emitConstOp(sema, .load_const, @intCast(int_const)); +} + +fn stringInst(sema: *Sema, inst: Ir.Inst) InnerError!void { + const str_const = try sema.makeConstant(.{ + .string = inst.data.string.start, + }); + return emitConstOp(sema, .load_const, @intCast(str_const)); +} + fn unaryInst(sema: *Sema, _: Ir.Inst, op: Story.Opcode) InnerError!void { return emitByteOp(sema, op); } @@ -72,34 +98,129 @@ fn contentInst(sema: *Sema, _: Ir.Inst) InnerError!void { return emitByteOp(sema, .stream_flush); } -fn blockInst(sema: *Sema, block_inst: Ir.Inst) InnerError!void { +fn allocLocal(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!void { + const gpa = sema.gpa; + // TODO: Add constraints on how many temporaries we can have. + // max(u8) or max(u16) are most likey appropriate. + try chunk.stack_map.put(gpa, inst, chunk.stack_size); + chunk.stack_size += 1; +} + +fn storeLocal(sema: *Sema, chunk: *Chunk, inst: Ir.Inst) InnerError!void { + const temp_inst = inst.data.bin.lhs; + const stack_offset = chunk.stack_map.get(temp_inst).?; + try emitConstOp(sema, .store, @intCast(stack_offset)); + try emitByteOp(sema, .pop); +} + +fn loadLocal(sema: *Sema, chunk: *Chunk, inst: Ir.Inst) InnerError!void { + const temp_inst = inst.data.un.lhs; + const stack_offset = chunk.stack_map.get(temp_inst).?; + try emitConstOp(sema, .load, @intCast(stack_offset)); +} + +fn declRef(sema: *Sema, inst: Ir.Inst) InnerError!void { + const ir = sema.ir; + const str = inst.data.string.start; + for (ir.globals) |global| { + if (str == global.name) { + const constant_index = try sema.makeConstant(.{ .string = str }); + return emitConstOp(sema, .load_global, @intCast(constant_index)); + } + } + return sema.fail("unknown global variable"); +} + +fn declVar( + sema: *Sema, + chunk: *Chunk, + name: Ir.NullTerminatedString, + inst: Ir.Inst, +) InnerError!void { + const ir = sema.ir; + const payload_index = inst.data.payload.payload_index; + const extra = ir.extraData(Ir.Inst.Block, payload_index); + const body = ir.bodySlice(extra.end, extra.data.body_len); + for (body) |body_index| try compileInst(sema, chunk, body_index); + for (ir.globals) |global| { + if (name == global.name) { + const constant_index = try sema.makeConstant(.{ .string = name }); + return emitConstOp(sema, .store_global, @intCast(constant_index)); + } + } + + return sema.fail("unknown global variable"); +} + +fn declKnot(sema: *Sema, name_ref: Ir.NullTerminatedString, inst: Ir.Inst) InnerError!void { + const gpa = sema.gpa; + const ir = sema.ir; + const extra = ir.extraData(Ir.Inst.Knot, inst.data.payload.payload_index); + const bytecode_start = sema.bytecode.items.len; + const const_start = sema.constants.items.len; + + var chunk: Chunk = .{ + .name = name_ref, + .arity = 0, + .stack_size = 0, + .stack_map = .empty, + }; + defer chunk.stack_map.deinit(gpa); + + const body_slice = ir.bodySlice(extra.end, extra.data.body_len); + for (body_slice) |body_inst| try compileInst(sema, &chunk, body_inst); + + try emitByteOp(sema, .exit); + + try sema.knots.append(gpa, .{ + .name_ref = name_ref, + .arity = chunk.arity, + .stack_size = chunk.stack_size, + .bytecode = .{ + .start = @intCast(bytecode_start), + .len = @intCast(sema.bytecode.items.len - bytecode_start), + }, + .constants = .{ + .start = @intCast(const_start), + .len = @intCast(sema.constants.items.len - const_start), + }, + }); +} + +fn blockInst(sema: *Sema, chunk: *Chunk, block_inst: Ir.Inst) InnerError!void { const ir = sema.ir; const extra = ir.extraData(Ir.Inst.Block, block_inst.data.payload.payload_index); const body = ir.bodySlice(extra.end, extra.data.body_len); + for (body) |body_index| try compileInst(sema, chunk, body_index); +} - for (body) |inst_index| { - const body_inst = resolveIndex(sema, inst_index); - try compileInst(sema, body_inst); +fn declaration(sema: *Sema, parent_chunk: ?*Chunk, inst: Ir.Inst) !void { + const ir = sema.ir; + const extra = ir.extraData(Ir.Inst.Declaration, inst.data.payload.payload_index); + const value_inst = sema.resolveIndex(extra.data.value); + const str = extra.data.name; + + switch (value_inst.tag) { + .decl_var => try declVar(sema, parent_chunk.?, str, value_inst), + .decl_knot => try declKnot(sema, str, value_inst), + else => unreachable, } } -fn integerInst(sema: *Sema, inst: Ir.Inst) InnerError!void { - const int_const = try sema.makeConstant(.{ - .integer = inst.data.integer.value, - }); - return emitConstOp(sema, .load_const, int_const); -} - -fn stringInst(sema: *Sema, inst: Ir.Inst) InnerError!void { - const str_const = try sema.makeConstant(.{ - .string = inst.data.string.start, - }); - return emitConstOp(sema, .load_const, str_const); -} - -fn compileInst(sema: *Sema, inst: Ir.Inst) InnerError!void { +fn compileInst(sema: *Sema, chunk: *Chunk, index: Ir.Inst.Index) InnerError!void { + const inst = sema.resolveIndex(index); switch (inst.tag) { - .block => try blockInst(sema, inst), + .file => unreachable, + .declaration => try declaration(sema, chunk, inst), + .decl_var => unreachable, // handled in declaration() + .decl_knot => unreachable, // handled in declaration() + .decl_ref => try declRef(sema, inst), + .block => try blockInst(sema, chunk, inst), + .alloc_local => try allocLocal(sema, chunk, index), + .store_local => try storeLocal(sema, chunk, inst), + .load_local => try loadLocal(sema, chunk, inst), + .true_literal => unreachable, // TODO + .false_literal => unreachable, // TODO .add => try binaryInst(sema, inst, .add), .sub => try binaryInst(sema, inst, .sub), .mul => try binaryInst(sema, inst, .mul), @@ -109,58 +230,16 @@ fn compileInst(sema: *Sema, inst: Ir.Inst) InnerError!void { .content => try contentInst(sema, inst), .string => try stringInst(sema, inst), .integer => try integerInst(sema, inst), - else => unreachable, } } -fn innerDecl(sema: *Sema, inst: Ir.Inst) !void { - const ir = sema.ir; - const extra = ir.extraData(Ir.Inst.Knot, inst.data.payload.payload_index); - const body_slice = ir.bodySlice(extra.end, extra.data.body_len); - for (body_slice) |inst_index| { - const body_inst = resolveIndex(sema, inst_index); - try compileInst(sema, body_inst); - } -} - -fn declaration(sema: *Sema, inst: Ir.Inst) !void { - const gpa = sema.gpa; - const ir = sema.ir; - const byte_index = sema.bytecode.items.len; - const const_index = sema.constants.items.len; - const extra = ir.extraData(Ir.Inst.Declaration, inst.data.payload.payload_index); - const value_inst = sema.resolveIndex(extra.data.value); - switch (value_inst.tag) { - .decl_var => try innerDecl(sema, value_inst), - .decl_knot => try innerDecl(sema, value_inst), - else => unreachable, - } - - const name_ref = extra.data.name; - try sema.knots.append(gpa, .{ - .name_ref = name_ref, - .arity = 0, - .stack_size = 0, - .bytecode = .{ - .start = @intCast(byte_index), - .len = @intCast(sema.bytecode.items.len - byte_index), - }, - .constants = .{ - .start = @intCast(const_index), - .len = @intCast(sema.constants.items.len - const_index), - }, - }); -} - fn file(sema: *Sema, inst: Ir.Inst) !void { const extra = sema.ir.extraData(Ir.Inst.Block, inst.data.payload.payload_index); const body = sema.ir.bodySlice(extra.end, extra.data.body_len); - for (body) |inst_index| { - const body_inst = sema.resolveIndex(inst_index); - switch (body_inst.tag) { - .declaration => try declaration(sema, body_inst), - else => unreachable, - } + for (body) |body_index| { + const body_inst = sema.resolveIndex(body_index); + assert(body_inst.tag == .declaration); + try declaration(sema, null, body_inst); } } diff --git a/src/Story.zig b/src/Story.zig index 47bb43d..20c5cec 100644 --- a/src/Story.zig +++ b/src/Story.zig @@ -501,9 +501,17 @@ pub fn loadFromString( var sem_ir = try AstGen.generate(gpa, &ast); defer sem_ir.deinit(gpa); + if (sem_ir.errors.len != 0) { + for (sem_ir.errors) |err| { + try options.stderr_writer.print("{any}\n", .{err}); + } + try options.stderr_writer.flush(); + return error.CompilationFailed; + } + if (options.dump_writer) |w| { try w.writeAll("=== Semantic IR ===\n"); - sem_ir.dumpStringsWithHex(); + try sem_ir.dumpInfo(w); try sem_ir.render(gpa, w); } diff --git a/testing/regression/semantics/global-shadowing-2.ink b/testing/regression/semantics/global-shadowing-2.ink new file mode 100644 index 0000000..cebb746 --- /dev/null +++ b/testing/regression/semantics/global-shadowing-2.ink @@ -0,0 +1,3 @@ +VAR foo = "global" +~ temp foo = "temp" +{foo} diff --git a/testing/regression/semantics/global-shadowing-3.ink b/testing/regression/semantics/global-shadowing-3.ink new file mode 100644 index 0000000..3e0ea28 --- /dev/null +++ b/testing/regression/semantics/global-shadowing-3.ink @@ -0,0 +1,2 @@ +VAR foo = "Hello, world!" +VAR foo = "Hello, world!" diff --git a/testing/regression/semantics/global-shadowing.ink b/testing/regression/semantics/global-shadowing.ink new file mode 100644 index 0000000..363e485 --- /dev/null +++ b/testing/regression/semantics/global-shadowing.ink @@ -0,0 +1,3 @@ +~ temp foo = "temp" +{foo} +VAR foo = "global"