diff --git a/examples/hello.c b/examples/hello.c new file mode 100644 index 0000000..7583bb3 --- /dev/null +++ b/examples/hello.c @@ -0,0 +1,6 @@ +#include + +int main() +{ + printf("hello\n"); +} diff --git a/parse.c b/parse.c new file mode 100644 index 0000000..6119f43 --- /dev/null +++ b/parse.c @@ -0,0 +1,184 @@ +#include "parse.h" +#include +#include +#include +#include + +// phases: +// https://en.cppreference.com/c/language/translation_phases + +// phase 1 - source chars + +void source_char_iter_init(SourceCharIter* r, const char* text) +{ + *r = (SourceCharIter) { + .text = text, + .i = 0, + .line = 1, + .col = 1, + }; +} + +SourceChar 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 (SourceChar) { ch, line, col }; +} + +// phase 2 - logical source lines + +void lsl_iter_init(LslCharIter* r, SourceCharIter* source_char_iter) +{ + SourceChar curr = source_iter_next(source_char_iter); + + *r = (LslCharIter) { + .source_char_iter = source_char_iter, + .curr = curr, + }; +} + +LslChar lsl_iter_next(LslCharIter* r) +{ + SourceChar 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); + } + + LslChar lsl_char = { r->curr.ch, r->curr.line, r->curr.col }; + + r->curr = next; + return lsl_char; +} + +// phase 3 - preprocessor tokens + +void lexer_init(Lexer* r, LslCharIter* lsl_char_iter) +{ + LslChar curr = lsl_iter_next(lsl_char_iter); + + *r = (Lexer) { + .lsl_char_iter = lsl_char_iter, + .curr = curr, + }; +} + +static Tok tok(TokTy ty, const char* text, int line, int col) +{ + return (Tok) { ty, strdup(text), line, col }; +} + +static bool l_done(Lexer* l) +{ + return l->curr.ch == '\0'; +} + +constexpr size_t max_token_text_len = 64; + +static void l_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 (l_done(l)) { + return tok(Tok_Eof, text, line, col); + } + + bool matched = false; + while (strchr(" \t\r", l->curr.ch)) { + matched = true; + l_step(l, text, &text_len); + } + if (matched) { + 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; + l_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; + l_step(l, text, &text_len); + } + if (matched) { + return tok(Tok_Int, text, line, col); + } + + if (l->curr.ch == '"') { + l_step(l, text, &text_len); + while (l->curr.ch != '"') { + if (l->curr.ch == '\\') { + l_step(l, text, &text_len); + if (l_done(l)) + break; + } + l_step(l, text, &text_len); + } + if (l_done(l) || l->curr.ch != '"') { + fprintf(stderr, "error: malformed string literal\n"); + return lexer_next(l); + } + l_step(l, text, &text_len); + return tok(Tok_Str, text, line, col); + } + + if (l->curr.ch == '\'') { + l_step(l, text, &text_len); + if (l->curr.ch == '\\') { + l_step(l, text, &text_len); + } + if (!l_done(l)) { + l_step(l, text, &text_len); + } + if (l_done(l) && l->curr.ch != '\'') { + fprintf(stderr, "error: malformed character literal\n"); + return lexer_next(l); + } + 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; + l_step(l, text, &text_len); + return tok(ty, text, line, col); + } + fprintf(stderr, "error: illegal character '%c'\n", l->curr.ch); + l_step(l, text, &text_len); + return lexer_next(l); +} diff --git a/parse.h b/parse.h new file mode 100644 index 0000000..b77b871 --- /dev/null +++ b/parse.h @@ -0,0 +1,67 @@ +#ifndef PARSE_H +#define PARSE_H + +#include + +typedef struct { + char ch; + int line; + int col; +} SourceChar; + +typedef struct { + const char* text; + size_t i; + int line; + int col; +} SourceCharIter; + +void source_char_iter_init(SourceCharIter* r, const char* text); +SourceChar source_iter_next(SourceCharIter* r); + +typedef struct { + char ch; + int line; + int col; +} LslChar; + +typedef struct { + SourceCharIter* source_char_iter; + SourceChar curr; +} LslCharIter; + +void lsl_iter_init(LslCharIter* r, SourceCharIter* source_char_iter); +LslChar lsl_iter_next(LslCharIter* r); + +typedef enum { + Tok_Eof, + Tok_Eol, + Tok_Ident, + Tok_Int, + Tok_Str, + Tok_Char, + Tok_HeaderName, + Tok_Newline = '\n', // NOTE: has value 10 + Tok_Semicolon = ';', + Tok_LParen = '(', + Tok_RParen = ')', + Tok_LBrace = '{', + Tok_RBrace = '}', +} TokTy; + +typedef struct { + TokTy ty; + char* text; + int line; + int col; +} Tok; + +typedef struct { + LslCharIter* lsl_char_iter; + LslChar curr; +} Lexer; + +void lexer_init(Lexer* r, LslCharIter* lsl_char_iter); +Tok lexer_next(Lexer* l); + +#endif