297 lines
6.9 KiB
C
297 lines
6.9 KiB
C
#include "collection.h"
|
|
#include "parse.h"
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
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";
|
|
case Tok_Comma:
|
|
return "Comma";
|
|
case Tok_KwInt:
|
|
return "KwInt";
|
|
case Tok_KwChar:
|
|
return "KwChar";
|
|
case Tok_KwConst:
|
|
return "KwConst";
|
|
case Tok_KwVoid:
|
|
return "KwVoid";
|
|
case Tok_EndOfMacroInvocation:
|
|
return "EndOfMacroInvocation";
|
|
}
|
|
return "<unknown>";
|
|
}
|
|
|
|
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);
|
|
}
|