pub const Token = struct { loc: Location, type: Type, pub const Type = union(enum) { 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(.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)); }, else => self.create(.invalid), }; } fn integerLiteral(self: *Self) Token { while (digitValue(self.peek()) != null) { _ = self.eat(); } return self.create(.{ .integer_literal = {} }); } fn create(self: *Self, tajp: Token.Type) Token { const start = self.last_end; self.last_end = self.pos; 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, }; }