lib/chant/src/parse/state/cursor.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const chant = @import("../../root.zig");
3 const types = @import("types.zig");
4 const diagnostic = @import("diagnostic.zig");
5 const Error = @import("../root.zig").Error;
6
7 const Token = chant.token.Token;
8 const Kind = chant.token.Kind;
9 const Parser = types.Parser;
10
11 pub fn peek(parser: *const Parser) Token {
12 return parser.tokens[parser.index];
13 }
14
15 pub fn ahead(parser: *const Parser, offset: usize) Token {
16 const at = @min(parser.index + offset, parser.tokens.len - 1);
17 return parser.tokens[at];
18 }
19
20 pub fn advance(parser: *Parser) Token {
21 const tok = parser.tokens[parser.index];
22 if (tok.kind != .eof) parser.index += 1;
23 return tok;
24 }
25
26 pub fn consume(parser: *Parser, expected: Kind) bool {
27 if (peek(parser).kind == expected) {
28 _ = advance(parser);
29 return true;
30 }
31 return false;
32 }
33
34 pub fn expect(parser: *Parser, expected: Kind) Error!Token {
35 if (peek(parser).kind != expected) {
36 return diagnostic.fail(parser, error.UnexpectedToken, "unexpected token");
37 }
38 return advance(parser);
39 }
40
41 test "cursor moves through tokens" {
42 var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
43 defer arena_state.deinit();
44 const arena = arena_state.allocator();
45
46 const tokens = [_]Token{
47 .{ .kind = .kw_int, .text = "int", .file = "test.c", .line = 1, .column = 1 },
48 .{ .kind = .identifier, .text = "value", .file = "test.c", .line = 1, .column = 5 },
49 .{ .kind = .eof, .text = "", .file = "test.c", .line = 1, .column = 10 },
50 };
51 var parser = try @import("test.zig").initParser(arena, &tokens);
52
53 try std.testing.expectEqual(.identifier, ahead(&parser, 1).kind);
54 try std.testing.expect(consume(&parser, .kw_int));
55 _ = try expect(&parser, .identifier);
56 }