aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lexer.zig66
-rw-r--r--src/main.zig46
-rw-r--r--src/parse.zig50
-rw-r--r--src/peek.zig51
4 files changed, 213 insertions, 0 deletions
diff --git a/src/lexer.zig b/src/lexer.zig
new file mode 100644
index 0000000..93ed6cc
--- /dev/null
+++ b/src/lexer.zig
@@ -0,0 +1,66 @@
+pub const Token = struct {
+ start: usize,
+ end: usize,
+ type: Type,
+
+ pub const Type = union(enum) {
+ LeftParen,
+ RightParen,
+ IntegerLiteral: usize,
+ Plus,
+ Invalid,
+ Eof,
+ };
+};
+
+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),
+ ' ' => {
+ self.last_end = self.pos;
+ continue :s (self.eat() orelse return self.create(.Eof));
+ },
+ else => self.create(.Invalid),
+ };
+}
+
+fn integerLiteral(self: *Self, first: u8) Token {
+ var value: usize = @intCast(digitValue(first).?);
+ while (digitValue(self.peek())) |d| {
+ _ = self.eat();
+ value = value *| 10 +| d;
+ }
+ return self.create(.{ .IntegerLiteral = value });
+}
+
+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 };
+}
+
+fn eat(self: *Self) ?u8 {
+ const token = self.peek();
+ if (token != null) self.pos += 1;
+ return token;
+}
+
+fn peek(self: *Self) ?u8 {
+ return if (self.pos < self.source.len) self.source[self.pos] else null;
+}
+
+const Self = @This();
+
+fn digitValue(c: ?u8) ?u8 {
+ return switch (c orelse return null) {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => c.? - '0',
+ else => null,
+ };
+}
diff --git a/src/main.zig b/src/main.zig
new file mode 100644
index 0000000..e897d41
--- /dev/null
+++ b/src/main.zig
@@ -0,0 +1,46 @@
+pub const peekable = peek.peekable;
+pub const Peekable = peek.Peekable;
+
+pub fn main() !void {
+ var arena: std.heap.ArenaAllocator = .init(std.heap.smp_allocator);
+ defer arena.deinit();
+ const allocator = arena.allocator();
+
+ var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
+ const stdout = bw.writer();
+
+ // var br = std.io.bufferedReader(std.io.getStdIn().reader());
+ // const stdin = br.reader();
+ //
+ // var line: std.ArrayList(u8) = .init(alloc);
+ // defer line.deinit();
+ // while (true) {
+ // try stdin.streamUntilDelimiter(line.writer(), '\n', null);
+ //
+ // const lexer = Lexer{.source = line};
+ //
+ // try stdout.print("{s}\n", .{line.items});
+ // }
+
+ const source = "69 + (32 + 1) + 2";
+ // var lexer = Lexer{ .source = source };
+ // while (true) {
+ // const token = lexer.next().?;
+ // try stdout.print("{}\n", .{token});
+ // if (token.type == .Eof) break;
+ // }
+ // try stdout.print("\n", .{});
+ var lexer2 = peekable(Lexer{ .source = source });
+ try stdout.print("{}\n", .{try parse.expression(allocator, &lexer2)});
+
+ 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
new file mode 100644
index 0000000..8ceabeb
--- /dev/null
+++ b/src/parse.zig
@@ -0,0 +1,50 @@
+pub const Expr = union(enum) {
+ IntegerLiteral: Token,
+ BinOp: struct { lhs: *const Expr, op: Token, rhs: *const Expr },
+ Invalid: Token,
+};
+
+pub fn expression(allocator: Allocator, lexer: *Peekable(Lexer)) error{OutOfMemory}!*Expr {
+ return addExpr(allocator, lexer);
+}
+
+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();
+
+ const rhs = try primaryExpr(allocator, lexer);
+ return allocate(allocator, .{ .BinOp = .{ .lhs = lhs, .op = op, .rhs = rhs } });
+}
+
+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 => {
+ const res = expression(allocator, lexer);
+ const right_paren = lexer.next().?;
+ if (right_paren.type != .RightParen)
+ return allocate(allocator, .{ .Invalid = right_paren });
+ return res;
+ },
+ .IntegerLiteral => .{ .IntegerLiteral = token },
+ else => .{ .Invalid = token },
+ });
+}
+
+fn allocate(allocator: Allocator, expr: Expr) !*Expr {
+ const res = try allocator.create(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;
diff --git a/src/peek.zig b/src/peek.zig
new file mode 100644
index 0000000..58911e9
--- /dev/null
+++ b/src/peek.zig
@@ -0,0 +1,51 @@
+pub fn peekable(iterator: anytype) Peekable(@TypeOf(iterator)) {
+ return .{ .iterator = iterator };
+}
+
+pub fn Peekable(Iterator: type) type {
+ const ActualIterator = switch (@typeInfo(Iterator)) {
+ .pointer => |p| p.child,
+ else => Iterator,
+ };
+ const Next = @typeInfo(@TypeOf(ActualIterator.next)).@"fn";
+ const Item = switch (@typeInfo(Next.return_type.?)) {
+ .optional => |o| o.child,
+ else => |i| i,
+ };
+ return struct {
+ iterator: Iterator,
+ peeked: ?Item = null,
+
+ pub fn peek(self: *Self) ?Item {
+ if (self.peeked) |peeked| return peeked;
+ const item = self.iterator.next();
+ self.peeked = item;
+ return item;
+ }
+
+ pub fn next(self: *Self) ?Item {
+ const item = if (self.peeked) |peeked| peeked else self.iterator.next();
+ self.peeked = null;
+ return item;
+ }
+
+ const Self = @This();
+ };
+}
+
+test peekable {
+ const expect = std.testing.expect;
+ // std.meta.de
+
+ var it = std.mem.window(u8, &[_]u8{ 1, 2, 3 }, 1, 1);
+ var peek = peekable(&it);
+ try expect(peek.next().?[0] == 1);
+ try expect(peek.peek().?[0] == 2);
+ try expect(peek.peek().?[0] == 2);
+ try expect(peek.next().?[0] == 2);
+ try expect(peek.next().?[0] == 3);
+ try expect(peek.peek() == null);
+ try expect(peek.next() == null);
+}
+
+const std = @import("std");