#include "parser.h" #include "config.h" #include "tokenizer.h" typedef struct { SandTokenizer tokenizer; SandToken previous; SandToken current; SandAllocator *a; SandState *S; } Parser; // Report an error at the given token and enter panic mode. static void error_at(Parser *parser, SandToken token, const char *message) { sand_print_diagnostic(parser->S, token.location, SAND_DIAGNOSTIC_ERROR, ""); } // Same as `error_at` except using the parser's current token. static void error_at_current(Parser *parser, const char *message) { error_at(parser, parser->current, message); } static void advance(Parser *parser) { parser->previous = parser->current; while (true) { parser->current = sand_get_next_token(&parser->tokenizer); if (parser->current.kind == SAND_TOKEN_ERROR) { // `content` is going to be NULL-terminated because it will always point to a C string literal for error tokens. error_at_current(parser, parser->current.content); } else { break; } } } static SandToken peek(const Parser *parser) { return parser->current; } static bool match(Parser *parser, SandTokenKind expected) { if (peek(parser).kind == expected) { advance(parser); return true; } else { return false; } } static void consume(Parser *parser, SandTokenKind expected, const char *error) { if (!match(parser, expected)) { error_at_current(parser, error); } } SandAst *sand_parse(SandState *S, SandAllocator *a, const char *source, size_t source_length, const char *filename) { Parser parser; parser.a = a; parser.S = S; parser.tokenizer = sand_create_tokenizer(source, source_length, filename); advance(&parser); // Prime the pump on the tokenizer. // Parse the document. SandAst *expr = expression(&parser); consume(&parser, SAND_TOKEN_EOF, "Expected end of expression"); return expr; }