From 674bce8c96309ae7c38a7e2b946f3f18430a0b00 Mon Sep 17 00:00:00 2001 From: Thomas Voss Date: Tue, 9 Jul 2024 16:14:23 +0200 Subject: Support static-local variables --- BUGS | 10 ---------- README | 16 ++++++++++++++++ src/analyzer.c | 2 ++ src/codegen.c | 26 +++++++++++++++++++------- src/parser.c | 21 +++++++++++++++++---- 5 files changed, 54 insertions(+), 21 deletions(-) diff --git a/BUGS b/BUGS index 156fa0b..e10c720 100644 --- a/BUGS +++ b/BUGS @@ -20,13 +20,3 @@ 2. Recursive functions crash the compiler. foo :: () { return foo(); } /* breaks */ - -3. Global variables are broken both when being declared and assigned to - function calls and other globals. Both of these cased should be - illegal; globals if assigned at declaration should only be able to be - assigned to constant expressions. - - foo :: () int { return 42; } - x := 5; - x′ := x; /* breaks */ - x″ := foo(); /* breaks */ diff --git a/README b/README index a8f0b7c..f30fdb1 100644 --- a/README +++ b/README @@ -168,3 +168,19 @@ The build script also accepts some subcommands. They are as follows: ((x)) = x*10 + y; /* legal */ (true ? x : y) = x*10 + y; /* illegal */ + +10. Static local variables allow for block-scoped global variables. This + is useful for having function state persist across multiple calls. + + iota :: () int { + static x := -1; + x = x + 1; + return x; + } + + pub main :: () { + zero := iota(); + one := iota(); + two := iota(); + three := iota(); + } diff --git a/src/analyzer.c b/src/analyzer.c index b67bc91..6154029 100644 --- a/src/analyzer.c +++ b/src/analyzer.c @@ -296,6 +296,8 @@ analyzedecl(struct azctx *ctx, idx_t i) nctx.decl = sv; ni = analyzeexpr(&nctx, p.rhs); rtype = ctx->types[p.rhs]; + if (isstatic && !TESTBIT(ctx->cnst, p.rhs)) + err("analyzer: Assigning non-constant expression to static"); } else ni = fwdnode(ctx->ast, i); diff --git a/src/codegen.c b/src/codegen.c index 5c81721..2286374 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -369,11 +369,13 @@ codegenalloca(struct cgctx ctx, idx_t i) i = codegenalloca(ctx, i); break; case ASTDECL: { - strview_t sv = ctx.toks.strs[ctx.ast.lexemes[i]]; - uchar *name = tmpalloc(ctx.s, sv.len + 1, 1); - LLVMTypeRef t = type2llvm(ctx, ctx.types[i]); - symtab_insert(&ctx.scps[ctx.scpi].map, sv, NULL)->v = - LLVMBuildAlloca(ctx.bob, t, svtocstr(name, sv)); + if (!ctx.aux.buf[ctx.ast.kids[i].lhs].decl.isstatic) { + strview_t sv = ctx.toks.strs[ctx.ast.lexemes[i]]; + uchar *name = tmpalloc(ctx.s, sv.len + 1, 1); + LLVMTypeRef t = type2llvm(ctx, ctx.types[i]); + symtab_insert(&ctx.scps[ctx.scpi].map, sv, NULL)->v = + LLVMBuildAlloca(ctx.bob, t, svtocstr(name, sv)); + } } /* fallthrough */ default: i = fwdnode(ctx.ast, i); @@ -465,9 +467,19 @@ codegendecl(struct cgctx ctx, idx_t i) if (ctx.aux.buf[p.lhs].decl.isstatic) { /* TODO: Namespace the name */ - char *name = tmpalloc(ctx.s, sv.len + 1, 1); + char *name; + if (ctx.namespace.len != 0) { + size_t bufsz = ctx.namespace.len + sv.len + 2; + name = tmpalloc(ctx.s, bufsz, 1); + snprintf(name, bufsz, "%.*s.%.*s", SV_PRI_ARGS(ctx.namespace), + SV_PRI_ARGS(sv)); + } else { + name = tmpalloc(ctx.s, sv.len + 1, 1); + svtocstr(name, sv); + } + LLVMTypeRef t = type2llvm(ctx, ctx.types[i]); - LLVMValueRef globl = LLVMAddGlobal(ctx.mod, t, svtocstr(name, sv)); + LLVMValueRef globl = LLVMAddGlobal(ctx.mod, t, name); symtab_insert(&ctx.scps[ctx.scpi].map, sv, NULL)->v = globl; LLVMValueRef v; diff --git a/src/parser.c b/src/parser.c index e602271..eff98df 100644 --- a/src/parser.c +++ b/src/parser.c @@ -158,9 +158,15 @@ parsedecl(ast_t *ast, aux_t *aux, lexemes_t toks, bool toplvl) aux->buf = bufalloc(aux->buf, aux->cap, sizeof(*aux->buf)); } - /* TODO: Support ‘static’ as a keyword */ - aux->buf[j].decl.isstatic = toplvl; - aux->buf[j].decl.isundef = false; + bool static_kw = toks.kinds[toksidx] == LEXIDENT + && strview_eq(SV("static"), toks.strs[toksidx]); + if (static_kw) { + toksidx++; + if (toplvl) + err("parser: The ‘static’ doesn’t apply to global variables"); + } + + aux->buf[j].decl.isundef = false; if (toplvl && toks.kinds[toksidx] == LEXIDENT && strview_eq(SV("pub"), toks.strs[toksidx])) { @@ -187,12 +193,17 @@ parsedecl(ast_t *ast, aux_t *aux, lexemes_t toks, bool toplvl) err("parser: No type provided in non-assigning declaration"); ast->kinds[i] = ASTDECL; ast->kids[i].rhs = AST_EMPTY; + aux->buf[j].decl.isstatic = toplvl || static_kw; return i; case LEXCOLON: ast->kinds[i] = ASTCDECL; + aux->buf[j].decl.isstatic = false; + if (static_kw) + err("parser: Cannot declare a constant symbol ‘static’"); break; case LEXEQ: ast->kinds[i] = ASTDECL; + aux->buf[j].decl.isstatic = toplvl || static_kw; break; default: err("parser: Expected colon, equals, or semicolon"); @@ -388,7 +399,9 @@ parsestmt(ast_t *ast, aux_t *aux, lexemes_t toks) ast->kids[i].rhs = rhs; if (toks.kinds[toksidx++] != LEXSEMI) err("parser: Expected semicolon"); - } else if (toks.kinds[toksidx + 1] == LEXCOLON) { + } else if (strview_eq(SV("static"), sv) + || toks.kinds[toksidx + 1] == LEXCOLON) + { i = parsedecl(ast, aux, toks, false); } else /* assignment or funcall */ { idx_t lhs, rhs; -- cgit v1.2.3