diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lexer.zig | 66 | ||||
-rw-r--r-- | src/main.zig | 46 | ||||
-rw-r--r-- | src/parse.zig | 50 | ||||
-rw-r--r-- | src/peek.zig | 51 |
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"); |