diff options
author | Mathias Magnusson <mathias@magnusson.space> | 2025-06-01 02:32:13 +0200 |
---|---|---|
committer | Mathias Magnusson <mathias@magnusson.space> | 2025-06-01 02:32:13 +0200 |
commit | 546bb5cb935832791a280d1561620cf7a7a64842 (patch) | |
tree | 8c9439fdc4728a93380f93914605b890b8e4806a /src | |
parent | a49805b7a8a9fc05df34f14cb8b6892c723e2a61 (diff) | |
download | huginn-546bb5cb935832791a280d1561620cf7a7a64842.tar.gz |
add identifiers, procedure calls and a built in print procedure
Diffstat (limited to 'src')
-rw-r--r-- | src/Lexer.zig | 29 | ||||
-rw-r--r-- | src/codegen.zig | 49 | ||||
-rw-r--r-- | src/compile.zig | 27 | ||||
-rw-r--r-- | src/main.zig | 3 | ||||
-rw-r--r-- | src/parse.zig | 30 |
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 } }, }); } |