aboutsummaryrefslogtreecommitdiff
path: root/src/analyzer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/analyzer.c')
-rw-r--r--src/analyzer.c156
1 files changed, 154 insertions, 2 deletions
diff --git a/src/analyzer.c b/src/analyzer.c
index e842859..eaf0ab2 100644
--- a/src/analyzer.c
+++ b/src/analyzer.c
@@ -1,10 +1,13 @@
#include <assert.h>
+#include <ctype.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <gmp.h>
+
#include "alloc.h"
#include "analyzer.h"
#include "common.h"
@@ -39,17 +42,26 @@ struct azctx {
definition */
struct strview decl;
+ /* The index of the current scope in the scopes array */
+ idx_t_ si;
+
/* If we need to check for return statements. Only true for the
outer body-block of a function that returns a value. */
bool chkrets;
+};
- /* The index of the current scope in the scopes array */
+struct cfctx {
+ arena *a;
+ struct strview decl;
idx_t_ si;
};
static void analyzeast(struct scope *, struct type *, struct ast,
struct lexemes, arena *)
__attribute__((nonnull));
+static void constfold(mpq_t *, struct scope *, struct type *, struct ast,
+ struct lexemes, arena *)
+ __attribute__((nonnull));
/* Perform a pass over the entire AST and return an array of symbol
tables, one for each scope in the program */
@@ -87,13 +99,16 @@ const struct type *typelookup(const uchar *, size_t)
void
analyzeprog(struct ast ast, struct lexemes toks, arena *a, struct type **types,
- struct scope **scps)
+ struct scope **scps, mpq_t **folds)
{
*types = bufalloc(NULL, ast.len, sizeof(**types));
memset(*types, 0, ast.len * sizeof(**types));
*scps = gensymtabs(ast, toks, a);
analyzeast(*scps, *types, ast, toks, a);
+
+ *folds = bufalloc(NULL, ast.len, sizeof(**folds));
+ constfold(*folds, *scps, *types, ast, toks, a);
}
struct scope *
@@ -322,6 +337,143 @@ analyzeblk(struct azctx ctx, struct scope *scps, struct type *types,
return i;
}
+static idx_t_
+constfolddecl(struct cfctx ctx, mpq_t *folds, struct scope *scps,
+ struct type *types, struct ast ast, struct lexemes toks, idx_t_ i);
+static idx_t_
+constfoldexpr(struct cfctx ctx, mpq_t *folds, struct scope *scps,
+ struct type *types, struct ast ast, struct lexemes toks, idx_t_ i);
+
+idx_t_
+constfoldstmt(struct cfctx ctx, mpq_t *folds, struct scope *scps,
+ struct type *types, struct ast ast, struct lexemes toks, idx_t_ i)
+{
+ switch (ast.kinds[i]) {
+ case ASTDECL:
+ case ASTCDECL:
+ case ASTPCDECL:
+ case ASTPDECL:
+ return constfolddecl(ctx, folds, scps, types, ast, toks, i);
+ case ASTRET:
+ return constfoldexpr(ctx, folds, scps, types, ast, toks,
+ ast.kids[i].rhs);
+ default:
+ __builtin_unreachable();
+ }
+}
+
+idx_t_
+constfoldblk(struct cfctx ctx, mpq_t *folds, struct scope *scps,
+ struct type *types, struct ast ast, struct lexemes toks, idx_t_ i)
+{
+ struct pair p = ast.kids[i];
+ while (scps[ctx.si].i != p.lhs)
+ ctx.si++;
+ for (i = p.lhs; i <= p.rhs;
+ i = constfoldstmt(ctx, folds, scps, types, ast, toks, i))
+ ;
+
+ return i;
+}
+
+idx_t_
+constfoldexpr(struct cfctx ctx, mpq_t *folds, struct scope *scps,
+ struct type *types, struct ast ast, struct lexemes toks, idx_t_ i)
+{
+ /* Check if this expression has already been constant folded. This
+ works because when an mpq_t is initialized via mpq_init(), it is
+ set to 0/1 meaning that the denominator pointer can’t be NULL. */
+ if ((*folds[i])._mp_den._mp_d != NULL)
+ return fwdnode(ast, i);
+
+ switch (ast.kinds[i]) {
+ case ASTNUMLIT: {
+ mpq_init(folds[i]);
+
+ /* TODO: Temporary allocator */
+ struct strview sv = toks.strs[ast.lexemes[i]];
+ char *buf = bufalloc(NULL, sv.len + 1, 1);
+ size_t len = 0;
+
+ for (size_t i = 0; i < sv.len; i++) {
+ if (isdigit(sv.p[i]))
+ buf[len++] = sv.p[i];
+ }
+ buf[len] = 0;
+
+ (void)mpq_set_str(folds[i], buf, 10);
+
+ free(buf);
+ return fwdnode(ast, i);
+ }
+ case ASTIDENT: {
+ struct strview sv = toks.strs[ast.lexemes[i]];
+
+ /* Variable shadowing */
+ if (strview_eq(sv, ctx.decl) && ctx.si > 0)
+ ctx.si--;
+
+ for (idx_t_ lvl = ctx.si;;) {
+ struct scope scp = scps[lvl];
+ idx_t_ *ip = symtab_insert(&scp.map, sv, NULL);
+
+ if (ip == NULL) {
+ assert(lvl != 0);
+ lvl = scp.up;
+ } else {
+ switch (ast.kinds[*ip]) {
+ case ASTDECL:
+ case ASTPDECL:
+ break;
+ case ASTCDECL:
+ case ASTPCDECL: {
+ *folds[i] = *folds[*ip];
+ if ((*folds[i])._mp_den._mp_d == NULL) {
+ ctx.si = lvl;
+ (void)constfolddecl(ctx, folds, scps, types, ast, toks,
+ *ip);
+ *folds[i] = *folds[*ip];
+ assert((*folds[i])._mp_den._mp_d != NULL);
+ }
+ break;
+ }
+ default:
+ __builtin_unreachable();
+ }
+
+ return fwdnode(ast, i);
+ }
+ }
+ }
+ case ASTFN:
+ return constfoldblk(ctx, folds, scps, types, ast, toks,
+ ast.kids[i].rhs);
+ default:
+ __builtin_unreachable();
+ }
+}
+
+idx_t_
+constfolddecl(struct cfctx ctx, mpq_t *folds, struct scope *scps,
+ struct type *types, struct ast ast, struct lexemes toks, idx_t_ i)
+{
+ if (ast.kids[i].rhs == AST_EMPTY)
+ return fwdnode(ast, i);
+ ctx.decl = toks.strs[ast.lexemes[i]];
+ return constfoldexpr(ctx, folds, scps, types, ast, toks, ast.kids[i].rhs);
+}
+
+void
+constfold(mpq_t *folds, struct scope *scps, struct type *types, struct ast ast,
+ struct lexemes toks, arena *a)
+{
+ struct cfctx ctx = {.a = a};
+ for (idx_t_ i = 0; likely(i < ast.len);) {
+ assert(ast.kinds[i] <= _AST_DECLS_END);
+ i = constfolddecl(ctx, folds, scps, types, ast, toks, i);
+ }
+}
+
bool
returns(struct ast ast, idx_t_ i)
{