aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Lexer.zig4
-rw-r--r--src/compile.zig17
-rw-r--r--src/main.zig18
-rw-r--r--src/parse.zig54
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;
}