From 0543c146ad5904e3f7ad5a6208e9f30f72f47ca5 Mon Sep 17 00:00:00 2001 From: Thomas Voss Date: Mon, 1 Jul 2024 19:58:46 +0200 Subject: Support assignments to mutable variables --- README | 19 +++++++++++++++++++ src/analyzer.c | 16 +++++++++++++++- src/codegen.c | 22 ++++++++++++++++++++++ src/parser.c | 20 ++++++++++++++------ src/parser.h | 4 ++++ 5 files changed, 74 insertions(+), 7 deletions(-) diff --git a/README b/README index c00a5d9..bf648e1 100644 --- a/README +++ b/README @@ -149,3 +149,22 @@ The build script also accepts some subcommands. They are as follows: returns_42′ :: () int { return --x; } + +9. Assignment statements (not expressions). Unlike in C, you cannot put + an assignment inside of an expression. + + return_42 :: () int { + x := 4; + y := 2; + x = x*10 + y; + return x; + } + + Due to quirks of the language grammar identifiers may be wrapped in + (arbitrary levels of) parenthesis, however assignments are only + permitted if the left-hand side with parenthesis removed is a lone + identifier. The rationale behind this is to allow in the future + assignments to expressions that return pointers. + + ((x)) = x*10 + y; /* legal */ + (true ? x : y) = x*10 + y; /* illegal */ diff --git a/src/analyzer.c b/src/analyzer.c index 4f5500a..68029fd 100644 --- a/src/analyzer.c +++ b/src/analyzer.c @@ -248,6 +248,18 @@ analyzestmt(struct azctx ctx, scope_t *scps, type_t *types, ast_t ast, case ASTDECL: case ASTCDECL: return analyzedecl(ctx, scps, types, ast, aux, toks, i); + case ASTASIGN: { + pair_t p = ast.kids[i]; + (void)analyzeexpr(ctx, scps, types, ast, aux, toks, p.lhs); + /* TODO: Allow assignments to expressions returning pointer types */ + if (ast.kinds[p.lhs] != ASTIDENT) + err("analyzer: Assignments may only be made to identifiers"); + idx_t ni = analyzeexpr(ctx, scps, types, ast, aux, toks, p.rhs); + if (!typecompat(types[p.lhs], types[p.rhs])) + err("analyzer: Assignment type mismatch"); + types[i] = types[p.lhs]; + return ni; + } case ASTRET: { idx_t expr = ast.kids[i].rhs; if (expr == AST_EMPTY) { @@ -437,6 +449,7 @@ constfoldstmt(struct cfctx ctx, mpq_t *folds, scope_t *scps, type_t *types, case ASTDECL: case ASTCDECL: return constfolddecl(ctx, folds, scps, types, ast, toks, i); + case ASTASIGN: case ASTRET: return constfoldexpr(ctx, folds, scps, types, ast, toks, types[i], ast.kids[i].rhs); @@ -733,8 +746,9 @@ bool returns(ast_t ast, idx_t i) { switch (ast.kinds[i]) { - case ASTDECL: + case ASTASIGN: case ASTCDECL: + case ASTDECL: return false; case ASTRET: return true; diff --git a/src/codegen.c b/src/codegen.c index a744f31..f81e9b9 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -55,6 +55,7 @@ struct cgctx { static void codegenast(struct cgctx); static LLVMTypeRef type2llvm(struct cgctx, type_t); +static symval_t *symtab_get_from_scopes(struct cgctx ctx, strview_t sv); extern bool lflag, sflag; extern const char *oflag; @@ -269,6 +270,16 @@ codegenstmt(struct cgctx ctx, idx_t i) case ASTDECL: case ASTCDECL: return codegendecl(ctx, i); + case ASTASIGN: { + pair_t p = ctx.ast.kids[i]; + assert(ctx.ast.kinds[p.lhs] == ASTIDENT); + LLVMValueRef var, val; + var = symtab_get_from_scopes(ctx, ctx.toks.strs[ctx.ast.lexemes[p.lhs]]) + ->v; + i = codegentypedexpr(ctx, p.rhs, ctx.types[i], &val); + LLVMBuildStore(ctx.bob, val, var); + return i; + } case ASTRET: { idx_t expr = ctx.ast.kids[i].rhs; if (expr == AST_EMPTY) { @@ -463,3 +474,14 @@ type2llvm(struct cgctx ctx, type_t t) __builtin_unreachable(); } } + +symval_t * +symtab_get_from_scopes(struct cgctx ctx, strview_t sv) +{ + for (;;) { + symval_t *p = symtab_insert(&ctx.scps[ctx.scpi].map, sv, NULL); + if (p != NULL || ctx.scpi == 0) + return p; + ctx.scpi = ctx.scps[ctx.scpi].up; + } +} diff --git a/src/parser.c b/src/parser.c index ea8f00e..a6d82a8 100644 --- a/src/parser.c +++ b/src/parser.c @@ -66,12 +66,13 @@ fwdnode(ast_t ast, idx_t i) return i + 1; i = ast.kids[i].rhs; break; + case ASTASIGN: case ASTBINADD: case ASTBINAND: case ASTBINDIV: + case ASTBINIOR: case ASTBINMOD: case ASTBINMUL: - case ASTBINIOR: case ASTBINSHL: case ASTBINSHR: case ASTBINSUB: @@ -340,9 +341,6 @@ parsestmt(ast_t *ast, aux_t *aux, lexemes_t toks) { idx_t i; - if (toks.kinds[toksidx] != LEXIDENT) - err("parser: Expected identifier"); - strview_t sv = toks.strs[toksidx]; if (strview_eq(SV("return"), sv)) { i = astalloc(ast); @@ -356,8 +354,18 @@ parsestmt(ast_t *ast, aux_t *aux, lexemes_t toks) err("parser: Expected semicolon"); } else if (toks.kinds[toksidx + 1] == LEXCOLON) { i = parsedecl(ast, aux, toks, false); - } else { - err("parser: Invalid statement"); + } 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; } return i; diff --git a/src/parser.h b/src/parser.h index 32acc96..e30ec8e 100644 --- a/src/parser.h +++ b/src/parser.h @@ -49,6 +49,10 @@ enum { ‘return rhs’ */ ASTRET, + /* Assignment, + ‘lhs = rhs’ */ + ASTASIGN, + /* Unary negation ‘-rhs’ */ ASTUNNEG, -- cgit v1.2.3