/* * Copyright (c) 2026, Apollo Telephone Laboratories. * Provided under the BSD-3 clause. */ #include #include #include #include #include "defconf/lexer.h" #include "defconf/ptrbox.h" /* Constants */ #define MAX_KW_LEN 32 /* * Returns true if the given character is a whitespace character * * @c: Character to check */ static inline bool lexer_is_ws(char c) { switch (c) { case '\t': case ' ': case '\f': case '\r': case '\n': return true; } return false; } /* * Place a character back in the putback buffer * * @state: Defconf state * @c: Character to put back */ static inline void lexer_putback(struct defconf_state *state, int c) { if (state == NULL) { return; } state->lex_cache = c; } /* * Nom a single character from the input source file * * @state: Defconf state machine * @skip_ws: If true, skip whitespace * * Returns the character on success, otherwise EOF on failure */ static int lexer_nom(struct defconf_state *state, bool skip_ws) { int c; if (state == NULL) { return EOF; } if ((c = state->lex_cache) != '\0') { state->lex_cache = '\0'; if (lexer_is_ws(c) && !skip_ws) return c; if (!lexer_is_ws(c)) return c; } while ((c = fgetc(state->in_fp)) != EOF) { if (lexer_is_ws(c) && skip_ws) continue; break; } return c; } /* * Scan a keyword * * @state: Defconf state * @lc: Last character provided */ static char * lexer_scan_ident(struct defconf_state *state, int lc) { char c, buf[MAX_KW_LEN]; size_t bufind = 0; if (state == NULL) { return NULL; } if (!isalpha(lc)) { return NULL; } buf[bufind++] = lc; for (;;) { if (bufind >= sizeof(buf) - 1) { printf("fatal: token exceeds maximum length\n"); return NULL; } c = lexer_nom(state, false); if (!isalnum(c) && c != '_') { lexer_putback(state, c); buf[bufind] = '\0'; return ptrbox_strdup(&state->ptrbox, buf); } buf[bufind++] = c; } return NULL; } /* * Check if a keyword matches against known keywords * * @kw: Keyword to check * @result: Result is written here * * Returns zero on success */ static int lexer_check_kw(const char *kw, struct token *result) { if (kw == NULL || result == NULL) { return -1; } switch (*kw) { case 'd': if (strcmp(kw, "define") == 0) { result->type = TT_DEFINE; return 0; } break; case 't': if (strcmp(kw, "true") == 0) { result->type = TT_TRUE; return 0; } break; case 'f': if (strcmp(kw, "false") == 0) { result->type = TT_FALSE; return 0; } break; } return -1; } int lexer_scan(struct defconf_state *state, struct token *result) { int c; char *ident; if (state == NULL || result == NULL) { return -1; } if ((c = lexer_nom(state, true)) == EOF) { return -1; } /* Scan for an identifier */ if ((ident = lexer_scan_ident(state, c)) != NULL) { if (lexer_check_kw(ident, result) == 0) { return 0; } result->type = TT_IDENT; result->s = ident; return 0; } printf("fatal: unexpected token '%c'\n", c); return -1; }