699 lines
16 KiB
C
699 lines
16 KiB
C
#include "parse.h"
|
|
#include "ast.h"
|
|
#include "collection.h"
|
|
#include "resolve.h"
|
|
#include "sema.h"
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
typedef struct {
|
|
char ch;
|
|
bool is_term;
|
|
unsigned short val;
|
|
} KwNodeOpt;
|
|
|
|
typedef struct {
|
|
ARRAY_FIELD(KwNodeOpt, opts);
|
|
size_t idx;
|
|
} KwNode;
|
|
|
|
typedef struct {
|
|
TokStream* tokstream;
|
|
Arena arena;
|
|
ARRAY_FIELD(KwNode, nodes);
|
|
} KwMatcher;
|
|
|
|
static KwNodeOpt* kwmatcher_create_path(
|
|
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) {
|
|
size_t new_idx = m->nodes_size;
|
|
|
|
KwNode new_node = {
|
|
ARRAY_FIELD_INIT(opts),
|
|
.idx = new_idx,
|
|
};
|
|
ARENA_ARRAY_FIELD_PUSH(&m->arena, m, nodes, &new_node);
|
|
// might have been relocated
|
|
node = &m->nodes[node_idx];
|
|
|
|
KwNodeOpt new_opt = {
|
|
.ch = kw[0],
|
|
.is_term = false,
|
|
.val = new_idx,
|
|
};
|
|
ARENA_ARRAY_FIELD_PUSH(&m->arena, node, opts, &new_opt);
|
|
|
|
opt = &node->opts[node->opts_size - 1];
|
|
}
|
|
if (!kw[1])
|
|
return opt;
|
|
|
|
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, 0, kw);
|
|
opt->is_term = true;
|
|
opt->val = tokty;
|
|
}
|
|
|
|
static KwNodeOpt* kwmatcher_find_node(
|
|
KwMatcher* m, KwNode* node, const char* kw)
|
|
{
|
|
assert(kw[0]);
|
|
KwNodeOpt* opt = nullptr;
|
|
for (size_t i = 0; i < node->opts_size; ++i) {
|
|
if (node->opts[i].ch == kw[0]) {
|
|
opt = &node->opts[i];
|
|
}
|
|
}
|
|
if (!opt)
|
|
return nullptr;
|
|
if (!kw[1])
|
|
return opt;
|
|
return kwmatcher_find_node(m, &m->nodes[opt->val], &kw[1]);
|
|
}
|
|
|
|
static TokTy kwmatcher_match(KwMatcher* m, const char* kw)
|
|
{
|
|
KwNodeOpt* opt = kwmatcher_find_node(m, &m->nodes[0], kw);
|
|
if (!opt || !opt->is_term)
|
|
return Tok_Ident;
|
|
return opt->val;
|
|
}
|
|
|
|
static void kwmatcher_init(KwMatcher* m, TokStream* tokstream)
|
|
{
|
|
*m = (KwMatcher) {
|
|
.tokstream = tokstream,
|
|
.arena = { },
|
|
ARRAY_FIELD_INIT(nodes),
|
|
};
|
|
|
|
arena_init(&m->arena, 512);
|
|
|
|
ARENA_ARRAY_FIELD_PUSH(&m->arena, m, nodes, (&(KwNode) { .idx = 0 }));
|
|
|
|
struct {
|
|
TokTy ty;
|
|
const char* kw;
|
|
} kws[] = {
|
|
{ Tok_KwInt, "int" },
|
|
{ Tok_KwChar, "char" },
|
|
{ Tok_KwConst, "const" },
|
|
{ Tok_KwVoid, "void" },
|
|
};
|
|
|
|
for (size_t i = 0; i < sizeof(kws) / sizeof(kws[0]); ++i) {
|
|
kwmatcher_add(m, kws[i].kw, kws[i].ty);
|
|
}
|
|
}
|
|
|
|
static void kwmatcher_deinit(KwMatcher* m)
|
|
{
|
|
arena_deinit(&m->arena);
|
|
}
|
|
|
|
static Tok kwmatcher_next(KwMatcher* m)
|
|
{
|
|
Tok tok = tokstream_next(m->tokstream);
|
|
if (tok.ty != Tok_Ident)
|
|
goto exit;
|
|
TokTy ty = kwmatcher_match(m, tok.text);
|
|
tok.ty = ty;
|
|
|
|
exit:
|
|
printf(" - %-10s %-10s %d:%d\n",
|
|
tokty_str(tok.ty),
|
|
tok.text,
|
|
tok.loc.line,
|
|
tok.loc.col);
|
|
return tok;
|
|
}
|
|
|
|
typedef enum : unsigned int {
|
|
ParserFlags_ParsingTypedef = 1 << 0,
|
|
} ParserFlags;
|
|
|
|
struct Parser {
|
|
KwMatcher kwmatcher;
|
|
Sema* sema;
|
|
Arena* arena;
|
|
Arena sym_arena;
|
|
|
|
Syms* syms;
|
|
|
|
Tok curr;
|
|
|
|
ParserFlags flags;
|
|
};
|
|
|
|
Parser* parser_new(TokStream* tokstream, Sema* sema, Arena* arena)
|
|
{
|
|
Parser* p = malloc(sizeof(Parser));
|
|
|
|
*p = (Parser) {
|
|
.kwmatcher = { },
|
|
.sema = sema,
|
|
.arena = arena,
|
|
.sym_arena = { },
|
|
|
|
.syms = syms_new(),
|
|
|
|
.curr = { },
|
|
|
|
.flags = 0,
|
|
};
|
|
|
|
kwmatcher_init(&p->kwmatcher, tokstream);
|
|
p->curr = kwmatcher_next(&p->kwmatcher);
|
|
|
|
return p;
|
|
}
|
|
|
|
void parser_free(Parser* p)
|
|
{
|
|
kwmatcher_deinit(&p->kwmatcher);
|
|
syms_free(p->syms);
|
|
free(p);
|
|
}
|
|
|
|
static void step(Parser* p)
|
|
{
|
|
p->curr = kwmatcher_next(&p->kwmatcher);
|
|
}
|
|
|
|
#define DEFINE_NEW(TYPE, FN_NAME, POST) \
|
|
[[maybe_unused]] static TYPE* new_##FN_NAME(Parser* p, TYPE init) \
|
|
{ \
|
|
TYPE* val = arena_alloc(p->arena, sizeof(TYPE)); \
|
|
*val = init; \
|
|
POST; \
|
|
return val; \
|
|
}
|
|
|
|
DEFINE_NEW(AstStmt, stmt, { sema_visit_stmt(p->sema, val); })
|
|
DEFINE_NEW(AstDecl, decl, { })
|
|
DEFINE_NEW(AstDeclar, declar, { })
|
|
DEFINE_NEW(AstExpr, expr, { sema_visit_expr(p->sema, val); })
|
|
|
|
DEFINE_NEW(AstStmtBlock, stmt_block, { })
|
|
|
|
static AstStmtBlock* parse_stmt_block(Parser* p);
|
|
static AstStmt* parse_stmt(Parser* p);
|
|
static AstDecl* parse_decl(Parser* p, bool is_param);
|
|
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 == '{');
|
|
step(p);
|
|
|
|
ARRAY_VAR(AstStmt*, stmts);
|
|
|
|
while (p->curr.ty != Tok_Eof && p->curr.ty != '}') {
|
|
AstStmt* stmt = parse_stmt(p);
|
|
ARENA_ARRAY_VAR_PUSH(p->arena, stmts, &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)
|
|
{
|
|
switch (ty) {
|
|
case Tok_KwInt:
|
|
case Tok_KwChar:
|
|
case Tok_KwConst:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool is_defty_ident(Parser* p, Tok tok)
|
|
{
|
|
if (tok.ty != Tok_Ident)
|
|
return false;
|
|
OldSym* sym = syms_get(p->syms, tok.text);
|
|
return sym && sym->ty == OldSym_Typedef;
|
|
}
|
|
|
|
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 = parse_decl(p, false);
|
|
|
|
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) {
|
|
.ty = AstStmt_Decl,
|
|
.loc = loc,
|
|
.decl = decl,
|
|
});
|
|
}
|
|
|
|
AstExpr* expr = parse_expr(p);
|
|
|
|
if (p->curr.ty != ';') {
|
|
fprintf(stderr, "error: expected ';' on line %d\n", p->curr.loc.line);
|
|
exit(1);
|
|
}
|
|
step(p);
|
|
|
|
return new_stmt(p,
|
|
(AstStmt) {
|
|
.ty = AstStmt_Expr,
|
|
.loc = loc,
|
|
.expr = expr,
|
|
});
|
|
}
|
|
|
|
static AstDecl* parse_decl(Parser* p, bool is_param)
|
|
{
|
|
Loc loc = p->curr.loc;
|
|
|
|
AstSpec spec = parse_decl_spec(p);
|
|
|
|
if (spec & AstSpec_Typedef) {
|
|
p->flags |= ParserFlags_ParsingTypedef;
|
|
}
|
|
|
|
ARRAY_VAR(AstDeclar*, declars);
|
|
|
|
AstDeclar* first = parse_declar(p);
|
|
|
|
ARENA_ARRAY_VAR_PUSH(p->arena, declars, &first);
|
|
|
|
AstStmtBlock* function_body = nullptr;
|
|
bool multi_allowed = !is_param;
|
|
|
|
if (!is_param && first->ty == AstDeclar_Function && p->curr.ty == '{') {
|
|
|
|
sema_function_enter(p->sema, spec, declars[0]);
|
|
function_body = parse_stmt_block(p);
|
|
sema_function_leave(p->sema);
|
|
|
|
multi_allowed = false;
|
|
}
|
|
|
|
while (multi_allowed && p->curr.ty == ',') {
|
|
step(p);
|
|
|
|
AstDeclar* declar = parse_declar(p);
|
|
|
|
ARENA_ARRAY_VAR_PUSH(p->arena, declars, &declar);
|
|
}
|
|
|
|
p->flags &= ~ParserFlags_ParsingTypedef;
|
|
|
|
return new_decl(p,
|
|
(AstDecl) {
|
|
.spec = spec,
|
|
.declars = declars,
|
|
.declars_cap = declars_cap,
|
|
.declars_size = declars_size,
|
|
.function_body = function_body,
|
|
.loc = loc,
|
|
});
|
|
}
|
|
|
|
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)
|
|
{
|
|
AstDeclar* declar = parse_declar_l1(p);
|
|
|
|
if (p->curr.ty == '=') {
|
|
step(p);
|
|
AstExpr* expr = parse_expr(p);
|
|
declar->init_expr = expr;
|
|
}
|
|
|
|
return declar;
|
|
}
|
|
|
|
static AstDeclar* parse_declar_l1(Parser* p)
|
|
{
|
|
Loc loc = p->curr.loc;
|
|
|
|
if (p->curr.ty == '*') {
|
|
step(p);
|
|
|
|
AstSpec spec = parse_decl_spec(p);
|
|
|
|
AstDeclar* declar = parse_declar_l1(p);
|
|
return new_declar(p,
|
|
(AstDeclar) {
|
|
.ty = AstDeclar_Pointer,
|
|
.loc = loc,
|
|
.pointer = {
|
|
.declar = declar,
|
|
.spec = spec,
|
|
},
|
|
});
|
|
}
|
|
|
|
return parse_declar_l2(p);
|
|
}
|
|
|
|
static AstDeclar* parse_declar_l2(Parser* p)
|
|
{
|
|
Loc loc = p->curr.loc;
|
|
|
|
AstDeclar* declar = parse_declar_l3(p);
|
|
|
|
while (p->curr.ty == '[') {
|
|
step(p);
|
|
AstExpr* expr = nullptr;
|
|
if (p->curr.ty != ']') {
|
|
expr = parse_expr(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_Array,
|
|
.loc = loc,
|
|
.array = {
|
|
.declar = declar,
|
|
.expr = expr,
|
|
},
|
|
});
|
|
}
|
|
|
|
return declar;
|
|
}
|
|
|
|
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);
|
|
|
|
if (p->curr.ty == Tok_KwVoid) {
|
|
step(p);
|
|
} else if (p->curr.ty != Tok_Eof && p->curr.ty != ')') {
|
|
AstDecl* d = parse_decl(p, true);
|
|
ARENA_ARRAY_VAR_PUSH(p->arena, params, &d);
|
|
|
|
while (p->curr.ty == ',') {
|
|
step(p);
|
|
|
|
d = parse_decl(p, true);
|
|
ARENA_ARRAY_VAR_PUSH(p->arena, params, &d);
|
|
}
|
|
}
|
|
|
|
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);
|
|
return new_declar(p,
|
|
(AstDeclar) {
|
|
.ty = AstDeclar_Ident,
|
|
.loc = loc,
|
|
.ident = ident,
|
|
});
|
|
}
|
|
|
|
if (p->curr.ty == '(') {
|
|
step(p);
|
|
|
|
AstDeclar* declar = parse_declar_l1(p);
|
|
|
|
if (p->curr.ty != ')') {
|
|
fprintf(
|
|
stderr, "error: expected ')' on line %d\n", p->curr.loc.line);
|
|
exit(1);
|
|
}
|
|
step(p);
|
|
|
|
return declar;
|
|
}
|
|
|
|
fprintf(
|
|
stderr, "error: expected declarator on line %d\n", p->curr.loc.line);
|
|
exit(1);
|
|
}
|
|
|
|
static AstSpec parse_decl_spec(Parser* p)
|
|
{
|
|
AstSpec spec = 0;
|
|
|
|
while (true) {
|
|
switch (p->curr.ty) {
|
|
case Tok_Ident: {
|
|
OldSym* sym = syms_get(p->syms, p->curr.text);
|
|
if (!sym || sym->ty != OldSym_Typedef)
|
|
break;
|
|
spec |= AstSpec_DefTy;
|
|
step(p);
|
|
continue;
|
|
}
|
|
case Tok_KwInt:
|
|
spec |= AstSpec_Int;
|
|
step(p);
|
|
continue;
|
|
case Tok_KwChar:
|
|
spec |= AstSpec_Char;
|
|
step(p);
|
|
continue;
|
|
case Tok_KwConst:
|
|
spec |= AstSpec_Const;
|
|
step(p);
|
|
continue;
|
|
default:
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return spec;
|
|
}
|
|
|
|
static AstExpr* parse_expr_1(Parser* p);
|
|
static AstExpr* parse_expr_2(Parser* p);
|
|
|
|
static AstExpr* parse_expr(Parser* p)
|
|
{
|
|
return parse_expr_1(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 == '(') {
|
|
|
|
ARRAY_VAR(AstExpr*, args);
|
|
|
|
if (p->curr.ty != ')') {
|
|
do {
|
|
step(p);
|
|
AstExpr* arg = parse_expr(p);
|
|
ARENA_ARRAY_VAR_PUSH(p->arena, 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;
|
|
}
|
|
|
|
if (dest)
|
|
dest[dest_i] = '\0';
|
|
return dest_i;
|
|
}
|
|
|
|
static AstExpr* parse_expr_2(Parser* p)
|
|
{
|
|
Loc loc = p->curr.loc;
|
|
|
|
if (p->curr.ty == Tok_Ident) {
|
|
char* ident = arena_strdup(p->arena, p->curr.text);
|
|
step(p);
|
|
return new_expr(p,
|
|
(AstExpr) {
|
|
.ty = AstExpr_Ident,
|
|
.loc = loc,
|
|
.ident = ident,
|
|
});
|
|
}
|
|
|
|
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;
|
|
}
|