aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compile.zig88
-rw-r--r--src/lexer.zig49
-rw-r--r--src/main.zig26
-rw-r--r--src/parse.zig76
4 files changed, 186 insertions, 53 deletions
diff --git a/src/compile.zig b/src/compile.zig
new file mode 100644
index 0000000..b9e1a86
--- /dev/null
+++ b/src/compile.zig
@@ -0,0 +1,88 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const root = @import("root");
+const Token = root.Lexer.Token;
+const Expr = root.Expr;
+const Location = root.Lexer.Location;
+
+pub const Reg = enum(u32) { _ };
+
+pub const Instr = union(enum) {
+ constant: Constant,
+ bin_op: BinOp,
+
+ const Constant = struct {
+ dest: Reg,
+ value: Location,
+ };
+
+ const BinOp = struct {
+ dest: Reg,
+ lhs: Reg,
+ rhs: Reg,
+ op: Op,
+
+ const Op = enum {
+ add,
+ };
+ };
+};
+
+pub const Block = struct {
+ // arguments: []Reg,
+ instrs: []Instr,
+};
+
+pub fn compile(allocator: Allocator, expr: *const Expr) !Block {
+ const instrs: std.ArrayListUnmanaged(Instr) = try .initCapacity(allocator, 0);
+ var ctx: CompileContext = .{
+ .allocator = allocator,
+ .register_counter = 0,
+ .instrs = instrs,
+ };
+ _ = try ctx.compileExpr(expr);
+ return .{ .instrs = ctx.instrs.items };
+}
+
+const CompileContext = struct {
+ allocator: Allocator,
+ register_counter: u32,
+ instrs: std.ArrayListUnmanaged(Instr),
+
+ const Self = @This();
+
+ fn addInstr(self: *Self, instr: Instr) !void {
+ try self.instrs.append(self.allocator, instr);
+ }
+
+ fn compileExpr(self: *Self, expr: *const Expr) !Reg {
+ const dest = self.register();
+ switch (expr.type) {
+ .integer_literal => try addInstr(self, .{
+ .constant = .{ .dest = dest, .value = expr.loc },
+ }),
+ .bin_op => |binop| {
+ const lhs = try self.compileExpr(binop.lhs);
+ const rhs = try self.compileExpr(binop.rhs);
+ try addInstr(self, .{
+ .bin_op = .{
+ .dest = dest,
+ .lhs = lhs,
+ .rhs = rhs,
+ .op = switch (binop.op) {
+ .plus => .add,
+ },
+ },
+ });
+ },
+ .invalid => return error.CantCompileInvalidExpr,
+ }
+ return dest;
+ }
+
+ fn register(self: *Self) Reg {
+ const reg: Reg = @enumFromInt(self.register_counter);
+ self.register_counter += 1;
+ return reg;
+ }
+};
diff --git a/src/lexer.zig b/src/lexer.zig
index 93ed6cc..67beec9 100644
--- a/src/lexer.zig
+++ b/src/lexer.zig
@@ -1,49 +1,56 @@
pub const Token = struct {
- start: usize,
- end: usize,
+ loc: Location,
type: Type,
pub const Type = union(enum) {
- LeftParen,
- RightParen,
- IntegerLiteral: usize,
- Plus,
- Invalid,
- Eof,
+ left_paren,
+ right_paren,
+ integer_literal,
+ plus,
+ invalid,
+ eof,
};
};
+pub const Location = struct {
+ start: usize,
+ end: usize,
+
+ pub fn combine(a: Location, b: Location) Location {
+ if (a.end > b.start) unreachable;
+ return .{ .start = @min(a.start, b.start), .end = @max(a.end, b.end) };
+ }
+};
+
source: []const u8,
last_end: usize = 0,
pos: usize = 0,
pub fn next(self: *Self) ?Token {
- return s: switch (self.eat() orelse return self.create(.Eof)) {
- '(' => self.create(.LeftParen),
- ')' => self.create(.RightParen),
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => |d| self.integerLiteral(d),
- '+' => self.create(.Plus),
+ return s: switch (self.eat() orelse return self.create(.eof)) {
+ '(' => self.create(.left_paren),
+ ')' => self.create(.right_paren),
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => self.integerLiteral(),
+ '+' => self.create(.plus),
' ' => {
self.last_end = self.pos;
- continue :s (self.eat() orelse return self.create(.Eof));
+ continue :s (self.eat() orelse return self.create(.eof));
},
- else => self.create(.Invalid),
+ else => self.create(.invalid),
};
}
-fn integerLiteral(self: *Self, first: u8) Token {
- var value: usize = @intCast(digitValue(first).?);
- while (digitValue(self.peek())) |d| {
+fn integerLiteral(self: *Self) Token {
+ while (digitValue(self.peek()) != null) {
_ = self.eat();
- value = value *| 10 +| d;
}
- return self.create(.{ .IntegerLiteral = value });
+ return self.create(.{ .integer_literal = {} });
}
fn create(self: *Self, tajp: Token.Type) Token {
const start = self.last_end;
self.last_end = self.pos;
- return .{ .start = start, .end = self.pos, .type = tajp };
+ return .{ .loc = .{ .start = start, .end = self.pos }, .type = tajp };
}
fn eat(self: *Self) ?u8 {
diff --git a/src/main.zig b/src/main.zig
index e897d41..d938d9b 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -1,5 +1,12 @@
+const std = @import("std");
+const parse = @import("./parse.zig");
+const peek = @import("./peek.zig");
+const compile = @import("./compile.zig");
+
pub const peekable = peek.peekable;
pub const Peekable = peek.Peekable;
+pub const Expr = parse.Expr;
+pub const Lexer = @import("./lexer.zig");
pub fn main() !void {
var arena: std.heap.ArenaAllocator = .init(std.heap.smp_allocator);
@@ -22,7 +29,7 @@ pub fn main() !void {
// try stdout.print("{s}\n", .{line.items});
// }
- const source = "69 + (32 + 1) + 2";
+ const source = "1 + 2 + 3";
// var lexer = Lexer{ .source = source };
// while (true) {
// const token = lexer.next().?;
@@ -30,17 +37,20 @@ pub fn main() !void {
// if (token.type == .Eof) break;
// }
// try stdout.print("\n", .{});
- var lexer2 = peekable(Lexer{ .source = source });
- try stdout.print("{}\n", .{try parse.expression(allocator, &lexer2)});
+ var lexer = peekable(Lexer{ .source = source });
+ const expr = try parse.expression(allocator, &lexer);
+ try stdout.print("{}\n", .{expr});
+ if (lexer.next()) |token| if (token.type != .eof) {
+ try stdout.print("Unexpected token {}, expected end of file\n", .{token});
+ };
+ const block = try compile.compile(allocator, expr);
+ for (block.instrs) |instr| {
+ try stdout.print("{}\n", .{instr});
+ }
try bw.flush(); // Don't forget to flush!
}
-const std = @import("std");
-const Lexer = @import("./lexer.zig");
-const parse = @import("./parse.zig");
-const peek = @import("./peek.zig");
-
test {
_ = peek;
}
diff --git a/src/parse.zig b/src/parse.zig
index 8ceabeb..2149cd7 100644
--- a/src/parse.zig
+++ b/src/parse.zig
@@ -1,7 +1,31 @@
-pub const Expr = union(enum) {
- IntegerLiteral: Token,
- BinOp: struct { lhs: *const Expr, op: Token, rhs: *const Expr },
- Invalid: Token,
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+
+const root = @import("root");
+const Lexer = root.Lexer;
+const Token = root.Lexer.Token;
+const Peekable = root.Peekable;
+const peekable = root.peekable;
+
+pub const Expr = struct {
+ loc: Lexer.Location,
+ type: Type,
+
+ pub const Type = union(enum) {
+ integer_literal,
+ bin_op: BinOp,
+ invalid: Token,
+
+ pub const BinOp = struct {
+ lhs: *const Expr,
+ rhs: *const Expr,
+ op: Op,
+
+ const Op = enum {
+ plus,
+ };
+ };
+ };
};
pub fn expression(allocator: Allocator, lexer: *Peekable(Lexer)) error{OutOfMemory}!*Expr {
@@ -9,28 +33,41 @@ pub fn expression(allocator: Allocator, lexer: *Peekable(Lexer)) error{OutOfMemo
}
pub fn addExpr(allocator: Allocator, lexer: *Peekable(Lexer)) !*Expr {
- const lhs = try primaryExpr(allocator, lexer);
- const token: ?Lexer.Token = lexer.peek();
- const op = (if (token) |t| if (t.type == .Plus) t else null else null) orelse return lhs;
- _ = lexer.next();
+ var lhs = try primaryExpr(allocator, lexer);
+ while (true) {
+ const token: Lexer.Token = if (lexer.peek()) |t| t else break;
+ const op: Expr.Type.BinOp.Op = switch (token.type) {
+ .plus => .plus,
+ else => break,
+ };
+
+ _ = lexer.next();
- const rhs = try primaryExpr(allocator, lexer);
- return allocate(allocator, .{ .BinOp = .{ .lhs = lhs, .op = op, .rhs = rhs } });
+ const rhs = try primaryExpr(allocator, lexer);
+ lhs = try allocate(allocator, .{
+ .loc = lhs.loc.combine(rhs.loc),
+ .type = .{ .bin_op = .{ .lhs = lhs, .op = op, .rhs = rhs } },
+ });
+ }
+ return lhs;
}
pub fn primaryExpr(allocator: Allocator, lexer: *Peekable(Lexer)) !*Expr {
const token = lexer.next().?;
// std.debug.print("term {}\n", .{token});
return allocate(allocator, switch (token.type) {
- .LeftParen => {
+ .left_paren => {
const res = expression(allocator, lexer);
const right_paren = lexer.next().?;
- if (right_paren.type != .RightParen)
- return allocate(allocator, .{ .Invalid = right_paren });
+ if (right_paren.type != .right_paren)
+ return allocate(allocator, .{
+ .loc = right_paren.loc,
+ .type = .{ .invalid = right_paren },
+ });
return res;
},
- .IntegerLiteral => .{ .IntegerLiteral = token },
- else => .{ .Invalid = token },
+ .integer_literal => .{ .loc = token.loc, .type = .integer_literal },
+ else => .{ .loc = token.loc, .type = .{ .invalid = token } },
});
}
@@ -39,12 +76,3 @@ fn allocate(allocator: Allocator, expr: Expr) !*Expr {
res.* = expr;
return res;
}
-
-const std = @import("std");
-const Allocator = std.mem.Allocator;
-
-const Lexer = @import("./lexer.zig");
-const Token = Lexer.Token;
-const root = @import("root");
-const Peekable = root.Peekable;
-const peekable = root.peekable;