#include "collection.h" #include "parse.h" #include #include #include #include #include #include char* read_textfile(const char* filename) { FILE* fp = fopen(filename, "r"); if (!fp) { goto couldnt_read_file; } if (fseek(fp, 0, SEEK_END) != 0) { goto couldnt_read_file; } long file_size = ftell(fp); if (file_size < 0) { goto couldnt_read_file; } if (fseek(fp, 0, SEEK_SET) != 0) { goto couldnt_read_file; } char* buffer = malloc(file_size + 1); assert(buffer); if (fread(buffer, sizeof(char), file_size, fp) != (size_t)file_size) { goto couldnt_read_file; } buffer[file_size] = '\0'; return buffer; couldnt_read_file: fprintf(stderr, "error: couldn't read file '%s': %s\n", filename, strerror(errno)); return nullptr; } const char* tokty_str(TokTy ty) { switch (ty) { case Tok_Eof: return "Eof"; case Tok_Ident: return "Ident"; case Tok_Int: return "Int"; case Tok_Str: return "Str"; case Tok_Char: return "Char"; case Tok_Whitespace: return "Whitespace"; case Tok_HeaderName: return "HeaderName"; case Tok_Eol: return "Eol"; case Tok_Semicolon: return "Semicolon"; case Tok_LParen: return "LParen"; case Tok_RParen: return "RParen"; case Tok_LBrace: return "LBrace"; case Tok_RBrace: return "RBrace"; case Tok_Asterisk: return "Asterisk"; case Tok_Hashtag: return "Hashtag"; default: return ""; } } void source_iter_init(SourceIter* r, const char* text) { *r = (SourceIter) { .text = text, .i = 0, .line = 1, .col = 1, }; } Char source_iter_next(SourceIter* r) { char ch = r->text[r->i]; Loc loc = { r->line, 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, loc }; } void lsl_iter_init(LslIter* r, SourceIter* source_char_iter) { Char curr = source_iter_next(source_char_iter); *r = (LslIter) { .source_char_iter = source_char_iter, .curr = curr, }; } Char lsl_iter_next(LslIter* 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 curr = r->curr; r->curr = next; return curr; } void lexer_init(Lexer* r, LslIter* lsl_char_iter, Arena* arena) { Char curr = lsl_iter_next(lsl_char_iter); *r = (Lexer) { .lsl_iter = lsl_char_iter, .arena = arena, .curr = curr, .lex_whitespace = false, .lex_headername = false, }; } static Tok tok(Lexer* l, TokTy ty, const char* text, Loc loc) { char* text_dup = arena_alloc(l->arena, strlen(text) + 1); text_dup[0] = '\0'; strcpy(text_dup, text); return (Tok) { ty, text_dup, loc }; } 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_iter); } Tok lexer_next(Lexer* l) { Loc loc = l->curr.loc; char text[max_token_text_len] = ""; size_t text_len = 0; if (done(l)) { return tok(l, Tok_Eof, text, loc); } 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(l, Tok_Whitespace, text, loc); } 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(l, Tok_Ident, text, loc); } 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(l, Tok_Int, text, loc); } if (l->lex_headername && (l->curr.ch == '"' || l->curr.ch == '<')) { char ter = l->curr.ch == '<' ? '>' : '"'; step(l, text, &text_len); 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.loc.line); return lexer_next(l); } step(l, text, &text_len); return tok(l, Tok_HeaderName, text, loc); } 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.loc.line); return lexer_next(l); } step(l, text, &text_len); return tok(l, Tok_Str, text, loc); } 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.loc.line); return lexer_next(l); } step(l, text, &text_len); return tok(l, Tok_Char, text, loc); } if (strchr("\n;(){},*#", l->curr.ch)) { TokTy ty = (TokTy)l->curr.ch; step(l, text, &text_len); return tok(l, ty, text, loc); } fprintf(stderr, "error: illegal character '%c' on line %d\n", l->curr.ch, l->curr.loc.line); step(l, text, &text_len); return lexer_next(l); }