aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Lexer.zig6
-rw-r--r--src/compile.zig12
-rw-r--r--src/main.zig4
-rw-r--r--src/parse.zig50
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;