From 96028efc97f57e67ccda04f92737ef1d974b6c94 Mon Sep 17 00:00:00 2001 From: Thomas Voss Date: Mon, 8 Jul 2024 16:06:25 +0200 Subject: Support function calls --- grammar.ebnf | 6 ++++++ src/analyzer.c | 27 +++++++++++++++++++++------ src/codegen.c | 36 ++++++++++++++++++++++++++++++------ src/parser.c | 45 ++++++++++++++++++++++++++++++++++++--------- src/parser.h | 8 ++++++++ 5 files changed, 101 insertions(+), 21 deletions(-) diff --git a/grammar.ebnf b/grammar.ebnf index ed4260e..b16a85b 100644 --- a/grammar.ebnf +++ b/grammar.ebnf @@ -34,6 +34,7 @@ prototype statement = declaration | assignment + | funcall | 'return', [expression], ';' ; @@ -43,6 +44,11 @@ expression | unop, expression | expression, binop, expression | '(', expression, ')' + | funcall + ; + +funcall + = expression '(' ')' ; unop diff --git a/src/analyzer.c b/src/analyzer.c index f40d12b..3683a68 100644 --- a/src/analyzer.c +++ b/src/analyzer.c @@ -355,6 +355,8 @@ analyzestmt(struct azctx *ctx, idx_t i) ctx->types[i] = ctx->fnret; return ni; } + case ASTCALLSTMT: + return analyzeexpr(ctx, i + 1); default: __builtin_unreachable(); } @@ -499,6 +501,14 @@ analyzeexpr(struct azctx *ctx, idx_t i) } case ASTFN: return analyzefn(ctx, i); + case ASTFUNCALL: { + /* TODO: Support function arguments */ + assert(ctx->ast.kids[i].rhs == AST_EMPTY); + idx_t ni, lhs = ctx->ast.kids[i].lhs; + ni = analyzeexpr(ctx, lhs); + ctx->types[i] = ctx->types[lhs]->ret; + return ni; + } default: __builtin_unreachable(); } @@ -595,6 +605,9 @@ constfoldexpr(struct azctx *ctx, type_t *T, idx_t i) struct azctx nctx; switch (ctx->ast.kinds[i]) { + case ASTFUNCALL: + /* TODO: Constfold funcall params */ + break; case ASTFN: return constfoldblk(ctx, ctx->ast.kids[i].rhs); case ASTNUMLIT: { @@ -910,16 +923,18 @@ typecompat(type_t *lhs, type_t *rhs) if (lhs == rhs) return true; - /* Boolean types are only compatible with boolean types */ - if (lhs->kind == TYPE_BOOL || rhs->kind == TYPE_BOOL) - return lhs->kind == TYPE_BOOL && rhs->kind == TYPE_BOOL; + if (lhs->kind != rhs->kind) + return false; /* Function types are compatible if they have the same parameter- and return types */ - if (lhs->kind == TYPE_FN && rhs->kind == TYPE_FN) + /* TODO: Compare the actual parameter types */ + if (lhs->kind == TYPE_FN) return lhs->paramcnt == rhs->paramcnt && lhs->ret == rhs->ret; - if (lhs->kind == TYPE_FN || rhs->kind == TYPE_FN) - return false; + + /* Boolean types are only compatible with boolean types */ + if (lhs->kind == TYPE_BOOL) + return true; /* At this point we only have numeric types left */ diff --git a/src/codegen.c b/src/codegen.c index 252ac29..3c8196d 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -146,6 +146,10 @@ static idx_t codegendecl(struct cgctx ctx, idx_t); idx_t codegentypedexpr(struct cgctx ctx, idx_t i, type_t *T, LLVMValueRef *outv) { + /* To avoid spamming NULL checks everywhere */ + if (T == NULL || outv == NULL) + goto callstmt; + if (T->kind == TYPE_NUM && TESTBIT(ctx.cnst, i) && !T->isfloat) { char buf[40 /* The max value of a u128 is length 39 */]; mpz_get_str(buf, 10, mpq_numref(ctx.folds[i].q)); @@ -194,7 +198,8 @@ codegentypedexpr(struct cgctx ctx, idx_t i, type_t *T, LLVMValueRef *outv) mpf_clear(x); return fwdnode(ctx.ast, i); } else if (T->kind == TYPE_BOOL && TESTBIT(ctx.cnst, i)) { - *outv = LLVMConstInt(type2llvm(ctx, ctx.types[i]), ctx.folds[i].b, false); + *outv = LLVMConstInt(type2llvm(ctx, ctx.types[i]), ctx.folds[i].b, + false); return fwdnode(ctx.ast, i); } @@ -280,6 +285,19 @@ codegentypedexpr(struct cgctx ctx, idx_t i, type_t *T, LLVMValueRef *outv) *outv = LLVMBuildICmp(ctx.bob, bo.pred, vl, vr, bo.name); return ni; } +callstmt: + case ASTFUNCALL: { + idx_t lhs = ctx.ast.kids[i].lhs; + assert(ctx.ast.kinds[lhs] == ASTIDENT); + strview_t sv = ctx.toks.strs[ctx.ast.lexemes[lhs]]; + symval_t *sym = symtab_get_from_scopes(ctx, sv); + LLVMTypeRef ft = LLVMGlobalGetValueType(sym->v); + LLVMValueRef call = LLVMBuildCall2(ctx.bob, ft, sym->v, NULL, 0, + outv == NULL ? "" : "call"); + if (outv != NULL) + *outv = call; + return fwdnode(ctx.ast, i); + } default: __builtin_unreachable(); } @@ -314,6 +332,8 @@ codegenstmt(struct cgctx ctx, idx_t i) (void)LLVMBuildRet(ctx.bob, v); return i; } + case ASTCALLSTMT: + return codegentypedexpr(ctx, i + 1, NULL, NULL); default: __builtin_unreachable(); } @@ -356,7 +376,7 @@ codegenalloca(struct cgctx ctx, idx_t i) } idx_t -codegenfunc(struct cgctx ctx, idx_t i, strview_t sv) +codegenfunc(struct cgctx ctx, idx_t i, strview_t sv, LLVMValueRef *outv) { size_t namesz = ctx.namespace.len + sv.len + 1; char *name = arena_new(ctx.a, char, namesz + 1); @@ -374,11 +394,14 @@ codegenfunc(struct cgctx ctx, idx_t i, strview_t sv) idx_t proto = ctx.ast.kids[i].lhs; idx_t blk = ctx.ast.kids[i].rhs; + while (ctx.scps[ctx.scpi].i != ctx.ast.kids[blk].lhs) + ctx.scpi++; + snapshot_t snap = arena_snapshot_create(*ctx.a); for (idx_t i = ctx.ast.kids[blk].lhs; i <= ctx.ast.kids[blk].rhs; i = fwdnode(ctx.ast, i)) { - if (ctx.ast.kinds[i] == ASTCDECL && ctx.ast.kids[i].rhs != AST_EMPTY + if (ctx.ast.kinds[i] == ASTCDECL && ctx.ast.kinds[ctx.ast.kids[i].rhs] == ASTFN) { (void)codegendecl(ctx, i); @@ -404,6 +427,7 @@ codegenfunc(struct cgctx ctx, idx_t i, strview_t sv) if (ctx.ast.kids[proto].rhs == AST_EMPTY) LLVMBuildRetVoid(ctx.bob); + *outv = ctx.func; return i; } @@ -411,6 +435,7 @@ idx_t codegendecl(struct cgctx ctx, idx_t i) { pair_t p = ctx.ast.kids[i]; + strview_t sv = ctx.toks.strs[ctx.ast.lexemes[i]]; if (ctx.ast.kinds[i] == ASTCDECL) { /* Constants are purely a compiler concept; they aren’t generated @@ -421,7 +446,8 @@ codegendecl(struct cgctx ctx, idx_t i) if (ctx.ast.kinds[p.rhs] != ASTFN || ctx.func != NULL) return fwdnode(ctx.ast, i); - return codegenfunc(ctx, p.rhs, ctx.toks.strs[ctx.ast.lexemes[i]]); + symval_t *sym = symtab_insert(&ctx.scps[ctx.scpi].map, sv, NULL); + return codegenfunc(ctx, p.rhs, ctx.toks.strs[ctx.ast.lexemes[i]], &sym->v); } assert(ctx.ast.kinds[i] == ASTDECL); @@ -431,7 +457,6 @@ codegendecl(struct cgctx ctx, idx_t i) return fwdnode(ctx.ast, i); if (ctx.aux.buf[p.lhs].decl.isstatic) { - strview_t sv = ctx.toks.strs[ctx.ast.lexemes[i]]; /* TODO: Namespace the name */ char *name = tmpalloc(ctx.s, sv.len + 1, 1); LLVMTypeRef t = type2llvm(ctx, ctx.types[i]); @@ -452,7 +477,6 @@ codegendecl(struct cgctx ctx, idx_t i) /* Non-static, non-undef, mutable */ LLVMValueRef var, val; - strview_t sv = ctx.toks.strs[ctx.ast.lexemes[i]]; var = symtab_insert(&ctx.scps[ctx.scpi].map, sv, NULL)->v; if (p.rhs == AST_EMPTY) { val = LLVMConstNull(type2llvm(ctx, ctx.types[i])); diff --git a/src/parser.c b/src/parser.c index 49067e2..6a4db5e 100644 --- a/src/parser.c +++ b/src/parser.c @@ -85,6 +85,12 @@ fwdnode(ast_t ast, idx_t i) case ASTUNNEG: i = ast.kids[i].rhs; break; + case ASTCALLSTMT: + i = ast.kids[i + 1].lhs; + break; + case ASTFUNCALL: + i = ast.kids[i].lhs; + break; case ASTIDENT: case ASTNUMLIT: case ASTTYPE: @@ -279,6 +285,20 @@ parseexpratom(ast_t *ast, lexemes_t toks) default: err("parser: Invalid expression leaf"); } + + if (toks.kinds[toksidx] == '(') { + toksidx++; + if (toks.kinds[toksidx++] != ')') + err("parser: Expected closing parenthesis"); + + idx_t j = astalloc(ast); + ast->kinds[j] = ast->kinds[i]; + ast->kids[j] = ast->kids[i]; + ast->lexemes[j] = ast->lexemes[i]; + ast->kinds[i] = ASTFUNCALL; + ast->kids[i] = (pair_t){j, AST_EMPTY}; + } + return i; } @@ -369,15 +389,22 @@ parsestmt(ast_t *ast, aux_t *aux, lexemes_t toks) } else /* assignment */ { idx_t lhs, rhs; i = astalloc(ast); - lhs = parseexpratom(ast, toks); - if (toks.kinds[toksidx++] != LEXEQ) - err("parser: Expected equals"); - rhs = parseexpr(ast, toks, 0); - if (toks.kinds[toksidx++] != LEXSEMI) - err("parser: Expected semicolon"); - ast->kinds[i] = ASTASIGN; - ast->kids[i].lhs = lhs; - ast->kids[i].rhs = rhs; + lhs = parseexpr(ast, toks, 0); + + if (ast->kinds[lhs] != ASTFUNCALL || toks.kinds[toksidx + 1] == LEXEQ) { + if (toks.kinds[toksidx++] != LEXEQ) + err("parser: Expected equals"); + rhs = parseexpr(ast, toks, 0); + if (toks.kinds[toksidx++] != LEXSEMI) + err("parser: Expected semicolon"); + ast->kinds[i] = ASTASIGN; + ast->kids[i].lhs = lhs; + ast->kids[i].rhs = rhs; + } else { + if (toks.kinds[toksidx++] != LEXSEMI) + err("parser: Expected semicolon"); + ast->kinds[i] = ASTCALLSTMT; + } } return i; diff --git a/src/parser.h b/src/parser.h index 31be1c8..5d5c53b 100644 --- a/src/parser.h +++ b/src/parser.h @@ -61,6 +61,14 @@ enum { ‘~rhs’ */ ASTUNCMPL, + /* Function call + ‘lhs(a, …)’; aux[rhs].funcall */ + ASTFUNCALL, + + /* A dummy node to deal with quirks of how the parser is setup. The + node directly after this is an ASTFUNCALL. */ + ASTCALLSTMT, + /* NOTE: Ensure that the enumerations defined above this comment do not exceed 37 — the value of ‘%’ — or they will conflict with the definitions below. */ -- cgit v1.2.3