feat: ir for declarations and semantic analyzer start
This commit is contained in:
parent
f16162b5bb
commit
197a37ebe7
4 changed files with 453 additions and 145 deletions
231
src/AstGen.zig
231
src/AstGen.zig
|
|
@ -18,21 +18,8 @@ errors: std.ArrayListUnmanaged(Ast.Error) = .empty,
|
|||
pub const InnerError = error{
|
||||
OutOfMemory,
|
||||
SemanticError,
|
||||
TooManyConstants,
|
||||
InvalidCharacter,
|
||||
} || anyerror;
|
||||
|
||||
pub const Fixup = struct {
|
||||
mode: enum {
|
||||
relative,
|
||||
absolute,
|
||||
},
|
||||
label_index: usize,
|
||||
code_offset: usize,
|
||||
};
|
||||
|
||||
pub const Label = struct {
|
||||
code_offset: usize,
|
||||
Overflow,
|
||||
};
|
||||
|
||||
const Scope = struct {
|
||||
|
|
@ -73,8 +60,20 @@ const GenIr = struct {
|
|||
instructions: *std.ArrayListUnmanaged(Ir.Inst.Index),
|
||||
instructions_top: usize,
|
||||
|
||||
pub fn unstack(gi: *GenIr) void {
|
||||
gi.instructions.items.len = gi.instructions_top;
|
||||
const unstacked_top = std.math.maxInt(usize);
|
||||
|
||||
pub fn unstack(self: *GenIr) void {
|
||||
if (self.instructions_top != unstacked_top) {
|
||||
self.instructions.items.len = self.instructions_top;
|
||||
self.instructions_top = unstacked_top;
|
||||
}
|
||||
}
|
||||
|
||||
fn instructionsSlice(self: *const GenIr) []Ir.Inst.Index {
|
||||
return if (self.instructions_top == unstacked_top)
|
||||
&[0]Ir.Inst.Index{}
|
||||
else
|
||||
self.instructions.items[self.instructions_top..];
|
||||
}
|
||||
|
||||
fn fail(
|
||||
|
|
@ -85,25 +84,14 @@ const GenIr = struct {
|
|||
return self.astgen.fail(tag, node);
|
||||
}
|
||||
|
||||
fn instructionsSlice(gi: *const GenIr) []Ir.Inst.Index {
|
||||
return gi.instructions.items[gi.instructions_top..];
|
||||
}
|
||||
|
||||
pub fn makeSubBlock(self: *GenIr) GenIr {
|
||||
return .{
|
||||
.astgen = self.astgen,
|
||||
.instructions = self.instructions,
|
||||
.instructions_top = self.astgen.instructions.items.len,
|
||||
.instructions_top = self.instructions.items.len,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addGlobal(self: *GenIr, decl: Decl) error{OutOfMemory}!void {
|
||||
const gpa = self.astgen.gpa;
|
||||
try self.astgen.globals.append(gpa, .{
|
||||
.variable = .{ .name = decl.string_index },
|
||||
});
|
||||
}
|
||||
|
||||
pub fn add(gi: *GenIr, inst: Ir.Inst) !Ir.Inst.Index {
|
||||
const gpa = gi.astgen.gpa;
|
||||
try gi.instructions.ensureUnusedCapacity(gpa, 1);
|
||||
|
|
@ -152,6 +140,15 @@ const GenIr = struct {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn addDeclRef(gi: *GenIr, decl_ref: Ir.NullTerminatedString) !Ir.Inst.Index {
|
||||
return add(gi, .{
|
||||
.tag = .decl_ref,
|
||||
.data = .{ .string = .{
|
||||
.start = decl_ref,
|
||||
} },
|
||||
});
|
||||
}
|
||||
|
||||
pub fn makePayloadNode(gi: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index {
|
||||
const gpa = gi.astgen.gpa;
|
||||
const inst_index: Ir.Inst.Index = @enumFromInt(gi.astgen.instructions.items.len);
|
||||
|
|
@ -164,41 +161,70 @@ const GenIr = struct {
|
|||
return inst_index;
|
||||
}
|
||||
|
||||
pub fn makeDeclNode(gi: *GenIr) !Ir.Inst.Index {
|
||||
return makePayloadNode(gi, .decl_knot);
|
||||
pub fn makeDeclaration(gi: *GenIr) !Ir.Inst.Index {
|
||||
return makePayloadNode(gi, .declaration);
|
||||
}
|
||||
|
||||
pub fn makeBlockInst(gi: *GenIr) !Ir.Inst.Index {
|
||||
return makePayloadNode(gi, .block);
|
||||
}
|
||||
|
||||
pub fn setDecl(
|
||||
gi: *GenIr,
|
||||
inst: Ir.Inst.Index,
|
||||
name_ref: Ir.NullTerminatedString,
|
||||
) !void {
|
||||
pub fn addKnot(gi: *GenIr) !Ir.Inst.Index {
|
||||
const astgen = gi.astgen;
|
||||
const gpa = astgen.gpa;
|
||||
const body = gi.instructionsSlice();
|
||||
const extra_len = @typeInfo(Ir.Inst.Knot).@"struct".fields.len + body.len;
|
||||
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
|
||||
|
||||
const inst_data = &astgen.instructions.items[@intFromEnum(inst)].data;
|
||||
const knot_node = try makePayloadNode(gi, .decl_knot);
|
||||
const inst_data = &astgen.instructions.items[@intFromEnum(knot_node)].data;
|
||||
inst_data.payload.payload_index = astgen.addExtraAssumeCapacity(
|
||||
Ir.Inst.Knot{ .name_ref = name_ref, .body_len = @intCast(body.len) },
|
||||
Ir.Inst.Knot{ .body_len = @intCast(body.len) },
|
||||
);
|
||||
|
||||
for (body) |inst_index| {
|
||||
astgen.extra.appendAssumeCapacity(@intFromEnum(inst_index));
|
||||
}
|
||||
gi.unstack();
|
||||
return knot_node;
|
||||
}
|
||||
|
||||
pub fn addVar(gi: *GenIr) !Ir.Inst.Index {
|
||||
const astgen = gi.astgen;
|
||||
const gpa = astgen.gpa;
|
||||
const new_index: Ir.Inst.Index = @enumFromInt(astgen.instructions.items.len);
|
||||
|
||||
try astgen.instructions.ensureUnusedCapacity(gpa, 1);
|
||||
try gi.instructions.ensureUnusedCapacity(gpa, 1);
|
||||
|
||||
gi.astgen.instructions.appendAssumeCapacity(.{
|
||||
.tag = .decl_var,
|
||||
.data = .{
|
||||
.payload = .{ .payload_index = undefined },
|
||||
},
|
||||
});
|
||||
|
||||
const body = gi.instructionsSlice();
|
||||
const extra_len = @typeInfo(Ir.Inst.Var).@"struct".fields.len + body.len;
|
||||
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
|
||||
|
||||
const inst_data = &astgen.instructions.items[@intFromEnum(new_index)].data;
|
||||
inst_data.payload.payload_index = astgen.addExtraAssumeCapacity(
|
||||
Ir.Inst.Var{ .body_len = @intCast(body.len) },
|
||||
);
|
||||
|
||||
for (body) |inst_index| {
|
||||
astgen.extra.appendAssumeCapacity(@intFromEnum(inst_index));
|
||||
}
|
||||
|
||||
gi.instructions.appendAssumeCapacity(new_index);
|
||||
return new_index;
|
||||
}
|
||||
|
||||
pub fn setBlockBody(gi: *GenIr, inst: Ir.Inst.Index) !void {
|
||||
const astgen = gi.astgen;
|
||||
const gpa = astgen.gpa;
|
||||
const body = gi.instructionsSlice();
|
||||
const extra_len = @typeInfo(Ir.Inst.Knot).@"struct".fields.len + body.len;
|
||||
const extra_len = @typeInfo(Ir.Inst.Block).@"struct".fields.len + body.len;
|
||||
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
|
||||
|
||||
const inst_data = &astgen.instructions.items[@intFromEnum(inst)].data;
|
||||
|
|
@ -220,6 +246,27 @@ pub fn deinit(astgen: *AstGen) void {
|
|||
astgen.errors.deinit(gpa);
|
||||
}
|
||||
|
||||
fn setDeclaration(
|
||||
decl_index: Ir.Inst.Index,
|
||||
args: struct {
|
||||
name: Ir.NullTerminatedString,
|
||||
ref: Ir.Inst.Index,
|
||||
body_gi: *GenIr,
|
||||
},
|
||||
) !void {
|
||||
const astgen = args.body_gi.astgen;
|
||||
const gpa = astgen.gpa;
|
||||
const extra_len = @typeInfo(Ir.Inst.Declaration).@"struct".fields.len;
|
||||
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
|
||||
|
||||
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 },
|
||||
);
|
||||
|
||||
args.body_gi.unstack();
|
||||
}
|
||||
|
||||
fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
|
||||
const fields = std.meta.fields(@TypeOf(extra));
|
||||
const extra_index: u32 = @intCast(astgen.extra.items.len);
|
||||
|
|
@ -280,7 +327,7 @@ fn stringFromBytes(astgen: *AstGen, bytes: []const u8) error{OutOfMemory}!Ir.Nul
|
|||
});
|
||||
if (gop.found_existing) {
|
||||
string_bytes.shrinkRetainingCapacity(str_index);
|
||||
return @enumFromInt(str_index);
|
||||
return @enumFromInt(gop.key_ptr.*);
|
||||
} else {
|
||||
gop.key_ptr.* = str_index;
|
||||
try string_bytes.append(gpa, 0);
|
||||
|
|
@ -346,17 +393,11 @@ fn logicalOp(
|
|||
}
|
||||
|
||||
fn trueLiteral(gi: *GenIr) InnerError!Ir.Inst.Index {
|
||||
return gi.add(.{
|
||||
.tag = .true_literal,
|
||||
.data = undefined,
|
||||
});
|
||||
return gi.add(.{ .tag = .true_literal, .data = undefined });
|
||||
}
|
||||
|
||||
fn falseLiteral(gi: *GenIr) InnerError!Ir.Inst.Index {
|
||||
return gi.add(.{
|
||||
.tag = .false_literal,
|
||||
.data = undefined,
|
||||
});
|
||||
return gi.add(.{ .tag = .false_literal, .data = undefined });
|
||||
}
|
||||
|
||||
fn numberLiteral(gen: *GenIr, node: *const Ast.Node) InnerError!Ir.Inst.Index {
|
||||
|
|
@ -382,10 +423,10 @@ fn stringExpr(gen: *GenIr, expr_node: *const Ast.Node) InnerError!Ir.Inst.Index
|
|||
return stringLiteral(gen, first_node);
|
||||
}
|
||||
|
||||
fn identifier(gen: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Index {
|
||||
const name_ref = try gen.astgen.stringFromNode(node);
|
||||
if (scope.lookup(name_ref)) |_| {}
|
||||
return gen.fail(.unknown_identifier, 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 expr(gen: *GenIr, scope: *Scope, optional_expr: ?*const Ast.Node) InnerError!Ir.Inst.Index {
|
||||
|
|
@ -795,48 +836,33 @@ fn choiceStmt(gen: *GenIr, scope: *Scope, stmt_node: *const Ast.Node) InnerError
|
|||
}
|
||||
}
|
||||
|
||||
fn varDecl(gen: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
|
||||
const identifier_node = decl_node.data.bin.lhs orelse unreachable;
|
||||
const expr_node = decl_node.data.bin.rhs orelse unreachable;
|
||||
const name_ref = try gen.astgen.stringFromNode(identifier_node);
|
||||
const decl_symbol: Decl = blk: switch (decl_node.tag) {
|
||||
.temp_decl => {
|
||||
const stack_slot = try gen.makeStackSlot();
|
||||
break :blk .{
|
||||
.local = .{
|
||||
.decl_node = decl_node,
|
||||
.stack_slot = stack_slot,
|
||||
},
|
||||
};
|
||||
},
|
||||
.var_decl, .const_decl => |tag| {
|
||||
const constant_slot = try gen.makeConstant(.{
|
||||
.string = name_ref,
|
||||
});
|
||||
break :blk .{
|
||||
.global = .{
|
||||
.decl_node = decl_node,
|
||||
.is_constant = tag == .const_decl,
|
||||
.constant_slot = constant_slot,
|
||||
},
|
||||
};
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
|
||||
const gpa = gi.astgen.gpa;
|
||||
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);
|
||||
|
||||
try scope.insert(name_ref, decl_symbol);
|
||||
try expr(gen, scope, expr_node);
|
||||
|
||||
switch (decl_symbol) {
|
||||
.local => |data| {
|
||||
try gen.emitConstInst(.store, data.stack_slot);
|
||||
},
|
||||
.global => |data| {
|
||||
try gen.emitConstInst(.store_global, data.constant_slot);
|
||||
},
|
||||
else => unreachable,
|
||||
if (scope.lookup(name_ref)) |_| {
|
||||
return gi.fail(.redefined_identifier, decl_node);
|
||||
}
|
||||
try gen.emitSimpleInst(.pop);
|
||||
|
||||
const decl_inst = try gi.makeDeclaration();
|
||||
try gi.instructions.append(gpa, decl_inst);
|
||||
|
||||
var decl_block = gi.makeSubBlock();
|
||||
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,
|
||||
.ref = var_inst,
|
||||
.body_gi = &decl_block,
|
||||
});
|
||||
}
|
||||
|
||||
fn blockInner(gi: *GenIr, parent_scope: *Scope, stmt_list: []*Ast.Node) !void {
|
||||
|
|
@ -845,8 +871,8 @@ fn blockInner(gi: *GenIr, parent_scope: *Scope, stmt_list: []*Ast.Node) !void {
|
|||
|
||||
for (stmt_list) |inner_node| {
|
||||
_ = switch (inner_node.tag) {
|
||||
//.var_decl => try varDecl(gen, scope, inner_node),
|
||||
//.const_decl => try varDecl(gen, scope, inner_node),
|
||||
.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),
|
||||
.content_stmt => try contentStmt(gi, &child_scope, inner_node),
|
||||
|
|
@ -884,7 +910,7 @@ fn defaultBlock(
|
|||
) InnerError!void {
|
||||
const astgen = gi.astgen;
|
||||
const gpa = astgen.gpa;
|
||||
const decl_inst = try gi.makeDeclNode();
|
||||
const decl_inst = try gi.makeDeclaration();
|
||||
try gi.instructions.append(gpa, decl_inst);
|
||||
|
||||
var decl_scope = gi.makeSubBlock();
|
||||
|
|
@ -894,9 +920,12 @@ fn defaultBlock(
|
|||
const block_stmts = body_node.data.list.items orelse unreachable;
|
||||
try blockInner(&decl_scope, scope, block_stmts);
|
||||
|
||||
std.debug.print("{any}\n", .{decl_scope});
|
||||
const name_ref = try decl_scope.astgen.stringFromBytes("$__main__$");
|
||||
try decl_scope.setDecl(decl_inst, name_ref);
|
||||
const knot_inst = try decl_scope.addKnot();
|
||||
try setDeclaration(decl_inst, .{
|
||||
.name = try astgen.stringFromBytes("$__main__$"),
|
||||
.ref = knot_inst,
|
||||
.body_gi = &decl_scope,
|
||||
});
|
||||
}
|
||||
|
||||
fn stitchDecl(_: *GenIr, _: *Scope, _: *const Ast.Node) InnerError!void {}
|
||||
|
|
@ -932,18 +961,16 @@ fn knotDecl(gen: *GenIr, scope: *Scope, decl_node: *const Ast.Node) InnerError!v
|
|||
fn file(root_gi: *GenIr, scope: *Scope, file_node: *const Ast.Node) InnerError!void {
|
||||
const astgen = root_gi.astgen;
|
||||
const gpa = astgen.gpa;
|
||||
|
||||
const file_inst = try root_gi.makePayloadNode(.file);
|
||||
try root_gi.instructions.append(gpa, file_inst);
|
||||
|
||||
var start_index: usize = 0;
|
||||
var file_scope = root_gi.makeSubBlock();
|
||||
defer file_scope.unstack();
|
||||
// TODO: Make sure this is non-nullable.
|
||||
const nested_decls_list = file_node.data.list.items orelse return;
|
||||
if (nested_decls_list.len == 0) return;
|
||||
|
||||
var start_index: usize = 0;
|
||||
|
||||
const first_child = nested_decls_list[0];
|
||||
if (first_child.tag == .block_stmt) {
|
||||
try defaultBlock(&file_scope, scope, first_child);
|
||||
|
|
@ -975,7 +1002,7 @@ pub fn generate(gpa: std.mem.Allocator, tree: *const Ast) !Ir {
|
|||
};
|
||||
defer astgen.deinit();
|
||||
|
||||
// First entry is reserved for the empty string sentinel.
|
||||
// First entry is reserved for Ir.NullTerminatedString.empty.
|
||||
try astgen.string_bytes.append(gpa, 0);
|
||||
|
||||
var instructions: std.ArrayListUnmanaged(Ir.Inst.Index) = .empty;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue