aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathias Magnusson <mathias@magnusson.space>2025-06-01 02:32:13 +0200
committerMathias Magnusson <mathias@magnusson.space>2025-06-01 02:32:13 +0200
commit546bb5cb935832791a280d1561620cf7a7a64842 (patch)
tree8c9439fdc4728a93380f93914605b890b8e4806a
parenta49805b7a8a9fc05df34f14cb8b6892c723e2a61 (diff)
downloadhuginn-546bb5cb935832791a280d1561620cf7a7a64842.tar.gz
add identifiers, procedure calls and a built in print procedure
-rw-r--r--src/Lexer.zig29
-rw-r--r--src/codegen.zig49
-rw-r--r--src/compile.zig27
-rw-r--r--src/main.zig3
-rw-r--r--src/parse.zig30
5 files changed, 132 insertions, 6 deletions
diff --git a/src/Lexer.zig b/src/Lexer.zig
index 3621a1c..62b2352 100644
--- a/src/Lexer.zig
+++ b/src/Lexer.zig
@@ -12,6 +12,10 @@ pub const Token = struct {
minus,
invalid,
eof,
+ identifier,
+
+ // Keywords
+ @"if",
};
};
@@ -33,6 +37,11 @@ pub const Location = struct {
}
return value;
}
+
+ /// Assumes that the location comes directly from an `identifier` token.
+ pub fn getIdent(self: Location, file_source: []const u8) []const u8 {
+ return file_source[self.start..self.end];
+ }
};
source: []const u8,
@@ -51,7 +60,10 @@ pub fn next(self: *Self) ?Token {
self.start = self.pos;
continue :s (self.eat() orelse return self.create(.eof));
},
- else => self.create(.invalid),
+ else => |c| if ('a' <= c and c <= 'z' or 'A' <= c and c <= 'Z')
+ self.identifierOrKeyword()
+ else
+ self.create(.invalid),
};
}
@@ -74,6 +86,21 @@ fn integerLiteral(self: *Self) Token {
self.create(.invalid);
}
+fn identifierOrKeyword(self: *Self) Token {
+ while (true) {
+ const c = self.peek() orelse 0;
+ if ('a' <= c and c <= 'z' or 'A' <= c and c <= 'Z' or c == '_') {
+ _ = self.eat();
+ continue;
+ }
+ const value = self.source[self.start..self.pos];
+ return self.create(switch (std.meta.stringToEnum(Token.Type, value) orelse .invalid) {
+ .@"if" => .@"if",
+ else => .identifier,
+ });
+ }
+}
+
fn create(self: *Self, tajp: Token.Type) Token {
const start = self.start;
return .{ .loc = .{ .start = start, .end = self.pos }, .type = tajp };
diff --git a/src/codegen.zig b/src/codegen.zig
index e52fb47..011d28a 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -529,6 +529,18 @@ const RegisterAllocator = struct {
std.debug.assert(std.mem.indexOfScalar(Register, self.available.items, reg) == null);
return self.available.appendAssumeCapacity(reg);
}
+
+ fn allocateAux(self: *RegisterAllocator) !Register {
+ const reg = self.available.pop() orelse return error.OutOfRegisters;
+ return reg;
+ }
+
+ fn freeAux(self: *RegisterAllocator, reg: Register) void {
+ var it = self.allocated.valueIterator();
+ while (it.next()) |r| std.debug.assert(reg != r.*);
+ std.debug.assert(std.mem.indexOfScalar(Register, self.available.items, reg) == null);
+ return self.available.appendAssumeCapacity(reg);
+ }
};
const Context = struct {
@@ -597,6 +609,41 @@ const Context = struct {
}
}
+ fn genPrint(self: *Context, print: compile.Instr.Print) !void {
+ const arg = self.register_allocator.get(print.arg);
+
+ const quot = try self.register_allocator.allocateAux();
+ defer self.register_allocator.freeAux(quot);
+ const aux1 = try self.register_allocator.allocateAux();
+ defer self.register_allocator.freeAux(aux1);
+ const count = try self.register_allocator.allocateAux();
+ defer self.register_allocator.freeAux(count);
+
+ try self.maybeFreeSources(print.sources());
+
+ try self.emit(.addi(aux1, .zero, '\n'));
+ try self.emit(.addi(.sp, .sp, -1));
+ try self.emit(.sb(.sp, 0, aux1));
+ try self.emit(.addi(count, .zero, 1));
+
+ try self.emit(.addi(aux1, arg, 0));
+ try self.emit(.addi(quot, .zero, 10));
+ try self.emit(.divu(arg, aux1, quot));
+ try self.emit(.remu(aux1, aux1, quot));
+ try self.emit(.addi(aux1, aux1, '0'));
+ try self.emit(.addi(.sp, .sp, -1));
+ try self.emit(.sb(.sp, 0, aux1));
+ try self.emit(.addi(count, count, 1));
+ try self.emit(.bne(arg, .zero, -4 * 8));
+
+ try self.emit(.addi(.a7, .zero, 64)); // no = write
+ try self.emit(.addi(.a0, .zero, 1)); // fd = stdout
+ try self.emit(.addi(.a1, .sp, 0)); // buf = sp
+ try self.emit(.addi(.a2, count, 0)); // count = count
+ try self.emit(.ecall()); // syscall(no, fd, buf, count)
+ try self.emit(.add(.sp, .sp, count));
+ }
+
fn codegenInstr(self: *Context, instr: compile.Instr) !void {
switch (instr.type) {
inline else => |ty| {
@@ -635,7 +682,7 @@ pub fn create_elf(allocator: Allocator, block: compile.Block) ![]u8 {
try ctx.codegenBlock(block);
try ctx.instructions.appendSlice(&[_]Instruction{
- .addi(.a0, ctx.register_allocator.get(block.instrs[block.instrs.len - 1].dest()), 0),
+ .addi(.a0, .zero, 0),
.addi(.a7, .zero, 93),
.ecall(),
});
diff --git a/src/compile.zig b/src/compile.zig
index 2bf404d..9d70a58 100644
--- a/src/compile.zig
+++ b/src/compile.zig
@@ -14,6 +14,7 @@ pub const Instr = struct {
pub const Type = union(enum) {
constant: Constant,
bin_op: BinOp,
+ print: Print,
};
pub const Constant = struct {
@@ -41,15 +42,24 @@ pub const Instr = struct {
}
};
+ pub const Print = struct {
+ arg: VReg,
+
+ pub fn sources(self: Print) Sources {
+ return Sources.fromSlice(&.{self.arg}) catch unreachable;
+ }
+ };
+
pub fn sources(self: Instr) Sources {
return switch (self.type) {
inline else => |instr| instr.sources(),
};
}
- pub fn dest(self: *const Instr) VReg {
+ pub fn dest(self: *const Instr) ?VReg {
return switch (self.type) {
inline .constant, .bin_op => |s| s.dest,
+ inline .print => null,
};
}
@@ -123,6 +133,21 @@ const CompileContext = struct {
},
});
},
+ .call => |call| {
+ if (call.proc.type == .identifier and
+ std.mem.eql(u8, call.proc.loc.getIdent(self.source), "print"))
+ {
+ const arg = try self.compileExpr(call.arg);
+ try self.addInstr(.{
+ .loc = expr.loc,
+ .type = .{ .print = .{ .arg = arg } },
+ });
+ // BUG: we're returning a bogus virtual register that we didn't write to
+ } else {
+ return error.CantCallAnythingButPrint;
+ }
+ },
+ .identifier => return error.CantCompileIdentifierExpr,
.invalid => return error.CantCompileInvalidExpr,
}
return dest;
diff --git a/src/main.zig b/src/main.zig
index 1ca22a3..3c21f19 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -32,7 +32,8 @@ pub fn main() !void {
// try stdout.print("{s}\n", .{line.items});
// }
- const source = "17216961135462248174 + 4095 - 4294967295 + 2147483647";
+ // const source = "17216961135462248174 + 4095 - 4294967295 + 2147483647";
+ const source = "print 18446744073709551615";
// var lexer = Lexer{ .source = source };
// while (true) {
// const token = lexer.next().?;
diff --git a/src/parse.zig b/src/parse.zig
index 7daa3f8..d5d3e9b 100644
--- a/src/parse.zig
+++ b/src/parse.zig
@@ -15,6 +15,8 @@ pub const Expr = struct {
integer_literal,
bin_op: BinOp,
invalid: Token,
+ call: Call,
+ identifier,
pub const BinOp = struct {
lhs: *const Expr,
@@ -35,6 +37,11 @@ pub const Expr = struct {
}
};
};
+
+ pub const Call = struct {
+ proc: *const Expr,
+ arg: *const Expr,
+ };
};
pub fn format(self: Expr, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
@@ -44,6 +51,8 @@ pub const Expr = struct {
.integer_literal => try writer.print("<int>", .{}),
.bin_op => |bin_op| try writer.print("({} {} {})", .{ bin_op.lhs, bin_op.op, bin_op.rhs }),
.invalid => try writer.print("<invalid>", .{}),
+ .call => |call| try writer.print("({} {})", .{ call.proc, call.arg }),
+ .identifier => try writer.print("<identifier>", .{}),
}
}
};
@@ -53,7 +62,7 @@ pub fn expression(allocator: Allocator, lexer: *Peekable(Lexer)) error{OutOfMemo
}
pub fn addExpr(allocator: Allocator, lexer: *Peekable(Lexer)) !*Expr {
- var lhs = try primaryExpr(allocator, lexer);
+ var lhs = try callExpr(allocator, lexer);
while (true) {
const token: Lexer.Token = if (lexer.peek()) |t| t else break;
const op: Expr.Type.BinOp.Op = switch (token.type) {
@@ -64,7 +73,7 @@ pub fn addExpr(allocator: Allocator, lexer: *Peekable(Lexer)) !*Expr {
_ = lexer.next();
- const rhs = try primaryExpr(allocator, lexer);
+ const rhs = try callExpr(allocator, lexer);
lhs = try allocate(allocator, .{
.loc = lhs.loc.combine(rhs.loc),
.type = .{ .bin_op = .{ .lhs = lhs, .op = op, .rhs = rhs } },
@@ -73,6 +82,22 @@ pub fn addExpr(allocator: Allocator, lexer: *Peekable(Lexer)) !*Expr {
return lhs;
}
+pub fn callExpr(allocator: Allocator, lexer: *Peekable(Lexer)) !*Expr {
+ var proc = try primaryExpr(allocator, lexer);
+ while (true) {
+ switch (lexer.peek().?.type) {
+ .left_paren, .integer_literal => {},
+ else => break,
+ }
+ const arg = try primaryExpr(allocator, lexer);
+ proc = try allocate(allocator, .{
+ .loc = proc.loc.combine(arg.loc),
+ .type = .{ .call = .{ .proc = proc, .arg = arg } },
+ });
+ }
+ return proc;
+}
+
pub fn primaryExpr(allocator: Allocator, lexer: *Peekable(Lexer)) !*Expr {
const token = lexer.next().?;
// std.debug.print("term {}\n", .{token});
@@ -88,6 +113,7 @@ pub fn primaryExpr(allocator: Allocator, lexer: *Peekable(Lexer)) !*Expr {
return res;
},
.integer_literal => .{ .loc = token.loc, .type = .integer_literal },
+ .identifier => .{ .loc = token.loc, .type = .identifier },
else => .{ .loc = token.loc, .type = .{ .invalid = token } },
});
}