diff options
-rw-r--r-- | src/Lexer.zig | 6 | ||||
-rw-r--r-- | src/compile.zig | 12 | ||||
-rw-r--r-- | src/main.zig | 4 | ||||
-rw-r--r-- | src/parse.zig | 50 |
4 files changed, 55 insertions, 17 deletions
diff --git a/src/Lexer.zig b/src/Lexer.zig index c85979d..c8f19c6 100644 --- a/src/Lexer.zig +++ b/src/Lexer.zig @@ -11,12 +11,13 @@ pub const Token = struct { plus, minus, semicolon, + equal, invalid, eof, identifier, // Keywords - @"if", + let, }; }; @@ -72,6 +73,7 @@ fn getNext(self: *Self) Token { '+' => self.create(.plus), '-' => self.create(.minus), ';' => self.create(.semicolon), + '=' => self.create(.equal), ' ', '\n' => { self.start = self.pos; continue :s (self.eatChar() orelse return self.create(.eof)); @@ -111,7 +113,7 @@ fn identifierOrKeyword(self: *Self) Token { } const value = self.source[self.start..self.pos]; return self.create(switch (std.meta.stringToEnum(Token.Type, value) orelse .invalid) { - .@"if" => .@"if", + .let => .let, else => .identifier, }); } diff --git a/src/compile.zig b/src/compile.zig index 9513a93..c352994 100644 --- a/src/compile.zig +++ b/src/compile.zig @@ -103,6 +103,7 @@ pub fn compile(allocator: Allocator, source: []const u8, stmts: []parse.Stmt) !B .allocator = allocator, .source = source, .register_counter = 0, + .scope = .empty, .instrs = instrs, }; for (stmts) |stmt| { @@ -115,6 +116,7 @@ const CompileContext = struct { allocator: Allocator, source: []const u8, register_counter: u32, + scope: std.StringHashMapUnmanaged(VReg), instrs: std.ArrayListUnmanaged(Instr), const Self = @This(); @@ -129,10 +131,16 @@ const CompileContext = struct { .loc = stmt.loc, .type = .{ .discard = .{ .vreg = try self.compileExpr(expr) } }, }), + .declare_var => |declare_var| { + const val = try self.compileExpr(declare_var.value); + const name = declare_var.ident.getIdent(self.source); + try self.scope.put(self.allocator, name, val); + }, } } fn compileExpr(self: *Self, expr: *const parse.Expr) !VReg { + // This is not used by all expression types, but creating an unused virtual register is not a problem. const dest = self.register(); switch (expr.type) { .integer_literal => try addInstr(self, .{ @@ -168,7 +176,9 @@ const CompileContext = struct { .type = .{ .print = .{ .dest = dest, .arg = arg } }, }); }, - .identifier => return error.CantCompileIdentifierExpr, + .identifier => { + return self.scope.get(expr.loc.getIdent(self.source)) orelse return error.UnknownVariable; + }, } return dest; } diff --git a/src/main.zig b/src/main.zig index 6692799..ced3826 100644 --- a/src/main.zig +++ b/src/main.zig @@ -37,9 +37,9 @@ pub fn main() !void { // } const source = - \\1; 1; 1; 1; 1; + \\let x = 1; \\print 18446744073709551615; - \\print (print (0 - 1)); + \\print (print (0 - x)); ; var lexer: Lexer = .{ .source = source }; std.debug.print("Tokens:\n", .{}); diff --git a/src/parse.zig b/src/parse.zig index cadc489..e240ce3 100644 --- a/src/parse.zig +++ b/src/parse.zig @@ -11,6 +11,12 @@ pub const Stmt = struct { pub const Type = union(enum) { expr: *const Expr, + declare_var: DeclareVar, + + pub const DeclareVar = struct { + ident: Lexer.Location, + value: *const Expr, + }; }; pub fn fmt(self: Stmt, file_source: []const u8) Format { @@ -24,6 +30,7 @@ pub const Stmt = struct { _ = options; return switch (self.type) { .expr => |expr| writer.print("{};", .{expr.fmt(file_source)}), + .declare_var => |declare_var| writer.print("let {s} = {};", .{ declare_var.ident.getIdent(file_source), declare_var.value.fmt(file_source) }), }; } }.format); @@ -48,9 +55,7 @@ pub const Expr = struct { plus, minus, - pub fn format(self: Op, comptime f: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - _ = f; - _ = options; + pub fn format(self: Op, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { try writer.writeByte(switch (self) { .plus => '+', .minus => '-', @@ -70,10 +75,8 @@ pub const Expr = struct { } const Format = std.fmt.Formatter(struct { - fn format(data: struct { Expr, []const u8 }, comptime f: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { + fn format(data: struct { Expr, []const u8 }, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { const self, const file_source = data; - _ = f; - _ = options; switch (self.type) { .integer_literal => try writer.print("{}", .{self.loc.getInt(file_source)}), .bin_op => |bin_op| try writer.print("({} {} {})", .{ bin_op.lhs.fmt(file_source), bin_op.op, bin_op.rhs.fmt(file_source) }), @@ -93,12 +96,28 @@ pub fn statements(allocator: Allocator, lexer: *Lexer) ![]Stmt { } 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 }, - }; + switch (lexer.peek().type) { + .let => { + const let = lexer.next(); + const ident = try mustEat(lexer, .identifier); + _ = try mustEat(lexer, .equal); + const value = try expression(allocator, lexer); + const semicolon = try mustEat(lexer, .semicolon); + return .{ + .loc = let.loc.combine(semicolon.loc), + .type = .{ .declare_var = .{ .ident = ident.loc, .value = value } }, + }; + }, + else => { + var expr = try expression(allocator, lexer); + const semicolon = lexer.next(); + if (semicolon.type != .semicolon) 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 { @@ -159,6 +178,13 @@ pub fn primaryExpr(allocator: Allocator, lexer: *Lexer) !*Expr { }); } +fn mustEat(lexer: *Lexer, ty: Lexer.Token.Type) !Lexer.Token { + const token = lexer.next(); + std.debug.print("Expected {}. Got {}\n", .{ ty, token.type }); + if (token.type != ty) return error.UnexpectedToken; + return token; +} + fn allocate(T: type, allocator: Allocator, t: T) !*T { const res = try allocator.create(T); res.* = t; |