aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Voss <mail@thomasvoss.com> 2024-07-09 16:14:23 +0200
committerThomas Voss <mail@thomasvoss.com> 2024-07-09 16:14:23 +0200
commit674bce8c96309ae7c38a7e2b946f3f18430a0b00 (patch)
tree9a2b58ad0194493e1998211c08e6c82109309654
parent1a769e8ef02893f737a88fe683add8ffea7affb1 (diff)
Support static-local variables
-rw-r--r--BUGS10
-rw-r--r--README16
-rw-r--r--src/analyzer.c2
-rw-r--r--src/codegen.c26
-rw-r--r--src/parser.c21
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;