diff --git a/Makefile b/Makefile index 1b9f97e..26816aa 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -larpcc: main.c lex.c preproc.c collection.c parse.c syms.c - gcc $^ -o $@ -std=c23 -pedantic -Wall -Wextra -g -fsanitize=address +larpcc: main.c lex.c preproc.c collection.c parse.c syms.c ast_str.c + gcc $^ -o $@ -std=c23 -pedantic -Wall -Wextra -g -fsanitize=address -Werror=switch diff --git a/ast.h b/ast.h index 99f57de..08f525f 100644 --- a/ast.h +++ b/ast.h @@ -57,6 +57,7 @@ typedef enum : unsigned int { struct AstDecl { AstSpec spec; + char* type_ident; ARRAY_FIELD(AstDeclar*, declars); AstStmtBlock* function_body; Loc loc; @@ -84,7 +85,8 @@ struct AstDeclar { AstExpr* expr; } array; struct { - ARRAY_FIELD(AstDecl, params); + AstDeclar* declar; + ARRAY_FIELD(AstDecl*, params); } function; }; }; @@ -96,6 +98,7 @@ typedef enum { struct AstExpr { AstExprTy ty; + Loc loc; union { char* str_val; struct { @@ -105,4 +108,17 @@ struct AstExpr { }; }; +typedef struct { + ARRAY_FIELD(char, string); + Arena* arena; + int depth; +} AstStringifier; + +void ast_stringifier_init(AstStringifier* s, Arena* arena); +void ast_stmt_block_to_string(AstStringifier* s, const AstStmtBlock* node); +void ast_stmt_to_string(AstStringifier* s, const AstStmt* node); +void ast_decl_to_string(AstStringifier* s, const AstDecl* node); +void ast_declar_to_string(AstStringifier* s, const AstDeclar* node); +void ast_expr_to_string(AstStringifier* s, const AstExpr* node); + #endif diff --git a/ast_str.c b/ast_str.c new file mode 100644 index 0000000..0feca1e --- /dev/null +++ b/ast_str.c @@ -0,0 +1,283 @@ +#include "ast.h" +#include "collection.h" +#include +#include +#include + +static void push(AstStringifier* s, const char* str) +{ + for (; *str; ++str) { + arena_array_push(s->arena, + (void**)&s->string, + &s->string_cap, + &s->string_size, + (void*)str, + sizeof(*str)); + } + + char ch = '\0'; + arena_array_push(s->arena, + (void**)&s->string, + &s->string_cap, + &s->string_size, + (void*)&ch, + sizeof(ch)); + s->string_size -= 1; +} + +#define pushf(S, ...) \ + do { \ + size_t len = snprintf(nullptr, 0, __VA_ARGS__); \ + char* buf = calloc(len + 1, 1); \ + snprintf(buf, len + 1, __VA_ARGS__); \ + push(S, buf); \ + free(buf); \ + } while (false) + +static void indent(AstStringifier* s) +{ + for (int i = 0; i < s->depth; ++i) { + push(s, " "); + } +} + +void ast_stringifier_init(AstStringifier* s, Arena* arena) +{ + *s = (AstStringifier) { + ARRAY_FIELD_INIT(string), + .arena = arena, + .depth = 0, + }; +} + +void ast_stmt_block_to_string(AstStringifier* s, const AstStmtBlock* node) +{ + pushf(s, "StmtBlock %d:%d {\n", node->loc.line, node->loc.col); + s->depth += 1; + indent(s); + push(s, "stmts: ["); + + if (node->stmts_size) { + s->depth += 1; + push(s, "\n"); + for (size_t i = 0; i < node->stmts_size; ++i) { + indent(s); + ast_stmt_to_string(s, node->stmts[i]); + push(s, ",\n"); + } + s->depth -= 1; + indent(s); + } + push(s, "],"); + + s->depth -= 1; + indent(s); + push(s, "}"); +} + +void ast_stmt_to_string(AstStringifier* s, const AstStmt* node) +{ + switch (node->ty) { + case AstStmt_Decl: { + pushf(s, "Stmt::Decl %d:%d {\n", node->loc.line, node->loc.col); + s->depth += 1; + + indent(s); + push(s, "decl: "); + ast_decl_to_string(s, node->decl); + push(s, ",\n"); + + s->depth -= 1; + indent(s); + push(s, "}"); + break; + } + case AstStmt_Expr: { + pushf(s, "Stmt::Expr %d:%d {\n", node->loc.line, node->loc.col); + s->depth += 1; + indent(s); + + push(s, "expr: "); + + ast_expr_to_string(s, node->expr); + push(s, ",\n"); + + s->depth -= 1; + indent(s); + push(s, "}"); + break; + } + } +} + +static void spec_to_string(AstStringifier* s, AstSpec spec) +{ + struct { + AstSpec specifier; + const char* ident; + } spec_idents[] = { + { AstSpec_Int, "int" }, + { AstSpec_Char, "char" }, + { AstSpec_Unsigned, "unsigned" }, + { AstSpec_Static, "static" }, + { AstSpec_Const, "const" }, + { AstSpec_Inline, "inline" }, + { AstSpec_Typedef, "typedef" }, + { AstSpec_Enum, "enum" }, + { AstSpec_Struct, "struct" }, + { AstSpec_Union, "union" }, + }; + + bool first = true; + + for (size_t i = 0; i < sizeof(spec_idents) / sizeof(spec_idents[0]); ++i) { + if (spec & spec_idents[i].specifier) { + if (!first) + push(s, " "); + first = false; + push(s, spec_idents[i].ident); + } + } +} + +void ast_decl_to_string(AstStringifier* s, const AstDecl* node) +{ + pushf(s, "Decl %d:%d {\n", node->loc.line, node->loc.col); + s->depth += 1; + + indent(s); + push(s, "spec: ["); + spec_to_string(s, node->spec); + push(s, "],\n"); + + if (node->spec & AstSpec_DefTy) { + assert(node->type_ident); + indent(s); + pushf(s, "type_ident: \"%s\",\n", node->type_ident); + } + + indent(s); + push(s, "declars: ["); + if (node->declars_size) { + push(s, "\n"); + s->depth += 1; + + for (size_t i = 0; i < node->declars_size; ++i) { + indent(s); + ast_declar_to_string(s, node->declars[i]); + push(s, ",\n"); + } + + s->depth -= 1; + indent(s); + push(s, "],\n"); + } else { + push(s, "],\n"); + } + + if (node->function_body) { + indent(s); + push(s, "function_body: "); + ast_stmt_block_to_string(s, node->function_body); + push(s, ",\n"); + } + + s->depth -= 1; + indent(s); + push(s, "}"); +} + +void ast_declar_to_string(AstStringifier* s, const AstDeclar* node) +{ + switch (node->ty) { + case AstDeclar_Ident: { + pushf(s, "Declar::Ident %d:%d {\n", node->loc.line, node->loc.col); + s->depth += 1; + + indent(s); + pushf(s, "ident: \"%s\",\n", node->ident); + + break; + } + case AstDeclar_Pointer: { + pushf( + s, "Declar::Pointer %d:%d {\n", node->loc.line, node->loc.col); + s->depth += 1; + + indent(s); + push(s, "declar: "); + ast_declar_to_string(s, node->pointer.declar); + push(s, ",\n"); + + indent(s); + push(s, "spec: ["); + spec_to_string(s, node->pointer.spec); + push(s, "],\n"); + + break; + } + case AstDeclar_Array: { + pushf(s, "Declar::Array %d:%d {\n", node->loc.line, node->loc.col); + s->depth += 1; + + indent(s); + push(s, "declar: "); + ast_declar_to_string(s, node->array.declar); + push(s, ",\n"); + + indent(s); + push(s, "expr: "); + ast_expr_to_string(s, node->array.expr); + push(s, ",\n"); + + break; + } + case AstDeclar_Function: { + pushf( + s, "Declar::Function %d:%d {\n", node->loc.line, node->loc.col); + s->depth += 1; + + indent(s); + push(s, "declar: "); + ast_declar_to_string(s, node->array.declar); + push(s, ",\n"); + + indent(s); + push(s, "params: ["); + if (node->function.params_size) { + push(s, "\n"); + s->depth += 1; + + for (size_t i = 0; i < node->function.params_size; ++i) { + indent(s); + ast_decl_to_string(s, node->function.params[i]); + push(s, ",\n"); + } + + s->depth -= 1; + indent(s); + push(s, "],\n"); + } else { + push(s, "],\n"); + } + + break; + } + } + + if (node->init_expr) { + indent(s); + push(s, "decl: "); + ast_expr_to_string(s, node->init_expr); + push(s, ",\n"); + } + + s->depth -= 1; + indent(s); + push(s, "}"); +} + +void ast_expr_to_string(AstStringifier* s, const AstExpr* node) +{ + push(s, ""); +} diff --git a/collection.h b/collection.h index e2044d9..c49f389 100644 --- a/collection.h +++ b/collection.h @@ -76,4 +76,20 @@ void arena_array_push(Arena* arena, char* arena_strdup(Arena* a, const char* str); +#define ARENA_ARRAY_FIELD_PUSH(ARENA, STRUCT_PTR, FIELD_NAME, ELEM_PTR) \ + arena_array_push((ARENA), \ + (void**)&(STRUCT_PTR)->FIELD_NAME, \ + &(STRUCT_PTR)->FIELD_NAME##_cap, \ + &(STRUCT_PTR)->FIELD_NAME##_size, \ + (ELEM_PTR), \ + sizeof(*(ELEM_PTR))) + +#define ARENA_ARRAY_VAR_PUSH(ARENA, VAR_NAME, ELEM_PTR) \ + arena_array_push((ARENA), \ + (void**)&VAR_NAME, \ + &VAR_NAME##_cap, \ + &VAR_NAME##_size, \ + (ELEM_PTR), \ + sizeof(*(ELEM_PTR))) + #endif diff --git a/compile_flags.txt b/compile_flags.txt index afde1f1..05ed4cb 100644 --- a/compile_flags.txt +++ b/compile_flags.txt @@ -2,5 +2,5 @@ -pedantic-errors -Wall -Wextra - +-Werror=switch diff --git a/lex.c b/lex.c index 3e64bb5..620aefd 100644 --- a/lex.c +++ b/lex.c @@ -78,9 +78,18 @@ const char* tokty_str(TokTy ty) return "Asterisk"; case Tok_Hashtag: return "Hashtag"; - default: - return ""; + case Tok_Comma: + return "Comma"; + case Tok_KwInt: + return "KwInt"; + case Tok_KwChar: + return "KwChar"; + case Tok_KwConst: + return "KwConst"; + case Tok_EndOfMacroInvocation: + return "EndOfMacroInvocation"; } + return ""; } void source_iter_init(SourceIter* r, const char* text) diff --git a/main.c b/main.c index 913a264..a6cfaa8 100644 --- a/main.c +++ b/main.c @@ -1,3 +1,4 @@ +#include "ast.h" #include "collection.h" #include "parse.h" #include @@ -20,39 +21,83 @@ int main(int argc, char* argv[]) printf("--- text ---\n%s\n", text); - Arena tok_arena; - arena_init(&tok_arena, 4096); + { - Preproc* preproc = preproc_new(strdup(filename), text, &tok_arena); + Arena tok_arena; + arena_init(&tok_arena, 4096); - TokStream tokstream; - tokstream_init(&tokstream, preproc, &tok_arena); + Preproc* preproc + = preproc_new(strdup(filename), strdup(text), &tok_arena); - printf("--- tokens ---\n"); + TokStream tokstream; + tokstream_init(&tokstream, preproc, &tok_arena); - Loc loc = { 1, 1 }; - Tok tok; - while ((tok = tokstream_next(&tokstream)).ty != Tok_Eof) { - // printf("%s\t%s\n", tokty_str(tok.ty), tok.text); - Loc tok_loc = tok.loc; - if (loc.line > tok_loc.line) { - loc.line = 0; + printf("--- tokens ---\n"); + + Loc loc = { 1, 1 }; + Tok tok; + while ((tok = tokstream_next(&tokstream)).ty != Tok_Eof) { + // printf("%s\t%s\n", tokty_str(tok.ty), tok.text); + Loc tok_loc = tok.loc; + if (loc.line > tok_loc.line) { + loc.line = 0; + } + while (loc.line < tok_loc.line) { + printf("\n"); + loc.line += 1; + loc.col = 1; + } + while (loc.col < tok_loc.col) { + printf(" "); + loc.col += 1; + } + printf("%s", tok.text); + loc.line = tok_loc.line; + loc.col = tok_loc.col + strlen(tok.text); } - while (loc.line < tok_loc.line) { - printf("\n"); - loc.line += 1; - loc.col = 1; - } - while (loc.col < tok_loc.col) { - printf(" "); - loc.col += 1; - } - printf("%s", tok.text); - loc.line = tok_loc.line; - loc.col = tok_loc.col + strlen(tok.text); + fputs("\n", stdout); + + preproc_free(preproc); + arena_deinit(&tok_arena); } - fputs("\n", stdout); - preproc_free(preproc); - arena_deinit(&tok_arena); + { + Arena tok_arena; + arena_init(&tok_arena, 4096); + + Preproc* preproc = preproc_new(strdup(filename), text, &tok_arena); + + TokStream tokstream; + tokstream_init(&tokstream, preproc, &tok_arena); + + Arena ast_arena; + arena_init(&ast_arena, 4096); + + printf("--- ast ---\n"); + + Parser* parser = parser_new(&tokstream, &ast_arena); + + AstStmt* stmt; + while (!parser_done(parser)) { + stmt = parser_parse_stmt(parser); + + Arena s_arena; + arena_init(&s_arena, 4096); + AstStringifier s = { }; + ast_stringifier_init(&s, &s_arena); + + ast_stmt_to_string(&s, stmt); + + printf("%s\n", s.string); + + arena_deinit(&s_arena); + } + + parser_free(parser); + + preproc_free(preproc); + arena_deinit(&tok_arena); + + arena_deinit(&ast_arena); + } } diff --git a/parse.c b/parse.c index 99dae39..0034254 100644 --- a/parse.c +++ b/parse.c @@ -6,6 +6,7 @@ #include #include #include +#include typedef struct { char ch; @@ -22,17 +23,34 @@ typedef struct { TokStream* tokstream; Arena arena; ARRAY_FIELD(KwNode, nodes); - KwNode* root; } KwMatcher; +static void kw_tree_print(KwMatcher* m, KwNode* node, int depth) +{ + for (size_t i = 0; i < node->opts_size; ++i) { + for (int i = 0; i < depth; ++i) + printf(" "); + printf("%c", node->opts[i].ch); + if (node->opts[i].is_term) { + printf(" = %s\n", tokty_str(node->opts[i].val)); + } else { + printf("\n"); + kw_tree_print(m, &m->nodes[node->opts[i].val], depth + 1); + } + } +} + static KwNodeOpt* kwmatcher_create_path( - KwMatcher* m, KwNode* node, const char* kw) + KwMatcher* m, size_t node_idx, const char* kw) { assert(kw[0]); + KwNode* node = &m->nodes[node_idx]; + KwNodeOpt* opt = nullptr; for (size_t i = 0; i < node->opts_size; ++i) { if (node->opts[i].ch == kw[0]) { opt = &node->opts[i]; + break; } } if (!opt) { @@ -48,7 +66,8 @@ static KwNodeOpt* kwmatcher_create_path( &m->nodes_size, &new_node, sizeof(new_node)); - KwNode* node = &m->nodes[new_idx]; + // might have been relocated + node = &m->nodes[node_idx]; KwNodeOpt new_opt = { .ch = kw[0], @@ -67,13 +86,12 @@ static KwNodeOpt* kwmatcher_create_path( if (!kw[1]) return opt; - KwNode* next_node = &m->nodes[opt->val]; - return kwmatcher_create_path(m, next_node, &kw[1]); + return kwmatcher_create_path(m, opt->val, &kw[1]); } static void kwmatcher_add(KwMatcher* m, const char* kw, TokTy tokty) { - KwNodeOpt* opt = kwmatcher_create_path(m, m->root, kw); + KwNodeOpt* opt = kwmatcher_create_path(m, 0, kw); opt->is_term = true; opt->val = tokty; } @@ -97,8 +115,8 @@ static KwNodeOpt* kwmatcher_find_node( static TokTy kwmatcher_match(KwMatcher* m, const char* kw) { - KwNodeOpt* opt = kwmatcher_find_node(m, m->root, kw); - if (!opt->is_term) + KwNodeOpt* opt = kwmatcher_find_node(m, &m->nodes[0], kw); + if (!opt || !opt->is_term) return Tok_Ident; return opt->val; } @@ -109,7 +127,6 @@ static void kwmatcher_init(KwMatcher* m, TokStream* tokstream) .tokstream = tokstream, .arena = { }, ARRAY_FIELD_INIT(nodes), - .root = nullptr, }; arena_init(&m->arena, 512); @@ -121,8 +138,6 @@ static void kwmatcher_init(KwMatcher* m, TokStream* tokstream) (&(KwNode) { .idx = 0 }), sizeof(*(&(KwNode) { .idx = 0 }))); - m->root = &m->nodes[0]; - struct { TokTy ty; const char* kw; @@ -135,6 +150,8 @@ static void kwmatcher_init(KwMatcher* m, TokStream* tokstream) for (size_t i = 0; i < sizeof(kws) / sizeof(kws[0]); ++i) { kwmatcher_add(m, kws[i].kw, kws[i].ty); } + + kw_tree_print(m, &m->nodes[0], 0); } static void kwmatcher_deinit(KwMatcher* m) @@ -146,11 +163,18 @@ static Tok kwmatcher_next(KwMatcher* m) { Tok tok = tokstream_next(m->tokstream); if (tok.ty != Tok_Ident) - return tok; + goto exit; TokTy ty = kwmatcher_match(m, tok.text); if (ty != Tok_Ident) printf("info: matched '%s' on line %d\n", tok.text, tok.loc.line); tok.ty = ty; + +exit: + printf(" - %s(%s) %d:%d\n", + tokty_str(tok.ty), + tok.text, + tok.loc.line, + tok.loc.col); return tok; } @@ -210,6 +234,47 @@ static void step(Parser* p) DEFINE_NEW(AstStmt, stmt) DEFINE_NEW(AstDecl, decl) DEFINE_NEW(AstDeclar, declar) +DEFINE_NEW(AstExpr, expr) + +DEFINE_NEW(AstStmtBlock, stmt_block) + +static AstStmtBlock* parse_stmt_block(Parser* p); +static AstDecl* parse_decl(Parser* p); +static AstDeclar* parse_declar(Parser* p); +static AstSpec parse_decl_spec(Parser* p); +static AstExpr* parse_expr(Parser* p); + +static AstStmtBlock* parse_stmt_block(Parser* p) +{ + Loc loc = p->curr.loc; + assert(p->curr.ty == '{'); + + ARRAY_VAR(AstStmt*, stmts); + + while (p->curr.ty != Tok_Eof && p->curr.ty != '}') { + AstStmt* stmt = parser_parse_stmt(p); + arena_array_push(p->arena, + (void**)&stmts, + &stmts_cap, + &stmts_size, + (&stmt), + sizeof(*(&stmt))); + } + + if (p->curr.ty != '}') { + fprintf(stderr, "error: expected '}' on line %d\n", p->curr.loc.line); + exit(1); + } + step(p); + + return new_stmt_block(p, + (AstStmtBlock) { + .stmts = stmts, + .stmts_cap = stmts_cap, + .stmts_size = stmts_size, + .loc = loc, + }); +} static bool is_decl_start_kw(TokTy ty) { @@ -231,12 +296,19 @@ static bool is_defty_ident(Parser* p, Tok tok) return sym && sym->ty == Sym_Typedef; } -AstStmt* parser_parse_stmt(Parser* p) +static AstStmt* parse_stmt(Parser* p) { Loc loc = p->curr.loc; if (is_decl_start_kw(p->curr.ty) || is_defty_ident(p, p->curr)) { - AstDecl* decl = parser_parse_decl(p); + AstDecl* decl = parse_decl(p); + + if (!decl->function_body && p->curr.ty != ';') { + fprintf( + stderr, "error: expected ';' on line %d\n", p->curr.loc.line); + exit(1); + } + step(p); return new_stmt(p, (AstStmt) { @@ -246,7 +318,7 @@ AstStmt* parser_parse_stmt(Parser* p) }); } - AstExpr* expr = parser_parse_expr(p); + AstExpr* expr = parse_expr(p); return new_stmt(p, (AstStmt) { @@ -256,12 +328,7 @@ AstStmt* parser_parse_stmt(Parser* p) }); } -static AstDeclar* parse_declar(Parser* p); -static AstSpec parse_decl_spec(Parser* p); - -static AstStmtBlock* parse_stmt_block(Parser* p); - -AstDecl* parser_parse_decl(Parser* p) +static AstDecl* parse_decl(Parser* p) { Loc loc = p->curr.loc; @@ -271,11 +338,7 @@ AstDecl* parser_parse_decl(Parser* p) AstDeclar* first = parse_declar(p); - array_push((void**)&declars, - &declars_cap, - &declars_size, - (&first), - sizeof(*(&first))); + ARENA_ARRAY_VAR_PUSH(p->arena, declars, &first); if (first->ty == AstDeclar_Function && p->curr.ty == '{') { AstStmtBlock* block = parse_stmt_block(p); @@ -316,6 +379,7 @@ AstDecl* parser_parse_decl(Parser* p) static AstDeclar* parse_declar_l1(Parser* p); static AstDeclar* parse_declar_l2(Parser* p); static AstDeclar* parse_declar_l3(Parser* p); +static AstDeclar* parse_declar_l4(Parser* p); static AstDeclar* parse_declar(Parser* p) { @@ -323,7 +387,7 @@ static AstDeclar* parse_declar(Parser* p) if (p->curr.ty == '=') { step(p); - AstExpr* expr = parser_parse_expr(p); + AstExpr* expr = parse_expr(p); declar->init_expr = expr; } @@ -364,7 +428,7 @@ static AstDeclar* parse_declar_l2(Parser* p) step(p); AstExpr* expr = nullptr; if (p->curr.ty != ']') { - expr = parser_parse_expr(p); + expr = parse_expr(p); } if (p->curr.ty != ']') { fprintf( @@ -390,6 +454,49 @@ static AstDeclar* parse_declar_l3(Parser* p) { Loc loc = p->curr.loc; + AstDeclar* declar = parse_declar_l4(p); + + if (p->curr.ty == '(') { + step(p); + + ARRAY_VAR(AstDecl*, params); + + while (true) { + AstDecl* d = parse_decl(p); + ARENA_ARRAY_VAR_PUSH(p->arena, params, &d); + + if (p->curr.ty != ',') + break; + step(p); + } + + if (p->curr.ty != ')') { + fprintf( + stderr, "error: expected ')' on line %d\n", p->curr.loc.line); + exit(1); + } + step(p); + + declar = new_declar(p, + (AstDeclar) { + .ty = AstDeclar_Function, + .loc = loc, + .function = { + .declar = declar, + .params = params, + .params_cap = params_cap, + .params_size = params_size, + }, + }); + } + + return declar; +} + +static AstDeclar* parse_declar_l4(Parser* p) +{ + Loc loc = p->curr.loc; + if (p->curr.ty == Tok_Ident) { char* ident = arena_strdup(p->arena, p->curr.text); step(p); @@ -413,44 +520,7 @@ static AstDeclar* parse_declar_l3(Parser* p) } step(p); - if (p->curr.ty != '(') { - return declar; - } - step(p); - - ARRAY_VAR(AstDecl, params); - - while (true) { - AstDecl* d = parser_parse_decl(p); - arena_array_push(p->arena, - (void**)¶ms, - ¶ms_cap, - ¶ms_size, - (&d), - sizeof(*(&d))); - - if (p->curr.ty != ',') - break; - step(p); - } - - if (p->curr.ty != '(') { - fprintf( - stderr, "error: expected '(' on line %d\n", p->curr.loc.line); - exit(1); - } - step(p); - - return new_declar(p, - (AstDeclar) { - .ty = AstDeclar_Function, - .loc = loc, - .function = { - .params = params, - .params_cap = params_cap, - .params_size = params_size, - }, - }); + return declar; } fprintf( @@ -466,7 +536,7 @@ static AstSpec parse_decl_spec(Parser* p) switch (p->curr.ty) { case Tok_Ident: { Sym* sym = syms_get(p->ord_ns, p->curr.text); - if (sym->ty != Sym_Typedef) + if (!sym || sym->ty != Sym_Typedef) break; spec |= AstSpec_DefTy; step(p); @@ -493,11 +563,139 @@ static AstSpec parse_decl_spec(Parser* p) return spec; } -static AstStmtBlock* parse_stmt_block(Parser* p) +static AstExpr* parse_expr_1(Parser* p); +static AstExpr* parse_expr_2(Parser* p); + +static AstExpr* parse_expr(Parser* p) { - Loc loc = p->curr.loc; + return parse_expr_1(p); } -AstExpr* parser_parse_expr(Parser* p) +static AstExpr* parse_expr_1(Parser* p) { + Loc loc = p->curr.loc; + + AstExpr* expr = parse_expr_2(p); + + while (true) { + + if (p->curr.ty == '(') { + step(p); + + ARRAY_VAR(AstExpr*, args); + + if (p->curr.ty != ')') { + do { + step(p); + AstExpr* arg = parse_expr(p); + ARRAY_VAR_PUSH(args, &arg); + } while (p->curr.ty == ','); + } + + if (p->curr.ty != ')') { + fprintf(stderr, + "error: expected ')' on line %d\n", + p->curr.loc.line); + exit(1); + } + step(p); + + expr = new_expr(p, + (AstExpr) { + .ty = AstExpr_Call, + .loc = loc, + .call = { + .expr = expr, + .args = args, + .args_cap = args_cap, + .args_size = args_size, + }, + }); + } + + break; + } + + return expr; +} + +static size_t unescape_string(char* dest, const char* src) +{ + size_t dest_i = 0; + size_t src_len = strlen(src); + + assert(src[0] == '"' && src[src_len - 1] == '"'); + + src -= 1; + src_len -= 2; + + for (size_t i = 0; i < src_len; ++i) { + char ch = src[i]; + + char dest_ch; + if (ch == '\\') { + i += 1; + assert(i < src_len); + ch = src[i]; + + switch (ch) { + case 't': + dest_ch = '\t'; + break; + case 'r': + dest_ch = '\r'; + break; + case 'n': + dest_ch = '\n'; + break; + case '0': + dest_ch = '\0'; + break; + default: + dest_ch = ch; + } + + } else { + dest_ch = ch; + } + if (dest) + dest[dest_i] = dest_ch; + dest_i += 1; + } + + return dest_i; +} + +static AstExpr* parse_expr_2(Parser* p) +{ + Loc loc = p->curr.loc; + + if (p->curr.ty == Tok_Str) { + size_t unescaped_len = unescape_string(nullptr, p->curr.text); + char* unescaped_str = arena_alloc(p->arena, unescaped_len + 1); + unescape_string(unescaped_str, p->curr.text); + + step(p); + + return new_expr(p, + (AstExpr) { + .ty = AstExpr_Str, + .loc = loc, + .str_val = unescaped_str, + }); + } + + fprintf( + stderr, "error: expected expression on line %d\n", p->curr.loc.line); + exit(1); +} + +AstStmt* parser_parse_stmt(Parser* p) +{ + return parse_stmt(p); +} + +bool parser_done(Parser* p) +{ + return p->curr.ty == Tok_Eof; } diff --git a/parse.h b/parse.h index 864a8ab..8a5ac31 100644 --- a/parse.h +++ b/parse.h @@ -109,7 +109,6 @@ typedef struct Parser Parser; Parser* parser_new(TokStream* tokstream, Arena* arena); void parser_free(Parser* p); AstStmt* parser_parse_stmt(Parser* p); -AstDecl* parser_parse_decl(Parser* p); -AstExpr* parser_parse_expr(Parser* p); +bool parser_done(Parser* p); #endif diff --git a/preproc.c b/preproc.c index 2fb3ece..212cbf1 100644 --- a/preproc.c +++ b/preproc.c @@ -637,6 +637,14 @@ Tok preproc_next(Preproc* p) return expand_tok(p); } +static Tok tokstream_next_filtered(TokStream* s) +{ + Tok tok; + while ((tok = preproc_next(s->preproc)).ty == Tok_Eol + || tok.ty == Tok_Whitespace) { } + return tok; +} + void tokstream_init(TokStream* s, Preproc* preproc, Arena* arena) { *s = (TokStream) { @@ -644,15 +652,7 @@ void tokstream_init(TokStream* s, Preproc* preproc, Arena* arena) .arena = arena, .curr = { }, }; - s->curr = preproc_next(s->preproc); -} - -static Tok tokstream_next_filtered(TokStream* s) -{ - Tok tok; - while ((tok = preproc_next(s->preproc)).ty == Tok_Eol - || tok.ty == Tok_Whitespace) { } - return tok; + s->curr = tokstream_next_filtered(s); } Tok tokstream_next(TokStream* s)