/* * Copyright (c) 2026, Chloe M. * Provided under the BSD-3 clause */ #include #include #include "cescal/log.h" #include "cescal/parser.h" #include "cescal/state.h" #include "cescal/lexer.h" /* Symbolic token */ #define symtok(tok) \ "[" tok "]" /* Quoted token */ #define qtok(tok) \ "'" tok "'" /* Convert token to string */ #define tokstr1(tt) \ toktab[(tt)] /* Convert token to string */ #define tokstr(tok) \ toktab[(tok)->type] /* Unexpected token */ #define utok(tok) \ cc_error("unexpected token %s\n", tokstr(tok)); #define utok1(tok, exp_tt) \ cc_error("unexpected token %s, expected %s\n", tokstr(tok), tokstr1(exp_tt)) static int parser_expect(struct cescal_state *state, struct token *res, tt_t exp); /* * Converts numeric tokens into human readable strings */ static const char *toktab[] = { [TT_NONE] = symtok("none"), [TT_IDENT] = symtok("ident"), [TT_COMMENT] = symtok("comment"), [TT_INTLIT] = symtok("number"), [TT_LPAREN] = qtok("("), [TT_RPAREN] = qtok(")"), [TT_COMMA] = qtok(","), [TT_COLON] = qtok(":"), [TT_ARROW] = qtok("->"), [TT_PLUS] = qtok("+"), [TT_MINUS] = qtok("-"), [TT_SLASH] = qtok("/"), [TT_STAR] = qtok("*"), [TT_EQUALS] = qtok("="), [TT_DEFINE] = qtok("#define"), [TT_IFNDEF] = qtok("#ifndef"), [TT_IFDEF] = qtok("#ifdef"), [TT_RETURN] = qtok("return"), [TT_PUB] = qtok("pub"), [TT_PROC] = qtok("proc"), [TT_BEGIN] = qtok("begin"), [TT_END] = qtok("end"), [TT_U8] = qtok("u8"), [TT_U16] = qtok("u16"), [TT_U32] = qtok("u32"), [TT_U64] = qtok("u64") }; /* * Parse a preprocessor define directive */ static int parse_define(struct cescal_state *state, struct token *tok) { struct symbol *sym; int error; if (state == NULL || tok == NULL) { return -1; } if (parser_expect(state, tok, TT_IDENT) < 0) { return -1; } /* XXX: Should probably be a warning */ if ((sym = symbol_byname(&state->symtab, tok->s)) != NULL) { cc_error("redefinition of symbol %s\n", tok->s); return -1; } /* Allocate the symbol */ error = symbol_allocate(&state->symtab, tok->s, SYMBOL_NONE, &sym); if (error < 0) { cc_error("[internal]: failed to allocate symbol\n"); return -1; } return 0; } /* * Push a token to the token buffer in the preprocessing stage * * @state: Compiler state * @tokbuf: Token buffer to push to * @tok: Token to push * * Returns zero on success */ static int preprocessor_push(struct cescal_state *state, struct tokbuf *tokbuf, struct token *tok) { if (tokbuf == NULL || tok == NULL) { return -1; } switch (tok->type) { case TT_IFNDEF: break; case TT_IFDEF: break; case TT_DEFINE: if (parse_define(state, tok) < 0) { return -1; } break; default: if (tokbuf_push(tokbuf, tok) < 0) { return -1; } } return 0; } static int parser_nom(struct cescal_state *state, struct token *res) { if (state == NULL || res == NULL) { errno = EINVAL; return -1; } switch (state->pass) { case 0: /* Pre-processor */ if (lexer_nom(state, res) < 0) { return -1; } if (preprocessor_push(state, &state->tokbuf, res) < 0) { return -1; } break; case 1: /* Parse */ if (tokbuf_pop(&state->tokbuf, res) < 0) { return -1; } break; } return 0; } /* * Assert that the next token is of a specific type * * @state: Compiler state * @res: Token result is written here * @exp: Token expedcted * * Returns zero on success */ static int parser_expect(struct cescal_state *state, struct token *res, tt_t exp) { if (state == NULL || res == NULL) { return -1; } if (parser_nom(state, res) < 0) { cc_error("unexpected end of file\n"); return -1; } if (res->type != exp) { utok1(res, exp); return -1; } return 0; } int parser_parse(struct cescal_state *state) { struct token tok; if (state == NULL) { errno = EINVAL; return -1; } while (parser_nom(state, &tok) == 0) { if (state->pass > 0) { cc_trace("got token %s\n", tokstr(&tok)); } } ++state->pass; return 0; }