larpcc/lex.c
2026-09-17 00:27:20 +02:00

204 lines
4.9 KiB
C

#include "parse.h"
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
void source_char_iter_init(SourceCharIter* r, const char* text)
{
*r = (SourceCharIter) {
.text = text,
.i = 0,
.line = 1,
.col = 1,
};
}
Char source_iter_next(SourceCharIter* r)
{
char ch = r->text[r->i];
int line = r->line;
int col = r->col;
if (r->text[r->i] != '\0') {
if (ch == '\n') {
r->line += 1;
r->col = 1;
} else {
r->col += 1;
}
r->i += 1;
}
return (Char) { ch, line, col };
}
void lsl_iter_init(LslCharIter* r, SourceCharIter* source_char_iter)
{
Char curr = source_iter_next(source_char_iter);
*r = (LslCharIter) {
.source_char_iter = source_char_iter,
.curr = curr,
};
}
Char lsl_iter_next(LslCharIter* r)
{
Char next = source_iter_next(r->source_char_iter);
if (r->curr.ch == '\\' && next.ch == '\n') {
r->curr = source_iter_next(r->source_char_iter);
return lsl_iter_next(r);
}
Char lsl_char = { r->curr.ch, r->curr.line, r->curr.col };
r->curr = next;
return lsl_char;
}
void lexer_init(Lexer* r, LslCharIter* lsl_char_iter)
{
Char curr = lsl_iter_next(lsl_char_iter);
*r = (Lexer) {
.lsl_char_iter = lsl_char_iter,
.curr = curr,
.lex_whitespace = false,
.lex_headername = false,
};
}
static Tok tok(TokTy ty, const char* text, int line, int col)
{
return (Tok) { ty, strdup(text), line, col };
}
static bool done(Lexer* l)
{
return l->curr.ch == '\0';
}
constexpr size_t max_token_text_len = 64;
static void step(Lexer* l, char text[], size_t* text_len)
{
assert(*text_len < max_token_text_len);
text[*text_len] = l->curr.ch;
*text_len += 1;
l->curr = lsl_iter_next(l->lsl_char_iter);
}
Tok lexer_next(Lexer* l)
{
int line = l->curr.line;
int col = l->curr.col;
char text[max_token_text_len] = "";
size_t text_len = 0;
if (done(l)) {
return tok(Tok_Eof, text, line, col);
}
bool matched = false;
while (strchr(" \t\r", l->curr.ch)) {
matched = true;
step(l, text, &text_len);
}
if (matched) {
if (l->lex_whitespace) {
return tok(Tok_Whitespace, text, line, col);
}
return lexer_next(l);
}
while ((l->curr.ch >= 'a' && l->curr.ch <= 'z')
|| (l->curr.ch >= 'A' && l->curr.ch <= 'Z')
|| (matched && l->curr.ch >= '0' && l->curr.ch <= '9')
|| strchr("_", l->curr.ch)) {
matched = true;
step(l, text, &text_len);
}
if (matched) {
return tok(Tok_Ident, text, line, col);
}
while ((l->curr.ch >= '1' && l->curr.ch <= '9')
|| (matched && l->curr.ch == '0')) {
matched = true;
step(l, text, &text_len);
}
if (matched) {
return tok(Tok_Int, text, line, col);
}
if (l->lex_headername && (l->curr.ch == '"' || l->curr.ch == '<')) {
char ter = l->curr.ch == '<' ? '>' : '"';
while (l->curr.ch != '\0' && l->curr.ch != '\n' && l->curr.ch != ter) {
step(l, text, &text_len);
}
if (l->curr.ch != ter) {
fprintf(stderr,
"error: malformed header name, expected '%c' on line %d\n",
ter,
l->curr.line);
return lexer_next(l);
}
step(l, text, &text_len);
return tok(Tok_HeaderName, text, line, col);
}
if (l->curr.ch == '"') {
step(l, text, &text_len);
while (l->curr.ch != '"') {
if (l->curr.ch == '\\') {
step(l, text, &text_len);
if (done(l))
break;
}
step(l, text, &text_len);
}
if (done(l) || l->curr.ch != '"') {
fprintf(stderr,
"error: malformed string literal on line %d\n",
l->curr.line);
return lexer_next(l);
}
step(l, text, &text_len);
return tok(Tok_Str, text, line, col);
}
if (l->curr.ch == '\'') {
step(l, text, &text_len);
if (l->curr.ch == '\\') {
step(l, text, &text_len);
}
if (!done(l)) {
step(l, text, &text_len);
}
if (done(l) && l->curr.ch != '\'') {
fprintf(stderr,
"error: malformed character literal on line %d\n",
l->curr.line);
return lexer_next(l);
}
step(l, text, &text_len);
return tok(Tok_Char, text, line, col);
}
if (strchr("\n;(){}#", l->curr.ch)) {
TokTy ty = (TokTy)l->curr.ch;
step(l, text, &text_len);
return tok(ty, text, line, col);
}
fprintf(stderr,
"error: illegal character '%c' on line %d\n",
l->curr.ch,
l->curr.line);
step(l, text, &text_len);
return lexer_next(l);
}