aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathias Magnusson <mathias@magnusson.space>2025-08-04 16:55:16 +0200
committerMathias Magnusson <mathias@magnusson.space>2025-08-04 16:56:00 +0200
commit0cd88f7549afbaab837392f8fc3e5b537551d5b8 (patch)
tree151cbcb56f5e8e8077f8b5621e30925498bca5ca
parent72c7be04a6d4ad126b46a977178a0e6e8d69e308 (diff)
downloadhuginn-0cd88f7549afbaab837392f8fc3e5b537551d5b8.tar.gz
allow if expressions to evaluate to either value
-rw-r--r--src/compile.zig94
-rw-r--r--src/parse.zig76
-rw-r--r--ternary.hgn4
3 files changed, 87 insertions, 87 deletions
diff --git a/src/compile.zig b/src/compile.zig
index d1cac8c..69ac270 100644
--- a/src/compile.zig
+++ b/src/compile.zig
@@ -5,6 +5,8 @@ const Token = root.Lexer.Token;
const parse = root.parse;
const Location = root.Lexer.Location;
+const log = std.log.scoped(.compile);
+
/// Virtual register
pub const VReg = enum(u32) { _ };
pub const BlockRef = enum(u32) { _ };
@@ -101,7 +103,7 @@ pub const BasicBlock = struct {
for (self.instrs.items, 0..) |instr, i| {
const is_last = i == self.instrs.items.len - 1;
if (instr.ends_block() != is_last) {
- std.log.debug("Instruction at {s}last position must {s}end block. Found instruction {}. Block is: {}", .{
+ log.debug("Instruction at {s}last position must {s}end block. Found instruction {}. Block is: {}", .{
if (is_last) "" else "not ",
if (is_last) "" else "not ",
instr,
@@ -263,8 +265,7 @@ pub const Instr = struct {
pub fn dest(self: *const Instr) ?VReg {
return switch (self.type) {
- inline .constant, .bin_op, .call, .get_local => |s| s.dest,
- .branch, .jump, .ret, .assign_local => null,
+ inline else => |instr| if (@hasField(@TypeOf(instr), "dest")) instr.dest else null,
};
}
@@ -398,18 +399,11 @@ fn compileProcedure(
try pctx.locals.putNoClobber(ctx.allocator, lvar, {});
break :blk lvar;
} else null;
- try pctx.compileBlock(proc.body);
- if (!pctx.currentBlockEnded()) {
- const proc_res = pctx.vreg_ctr.get();
- try pctx.addInstr(.{
- .loc = .{ .start = 0, .end = 0 },
- .type = .{ .constant = .{ .dest = proc_res, .value = 0 } },
- });
- try pctx.addInstr(.{
- .loc = .{ .start = 0, .end = 0 },
- .type = .{ .ret = .{ .val = proc_res } },
- });
- }
+ const body_res = try pctx.compileExpr(proc.body);
+ try pctx.addInstrUnlessEnded(.{
+ .loc = .{ .start = 0, .end = 0 },
+ .type = .{ .ret = .{ .val = body_res } },
+ });
var blocks = try ctx.allocator.alloc(BasicBlock, pctx.blocks.count());
var it = pctx.blocks.iterator();
var i: usize = 0;
@@ -417,6 +411,9 @@ fn compileProcedure(
std.debug.assert(kv.key_ptr.* == kv.value_ptr.ref);
blocks[i] = kv.value_ptr.*;
}
+ for (blocks) |block| {
+ log.debug("{s}\n{}", .{ name.getIdent(ctx.source), block });
+ }
return try .init(
ctx.allocator,
name.getIdent(ctx.source),
@@ -438,31 +435,13 @@ const ProcedureContext = struct {
blocks: std.AutoArrayHashMapUnmanaged(BlockRef, BasicBlock),
current_block: BlockRef,
- fn compileBlock(self: *ProcedureContext, block: parse.Block) !void {
- var parent = self.scope;
- self.scope = .{
- .locals = .empty,
- .parent = &parent,
- };
-
- for (block.stmts) |stmt| {
- try self.compileStmt(stmt);
- }
-
- self.scope.locals.deinit(self.ctx.allocator);
- self.scope = parent;
- }
-
fn compileStmt(self: *ProcedureContext, stmt: parse.Stmt) CompileError!void {
switch (stmt.type) {
.expr => |expr| _ = try self.compileExpr(expr),
- .block => |block| {
- try self.compileBlock(block);
- },
.assign_var => |assign_var| {
const local = if (assign_var.is_decl) blk: {
- const local = self.lvar_ctr.get();
const name = assign_var.ident.getIdent(self.ctx.source);
+ const local = self.lvar_ctr.get();
try self.scope.locals.put(self.ctx.allocator, name, local);
try self.locals.put(self.ctx.allocator, local, {});
break :blk local;
@@ -472,7 +451,7 @@ const ProcedureContext = struct {
if (s.locals.get(assign_var.ident.getIdent(self.ctx.source))) |local|
break :blk local;
} else {
- std.log.debug("{s}", .{assign_var.ident.getIdent(self.ctx.source)});
+ log.debug("{s}", .{assign_var.ident.getIdent(self.ctx.source)});
return error.UnknownVariable;
}
};
@@ -507,7 +486,7 @@ const ProcedureContext = struct {
} },
});
self.current_block = do;
- try self.compileBlock(@"while".do);
+ _ = try self.compileExpr(@"while".do);
try self.addInstrUnlessEnded(.{
.loc = stmt.loc,
.type = .{ .jump = .{ .to = cond_block } },
@@ -572,7 +551,7 @@ const ProcedureContext = struct {
break :blk local;
}
}
- std.log.debug("{s}", .{expr.loc.getIdent(self.ctx.source)});
+ log.debug("{s}", .{expr.loc.getIdent(self.ctx.source)});
return error.UnknownVariable;
};
try self.addInstr(.{
@@ -589,8 +568,14 @@ const ProcedureContext = struct {
const after = try self.switchToNewBlock();
+ const phony = self.lvar_ctr.get();
+ try self.locals.put(self.ctx.allocator, phony, {});
const t = try self.switchToNewBlock();
- try self.compileBlock(@"if".then);
+ const t_val = try self.compileExpr(@"if".then);
+ try self.addInstrUnlessEnded(.{
+ .loc = expr.loc,
+ .type = .{ .assign_local = .{ .local = phony, .val = t_val } },
+ });
try self.addInstrUnlessEnded(.{
.loc = expr.loc,
.type = .{ .jump = .{ .to = after } },
@@ -598,7 +583,11 @@ const ProcedureContext = struct {
const f = if (@"if".@"else") |@"else"| blk: {
const f = try self.switchToNewBlock();
- try self.compileBlock(@"else");
+ const f_val = try self.compileExpr(@"else");
+ try self.addInstrUnlessEnded(.{
+ .loc = expr.loc,
+ .type = .{ .assign_local = .{ .local = phony, .val = f_val } },
+ });
try self.addInstrUnlessEnded(.{
.loc = expr.loc,
.type = .{ .jump = .{ .to = after } },
@@ -617,11 +606,12 @@ const ProcedureContext = struct {
});
self.current_block = after;
+ try self.addInstr(.{
+ .loc = expr.loc,
+ .type = .{ .get_local = .{ .dest = dest, .local = phony } },
+ });
},
- .proc => |proc| {
- _ = proc;
- return error.CannotDefineProcedureHere;
- },
+ .proc => return error.CannotDefineProcedureHere,
.@"return" => |ret| {
const val = try self.compileExpr(ret.value);
try self.addInstr(.{
@@ -629,6 +619,24 @@ const ProcedureContext = struct {
.type = .{ .ret = .{ .val = val } },
});
},
+ .block => |block| {
+ var parent = self.scope;
+ defer self.scope = parent;
+ self.scope = .{
+ .locals = .empty,
+ .parent = &parent,
+ };
+ defer self.scope.locals.deinit(self.ctx.allocator);
+
+ for (block.stmts) |stmt| {
+ try self.compileStmt(stmt);
+ }
+
+ try self.addInstrUnlessEnded(.{
+ .loc = expr.loc,
+ .type = .{ .constant = .{ .dest = dest, .value = 0 } },
+ });
+ },
}
return dest;
}
diff --git a/src/parse.zig b/src/parse.zig
index 95a9bc9..4adeffb 100644
--- a/src/parse.zig
+++ b/src/parse.zig
@@ -44,20 +44,6 @@ pub const File = struct {
}
};
-pub const Block = struct {
- loc: Location,
- stmts: []Stmt,
-
- fn format(self: Block, writer: anytype, source: []const u8, indent: usize) !void {
- try writer.writeAll("{\n");
- for (self.stmts) |stmt| {
- try writer.print("{}\n", .{fmt(stmt, source, indent + 4)});
- }
- try writer.writeByteNTimes(' ', indent);
- try writer.writeAll("}");
- }
-};
-
pub const Stmt = struct {
loc: Location,
type: Type,
@@ -65,7 +51,6 @@ pub const Stmt = struct {
pub const Type = union(enum) {
expr: *const Expr,
assign_var: AssignVar,
- block: Block,
@"while": While,
pub const AssignVar = struct {
@@ -76,7 +61,7 @@ pub const Stmt = struct {
pub const While = struct {
cond: *const Expr,
- do: Block,
+ do: *const Expr,
};
};
@@ -84,7 +69,6 @@ pub const Stmt = struct {
try writer.writeByteNTimes(' ', indent);
return switch (self.type) {
.expr => |expr| writer.print("{}", .{fmt(expr, source, indent)}),
- .block => |b| writer.print("{}", .{fmt(b, source, indent)}),
.assign_var => |assign_var| writer.print("{s} {s} {}", .{
assign_var.ident.getIdent(source),
if (assign_var.is_decl) ":=" else "=",
@@ -110,6 +94,7 @@ pub const Expr = struct {
@"if": If,
proc: Proc,
@"return": Return,
+ block: Block,
pub const BinOp = struct {
lhs: *const Expr,
@@ -166,18 +151,22 @@ pub const Expr = struct {
pub const If = struct {
cond: *const Expr,
- then: Block,
- @"else": ?Block,
+ then: *const Expr,
+ @"else": ?*const Expr,
};
pub const Proc = struct {
- body: Block,
+ body: *const Expr,
param: ?Location,
};
pub const Return = struct {
value: *const Expr,
};
+
+ pub const Block = struct {
+ stmts: []Stmt,
+ };
};
fn format(self: Expr, writer: anytype, source: []const u8, indent: usize) !void {
@@ -201,6 +190,14 @@ pub const Expr = struct {
},
.proc => |proc| try writer.print("proc({s}) {}", .{ if (proc.param) |p| p.getIdent(source) else "", fmt(proc.body, source, indent) }),
.@"return" => |ret| try writer.print("return {}", .{fmt(ret.value, source, indent)}),
+ .block => |block| {
+ try writer.writeAll("{\n");
+ for (block.stmts) |stmt| {
+ try writer.print("{}\n", .{fmt(stmt, source, indent + 4)});
+ }
+ try writer.writeByteNTimes(' ', indent);
+ try writer.writeAll("}");
+ },
}
}
};
@@ -230,32 +227,12 @@ pub fn file(allocator: Allocator, lexer: *Lexer) !File {
};
}
-fn parseBlock(allocator: Allocator, lexer: *Lexer) !Block {
- const left_curly = try mustEat(lexer, .left_curly);
- var stmts: std.ArrayList(Stmt) = .init(allocator);
- while (lexer.peek().type != .right_curly) {
- try stmts.append(try parseStatement(allocator, lexer));
- }
- const right_curly = try mustEat(lexer, .right_curly);
- return .{
- .loc = left_curly.loc.combine(right_curly.loc),
- .stmts = try stmts.toOwnedSlice(),
- };
-}
-
fn parseStatement(allocator: Allocator, lexer: *Lexer) ParseError!Stmt {
switch (lexer.peek().type) {
- .left_curly => {
- const b = try parseBlock(allocator, lexer);
- return .{
- .loc = b.loc,
- .type = .{ .block = b },
- };
- },
.@"while" => {
const @"while" = lexer.next();
const cond = try expression(allocator, lexer);
- const do = try parseBlock(allocator, lexer);
+ const do = try expression(allocator, lexer);
return .{
.loc = @"while".loc.combine(do.loc),
.type = .{ .@"while" = .{
@@ -318,7 +295,7 @@ fn parseProc(allocator: Allocator, lexer: *Lexer) ParseError!*Expr {
else => return expected(&.{ .identifier, .right_paren }, param_tok),
};
if (param != null) _ = try mustEat(lexer, .right_paren);
- const body = try parseBlock(allocator, lexer);
+ const body = try expression(allocator, lexer);
return allocate(Expr, allocator, .{
.loc = proc.loc.combine(body.loc),
@@ -354,10 +331,10 @@ fn parseIf(allocator: Allocator, lexer: *Lexer) !*Expr {
.@"if" => {
const @"if" = lexer.next();
const cond = try expression(allocator, lexer);
- const then = try parseBlock(allocator, lexer);
+ const then = try expression(allocator, lexer);
const @"else" = if (lexer.peek().type == .@"else") blk: {
_ = lexer.next();
- break :blk try parseBlock(allocator, lexer);
+ break :blk try expression(allocator, lexer);
} else null;
return try allocate(Expr, allocator, .{
.loc = @"if".loc.combine((@"else" orelse then).loc),
@@ -397,6 +374,17 @@ fn parsePrimaryExpr(allocator: Allocator, lexer: *Lexer) !*Expr {
return error.ExpectedRightParen;
return res;
},
+ .left_curly => {
+ var stmts: std.ArrayList(Stmt) = .init(allocator);
+ while (lexer.peek().type != .right_curly) {
+ try stmts.append(try parseStatement(allocator, lexer));
+ }
+ const right_curly = try mustEat(lexer, .right_curly);
+ return allocate(Expr, allocator, .{
+ .loc = token.loc.combine(right_curly.loc),
+ .type = .{ .block = .{ .stmts = try stmts.toOwnedSlice() } },
+ });
+ },
.integer_literal => .{ .loc = token.loc, .type = .integer_literal },
.identifier => .{ .loc = token.loc, .type = .identifier },
else => return expected(&.{ .left_paren, .integer_literal, .identifier }, token),
diff --git a/ternary.hgn b/ternary.hgn
new file mode 100644
index 0000000..3740fd8
--- /dev/null
+++ b/ternary.hgn
@@ -0,0 +1,4 @@
+main := proc() {
+ print(if read_int(0) 1 else 2)
+ if 1 exit(0)
+}