diff options
Diffstat (limited to 'src/Lexer.zig')
-rw-r--r-- | src/Lexer.zig | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/src/Lexer.zig b/src/Lexer.zig new file mode 100644 index 0000000..3621a1c --- /dev/null +++ b/src/Lexer.zig @@ -0,0 +1,99 @@ +const std = @import("std"); + +pub const Token = struct { + loc: Location, + type: Type, + + pub const Type = enum { + left_paren, + right_paren, + integer_literal, + plus, + minus, + invalid, + eof, + }; +}; + +pub const Location = struct { + start: usize, + end: usize, + + pub fn combine(a: Location, b: Location) Location { + std.debug.assert(a.end <= b.start); + return .{ .start = @min(a.start, b.start), .end = @max(a.end, b.end) }; + } + + /// Assumes that the location comes directly from an `integer_literal` token. + pub fn getInt(self: Location, file_source: []const u8) u64 { + var value: u64 = 0; + for (file_source[self.start..self.end]) |c| { + std.debug.assert('0' <= c and c <= '9'); + value = value * 10 + (c - '0'); + } + return value; + } +}; + +source: []const u8, +start: usize = 0, +pos: usize = 0, + +pub fn next(self: *Self) ?Token { + self.start = self.pos; + 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.create(.minus), + ' ' => { + self.start = self.pos; + continue :s (self.eat() orelse return self.create(.eof)); + }, + else => self.create(.invalid), + }; +} + +fn integerLiteral(self: *Self) Token { + var value: ?u64 = self.source[self.start] - '0'; + while (digitValue(self.peek())) |v| { + var nxt: ?u64 = null; + if (value) |val| + if (std.math.mul(u64, val, 10) catch null) |p| + if (std.math.add(u64, p, v) catch null) |s| { + nxt = s; + }; + + value = nxt; + _ = self.eat(); + } + return if (value != null) + self.create(.integer_literal) + else + self.create(.invalid); +} + +fn create(self: *Self, tajp: Token.Type) Token { + const start = self.start; + return .{ .loc = .{ .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, + }; +} |