summaryrefslogtreecommitdiff
path: root/fn.c
diff options
context:
space:
mode:
Diffstat (limited to 'fn.c')
-rw-r--r--fn.c204
1 files changed, 204 insertions, 0 deletions
diff --git a/fn.c b/fn.c
new file mode 100644
index 0000000..4145b26
--- /dev/null
+++ b/fn.c
@@ -0,0 +1,204 @@
+#include "fn.h"
+#include "div.h"
+#include "errloc.h"
+#include "gl.h"
+#include "parse.h"
+#include "prv.h"
+#include "pr.h"
+#include "stmt.h"
+#include "storage.h"
+#include "td.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+static int pubfn(const struct lex *l, struct prv *p);
+static int prvfn(const struct lex *l, struct prv *p);
+static int end(const struct lex *l, struct prv *p);
+
+static const struct seq fnseq[] =
+{
+ {(const struct step[])
+ {{ID, "function"}, {ID}, {ID, "public"}, {0}}, .fn = pubfn, .end = end},
+ {(const struct step[])
+ {{ID, "function"}, {ID}, {0}}, .fn = prvfn, .end = end},
+ {0}
+};
+
+static int end(const struct lex *l, struct prv *p)
+{
+ const struct fn *fn = fn_cur(p);
+
+ if (!fn->pr)
+ {
+ const struct tk *tk = fn->tk;
+
+ errloc(tk, "function \"%s\" has no body", tk->s);
+ return -1;
+ }
+
+ return 0;
+}
+
+static const struct tk *findfn(const char *s, const struct ast *ast)
+{
+ for (size_t i = 0; i < ast->nfns; i++)
+ {
+ const struct tk *tk = ast->fns[i].tk;
+
+ if (!strcmp(tk->s, s))
+ return tk;
+ }
+
+ return NULL;
+}
+
+static int pushfn(const struct lex *lex, struct prv *p, enum linkage l)
+{
+ const struct tk *tk = p->stk + 1, *def;
+ struct ast *ast = p->ast;
+ size_t n = ast->nfns + 1;
+ struct fn *fns;
+ struct pos init =
+ {
+ .seq = divseq,
+ .stseq = divseq,
+ .step = divseq->steps
+ };
+
+ if ((def = findfn(tk->s, ast)))
+ {
+ const struct loc *loc = &def->loc;
+
+ errloc(tk, "function \"%s\" already defined at %s:%d:%d", tk->s,
+ loc->f, loc->line, loc->col);
+ return -1;
+ }
+ else if (!(fns = realloc(ast->fns, n * sizeof *fns)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+ else if (push(&init, p))
+ return -1;
+
+ fns[ast->nfns++] = (struct fn){.linkage = l, .tk = tk};
+ ast->fns = fns;
+ p->stk = p->tk;
+ fprintf(stderr, "adding %s function %s\n",
+ l == STATIC ? "private" : "public", tk->s);
+ return 1;
+}
+
+static int pubfn(const struct lex *l, struct prv *p)
+{
+ return pushfn(l, p, EXTERNAL);
+}
+
+static int prvfn(const struct lex *l, struct prv *p)
+{
+ return pushfn(l, p, STATIC);
+}
+
+const struct stentry *fn_var(const struct fn *fn, const struct tk *tk)
+{
+ const struct stentry *e = NULL;
+ const struct storage *lk = fn->lk, *ws = fn->ws, *gl = fn->gl;
+
+ if ((!lk || !(e = storage_find(tk->s, lk)))
+ && (!ws || !(e = storage_find(tk->s, ws)))
+ && (!gl || !(e = storage_find(tk->s, gl))))
+ return NULL;
+
+ return e;
+}
+
+int fn_cgen(const struct fn *fn, struct cgen *c)
+{
+ const struct pr *pr = fn->pr;
+ const struct param *ret = pr->ret;
+
+ if (fn->gl && gl_cgen(fn->gl, c))
+ return -1;
+
+ switch (fn->linkage)
+ {
+ case STATIC:
+ printf("function ");
+ break;
+ case EXTERNAL:
+ printf("export function ");
+ break;
+ }
+
+ if (ret)
+ printf("%s ", cgen_abity(ret->entry->t));
+
+ printf("$%s(", fn->tk->s);
+
+ for (size_t i = 0; i < pr->nparams; i++)
+ {
+ const struct param *p = &pr->params[i];
+
+ printf(" %s", cgen_abity(p->entry->t));
+ printf(" %%%s_ ", p->tk->s);
+
+ if (i + 1 < pr->nparams)
+ fputs(", ", stdout);
+ }
+
+ puts(") {\n@start");
+
+ for (size_t i = 0; i < fn->nstmts; i++)
+ if (stmt_cgen(&fn->stmts[i], c))
+ return -1;
+
+ fputs("ret", stdout);
+
+ if (pr->ret)
+ fputs(" %__ret", stdout);
+
+ puts("\n}");
+ return 0;
+}
+
+void fn_cgen_free(struct fn_cgen *c)
+{
+ free(c);
+}
+
+void fn_free(struct fn *fn)
+{
+ for (size_t i = 0; i < fn->nstmts; i++)
+ stmt_free(&fn->stmts[i]);
+
+ storage_free(fn->lk);
+ storage_free(fn->ws);
+ storage_free(fn->gl);
+ pr_free(fn->pr);
+ td_free(fn->td);
+ free(fn->stmts);
+}
+
+struct fn *fn_cur(const struct prv *p)
+{
+ struct ast *ast = p->ast;
+
+ return &ast->fns[p->ast->nfns - 1];
+}
+
+int fn(const struct lex *l, struct prv *p)
+{
+ struct pos init =
+ {
+ .seq = fnseq,
+ .stseq = fnseq,
+ .step = fnseq->steps
+ };
+
+ if (push(&init, p))
+ return -1;
+
+ p->stk = p->tk;
+ return 1;
+}