diff options
author | Mathias Magnusson <mathias@magnusson.space> | 2025-06-02 15:17:22 +0200 |
---|---|---|
committer | Mathias Magnusson <mathias@magnusson.space> | 2025-06-02 15:17:22 +0200 |
commit | a39f75cae1d74d89efc0a871bba953c1f1af3b1b (patch) | |
tree | d7ff616ba70aea4424d93be17c885e202904ce8e /src | |
parent | 939ee7606fbfe3afa6b6a008fb617120a6dd8114 (diff) | |
download | huginn-a39f75cae1d74d89efc0a871bba953c1f1af3b1b.tar.gz |
add statements ending in ; and allow parsing multiple of them
Diffstat (limited to 'src')
-rw-r--r-- | src/Lexer.zig | 4 | ||||
-rw-r--r-- | src/compile.zig | 17 | ||||
-rw-r--r-- | src/main.zig | 18 | ||||
-rw-r--r-- | src/parse.zig | 54 |
4 files changed, 66 insertions, 27 deletions
diff --git a/src/Lexer.zig b/src/Lexer.zig index 8c23b26..c85979d 100644 --- a/src/Lexer.zig +++ b/src/Lexer.zig @@ -10,6 +10,7 @@ pub const Token = struct { integer_literal, plus, minus, + semicolon, invalid, eof, identifier, @@ -70,7 +71,8 @@ fn getNext(self: *Self) Token { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => self.integerLiteral(), '+' => self.create(.plus), '-' => self.create(.minus), - ' ' => { + ';' => self.create(.semicolon), + ' ', '\n' => { self.start = self.pos; continue :s (self.eatChar() orelse return self.create(.eof)); }, diff --git a/src/compile.zig b/src/compile.zig index 9d70a58..6a79efd 100644 --- a/src/compile.zig +++ b/src/compile.zig @@ -2,7 +2,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const root = @import("root"); const Token = root.Lexer.Token; -const Expr = root.Expr; +const parse = root.parse; const Location = root.Lexer.Location; pub const VReg = enum(u32) { _ }; @@ -84,7 +84,7 @@ pub const Block = struct { } }; -pub fn compile(allocator: Allocator, source: []const u8, expr: *const Expr) !Block { +pub fn compile(allocator: Allocator, source: []const u8, stmts: []parse.Stmt) !Block { const instrs: std.ArrayListUnmanaged(Instr) = try .initCapacity(allocator, 0); var ctx: CompileContext = .{ .allocator = allocator, @@ -92,7 +92,9 @@ pub fn compile(allocator: Allocator, source: []const u8, expr: *const Expr) !Blo .register_counter = 0, .instrs = instrs, }; - _ = try ctx.compileExpr(expr); + for (stmts) |stmt| { + try ctx.compileStmt(stmt); + } return .init(allocator, ctx.instrs.items); } @@ -108,7 +110,13 @@ const CompileContext = struct { try self.instrs.append(self.allocator, instr); } - fn compileExpr(self: *Self, expr: *const Expr) !VReg { + fn compileStmt(self: *Self, stmt: parse.Stmt) !void { + switch (stmt.type) { + .expr => |expr| _ = try self.compileExpr(expr), + } + } + + fn compileExpr(self: *Self, expr: *const parse.Expr) !VReg { const dest = self.register(); switch (expr.type) { .integer_literal => try addInstr(self, .{ @@ -148,7 +156,6 @@ const CompileContext = struct { } }, .identifier => return error.CantCompileIdentifierExpr, - .invalid => return error.CantCompileInvalidExpr, } return dest; } diff --git a/src/main.zig b/src/main.zig index 87dc493..d4951e6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,8 +1,7 @@ const std = @import("std"); -const parse = @import("./parse.zig"); const codegen = @import("./codegen.zig"); -pub const Expr = parse.Expr; +pub const parse = @import("./parse.zig"); pub const Lexer = @import("./Lexer.zig"); pub const compile = @import("./compile.zig"); @@ -30,7 +29,11 @@ pub fn main() !void { // } // const source = "17216961135462248174 + 4095 - 4294967295 + 2147483647"; - const source = "print 18446744073709551615"; + const source = + \\print 18446744073709551615; + \\print (0 - 1); + \\print 69; + ; var lexer: Lexer = .{ .source = source }; std.debug.print("Tokens:\n", .{}); while (true) { @@ -39,12 +42,15 @@ pub fn main() !void { if (token.type == .eof) break; } lexer = .{ .source = source }; - const expr = try parse.expression(allocator, &lexer); - std.debug.print("{}\n", .{expr}); - const block = try compile.compile(allocator, source, expr); + const stmts = try parse.statements(allocator, &lexer); + std.debug.print("Statements:\n", .{}); + for (stmts) |stmt| { + std.debug.print(" {}\n", .{stmt}); + } if (lexer.peek().type != .eof) { std.debug.print("Unexpected token {}, expected end of file\n", .{lexer.next()}); } + const block = try compile.compile(allocator, source, stmts); std.debug.print("Bytecode instructions:\n", .{}); for (block.instrs) |instr| { std.debug.print(" {}\n", .{instr}); diff --git a/src/parse.zig b/src/parse.zig index 901ce0d..1dda517 100644 --- a/src/parse.zig +++ b/src/parse.zig @@ -5,6 +5,15 @@ const root = @import("root"); const Lexer = root.Lexer; const Token = root.Lexer.Token; +pub const Stmt = struct { + loc: Lexer.Location, + type: Type, + + pub const Type = union(enum) { + expr: *const Expr, + }; +}; + pub const Expr = struct { loc: Lexer.Location, type: Type, @@ -12,7 +21,6 @@ pub const Expr = struct { pub const Type = union(enum) { integer_literal, bin_op: BinOp, - invalid: Token, call: Call, identifier, @@ -48,14 +56,30 @@ pub const Expr = struct { switch (self.type) { .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>", .{}), } } }; -pub fn expression(allocator: Allocator, lexer: *Lexer) error{OutOfMemory}!*Expr { +pub fn statements(allocator: Allocator, lexer: *Lexer) ![]Stmt { + var stmts: std.ArrayList(Stmt) = .init(allocator); + while (lexer.peek().type != .eof) { + try stmts.append(try statement(allocator, lexer)); + } + return try stmts.toOwnedSlice(); +} + +pub fn statement(allocator: Allocator, lexer: *Lexer) !Stmt { + var expr = try expression(allocator, lexer); + const semicolon: Lexer.Token = if (lexer.peek().type == .semicolon) lexer.next() else return error.ExpectedSemicolon; + return .{ + .loc = expr.loc.combine(semicolon.loc), + .type = .{ .expr = expr }, + }; +} + +pub fn expression(allocator: Allocator, lexer: *Lexer) error{ OutOfMemory, ExpectedRightParen, UnexpectedToken }!*Expr { return addExpr(allocator, lexer); } @@ -70,7 +94,7 @@ pub fn addExpr(allocator: Allocator, lexer: *Lexer) !*Expr { _ = lexer.next(); const rhs = try callExpr(allocator, lexer); - lhs = try allocate(allocator, .{ + lhs = try allocate(Expr, allocator, .{ .loc = lhs.loc.combine(rhs.loc), .type = .{ .bin_op = .{ .lhs = lhs, .op = op, .rhs = rhs } }, }); @@ -82,11 +106,11 @@ pub fn callExpr(allocator: Allocator, lexer: *Lexer) !*Expr { var proc = try primaryExpr(allocator, lexer); while (true) { switch (lexer.peek().type) { - .left_paren, .integer_literal => {}, + .left_paren, .integer_literal, .identifier => {}, else => break, } const arg = try primaryExpr(allocator, lexer); - proc = try allocate(allocator, .{ + proc = try allocate(Expr, allocator, .{ .loc = proc.loc.combine(arg.loc), .type = .{ .call = .{ .proc = proc, .arg = arg } }, }); @@ -97,25 +121,25 @@ pub fn callExpr(allocator: Allocator, lexer: *Lexer) !*Expr { pub fn primaryExpr(allocator: Allocator, lexer: *Lexer) !*Expr { const token = lexer.next(); // std.debug.print("term {}\n", .{token}); - return allocate(allocator, switch (token.type) { + return allocate(Expr, allocator, switch (token.type) { .left_paren => { const res = expression(allocator, lexer); const right_paren = lexer.next(); if (right_paren.type != .right_paren) - return allocate(allocator, .{ - .loc = right_paren.loc, - .type = .{ .invalid = right_paren }, - }); + return error.ExpectedRightParen; return res; }, .integer_literal => .{ .loc = token.loc, .type = .integer_literal }, .identifier => .{ .loc = token.loc, .type = .identifier }, - else => .{ .loc = token.loc, .type = .{ .invalid = token } }, + else => |t| { + std.debug.print("Expected '(', integer literal, or identifier. Got {}\n", .{t}); + return error.UnexpectedToken; + }, }); } -fn allocate(allocator: Allocator, expr: Expr) !*Expr { - const res = try allocator.create(Expr); - res.* = expr; +fn allocate(T: type, allocator: Allocator, t: T) !*T { + const res = try allocator.create(T); + res.* = t; return res; } |