aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Voss <mail@thomasvoss.com> 2024-07-01 19:58:46 +0200
committerThomas Voss <mail@thomasvoss.com> 2024-07-01 19:58:46 +0200
commit0543c146ad5904e3f7ad5a6208e9f30f72f47ca5 (patch)
tree7a714c6206a99eb469794e3ff0f12bfe85719dcb
parent4a95caabf15db0beeaceea09d1fb4b0964b119c9 (diff)
Support assignments to mutable variables
-rw-r--r--README19
-rw-r--r--src/analyzer.c16
-rw-r--r--src/codegen.c22
-rw-r--r--src/parser.c20
-rw-r--r--src/parser.h4
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,