diff options
Diffstat (limited to 'pit/src/lexer.c')
| -rw-r--r-- | pit/src/lexer.c | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/pit/src/lexer.c b/pit/src/lexer.c new file mode 100644 index 0000000..a2b0c7d --- /dev/null +++ b/pit/src/lexer.c @@ -0,0 +1,139 @@ +#include <lcq/pit/utils.h> +#include <lcq/pit/lexer.h> + +const char *PIT_LEX_TOKEN_NAMES[PIT_LEX_TOKEN__SENTINEL] = { + /* [PIT_LEX_TOKEN_EOF] = */ "eof", + /* [PIT_LEX_TOKEN_LPAREN] = */ "lparen", + /* [PIT_LEX_TOKEN_RPAREN] = */ "rparen", + /* [PIT_LEX_TOKEN_LSQUARE] = */ "lsquare", + /* [PIT_LEX_TOKEN_RSQUARE] = */ "rsquare", + /* [PIT_LEX_TOKEN_DOT] = */ "dot", + /* [PIT_LEX_TOKEN_QUOTE] = */ "quote", + /* [PIT_LEX_TOKEN_INTEGER_LITERAL] = */ "integer_literal", + /* [PIT_LEX_TOKEN_STRING_LITERAL] = */ "string_literal", + /* [PIT_LEX_TOKEN_SYMBOL] = */ "symbol", +}; + +const char *pit_lex_token_name(pit_lex_token t) { + return PIT_LEX_TOKEN_NAMES[t]; +} + +static bool is_more_input(pit_lexer *st) { + return st && st->end < st->len; +} + +static int is_symchar(int c) { + return c != '(' && c != ')' && c != '[' && c != ']' && c != '.' && c != '\'' && c != '"' + && pit_libc_ctype_isprint(c) + && !pit_libc_ctype_isspace(c); +} + +static int is_hexdigit(int c) { + return pit_libc_ctype_isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} + +static char peek(pit_lexer *st) { + if (is_more_input(st)) return st->input[st->end]; + else return 0; +} + +static char advance(pit_lexer *st) { + if (is_more_input(st)) { + char ret = st->input[st->end++]; + if (ret == '\n') { + st->line += 1; + st->column = 0; + } else { + st->column += 1; + } + return ret; + } + else return 0; +} + +static bool match(pit_lexer *st, int (*f)(int)) { + if (f(peek(st))) { + advance(st); + return true; + } else return false; +} + +void pit_lex_bytes(pit_lexer *ret, char *buf, i64 len) { + ret->len = len; + ret->input = buf; + ret->start = 0; + ret->end = 0; + ret->line = ret->start_line = 1; + ret->column = ret->start_column = 0; + ret->error = NULL; +} + +pit_lex_token pit_lex_next(pit_lexer *st) { +restart: + st->start = st->end; + st->start_line = st->line; + st->start_column = st->column; + char c = advance(st); + switch (c) { + case 0: return PIT_LEX_TOKEN_EOF; + case ';': while (is_more_input(st) && advance(st) != '\n'); goto restart; + case '(': return PIT_LEX_TOKEN_LPAREN; + case ')': return PIT_LEX_TOKEN_RPAREN; + case '[': return PIT_LEX_TOKEN_LSQUARE; + case ']': return PIT_LEX_TOKEN_RSQUARE; + case '.': return PIT_LEX_TOKEN_DOT; + case '\'': return PIT_LEX_TOKEN_QUOTE; + case '"': + while (peek(st) != '"') { + if (peek(st) == '\\') advance(st); /* skip escaped characters */ + if (!advance(st)) { + st->error = "unterminated string"; + return PIT_LEX_TOKEN_ERROR; + } + } + advance(st); + return PIT_LEX_TOKEN_STRING_LITERAL; + default: { + if (pit_libc_ctype_isspace(c)) goto restart; + pit_lex_token ret = PIT_LEX_TOKEN_INTEGER_LITERAL; + int num_idx = 0; + bool hex = false; + bool leading_dash = false; + bool zero_prefix = false; + if (!is_symchar(c)) { + st->error = "unknown character"; + return PIT_LEX_TOKEN_ERROR; + } else { + do { + leading_dash = false; + switch (num_idx) { + case 0: + if (c == '0') zero_prefix = true; + else if (c == '-') { leading_dash = true; continue; } + break; + case 1: + if (zero_prefix) { + if (c == 'x') { hex = true; continue; } + else if (c == 'o' || c == 'b') continue; + } + break; + } + if (!(pit_libc_ctype_isdigit(c) || (hex && is_hexdigit(c)))) ret = PIT_LEX_TOKEN_SYMBOL; + ++num_idx; + } while (c = peek(st), match(st, is_symchar)); + } + if (leading_dash) return PIT_LEX_TOKEN_SYMBOL; + return ret; + } + } +} + +void pit_lex_cstr(pit_lexer *ret, char *buf) { + ret->input = buf; + ret->len = (i64) pit_libc_string_strlen(buf); + ret->start = 0; + ret->end = 0; + ret->line = ret->start_line = 1; + ret->column = ret->start_column = 0; + ret->error = NULL; +} |
