aboutsummaryrefslogtreecommitdiff
path: root/src/prc1
diff options
context:
space:
mode:
Diffstat (limited to 'src/prc1')
-rw-r--r--src/prc1/CMakeLists.txt39
-rw-r--r--src/prc1/call.c488
-rw-r--r--src/prc1/call.h37
-rw-r--r--src/prc1/cgen.c195
-rw-r--r--src/prc1/cgen.h23
-rw-r--r--src/prc1/display.c142
-rw-r--r--src/prc1/display.h17
-rw-r--r--src/prc1/div.c89
-rw-r--r--src/prc1/div.h11
-rw-r--r--src/prc1/end.c80
-rw-r--r--src/prc1/end.h18
-rw-r--r--src/prc1/errloc.c28
-rw-r--r--src/prc1/errloc.h9
-rw-r--r--src/prc1/exit.c8
-rw-r--r--src/prc1/exit.h16
-rw-r--r--src/prc1/fn.c273
-rw-r--r--src/prc1/fn.h22
-rw-r--r--src/prc1/from.c364
-rw-r--r--src/prc1/from.h34
-rw-r--r--src/prc1/gl.c24
-rw-r--r--src/prc1/gl.h12
-rw-r--r--src/prc1/im.c125
-rw-r--r--src/prc1/im.h11
-rw-r--r--src/prc1/kw.c41
-rw-r--r--src/prc1/lex.c249
-rw-r--r--src/prc1/lex.h34
-rw-r--r--src/prc1/lit.c102
-rw-r--r--src/prc1/lit.h12
-rw-r--r--src/prc1/lk.c12
-rw-r--r--src/prc1/lk.h9
-rw-r--r--src/prc1/parse.c183
-rw-r--r--src/prc1/parse.h138
-rw-r--r--src/prc1/pop.c37
-rw-r--r--src/prc1/pr.c375
-rw-r--r--src/prc1/pr.h11
-rw-r--r--src/prc1/prc1.c53
-rw-r--r--src/prc1/print.c103
-rw-r--r--src/prc1/print.h26
-rw-r--r--src/prc1/prv.h51
-rw-r--r--src/prc1/push.c27
-rw-r--r--src/prc1/sdup.c16
-rw-r--r--src/prc1/set.c552
-rw-r--r--src/prc1/set.h29
-rw-r--r--src/prc1/stmt.c92
-rw-r--r--src/prc1/stmt.h44
-rw-r--r--src/prc1/storage.c219
-rw-r--r--src/prc1/storage.h29
-rw-r--r--src/prc1/td.c587
-rw-r--r--src/prc1/td.h22
-rw-r--r--src/prc1/tmp.c69
-rw-r--r--src/prc1/tmp.h9
-rw-r--r--src/prc1/type.c459
-rw-r--r--src/prc1/type.h16
-rw-r--r--src/prc1/warn.c146
-rw-r--r--src/prc1/warn.h17
-rw-r--r--src/prc1/ws.c29
-rw-r--r--src/prc1/ws.h12
57 files changed, 5875 insertions, 0 deletions
diff --git a/src/prc1/CMakeLists.txt b/src/prc1/CMakeLists.txt
new file mode 100644
index 0000000..3579ca6
--- /dev/null
+++ b/src/prc1/CMakeLists.txt
@@ -0,0 +1,39 @@
+add_executable(prc1
+ call.c
+ cgen.c
+ display.c
+ div.c
+ end.c
+ errloc.c
+ exit.c
+ fn.c
+ from.c
+ gl.c
+ im.c
+ kw.c
+ lex.c
+ lit.c
+ lk.c
+ prc1.c
+ set.c
+ parse.c
+ pop.c
+ pr.c
+ print.c
+ push.c
+ sdup.c
+ stmt.c
+ storage.c
+ td.c
+ tmp.c
+ type.c
+ warn.c
+ ws.c
+)
+
+target_compile_options(prc1 PRIVATE -g -Wall -pedantic)
+set_target_properties(prc1 PROPERTIES C_STANDARD 99 C_EXTENSIONS OFF)
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ target_compile_options(prc1 PRIVATE -Og)
+endif()
+# install(TARGETS prc1)
diff --git a/src/prc1/call.c b/src/prc1/call.c
new file mode 100644
index 0000000..8140eba
--- /dev/null
+++ b/src/prc1/call.c
@@ -0,0 +1,488 @@
+#include "call.h"
+#include "errloc.h"
+#include "fn.h"
+#include "lex.h"
+#include "lit.h"
+#include "parse.h"
+#include "prv.h"
+#include "stmt.h"
+#include "type.h"
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int callpr(const struct lex *l, struct prv *p);
+static int callp(const struct lex *l, struct prv *p);
+static int callr(const struct lex *l, struct prv *p);
+static int callv(const struct lex *l, struct prv *p);
+
+static const struct seq seqs[] =
+{
+ {(struct step[]){
+ {ID},
+ {ID, "using"},
+ {ANY, NULL, 1},
+ {ID, "returning"},
+ {ID},
+ {0}}, .fn = callpr},
+ {(struct step[]){
+ {ID},
+ {ID, "using"},
+ {ANY, NULL, 1},
+ {0}}, .fn = callp},
+ {(struct step[]){
+ {ID},
+ {ID, "returning"},
+ {ID},
+ {0}}, .fn = callr},
+ {(struct step[]){
+ {ID},
+ {0}}, .fn = callv},
+ {0}
+};
+
+static int param(const struct lex *l, struct prv *p, const struct tk *tk,
+ struct call *c)
+{
+ const struct fn *fn = fn_cur(p);
+ size_t n = c->nparams + 1;
+ struct callparam *params;
+ const struct lit *lit = NULL;
+ const struct stentry *e = NULL;
+ const struct type *t = NULL;
+ int sign = 0;
+ union callv u = {0};
+
+ if (tk->type == LIT)
+ {
+ if (!(lit = lit_push(tk, p)))
+ return -1;
+ }
+ else if (tk->type == NUM)
+ {
+ if ((sign = *tk->s == '-'))
+ u.v = strtoll(tk->s, NULL, 0);
+ else
+ u.uv = strtoull(tk->s, NULL, 0);
+ }
+ else if ((t = type_find(fn, tk->s)))
+ {
+ if (t->type != C)
+ {
+ errloc(tk, "type \"%s\" not a constant", tk->s);
+ return -1;
+ }
+ }
+ else if (!(e = fn_var(fn, tk)))
+ {
+ errloc(tk, "undefined reference to parameter \"%s\"", tk->s);
+ return -1;
+ }
+
+ if (!(params = realloc(c->params, n * sizeof *params)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ c->params = params;
+ c->params[c->nparams++] = (struct callparam)
+ {
+ .entry = e,
+ .lit = lit,
+ .t = t,
+ .u = u
+ };
+
+ return 0;
+}
+
+static int callpr(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = p->stk + 2;
+ struct call *c = &stmt_cur(p)->u.call;
+ const struct pr *pr = c->pr;
+ const struct stentry *e;
+ size_t n = 0;
+
+ while (!lex_eof(l, tk) && !kw((tk++)->s))
+ n++;
+
+ if (pr->variadic)
+ {
+ if (n < pr->nparams)
+ {
+ errloc(tk, "function \"%s\" expects at least %zu parameters, "
+ "but %zu were given", c->tk->s, pr->nparams, n);
+ return -1;
+ }
+ }
+ else if (n != pr->nparams)
+ {
+ tk -= 2;
+ errloc(tk, "function \"%s\" expects %zu parameters, "
+ "but %zu were given", c->tk->s, pr->nparams, n);
+ return -1;
+ }
+
+ if (!pr->ret)
+ {
+ errloc(tk, "function \"%s\" does not expect any return variable",
+ c->tk->s);
+ return -1;
+ }
+
+ tk = p->stk + 2;
+
+ for (size_t i = 0; i < n; i++)
+ if (param(l, p, tk++, c))
+ return -1;
+
+ if (!(e = fn_var(fn_cur(p), ++tk)))
+ {
+ errloc(tk, "undefined reference to return variable \"%s\"",
+ tk->s);
+ return -1;
+ }
+ else if (pop(l, p))
+ return -1;
+
+ fprintf(stderr, "\t\tadding call to %s using %zu params "
+ "and %s as return variable\n", c->tk->s, n, tk->s);
+ c->ret = e;
+ c->nparams = n;
+ p->stk = ++tk;
+ return 1;
+}
+
+static int callr(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = p->stk + 2;
+ const struct fn *fn = fn_cur(p);
+ const struct stentry *e;
+ struct call *c = &stmt_cur(p)->u.call;
+ const struct pr *pr = c->pr;
+
+ if (!pr->ret)
+ {
+ errloc(tk, "function \"%s\" does not expect any return variable",
+ c->tk->s);
+ return -1;
+ }
+ else if (pr->nparams)
+ {
+ errloc(tk, "function \"%s\" expects %zu parameters, "
+ "but none were given", c->tk->s, pr->nparams);
+ return -1;
+ }
+ else if (!(e = fn_var(fn, tk)))
+ {
+ errloc(tk, "undefined reference to return variable \"%s\"", tk->s);
+ return -1;
+ }
+ else if (pop(l, p))
+ return -1;
+
+ fprintf(stderr, "\t\tadding call to %s with %s as return variable\n",
+ c->tk->s, tk->s);
+ c->ret = e;
+ p->stk = ++tk;
+ return 1;
+}
+
+static int callp(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = p->stk + 2;
+ struct call *c = &stmt_cur(p)->u.call;
+ const struct pr *pr = c->pr;
+ size_t n = 0;
+
+ while (!lex_eof(l, tk) && !kw((tk++)->s))
+ n++;
+
+ if (lex_eof(l, tk))
+ tk--;
+
+ if (pr->variadic)
+ {
+ if (n < pr->nparams)
+ {
+ errloc(tk, "function \"%s\" expects at least %zu parameters, "
+ "but %zu were given", c->tk->s, pr->nparams, n);
+ return -1;
+ }
+ }
+ else if (pr->nparams != n)
+ {
+ errloc(tk, "function \"%s\" expects %zu parameters, "
+ "but %zu were given", c->tk->s, pr->nparams, n);
+ return -1;
+ }
+
+ tk = p->stk + 2;
+
+ for (size_t i = 0; i < n; i++)
+ if (param(l, p, tk++, c))
+ return -1;
+
+ if (pop(l, p))
+ return -1;
+
+ fprintf(stderr, "\t\tadding call to %s with 1 param\n", c->tk->s);
+ p->stk = tk;
+ return 1;
+}
+
+static int callv(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = p->stk;
+ struct call *c = &stmt_cur(p)->u.call;
+ const struct pr *pr = c->pr;
+
+ if (pr->nparams)
+ {
+ errloc(tk, "function \"%s\" expects %zu parameters, "
+ "but none were given", c->tk->s, pr->nparams);
+ return -1;
+ }
+ else if (pop(l, p))
+ return -1;
+
+ fprintf(stderr, "\t\tadding call to %s with %s as return variable\n",
+ c->tk->s, tk->s);
+ p->stk = ++tk;
+ return 1;
+}
+
+static const struct pr *find_proto(const struct fn *fn, const char *s)
+{
+ const struct type *t = type_find(fn, s);
+
+ if (t && t->type == P)
+ return t->u.p.fn->pr;
+
+ return NULL;
+}
+
+static const struct pr *find(const struct ast *ast, const char *s)
+{
+ for (size_t i = 0; i < ast->nfns; i++)
+ {
+ const struct fn *fn = &ast->fns[i];
+ const struct pr *pr;
+
+ if ((pr = find_proto(fn, s)))
+ return pr;
+ else if (!strcmp(fn->tk->s, s))
+ return fn->pr;
+ }
+
+ return NULL;
+}
+
+static int gettmp(char *tmp, size_t n)
+{
+ size_t j;
+
+ for (j = 0; j < n; j++)
+ {
+ char c = tmp[j];
+
+ if (!c)
+ return tmp[j] = 'a';
+ else if (c != 'z')
+ return ++(tmp[j]);
+ }
+
+ return EOF;
+}
+
+static int param_lit(const struct lit *lit, struct cgen *c)
+{
+ printf("l $%s", lit->name);
+ return 0;
+}
+
+static int param_id(const struct stentry *e, struct cgen *c, char *tmp,
+ size_t n)
+{
+ const char *name = e->tk->s;
+ const struct type *t = e->t;
+
+ printf("%s ", cgen_sz(t->sz));
+
+ if (cgen_global(c->fn, e))
+ printf("$%s", name);
+ else if (cgen_abity(t))
+ {
+ if (gettmp(tmp, n) == EOF)
+ return -1;
+
+ printf("%%%s", tmp);
+ }
+ else
+ printf("%%%s_", name);
+
+ return 0;
+}
+
+static int param_c(const struct type *t, struct cgen *cg)
+{
+ const union c *c = &t->u.c;
+
+ /* TODO: adapt type to expected param size */
+ fputs("l ", stdout);
+
+ if (t->sign)
+ printf("%lld", c->v);
+ else
+ printf("%llu", c->uv);
+
+ return 0;
+}
+
+static int param_num(const struct callparam *cp, struct cgen *c)
+{
+ const union callv *u = &cp->u;
+
+ /* TODO: adapt type to expected param size */
+ fputs("w ", stdout);
+
+ if (cp->sign)
+ printf("%lld", u->v);
+ else
+ printf("%llu", u->uv);
+
+ return 0;
+}
+
+static int param_load(const struct call *m, struct cgen *c)
+{
+ char tmp[sizeof "abcdefghijklmnopqrstuvwxyz"] = {0};
+
+ for (size_t i = 0; i < m->nparams; i++)
+ {
+ const struct callparam *cp = &m->params[i];
+ const struct stentry *e = cp->entry;
+
+ if (!e || cgen_global(c->fn, e) || !cgen_abity(e->t))
+ continue;
+ else if (gettmp(tmp, sizeof tmp - 1) == EOF)
+ {
+ fprintf(stderr, "%s: exhausted temporaries\n", __func__);
+ return -1;
+ }
+
+ printf("%%%s =%s load%s %%%s_\n", tmp, cgen_sz(e->t->sz),
+ cgen_load(e->t), e->tk->s);
+ }
+
+ return 0;
+}
+
+int call_cgen(const struct call *m, struct cgen *c)
+{
+ char tmps[sizeof "abcdefghijklmnopqrstuvwxyz"] = {0};
+ const struct stentry *ret = m->ret;
+ const char *tmp;
+ size_t sz;
+
+ if (param_load(m, c))
+ return -1;
+ else if (ret)
+ {
+ tmp = cgen_tmp((sz = ret->t->sz));
+ printf("%%%s =%s ", tmp, cgen_sz(sz));
+ }
+
+ printf("call $%s(", m->tk->s);
+
+ for (size_t i = 0; i < m->nparams; i++)
+ {
+ const struct callparam *cp = &m->params[i];
+ int var = m->pr->variadic;
+
+ if (cp->lit)
+ param_lit(cp->lit, c);
+ else if (cp->entry)
+ param_id(cp->entry, c, tmps, sizeof tmps - 1);
+ else if (cp->t)
+ param_c(cp->t, c);
+ else
+ param_num(cp, c);
+
+ if (var && i + 1 == var - 1)
+ fputs(", ...", stdout);
+
+ if (i + 1 < m->nparams)
+ fputs(", ", stdout);
+ }
+
+ puts(")");
+
+ if (ret)
+ {
+ printf("store%s %%%s, ", cgen_sz(sz), tmp);
+ cgen_printvar(c->fn, ret);
+ putchar('\n');
+ }
+
+ return 0;
+}
+
+void call_free(struct call *c)
+{
+ if (c)
+ free(c->params);
+}
+
+int call(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = p->tk;
+ const struct pr *pr;
+ struct fn *fn = fn_cur(p);
+ size_t n = fn->nstmts +1;
+ struct stmt *stmts;
+ struct pos init =
+ {
+ .seq = seqs,
+ .stseq = seqs,
+ .step = seqs->steps
+ };
+
+ /* TODO: function pointers */
+
+ if (lex_eof(l, tk))
+ {
+ errloc(tk - 1, "incomplete call statement");
+ return -1;
+ }
+ else if (!(pr = find(p->ast, tk->s)))
+ {
+ errloc(tk, "undefined reference to function \"%s\"", tk->s);
+ return -1;
+ }
+ else if (!(stmts = realloc(fn->stmts, n * sizeof *stmts)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ fn->stmts = stmts;
+ fn->stmts[fn->nstmts++] = (struct stmt)
+ {
+ .type = CALL,
+ .u.call =
+ {
+ .tk = tk,
+ .pr = pr
+ }
+ };
+
+ if (push(&init, p))
+ return -1;
+
+ p->stk = p->tk;
+ return 1;
+}
diff --git a/src/prc1/call.h b/src/prc1/call.h
new file mode 100644
index 0000000..0126e7f
--- /dev/null
+++ b/src/prc1/call.h
@@ -0,0 +1,37 @@
+#ifndef CALL_H
+#define CALL_H
+
+#include "cgen.h"
+#include "lex.h"
+#include "parse.h"
+#include "prv.h"
+#include <stddef.h>
+
+struct callparam
+{
+ const struct lit *lit;
+ const struct type *t;
+ const struct stentry *entry;
+ int sign;
+
+ union callv
+ {
+ long long v;
+ unsigned long long uv;
+ } u;
+};
+
+struct call
+{
+ const struct tk *tk;
+ const struct pr *pr;
+ const struct stentry *ret;
+ struct callparam *params;
+ size_t nparams;
+};
+
+int call(const struct lex *l, struct prv *p);
+int call_cgen(const struct call *m, struct cgen *c);
+void call_free(struct call *c);
+
+#endif
diff --git a/src/prc1/cgen.c b/src/prc1/cgen.c
new file mode 100644
index 0000000..44e9d24
--- /dev/null
+++ b/src/prc1/cgen.c
@@ -0,0 +1,195 @@
+#include "cgen.h"
+#include "fn.h"
+#include "lex.h"
+#include "lit.h"
+#include "parse.h"
+#include "storage.h"
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+void cgen_free(struct cgen *c)
+{
+}
+
+const char *cgen_sz(const size_t sz)
+{
+ switch (sz)
+ {
+ case 1:
+ return "b";
+ case 2:
+ return "h";
+ case 4:
+ return "w";
+ case 8:
+ return "l";
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+const char *cgen_tmp(const size_t sz)
+{
+ switch (sz)
+ {
+ case 1:
+ return "vb";
+ case 2:
+ return "vh";
+ case 4:
+ return "vw";
+ case 8:
+ return "vl";
+ default:
+ break;
+ }
+
+ return "v";
+}
+
+const char *cgen_type(const struct type *t)
+{
+ switch (t->sz)
+ {
+ case 1:
+ return t->sign ? "b" : "ub";
+ case 2:
+ return t->sign ? "h" : "uh";
+ case 4:
+ return t->sign ? "w" : "uw";
+ case 8:
+ return t->sign ? "l" : "ul";
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+const char *cgen_abity(const struct type *t)
+{
+ switch (t->sz)
+ {
+ case 1:
+ return t->sign ? "b" : "ub";
+ case 2:
+ return t->sign ? "h" : "uh";
+ case 4:
+ return "w";
+ case 8:
+ return "l";
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+const char *cgen_load(const struct type *t)
+{
+ switch (t->sz)
+ {
+ case 1:
+ return t->sign ? "sb" : "ub";
+ case 2:
+ return t->sign ? "sh" : "uh";
+ case 4:
+ return t->sign ? "sw" : "uw";
+ case 8:
+ return "l";
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+int cgen_param(const struct fn *fn, const struct stentry *e)
+{
+ const struct pr *pr = fn->pr;
+ const char *name = e->tk->s;
+
+ for (size_t i = 0; i < pr->nparams; i++)
+ if (!strcmp(pr->params[i].tk->s, name))
+ return 1;
+
+ return 0;
+}
+
+void cgen_printvar(const struct fn *fn, const struct stentry *e)
+{
+ const char *name = e->tk->s;
+
+ if (cgen_global(fn, e))
+ printf("$%s", name);
+ else
+ printf("%%%s_", name);
+}
+
+int cgen_global(const struct fn *fn, const struct stentry *e)
+{
+ const struct storage *ws = fn->ws, *gl = fn->gl;
+ const struct pr *pr = fn->pr;
+ const struct param *ret = pr->ret;
+ const char *name = e->tk->s;
+
+ /* A variable is only global if it is defined inside the global
+ * variables section ("gl"), or if it is not listed on the working
+ * storage section ("ws"), as a function parameter or a return value. */
+
+ if (gl)
+ for (size_t i = 0; i < gl->nentries; i++)
+ if (!strcmp(gl->entries[i].tk->s, name))
+ return 1;
+
+ if (cgen_param(fn, e) || (ret && !strcmp(ret->tk->s, name)))
+ return 0;
+ else if (ws)
+ for (size_t i = 0; i < ws->nentries; i++)
+ if (!strcmp(ws->entries[i].tk->s, name))
+ return 0;
+
+ for (const struct tmp *t = fn->tmps; t; t = t->next)
+ if (!strcmp(t->tk.s, name))
+ return 0;
+
+ return 1;
+}
+
+static void pushfmt(const char *type, const char *fmt)
+{
+ printf("data $____fmt%s = { b \"%s\" , b 0 }\n", type, fmt);
+}
+
+int cgen(const struct ast *ast, struct cgen *c)
+{
+ /* generate literals to use with printf(3) */
+ pushfmt("b", "%hhd");
+ pushfmt("h", "%hd");
+ pushfmt("w", "%d");
+ pushfmt("l", "%ld");
+ pushfmt("ub", "%hhu");
+ pushfmt("uh", "%hu");
+ pushfmt("uw", "%u");
+ pushfmt("ul", "%lu");
+ pushfmt("bx", "%#hhx");
+ pushfmt("hx", "%#hx");
+ pushfmt("wx", "%#x");
+ pushfmt("lx", "%#lx");
+
+ for (const struct lit *l = ast->lits; l; l = l->next)
+ if (lit_cgen(l))
+ return-1;
+
+ for (size_t i = 0; i < ast->nfns; i++)
+ if (fn_cgen((c->fn = &ast->fns[i]), c))
+ return -1;
+
+ return 0;
+}
diff --git a/src/prc1/cgen.h b/src/prc1/cgen.h
new file mode 100644
index 0000000..0a30138
--- /dev/null
+++ b/src/prc1/cgen.h
@@ -0,0 +1,23 @@
+#ifndef CGEN_H
+#define CGEN_H
+
+#include "parse.h"
+#include "storage.h"
+
+struct cgen
+{
+ const struct fn *fn;
+};
+
+int cgen(const struct ast *ast, struct cgen *c);
+int cgen_param(const struct fn *fn, const struct stentry *e);
+int cgen_global(const struct fn *fn, const struct stentry *e);
+void cgen_printvar(const struct fn *fn, const struct stentry *e);
+const char *cgen_sz(const size_t sz);
+const char *cgen_tmp(const size_t sz);
+const char *cgen_type(const struct type *t);
+const char *cgen_abity(const struct type *t);
+const char *cgen_load(const struct type *t);
+void cgen_free(struct cgen *c);
+
+#endif
diff --git a/src/prc1/display.c b/src/prc1/display.c
new file mode 100644
index 0000000..885fbe7
--- /dev/null
+++ b/src/prc1/display.c
@@ -0,0 +1,142 @@
+#include "display.h"
+#include "cgen.h"
+#include "parse.h"
+#include "prv.h"
+#include "stmt.h"
+#include "storage.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int finalize(const struct lex *l, const struct prv *p,
+ const struct print *pr)
+{
+ const struct ast *ast = p->ast;
+ struct fn *fn = &ast->fns[ast->nfns - 1];
+ size_t n = fn->nstmts + 1;
+ struct stmt *stmts = realloc(fn->stmts, n * sizeof *stmts);
+
+ if (!stmts)
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ fn->stmts = stmts;
+ fn->stmts[fn->nstmts++] = (struct stmt)
+ {
+ .type = DISPLAY,
+ .u.display.p = *pr
+ };
+
+ fprintf(stderr, "\t\tadding display statement with %zu entries%s\n",
+ pr->nentries, pr->println ? "" : ", no lf");
+ return 0;
+}
+
+static int cg_val(const struct tk *tk)
+{
+ const char *num = tk->s;
+ int neg = *num == '-';
+ unsigned long long uv;
+ char *end;
+
+ fputs("call $printf(l $____fmt", stdout);
+ errno = 0;
+ uv = strtoull(num, &end, 16);
+
+ if (!neg && !errno && !*end)
+ printf("lx, ..., l %llu )\n", uv);
+ else
+ {
+ errno = 0;
+ uv = strtoull(num, &end, 0);
+
+ if (neg || errno || *end)
+ {
+ long long v = strtoll(num, NULL, 0);
+ printf("l, ..., l %lld )\n", v);
+ }
+ else
+ printf("l, ..., l %llu )\n", uv);
+ }
+
+ return 0;
+}
+
+static int cg_lit(const struct prientry *e)
+{
+ const struct lit *l = e->lit;
+ const char *tmp;
+
+ if (!l)
+ {
+ fprintf(stderr, "%s: unexpected null literal\n", __func__);
+ return -1;
+ }
+
+ printf("%%%s =l loadl $stdout\n", (tmp = cgen_tmp(sizeof (void *))));
+ printf("call $fputs(l $%s, w %%%s)\n", l->name, tmp);
+ return 0;
+}
+
+static int cg_id(const struct prientry *de, const struct fn *fn)
+{
+ const struct stentry *e = de->entry;
+ const struct type *t = e->t;
+ const char *id = e->tk->s;
+ int global = cgen_global(fn, e);
+ size_t sz = t->sz;
+ const char *asz = cgen_sz(sz < 4 ? 4 : sz), *tmp = cgen_tmp(sz);
+
+ if (global)
+ printf("%%%s =%s load%s $%s\n", tmp, asz, cgen_load(t), id);
+ else
+ printf("%%%s =%s load%s %%%s_\n", tmp, asz, cgen_load(t), id);
+
+ fputs("call $printf(l $____fmt", stdout);
+
+ if (t->type == PTR)
+ fputs("lx, ..., l ", stdout);
+ else
+ printf("%s, ..., %s ", cgen_type(t), asz);
+
+ printf("%%%s)\n", tmp);
+
+ return 0;
+}
+
+int display_cgen(const struct display *d, struct cgen *c)
+{
+ const struct print *p = &d->p;
+
+ for (size_t i = 0; i < p->nentries; i++)
+ {
+ const struct prientry *e = &p->entries[i];
+ const struct tk *tk = e->tk;
+
+ if (tk->type == LIT && cg_lit(e))
+ return -1;
+ else if (tk->type == NUM && cg_val(tk))
+ return -1;
+ else if (tk->type == ID && e->entry && cg_id(e, c->fn))
+ return -1;
+ else if (tk->type == ID && e->lit && cg_lit(e))
+ return -1;
+ }
+
+ if (p->println)
+ puts("call $putchar(l 10)");
+
+ return 0;
+}
+
+void display_free(struct display *d)
+{
+ print_free(&d->p);
+}
+
+int display(const struct lex *l, struct prv *p)
+{
+ return print(l, p, finalize);
+}
diff --git a/src/prc1/display.h b/src/prc1/display.h
new file mode 100644
index 0000000..9b073b1
--- /dev/null
+++ b/src/prc1/display.h
@@ -0,0 +1,17 @@
+#ifndef DISPLAY_H
+#define DISPLAY_H
+
+#include "lex.h"
+#include "print.h"
+#include "prv.h"
+
+struct display
+{
+ struct print p;
+};
+
+int display(const struct lex *l, struct prv *p);
+int display_cgen(const struct display *d, struct cgen *c);
+void display_free(struct display *d);
+
+#endif
diff --git a/src/prc1/div.c b/src/prc1/div.c
new file mode 100644
index 0000000..9061ac6
--- /dev/null
+++ b/src/prc1/div.c
@@ -0,0 +1,89 @@
+#include "div.h"
+#include "gl.h"
+#include "im.h"
+#include "lk.h"
+#include "pr.h"
+#include "td.h"
+#include "ws.h"
+
+static const struct seq linkage =
+ {(struct step[]){{ID, "linkage"}, {0}}, .fn = lk},
+ types = {(struct step[]){{ID, "types"}, {0}}, .fn = td},
+ imports = {(struct step[]){{ID, "imports"}, {0}}, .fn = im},
+ procedure = {(struct step[]){{ID, "procedure"}, {0}}, .fn = pr};
+
+static const struct seq fnseq[] =
+{
+ linkage,
+ types,
+ imports,
+ procedure,
+ {(struct step[]){{ID, "storage"}, {0}}, .fn = ws},
+ {(struct step[]){{ID, "globals"}, {0}}, .fn = gl},
+ {0}
+};
+
+static const struct seq imseq[] =
+{
+ linkage,
+ types,
+ imports,
+ {0}
+};
+
+static const struct seq pseq[] =
+{
+ linkage,
+ procedure,
+ {0}
+};
+
+int div_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 0;
+}
+
+int div_im(const struct lex *l, struct prv *p)
+{
+ struct pos init =
+ {
+ .seq = imseq,
+ .stseq = imseq,
+ .step = imseq->steps
+ };
+
+ if (lex_eof(l, p->stk))
+ return 0;
+ else if (push(&init, p))
+ return -1;
+
+ p->stk = p->tk;
+ return 1;
+}
+
+int div_p(const struct lex *l, struct prv *p)
+{
+ struct pos init =
+ {
+ .seq = pseq,
+ .stseq = pseq,
+ .step = pseq->steps
+ };
+
+ if (push(&init, p))
+ return -1;
+
+ p->stk = p->tk;
+ return 1;
+}
diff --git a/src/prc1/div.h b/src/prc1/div.h
new file mode 100644
index 0000000..8a0df6d
--- /dev/null
+++ b/src/prc1/div.h
@@ -0,0 +1,11 @@
+#ifndef DIV_H
+#define DIV_H
+
+#include "lex.h"
+#include "prv.h"
+
+int div_fn(const struct lex *l, struct prv *p);
+int div_im(const struct lex *l, struct prv *p);
+int div_p(const struct lex *l, struct prv *p);
+
+#endif
diff --git a/src/prc1/end.c b/src/prc1/end.c
new file mode 100644
index 0000000..b9ad790
--- /dev/null
+++ b/src/prc1/end.c
@@ -0,0 +1,80 @@
+#include "end.h"
+#include "errloc.h"
+#include "fn.h"
+#include "stmt.h"
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void end_free(struct end *e)
+{
+}
+
+int end_cgen(const struct end *e, struct cgen *c)
+{
+ const struct stmt *s = &c->fn->stmts[e->block_i];
+
+ if (!s->end)
+ {
+ fprintf(stderr, "%s: unreachable\n", __func__);
+ return -1;
+ }
+ else if (s->end(s, c))
+ return -1;
+
+ printf("@__end%zu\n", e->block_i);
+ return 0;
+}
+
+int end(const struct lex *l, struct prv *p)
+{
+ struct pos *pos = &p->pos[p->i];
+ struct fn *fn = fn_cur(p);
+ size_t nst = fn->nstmts + 1, *bl, bl_i;
+ struct stmt *s;
+
+ if (!fn->nblocks)
+ {
+ errloc(p->stk, "unexpected \"end\" statement");
+ return -1;
+ }
+
+ bl_i = fn->blocks[fn->nblocks - 1];
+
+ if (fn->nblocks > 1)
+ {
+ if (!(bl = realloc(fn->blocks, (fn->nblocks - 1) * sizeof *bl)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ fn->blocks = bl;
+ }
+ else
+ {
+ free(fn->blocks);
+ fn->blocks = NULL;
+ }
+
+ fn->nblocks--;
+
+ if (!(s = realloc(fn->stmts, nst * sizeof *s)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ s[fn->nstmts++] = (struct stmt)
+ {
+ .type = END,
+ .u.end.block_i = bl_i
+ };
+
+ fn->stmts = s;
+ pos->seq = pos->stseq = stmts;
+ pos->step = stmts->steps;
+ p->stk = p->tk;
+ fprintf(stderr, "\t\tadding end statement, block index %zu\n", bl_i);
+ return 1;
+}
diff --git a/src/prc1/end.h b/src/prc1/end.h
new file mode 100644
index 0000000..5286f4f
--- /dev/null
+++ b/src/prc1/end.h
@@ -0,0 +1,18 @@
+#ifndef END_H
+#define END_H
+
+#include "cgen.h"
+#include "lex.h"
+#include "prv.h"
+#include <stddef.h>
+
+struct end
+{
+ size_t block_i;
+};
+
+int end(const struct lex *l, struct prv *p);
+int end_cgen(const struct end *e, struct cgen *c);
+void end_free(struct end *e);
+
+#endif
diff --git a/src/prc1/errloc.c b/src/prc1/errloc.c
new file mode 100644
index 0000000..1f3e224
--- /dev/null
+++ b/src/prc1/errloc.c
@@ -0,0 +1,28 @@
+#include "errloc.h"
+#include "lex.h"
+#include <stdarg.h>
+#include <stdio.h>
+
+void errcloc(const struct lex *l, const char *fmt, ...)
+{
+ const struct loc *loc = &l->loc;
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d:%d: error: ", loc->f, loc->line, loc->col);
+ vfprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ va_end(ap);
+}
+
+void errloc(const struct tk *tk, const char *fmt, ...)
+{
+ const struct loc *loc = &tk->loc;
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d:%d: error: ", loc->f, loc->line, loc->col);
+ vfprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ va_end(ap);
+}
diff --git a/src/prc1/errloc.h b/src/prc1/errloc.h
new file mode 100644
index 0000000..127caa7
--- /dev/null
+++ b/src/prc1/errloc.h
@@ -0,0 +1,9 @@
+#ifndef ERRLOC_H
+#define ERRLOC_H
+
+#include "lex.h"
+
+void errloc(const struct tk *tk, const char *fmt, ...);
+void errcloc(const struct lex *l, const char *fmt, ...);
+
+#endif
diff --git a/src/prc1/exit.c b/src/prc1/exit.c
new file mode 100644
index 0000000..0438b66
--- /dev/null
+++ b/src/prc1/exit.c
@@ -0,0 +1,8 @@
+#include "exit.h"
+#include "prv.h"
+
+int s_exit(const struct lex *l, struct prv *p)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}
diff --git a/src/prc1/exit.h b/src/prc1/exit.h
new file mode 100644
index 0000000..24b1292
--- /dev/null
+++ b/src/prc1/exit.h
@@ -0,0 +1,16 @@
+#ifndef EXIT_H
+#define EXIT_H
+
+#include "lex.h"
+#include "prv.h"
+#include <stddef.h>
+
+struct exit
+{
+ int dummy;
+};
+
+int s_exit(const struct lex *l, struct prv *p);
+void exit_free(struct exit *d);
+
+#endif
diff --git a/src/prc1/fn.c b/src/prc1/fn.c
new file mode 100644
index 0000000..c2d3b4f
--- /dev/null
+++ b/src/prc1/fn.c
@@ -0,0 +1,273 @@
+#include "fn.h"
+#include "div.h"
+#include "errloc.h"
+#include "gl.h"
+#include "im.h"
+#include "parse.h"
+#include "prv.h"
+#include "pr.h"
+#include "stmt.h"
+#include "storage.h"
+#include "td.h"
+#include "ws.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 const struct seq fnseq[] =
+{
+ {(struct step[])
+ {{ID, "public"}, {ID, "function"}, {ID}, {0}}, .fn = pubfn,
+ .end = fn_end},
+ {(struct step[])
+ {{ID, "function"}, {ID}, {0}}, .fn = prvfn, .end = fn_end},
+ {0}
+};
+
+int fn_end(const struct lex *l, struct prv *p)
+{
+ const struct fn *fn = fn_cur(p);
+
+ if (!fn->pr)
+ {
+ const struct tk *tk = fn->tk;
+ const char *s = p->proto ? "prototype" : "function";
+
+ errloc(p->stk - 1, "%s \"%s\" has no procedure", s, tk->s);
+ return -1;
+ }
+ else if (fn->nblocks)
+ {
+ const struct tk *tk = lex_eof(l, p->stk) ? p->tk - 1 : p->stk;
+
+ errloc(tk, "missing \"end\" in function \"%s\"", fn->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 tk *tk, const struct lex *lex, struct prv *p,
+ enum linkage l)
+{
+ const struct tk *def;
+ struct ast *ast = p->ast;
+ size_t n = ast->nfns + 1;
+ struct fn *fns;
+
+ 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;
+ }
+
+ fns[ast->nfns++] = (struct fn){.linkage = l, .tk = tk};
+ ast->fns = fns;
+
+ if (div_fn(lex, p))
+ return -1;
+
+ 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(p->stk + 2, l, p, EXTERNAL);
+}
+
+static int prvfn(const struct lex *l, struct prv *p)
+{
+ return pushfn(p->stk + 1, l, p, STATIC);
+}
+
+const struct stentry *fn_var(const struct fn *fn, const struct tk *tk)
+{
+ const struct storage *lk = fn->lk, *ws = fn->ws, *gl = fn->gl;
+ const struct stentry *e;
+
+ if ((lk && (e = storage_find(tk->s, lk)))
+ || (ws && (e = storage_find(tk->s, ws)))
+ || (gl && (e = storage_find(tk->s, gl))))
+ return e;
+
+ for (size_t i = 0; i < fn->nimps; i++)
+ if ((e = fn_var(fn->imps[i].ast.fns, tk)))
+ return e;
+
+ return NULL;
+}
+
+int fn_cgen(const struct fn *fn, struct cgen *c)
+{
+ const struct pr *pr = fn->pr;
+ const struct param *ret = pr->ret;
+ const struct type *rt = NULL;
+ const char *s = NULL;
+
+ 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)
+ {
+ rt = ret->entry->t;
+ s = ret->entry->tk->s;
+ 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(" %%__param_%s ", p->tk->s);
+
+ if (i + 1 < pr->nparams)
+ fputs(", ", stdout);
+ }
+
+ puts(") {\n@start");
+
+ for (size_t i = 0; i < pr->nparams; i++)
+ {
+ const struct param *p = &pr->params[i];
+ const struct type *t = p->entry->t;
+ const char *s = p->tk->s;
+
+ printf("%%%s_ =l alloc%zu %zu\n", s, t->align, t->sz);
+ printf("store%s %%__param_%s, %%%s_\n", cgen_sz(t->sz), s, s);
+ }
+
+ if (ret)
+ printf("%%%s_ =l alloc%zu %zu\n", s, rt->align, rt->sz);
+
+ if (fn->ws && ws_cgen(fn->ws, c))
+ return -1;
+
+ for (const struct tmp *tmp = fn->tmps; tmp; tmp = tmp->next)
+ {
+ const struct stentry *e = &tmp->e;
+ const struct type *t = tmp->e.t;
+
+ printf("%%%s_ =l alloc%zu %zu\n", e->tk->s, t->align, t->sz);
+ }
+
+ for (size_t i = 0; i < fn->nstmts; i++)
+ if (stmt_cgen(&fn->stmts[i], c))
+ return -1;
+
+ if (ret)
+ {
+ const char *t = cgen_tmp(rt->sz);
+
+ printf("%%%s =%s load%s %%%s_\n", t, cgen_sz(rt->sz), cgen_load(rt), s);
+ printf("ret %%%s", t);
+ }
+ else
+ 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->nimps; i++)
+ im_free(&fn->imps[i]);
+
+ for (size_t i = 0; i < fn->nstmts; i++)
+ stmt_free(&fn->stmts[i]);
+
+ for (struct tmp *t = fn->tmps; t;)
+ {
+ struct tmp *next = t->next;
+
+ free(t->tk.s);
+ free(t);
+ t = next;
+ }
+
+ storage_free(fn->lk);
+ storage_free(fn->ws);
+ storage_free(fn->gl);
+ pr_free(fn->pr);
+ td_free(fn->td);
+ free(fn->imps);
+ free(fn->stmts);
+ free(fn->blocks);
+}
+
+struct fn *fn_cur(const struct prv *p)
+{
+ struct ast *ast = p->ast;
+
+ if (p->proto)
+ return p->proto;
+
+ 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 (lex_eof(l, p->stk))
+ return 0;
+ else if (lex_eof(l, p->stk + 1))
+ {
+ errloc(p->stk, "incomplete function statement");
+ return -1;
+ }
+ else if (push(&init, p))
+ return -1;
+
+ p->stk = p->tk;
+ return 1;
+}
diff --git a/src/prc1/fn.h b/src/prc1/fn.h
new file mode 100644
index 0000000..6c65ae5
--- /dev/null
+++ b/src/prc1/fn.h
@@ -0,0 +1,22 @@
+#ifndef FN_H
+#define FN_H
+
+#include "cgen.h"
+#include "lex.h"
+#include "parse.h"
+#include "prv.h"
+
+struct fn_cgen
+{
+ int dummy;
+};
+
+int fn(const struct lex *l, struct prv *p);
+struct fn *fn_cur(const struct prv *p);
+int fn_cgen(const struct fn *fn, struct cgen *c);
+const struct stentry *fn_var(const struct fn *fn, const struct tk *tk);
+int fn_end(const struct lex *l, struct prv *p);
+void fn_free(struct fn *fn);
+void fn_cgen_free(struct fn_cgen *c);
+
+#endif
diff --git a/src/prc1/from.c b/src/prc1/from.c
new file mode 100644
index 0000000..b9cb99e
--- /dev/null
+++ b/src/prc1/from.c
@@ -0,0 +1,364 @@
+#include "from.h"
+#include "cgen.h"
+#include "fn.h"
+#include "errloc.h"
+#include "stmt.h"
+#include "storage.h"
+#include "tmp.h"
+#include "type.h"
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void from_free(struct from *fr)
+{
+}
+
+static int loadvar(const struct from_it *st, struct cgen *c)
+{
+ const struct stentry *e = st->u.e;
+ size_t sz = e->t->sz;
+ const char *tmp = cgen_tmp(sz);
+
+ printf("%%%s =%s load%s ", tmp, cgen_sz(sz), cgen_load(e->t));
+ cgen_printvar(c->fn, e);
+ putchar('\n');
+ return 0;
+}
+
+/* TODO: de-duplicate function */
+static unsigned long long tonum(const char *s)
+{
+ unsigned long long v;
+ char *end;
+
+ errno = 0;
+ v = strtoull(s, &end, 0);
+
+ if (*s == '-' || errno || *end)
+ return strtoll(s, NULL, 0);
+
+ return v;
+}
+
+static int loadval(const struct from *fr, struct cgen *c)
+{
+ const struct stentry *dst = fr->var;
+ const struct from_it *st = &fr->start;
+ unsigned long long v = tonum(st->u.tk->s);
+
+ printf("store%s %llu, ", cgen_sz(dst->t->sz), v);
+ cgen_printvar(c->fn, dst);
+ putchar('\n');
+ return 0;
+}
+
+static int endvar(const struct from *fr, struct cgen *c)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}
+
+static int endval(const struct from *fr, struct cgen *c)
+{
+ const struct stentry *src = fr->var;
+ size_t sz = src->t->sz;
+ const char *tmp = cgen_tmp(sz);
+ const struct from_it *end = &fr->end;
+ const struct tk *tk = end->u.tk;
+ int neg = *tk->s == '-';
+ size_t bl = fr->block_i;
+
+ printf("%%%s =%s load%s ", tmp, cgen_sz(sz), cgen_load(src->t));
+ cgen_printvar(c->fn, src);
+ putchar('\n');
+ printf("%%%s =%s c", tmp, cgen_sz(sz));
+ fputs(neg ? "sge" : "uge", stdout);
+ printf("%s ", cgen_sz(sz));
+ printf("%%%s, %s\n", tmp, tk->s);
+ printf("jnz %%%s, @__end%zu, @__blockbody%zu\n", tmp, bl, bl);
+ printf("@__blockbody%zu\n", bl);
+ return 0;
+}
+
+static int from_var(const struct from *fr, struct cgen *c)
+{
+ const struct from_it *st = &fr->start;
+
+ switch (st->type)
+ {
+ case FR_VAR:
+ if (loadvar(st, c))
+ return -1;
+ break;
+
+ case FR_VAL:
+ if (loadval(fr, c))
+ return -1;
+ break;
+ }
+
+ printf("@__block%zu\n", fr->block_i);
+
+ switch (fr->end.type)
+ {
+ case FR_VAR:
+ if (endvar(fr, c))
+ return -1;
+ break;
+
+ case FR_VAL:
+ if (endval(fr, c))
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+int from_cgen(const struct from *fr, struct cgen *c)
+{
+ return from_var(fr, c);
+}
+
+static int from_end(const struct stmt *s, struct cgen *c)
+{
+ const struct from *fr = &s->u.from;
+ const struct stentry *src = fr->var;
+ const struct type *t = src->t;
+ size_t sz = t->sz;
+ const char *tmp = cgen_tmp(sz);
+ const struct fn *fn = c->fn;
+
+ printf("%%%s =%s load%s ", tmp, cgen_sz(sz), cgen_load(t));
+ cgen_printvar(fn, src);
+ putchar('\n');
+ printf("%%%s =%s add %%%s, 1\n", tmp, cgen_sz(sz), tmp);
+ printf("store%s %%%s, ", cgen_sz(sz), tmp);
+ cgen_printvar(fn, src);
+ putchar('\n');
+ printf("jmp @__block%zu\n", fr->block_i);
+ return 0;
+}
+
+static int checksym(const struct fn *fn, const struct tk *tk,
+ struct from_it *fr)
+{
+ const struct stentry *e = fn_var(fn, tk);
+
+ if (!e)
+ {
+ errloc(tk, "undefined reference to \"%s\"", tk->s);
+ return -1;
+ }
+
+ switch (e->t->type)
+ {
+ case C:
+ case BUILTIN:
+ break;
+ case U:
+ case S:
+ case P:
+ case T:
+ case PTR:
+ case ARY:
+ {
+ char *s = type_name(e->t);
+
+ if (s)
+ errloc(tk, "\"%s\" (\"%s\") cannot be used in \"from\" loop",
+ tk->s, s);
+
+ free(s);
+ return -1;
+ }
+ }
+
+ fr->type = FR_VAR;
+ fr->u.e = e;
+ return 0;
+}
+
+static int check(const struct fn *fn, const struct tk *tk, struct from_it *fr)
+{
+ switch (tk->type)
+ {
+ case NUM:
+ fr->type = FR_VAL;
+ fr->u.tk = tk;
+ break;
+ case ID:
+ return checksym(fn, tk, fr);
+ case LIT:
+ errloc(tk, "unexpected literal");
+ return -1;
+ case UNDEF:
+ case ANY:
+ fprintf(stderr, "%s: unreachable\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int finalize(struct prv *p, struct fn *fn, struct from *fr)
+{
+ struct stmt *s;
+ struct pos *pos = &p->pos[p->i];
+ size_t nst = fn->nstmts + 1, nbl = fn->nblocks + 1,
+ *bl = realloc(fn->blocks, nbl * sizeof *bl);
+
+ if (!bl)
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ fr->block_i = bl[fn->nblocks++] = fn->nstmts;
+ fn->blocks = bl;
+
+ if (!(s = realloc(fn->stmts, nst * sizeof *s)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ s[fn->nstmts++] = (struct stmt)
+ {
+ .type = FROM,
+ .u.from = *fr,
+ .end = from_end
+ };
+
+ fn->stmts = s;
+ pos->seq = pos->stseq = stmts;
+ pos->step = stmts->steps;
+ fprintf(stderr, ", iterator %s, block index %zu\n", fr->var->tk->s,
+ bl[fn->nblocks - 1]);
+ return 0;
+}
+
+static const struct type *valtype(const struct fn *fn, const struct tk *tk)
+{
+ char *end;
+
+ errno = 0;
+ strtol(tk->s, &end, 0);
+
+ if (errno || *end)
+ {
+ errno = 0;
+ strtoll(tk->s, &end, 0);
+
+ if (errno || *end)
+ return type_find(fn, "ulong");
+
+ return type_find(fn, "long");
+ }
+
+ return type_find(fn, "word");
+}
+
+static const struct type *ittype(const struct fn *fn, const struct from_it *it)
+{
+ switch (it->type)
+ {
+ case FR_VAL:
+ return valtype(fn, it->u.tk);
+ case FR_VAR:
+ return it->u.e->t;
+ }
+
+ fprintf(stderr, "%s: unreachable\n", __func__);
+ return NULL;
+}
+
+static const struct type *gettype(const struct fn *fn,
+ const struct from_it *a, const struct from_it *b)
+{
+ const struct type *ta = ittype(fn, a), *tb = ittype(fn, b);
+
+ return ta->sz > tb->sz ? ta : tb;
+}
+
+int from(const struct lex *l, struct prv *p)
+{
+ const struct tk *start = p->stk + 1, *end = p->stk + 3;
+ struct fn *fn = fn_cur(p);
+ struct from fr = {0};
+ const struct type *t;
+
+ if (check(fn, start, &fr.start)
+ || check(fn, end, &fr.end)
+ || !(t = gettype(fn, &fr.start, &fr.end))
+ || !(fr.var = tmp_create(fn, t)))
+ return -1;
+
+ fprintf(stderr, "\t\tadding from statement [%s, %s]", start->s, end->s);
+
+ if (finalize(p, fn, &fr))
+ return -1;
+
+ p->stk = ++end;
+ return 1;
+}
+
+static int checkvar(const struct fn *fn, const struct tk *tk, struct from *fr)
+{
+ const struct stentry *e;
+
+ if (tk->type != ID)
+ {
+ errloc(tk, "\"%s\" not a variable", tk->s);
+ return -1;
+ }
+ else if (!(e = fn_var(fn, tk)))
+ {
+ errloc(tk, "undefined reference to \"%s\"", tk->s);
+ return -1;
+ }
+ else if (e->t->type != BUILTIN)
+ {
+ errloc(tk, "variable \"%s\" not an integer", tk->s);
+ return -1;
+ }
+
+ fr->var = e;
+ return 0;
+}
+
+int from_id(const struct lex *l, struct prv *p)
+{
+ const struct tk *start = p->stk + 1, *end = p->stk + 3, *var = p->stk + 5;
+ struct fn *fn = fn_cur(p);
+ struct from fr = {0};
+
+ if (check(fn, start, &fr.start)
+ || check(fn, end, &fr.end)
+ || checkvar(fn, var, &fr))
+ return -1;
+
+ fprintf(stderr, "\t\tadding from statement [%s, %s] using %s",
+ start->s, end->s, var->s);
+
+ if (finalize(p, fn, &fr))
+ return -1;
+
+ p->stk = ++var;
+ return 1;
+}
+
+int from_by(const struct lex *l, struct prv *p)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}
+
+int from_idby(const struct lex *l, struct prv *p)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}
diff --git a/src/prc1/from.h b/src/prc1/from.h
new file mode 100644
index 0000000..8a77629
--- /dev/null
+++ b/src/prc1/from.h
@@ -0,0 +1,34 @@
+#ifndef FROM_H
+#define FROM_H
+
+#include "cgen.h"
+#include "lex.h"
+#include "storage.h"
+#include "prv.h"
+#include <stddef.h>
+
+struct from
+{
+ struct from_it
+ {
+ enum {FR_VAL, FR_VAR} type;
+
+ union
+ {
+ const struct tk *tk;
+ const struct stentry *e;
+ } u;
+ } start, end;
+
+ size_t block_i;
+ const struct stentry *var;
+};
+
+int from(const struct lex *l, struct prv *p);
+int from_id(const struct lex *l, struct prv *p);
+int from_by(const struct lex *l, struct prv *p);
+int from_idby(const struct lex *l, struct prv *p);
+int from_cgen(const struct from *m, struct cgen *c);
+void from_free(struct from *fr);
+
+#endif
diff --git a/src/prc1/gl.c b/src/prc1/gl.c
new file mode 100644
index 0000000..de0293f
--- /dev/null
+++ b/src/prc1/gl.c
@@ -0,0 +1,24 @@
+#include "gl.h"
+#include "fn.h"
+#include "prv.h"
+#include "parse.h"
+#include "storage.h"
+
+int gl_cgen(const struct storage *gl, struct cgen *c)
+{
+ for (size_t i = 0; i < gl->nentries; i++)
+ {
+ const struct stentry *e = &gl->entries[i];
+
+ printf("export data $%s = { z %zu }\n", e->tk->s, e->t->sz);
+ }
+
+ return 0;
+}
+
+int gl(const struct lex *l, struct prv *p)
+{
+ struct fn *fn = fn_cur(p);
+
+ return storage(l, p, "globals section", 1, &fn->gl);
+}
diff --git a/src/prc1/gl.h b/src/prc1/gl.h
new file mode 100644
index 0000000..d8aaaaf
--- /dev/null
+++ b/src/prc1/gl.h
@@ -0,0 +1,12 @@
+#ifndef GL_H
+#define GL_H
+
+#include "cgen.h"
+#include "lex.h"
+#include "prv.h"
+#include "storage.h"
+
+int gl(const struct lex *l, struct prv *p);
+int gl_cgen(const struct storage *gl, struct cgen *c);
+
+#endif
diff --git a/src/prc1/im.c b/src/prc1/im.c
new file mode 100644
index 0000000..e9a208c
--- /dev/null
+++ b/src/prc1/im.c
@@ -0,0 +1,125 @@
+#include "im.h"
+#include "errloc.h"
+#include "fn.h"
+#include "prv.h"
+#include "parse.h"
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int imlit(const struct lex *, struct prv *);
+
+static const struct seq seqs[] =
+{
+ {(struct step[]){{LIT, NULL, 1}, {0}}, .fn = imlit},
+ {0}
+};
+
+void im_free(struct im *im)
+{
+ ast_free(&im->ast);
+ lex_free(&im->l);
+}
+
+static int doim(const struct lex *l, const struct tk *tk, struct prv *p)
+{
+ int ret = -1;
+ FILE *f = NULL;
+ struct im *im;
+ struct fn *fn = fn_cur(p);
+ struct ast *ast;
+ size_t n = fn->nimps + 1;
+ const char *path = tk->s;
+
+ if (!strcmp(l->loc.f, path))
+ {
+ errloc(tk, "detected recursive import \"%s\"", path);
+ goto end;
+ }
+ /* TODO: honor -I flags */
+ if (!(f = fopen(path, "rb")))
+ {
+ errloc(tk, "failed to open import \"%s\": %s", path, strerror(errno));
+ goto end;
+ }
+ else if (!(im = realloc(fn->imps, n * sizeof *im)))
+ {
+ perror("realloc(3)");
+ goto end;
+ }
+
+ fn->imps = im;
+ im = &fn->imps[fn->nimps++];
+ *im = (struct im){.l.loc.f = path};
+ ast = &im->ast;
+
+ if (!(ast->fns = malloc(sizeof *ast->fns)))
+ {
+ perror("malloc(3)");
+ goto end;
+ }
+
+ *ast->fns = (struct fn){.tk = tk};
+ /* hack: treat imports as a function */
+ ast->nfns = 1;
+
+ if (lex(&im->l, f) || parse_im(&im->l, ast))
+ goto end;
+
+ ret = 0;
+
+end:
+
+ if (f && fclose(f))
+ {
+ fprintf(stderr, "fclose(3) %s: %s\n", path, strerror(errno));
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int imlit(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = p->stk;
+
+ while (!lex_eof(l, tk) && !kw(tk->s))
+ if (doim(l, tk++, p))
+ return -1;
+
+ if (pop(l, p))
+ return -1;
+
+ p->stk = p->tk;
+ return 1;
+}
+
+int im(const struct lex *l, struct prv *p)
+{
+ struct pos init =
+ {
+ .seq = seqs,
+ .stseq = seqs,
+ .step = seqs->steps
+ };
+
+ struct fn *fn = fn_cur(p);
+
+ if (fn->im)
+ {
+ const struct loc *loc = &fn->im->loc;
+
+ errloc(p->stk, "imports section already defined at %s:%d:%d",
+ loc->f, loc->line, loc->col);
+ return -1;
+ }
+
+ if (push(&init, p))
+ return -1;
+
+ fn->im = p->stk;
+ p->stk = p->tk;
+ return 1;
+}
diff --git a/src/prc1/im.h b/src/prc1/im.h
new file mode 100644
index 0000000..30382ed
--- /dev/null
+++ b/src/prc1/im.h
@@ -0,0 +1,11 @@
+#ifndef IM_H
+#define IM_H
+
+#include "lex.h"
+#include "parse.h"
+#include "prv.h"
+
+int im(const struct lex *l, struct prv *p);
+void im_free(struct im *im);
+
+#endif
diff --git a/src/prc1/kw.c b/src/prc1/kw.c
new file mode 100644
index 0000000..d5e22ca
--- /dev/null
+++ b/src/prc1/kw.c
@@ -0,0 +1,41 @@
+#include "prv.h"
+#include <stddef.h>
+#include <string.h>
+
+static const char *kws[] =
+{
+ /* headers */
+ "public", "function",
+ /* sections */
+ "types", "storage", "linkage", "procedure", "locals", "globals", "imports",
+ /* types */
+ "union", "struct", "constant", "alias", "prototype",
+ /* operators */
+ "add", "subtract", "multiply", "divide", "xor", "bitor", "bitand",
+ "leftshift", "rightshift", "address", "size", "call", "set",
+ /* procedures */
+ "using", "returning",
+ /* built-in functions */
+ "display", "warn",
+ /* flow control */
+ "while", "if", "else", "end", "return", "go", "label", "default",
+ /* prepositions */
+ "by", "from", "to", "than", "of", "etc",
+ /* comparators */
+ "is", "not", "equal", "greater", "smaller", "and", "or",
+ /* built-in types */
+ "void", "array", "pointer",
+ /* built-in signed types */
+ "byte", "halfword", "word", "long",
+ /* built-in unsigned types */
+ "ubyte", "uhalfword", "uword", "ulong"
+};
+
+int kw(const char *s)
+{
+ for (size_t i = 0; i < sizeof kws / sizeof *kws; i++)
+ if (!strcmp(s, kws[i]))
+ return 1;
+
+ return 0;
+}
diff --git a/src/prc1/lex.c b/src/prc1/lex.c
new file mode 100644
index 0000000..736ebdd
--- /dev/null
+++ b/src/prc1/lex.c
@@ -0,0 +1,249 @@
+#include "lex.h"
+#include "errloc.h"
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+static void free_tk(struct tk *tk)
+{
+ free(tk->s);
+}
+
+static int invnum(const struct tk *tk, char *end)
+{
+ if (errno)
+ errloc(tk, "invalid number: %s (%s)", tk->s, strerror(errno));
+ else if (*end)
+ errloc(tk, "invalid number: %s", tk->s);
+
+ return -1;
+}
+
+static int fintok(struct lex *lex)
+{
+ struct tk *tk = &lex->tk, *tokens;
+ const struct tk empty = {.loc = tk->loc};
+ size_t ntk = lex->ntok + 1;
+ char *s;
+
+ if (tk->type == UNDEF)
+ return 0;
+ else if (!(s = realloc(tk->s, lex->len + 1)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ tk->s = s;
+ tk->s[lex->len] = '\0';
+
+ switch (tk->type)
+ {
+ case UNDEF:
+ break;
+
+ case ANY:
+ errloc(tk, "%s: unreachable", __func__);
+ return -1;
+
+ case LIT:
+ case ID:
+ break;
+
+ case NUM:
+ {
+ int neg = *tk->s == '-';
+ char *end;
+
+ errno = 0;
+ strtoll(tk->s, &end, 0);
+
+ if (errno || *end)
+ {
+ if (neg)
+ return invnum(tk, end);
+
+ errno = 0;
+ strtoull(tk->s, &end, 0);
+
+ if (errno || *end)
+ return invnum(tk, end);
+ }
+ }
+ }
+
+ if (!(tokens = realloc(lex->tokens, ntk * sizeof *tokens)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ tokens[lex->ntok++] = lex->tk;
+ lex->tokens = tokens;
+ lex->tk = empty;
+ lex->len = 0;
+ return 0;
+}
+
+static int printable(char c)
+{
+ return c >= '!' && c <= '~';
+}
+
+static void invch(const struct lex *l, char c)
+{
+ if (printable(c))
+ errcloc(l, "invalid character: %c", c);
+ else
+ errcloc(l, "invalid character: (%#hhx)", c);
+}
+
+static int ch(char c, struct lex *lex)
+{
+ struct tk *tk = &lex->tk;
+ char *s;
+
+ switch (tk->type)
+ {
+ case UNDEF:
+ tk->loc = lex->loc;
+
+ if (c == '\"')
+ {
+ tk->type = LIT;
+ return 0;
+ }
+ else if (c == '-' || c == '+'
+ || (c >= '0' && c <= '9'))
+ tk->type = NUM;
+ else if (c == '_'
+ || (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z'))
+ tk->type = ID;
+ else
+ {
+ invch(lex, c);
+ return -1;
+ }
+
+ break;
+
+ case LIT:
+ if (c == '\"')
+ return fintok(lex);
+
+ break;
+
+ case NUM:
+ if (c == '\"')
+ {
+ invch(lex, c);
+ return -1;
+ }
+
+ break;
+
+ case ID:
+ if (c == '\"' || c == '-' || c == '+')
+ {
+ invch(lex, c);
+ return -1;
+ }
+ else if (!(c == '_'
+ || (c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')))
+ {
+ invch(lex, c);
+ return -1;
+ }
+
+ break;
+ case ANY:
+ errcloc(lex, "%s: unreachable", __func__);
+ return -1;
+ }
+
+ if (!(s = realloc(tk->s, lex->len + 1)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ s[lex->len++] = c;
+ tk->s = s;
+ return 0;
+}
+
+static int clex(char c, struct lex *lex)
+{
+ struct tk *tk = &lex->tk;
+ struct loc *loc = &lex->loc;
+
+ ++loc->col;
+
+ switch (c)
+ {
+ case '*':
+ lex->comment = 1;
+ return fintok(lex);
+
+ case '\n':
+ if (tk->type == LIT)
+ {
+ errcloc(lex, "unterminated literal");
+ return -1;
+ }
+ else if (fintok(lex))
+ return -1;
+
+ loc->line++;
+ loc->col = lex->comment = 0;
+ return 0;
+
+ case '\t':
+ /* assume columns as 8-space width */
+ loc->col += 7;
+ case ' ':
+ if (tk->type != LIT)
+ return lex->comment ? 0 : fintok(lex);
+ default:
+ return lex->comment ? 0 : ch(c, lex);
+ }
+
+ invch(lex, c);
+ return -1;
+}
+
+void lex_free(struct lex *lex)
+{
+ for (size_t i = 0; i < lex->ntok; i++)
+ free_tk(&lex->tokens[i]);
+
+ free(lex->tokens);
+ free_tk(&lex->tk);
+}
+
+int lex_eof(const struct lex *lex, const struct tk *tk)
+{
+ return tk - lex->tokens >= lex->ntok;
+}
+
+int lex(struct lex *l, FILE *f)
+{
+ l->loc.line = 1;
+
+ for (;;)
+ {
+ int c = fgetc(f);
+
+ if (c == EOF)
+ break;
+ else if (clex(c, l))
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/prc1/lex.h b/src/prc1/lex.h
new file mode 100644
index 0000000..6e836ac
--- /dev/null
+++ b/src/prc1/lex.h
@@ -0,0 +1,34 @@
+#ifndef LEX_H
+#define LEX_H
+
+#include <stddef.h>
+#include <stdio.h>
+
+enum tktype {UNDEF, ID, LIT, NUM, ANY};
+
+struct loc
+{
+ const char *f;
+ int line, col;
+};
+
+struct tk
+{
+ enum tktype type;
+ struct loc loc;
+ char *s;
+};
+
+struct lex
+{
+ struct loc loc;
+ struct tk *tokens, tk;
+ size_t ntok, len;
+ int comment;
+};
+
+int lex(struct lex *lex, FILE *f);
+int lex_eof(const struct lex *lex, const struct tk *tk);
+void lex_free(struct lex *lex);
+
+#endif
diff --git a/src/prc1/lit.c b/src/prc1/lit.c
new file mode 100644
index 0000000..09528a2
--- /dev/null
+++ b/src/prc1/lit.c
@@ -0,0 +1,102 @@
+#include "lit.h"
+#include "fn.h"
+#include "lex.h"
+#include "parse.h"
+#include "prv.h"
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+void lit_free(struct lit *l)
+{
+ free(l->name);
+}
+
+static char *gen(struct ast *ast, struct fn *fn)
+{
+ static const char fmt[] = "__str_%zu";
+ int n = snprintf(NULL, 0, fmt, ast->litcnt);
+ char *ret = NULL;
+ size_t sz;
+
+ if (n < 0)
+ {
+ fprintf(stderr, "%s: snprintf(3) failed\n", __func__);
+ goto failure;
+ }
+ else if (!(ret = malloc((sz = n + 1))))
+ {
+ perror("malloc(3)");
+ goto failure;
+ }
+
+ n = snprintf(ret, sz, fmt, ast->litcnt);
+
+ if (n < 0 || n >= sz)
+ {
+ fprintf(stderr, "%s: snprintf(3) failed with %d\n", __func__, n);
+ goto failure;
+ }
+
+ ast->litcnt++;
+ return ret;
+
+failure:
+ free(ret);
+ return NULL;
+}
+
+static const struct lit *find(const struct tk *tk, const struct ast *ast)
+{
+ for (const struct lit *l = ast->lits; l; l = l->next)
+ if (!strcmp(l->tk->s, tk->s))
+ return l;
+
+ return NULL;
+}
+
+int lit_cgen(const struct lit *l)
+{
+ printf("data $%s = { b \"%s\", b 0 }\n", l->name, l->tk->s);
+ return 0;
+}
+
+const struct lit *lit_push(const struct tk *tk, struct prv *p)
+{
+ char *name = NULL;
+ struct fn *fn = fn_cur(p);
+ struct ast *ast = p->ast;
+ const struct lit *prev = find(tk, ast);
+ struct lit *l;
+
+ if (prev)
+ return prev;
+ else if (!(name = gen(ast, fn)))
+ goto failure;
+ else if (!(l = malloc(sizeof *l)))
+ {
+ perror("malloc(3)");
+ goto failure;
+ }
+
+ *l = (struct lit)
+ {
+ .name = name,
+ .fn = fn,
+ .tk = tk
+ };
+
+ if (!ast->lits)
+ ast->lits = l;
+ else if (ast->lastlit)
+ ast->lastlit->next = l;
+
+ ast->lastlit = l;
+ fprintf(stderr, "adding literal \"%s\"\n", name);
+ return l;
+
+failure:
+ free(name);
+ return NULL;
+}
diff --git a/src/prc1/lit.h b/src/prc1/lit.h
new file mode 100644
index 0000000..e94263c
--- /dev/null
+++ b/src/prc1/lit.h
@@ -0,0 +1,12 @@
+#ifndef LIT_H
+#define LIT_H
+
+#include "lex.h"
+#include "parse.h"
+#include "prv.h"
+
+const struct lit *lit_push(const struct tk *tk, struct prv *p);
+int lit_cgen(const struct lit *l);
+void lit_free(struct lit *l);
+
+#endif
diff --git a/src/prc1/lk.c b/src/prc1/lk.c
new file mode 100644
index 0000000..8bfb738
--- /dev/null
+++ b/src/prc1/lk.c
@@ -0,0 +1,12 @@
+#include "lk.h"
+#include "fn.h"
+#include "prv.h"
+#include "parse.h"
+#include "storage.h"
+
+int lk(const struct lex *l, struct prv *p)
+{
+ struct fn *fn = fn_cur(p);
+
+ return storage(l, p, "linkage section", 1, &fn->lk);
+}
diff --git a/src/prc1/lk.h b/src/prc1/lk.h
new file mode 100644
index 0000000..35df205
--- /dev/null
+++ b/src/prc1/lk.h
@@ -0,0 +1,9 @@
+#ifndef LK_H
+#define LK_H
+
+#include "lex.h"
+#include "prv.h"
+
+int lk(const struct lex *l, struct prv *p);
+
+#endif
diff --git a/src/prc1/parse.c b/src/prc1/parse.c
new file mode 100644
index 0000000..1c3aba2
--- /dev/null
+++ b/src/prc1/parse.c
@@ -0,0 +1,183 @@
+#include "parse.h"
+#include "div.h"
+#include "errloc.h"
+#include "fn.h"
+#include "lex.h"
+#include "lit.h"
+#include "prv.h"
+#include "type.h"
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+void ast_free(struct ast *ast)
+{
+ struct type *t = ast->types;
+
+ while (t)
+ {
+ struct type *next = t->next;
+
+ type_lfree(t);
+ t = next;
+ }
+
+ for (struct lit *l = ast->lits, *next; l; l = next)
+ {
+ next = l->next;
+ lit_free(l);
+ free(l);
+ }
+
+ for (size_t i = 0; i < ast->nfns; i++)
+ fn_free(&ast->fns[i]);
+
+ free(ast->fns);
+}
+
+static const char *expected(const struct step *s)
+{
+ static const char *tmap[] =
+ {
+ [ID] = "identifier", [LIT] = "literal", [NUM] = "number"
+ };
+
+ return s->id ? s->id : tmap[s->type];
+}
+
+static int iter(const struct lex *l, struct prv *p)
+{
+ struct pos *pos = p->pos;
+ const struct step *s;
+ const struct tk *tk = p->tk;
+
+ if (!pos)
+ return p->entry(l, p);
+
+ pos = &p->pos[p->i];
+
+ if (!(s = pos->step))
+ {
+ if (lex_eof(l, tk))
+ return 0;
+ else if (p->n > 1)
+ return pop(l, p) ? -1 : 1;
+ else
+ {
+ switch (tk->type)
+ {
+ case LIT:
+ errloc(tk, "unexpected literal \"%s\"", tk->s);
+ break;
+ case NUM:
+ errloc(tk, "unexpected number %s", tk->s);
+ break;
+ case ID:
+ errloc(tk, "unexpected %s %s", kw(tk->s) ?
+ "keyword" : "identifier", tk->s);
+ break;
+ case ANY:
+ case UNDEF:
+ errloc(tk, "%s: unreachable", __func__);
+ break;
+ }
+
+ return -1;
+ }
+ }
+ else if (lex_eof(l, tk))
+ {
+ if (p->stk == p->tk)
+ return pop(l, p) ? -1 : 1;
+ else if (s->type)
+ {
+ tk--;
+
+ if (lex_eof(l, p->stk))
+ {
+ errloc(tk, "expected %s", expected(pos->step));
+ return -1;
+ }
+ }
+ }
+
+ if (!s->type)
+ return pos->seq->fn(l, p);
+ else if ((s->type == ID || s->type == ANY) && !s->id && kw(tk->s)
+ && !s->chain)
+ {
+ if (tk != p->stk || !p->n)
+ {
+ errloc(tk, "unexpected keyword %s", tk->s);
+ return -1;
+ }
+ else if (pop(l, p))
+ return -1;
+
+ pos = &p->pos[p->i];
+ }
+ else if ((s->type != tk->type && s->type != ANY)
+ || (s->type == ID && s->id && strcmp(s->id, tk->s))
+ || (s->type == ID && kw(tk->s) && s->chain))
+ {
+ pos->step = (++pos->seq)->steps;
+ p->tk = p->stk;
+ }
+ else
+ {
+ const struct tk *next = tk + 1;
+
+ if (!s->chain)
+ pos->step++;
+ else if (!lex_eof(l, next))
+ {
+ if (s->chain && kw(next->s))
+ pos->step++;
+ }
+ else
+ pos->step++;
+
+ if (!lex_eof(l, p->tk))
+ p->tk++;
+ }
+
+ return 1;
+}
+
+static void free_prv(struct prv *p)
+{
+ free(p->tdc);
+ free(p->pos);
+}
+
+static int comm(const struct lex *l, struct ast *ast,
+ int (*entry)(const struct lex *, struct prv *))
+{
+ int ret;
+ struct prv prv =
+ {
+ .ast = ast,
+ .tk = l->tokens,
+ .stk = l->tokens,
+ .entry = entry
+ };
+
+ if (!prv.stk)
+ return 0;
+
+ while ((ret = iter(l, &prv)) > 0)
+ ;
+
+ free_prv(&prv);
+ return ret;
+}
+
+int parse(const struct lex *l, struct ast *ast)
+{
+ return comm(l, ast, fn);
+}
+
+int parse_im(const struct lex *l, struct ast *ast)
+{
+ return comm(l, ast, div_im);
+}
diff --git a/src/prc1/parse.h b/src/prc1/parse.h
new file mode 100644
index 0000000..b85d618
--- /dev/null
+++ b/src/prc1/parse.h
@@ -0,0 +1,138 @@
+#ifndef PARSE_H
+#define PARSE_H
+
+#include "lex.h"
+#include "storage.h"
+
+struct u
+{
+ struct storage *storage;
+};
+
+struct s
+{
+ struct storage *storage;
+};
+
+union c
+{
+ unsigned long long uv;
+ long long v;
+};
+
+struct p
+{
+ struct fn *fn;
+};
+
+struct t
+{
+ const struct type *alias;
+};
+
+struct ptr
+{
+ const struct type *t;
+};
+
+struct ary
+{
+ unsigned long long n;
+ int etc;
+};
+
+struct builtin
+{
+ const char *name;
+};
+
+struct type
+{
+ enum {U, S, C, P, T, PTR, ARY, BUILTIN} type;
+ const struct tk *tk;
+ size_t sz, align;
+ int sign;
+ struct type *child, *next;
+
+ union
+ {
+ struct u u;
+ struct s s;
+ union c c;
+ struct p p;
+ struct t t;
+ struct ptr ptr;
+ struct ary ary;
+ struct builtin b;
+ } u;
+};
+
+struct param
+{
+ const struct tk *tk;
+ const struct stentry *entry;
+};
+
+struct pr
+{
+ const struct tk *tk;
+ struct param *params, *ret;
+ size_t nparams;
+ int variadic;
+};
+
+struct td
+{
+ const struct tk *tk;
+ struct type *types;
+ size_t ntypes;
+};
+
+struct tmp
+{
+ struct tk tk;
+ struct stentry e;
+ struct tmp *next;
+};
+
+struct fn
+{
+ const struct tk *tk, *im;
+ const struct fn *parent;
+ enum linkage {EXTERNAL, STATIC} linkage;
+ struct storage *lk, *ws, *gl;
+ struct stmt *stmts;
+ struct tmp *tmps;
+ struct im *imps;
+ struct pr *pr;
+ struct td *td;
+ size_t nstmts, nimps, nblocks, ntmps, *blocks;
+};
+
+struct lit
+{
+ char *name;
+ const struct tk *tk;
+ const struct fn *fn;
+ struct lit *next;
+};
+
+struct ast
+{
+ struct fn *fns;
+ struct lit *lits, *lastlit;
+ struct type *types;
+ size_t nfns, litcnt;
+};
+
+struct im
+{
+ struct lex l;
+ struct ast ast;
+};
+
+int parse(const struct lex *l, struct ast *ast);
+int parse_im(const struct lex *l, struct ast *ast);
+void ast_free(struct ast *ast);
+
+#endif
diff --git a/src/prc1/pop.c b/src/prc1/pop.c
new file mode 100644
index 0000000..ba2f3a1
--- /dev/null
+++ b/src/prc1/pop.c
@@ -0,0 +1,37 @@
+#include "prv.h"
+#include <stdlib.h>
+
+int pop(const struct lex *l, struct prv *p)
+{
+ size_t n;
+ struct pos *pos = NULL;
+
+ if (!p->n)
+ {
+ fprintf(stderr, "%s: underflow\n", __func__);
+ return -1;
+ }
+ else if (!(n = p->n - 1))
+ {
+ free(p->pos);
+ p->pos = NULL;
+ }
+ else if (!(pos = realloc(p->pos, n * sizeof *pos)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ p->pos = pos;
+ p->n = n;
+
+ if (p->n)
+ {
+ const struct pos *old = &p->pos[--p->i];
+
+ if (old->end && old->end(l, p))
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/prc1/pr.c b/src/prc1/pr.c
new file mode 100644
index 0000000..a171554
--- /dev/null
+++ b/src/prc1/pr.c
@@ -0,0 +1,375 @@
+#include "pr.h"
+#include "errloc.h"
+#include "fn.h"
+#include "prv.h"
+#include "parse.h"
+#include "stmt.h"
+#include "storage.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int prey(const struct lex *l, struct prv *p);
+static int pren(const struct lex *l, struct prv *p);
+static int pryy(const struct lex *l, struct prv *p);
+static int pryn(const struct lex *l, struct prv *p);
+static int prny(const struct lex *l, struct prv *p);
+static int prnn(const struct lex *l, struct prv *p);
+
+static const struct seq seqs[] =
+{
+ /* function definition with variadic parameters and return variable */
+ {
+ (struct step[])
+ {
+ {ID, "using"},
+ {ID, NULL, 1},
+ {ID, "etc",},
+ {ID, "returning"},
+ {ID},
+ {0}
+ },
+ .fn = prey
+ },
+
+ /* function definition with variadic parameters and no return variable */
+ {
+ (struct step[])
+ {
+ {ID, "using"},
+ {ID, NULL, 1},
+ {ID, "etc",},
+ {0}
+ },
+ .fn = pren
+ },
+
+ /* function definition with parameters and return variable */
+ {
+ (struct step[])
+ {
+ {ID, "using"},
+ {ID, NULL, 1},
+ {ID, "returning"},
+ {ID},
+ {0}
+ },
+ .fn = pryy
+ },
+
+ /* function definition with parameters, no return variable */
+ {
+ (struct step[])
+ {
+ {ID, "using"},
+ {ID, NULL, 1},
+ {0}
+ },
+
+ .fn = pryn
+ },
+
+ /* function definition with no parameters, with return variable */
+ {
+ (struct step[])
+ {
+ {ID, "returning"},
+ {ID},
+ {0}
+ },
+
+ .fn = prny
+ },
+
+ {0}
+};
+
+static const struct tk *findparam(const char *s, const struct pr *pr)
+{
+ for (size_t i = 0; i < pr->nparams; i++)
+ {
+ const struct param *p = &pr->params[i];
+ const struct tk *tk = p->tk;
+
+ if (!strcmp(tk->s, s))
+ return tk;
+ }
+
+ return NULL;
+}
+
+static int param(const struct tk *tk, const struct stentry *e, struct pr *pr)
+{
+ size_t n = pr->nparams + 1;
+ struct param *params = realloc(pr->params, n * sizeof *params);
+
+ if (!params)
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ pr->params = params;
+ pr->params[pr->nparams++] = (struct param){.tk = tk, .entry = e};
+ return 0;
+}
+
+static int setret(const struct tk *tk, const struct stentry *e, struct pr *pr)
+{
+ if (!(pr->ret = malloc(sizeof *pr->ret)))
+ {
+ perror("malloc(3)");
+ return -1;
+ }
+
+ *pr->ret = (struct param){.tk = tk, .entry = e};
+ return 0;
+}
+
+static int finalize(const struct lex *l, struct prv *p)
+{
+ const struct fn *fn = fn_cur(p);
+ const struct pr *pr = fn->pr;
+ struct pos init =
+ {
+ .seq = stmts,
+ .stseq = stmts,
+ .step = stmts->steps
+ };
+
+ if (p->proto)
+ {
+ if (pop(l, p))
+ return -1;
+
+ fputc('\t', stderr);
+ }
+ else if (push(&init, p))
+ return -1;
+
+ fprintf(stderr, "\tadding procedure using %zu%s parameters "
+ "and%s return value\n", pr->nparams, pr->variadic ? " variadic" : "",
+ pr->ret ? "" : " no");
+ p->stk = p->tk;
+ return 1;
+}
+
+static int prret(const struct lex *l, const struct tk *tk, struct prv *p)
+{
+ const struct fn *fn = fn_cur(p);
+ struct pr *pr = fn->pr;
+ const char *s = tk->s;
+ const struct storage *lk = fn->lk, *gl = fn->gl;
+ const struct stentry *e, *gle;
+ const struct tk *prm;
+
+ if (!lk || !(e = storage_find(s, lk)))
+ {
+ errloc(tk, "return value \"%s\" not in %s", s, tk->s);
+ return -1;
+ }
+ else if (gl && (gle = storage_find(s, gl)))
+ {
+ const struct loc *loc = &gle->tk->loc;
+
+ errloc(tk, "return value \"%s\" also in %s at %s:%d:%d", s, gl->name,
+ loc->f, loc->line, loc->col);
+ return -1;
+ }
+ else if ((prm = findparam(s, pr)))
+ {
+ const struct loc *loc = &prm->loc;
+
+ errloc(tk, "return value \"%s\" already listed as "
+ "parameter at %s:%d:%d", tk->s, loc->f, loc->line, loc->col);
+ return -1;
+ }
+ else if (setret(tk, e, pr))
+ return -1;
+
+ return finalize(l, p);
+}
+
+static const struct tk *prparams(const struct lex *l, struct prv *p, int *ret)
+{
+ const struct tk *tk = p->stk + 1;
+ const struct fn *fn = fn_cur(p);
+ struct pr *pr = fn->pr;
+
+ if (ret)
+ *ret = 0;
+
+ for (;;)
+ {
+ const struct storage *lk = fn->lk, *gl = fn->gl;
+ const struct stentry *e, *gle;
+ const struct tk *prm;
+ const char *s;
+
+ if (lex_eof(l, tk))
+ return tk;
+ else if (!strcmp((s = tk->s), "returning") && ret)
+ {
+ *ret = 1;
+ return ++tk;
+ }
+ else if (!strcmp(s, "etc"))
+ {
+ if (pr->variadic)
+ {
+ errloc(tk, "keyword \"etc\" may only appear once as a "
+ "parameter");
+ return NULL;
+ }
+
+ pr->variadic = tk++ - p->stk;
+ }
+ else if (kw(s))
+ return tk;
+ else if (pr->variadic)
+ {
+ errloc(tk, "keyword \"etc\" must appear as the last parameter");
+ return NULL;
+ }
+ else if ((prm = findparam(s, pr)))
+ {
+ const struct loc *loc = &prm->loc;
+
+ errloc(tk, "parameter \"%s\" already listed at %d:%d", s,
+ loc->line, loc->col);
+ return NULL;
+ }
+ else if (!lk || !(e = storage_find(s, lk)))
+ {
+ if (p->proto)
+ {
+ /* ambiguous syntax: param might be confused
+ * with next custom type. */
+ p->tk = p->stk + pr->nparams + 1;
+ return tk;
+ }
+ else
+ {
+ errloc(tk, "parameter \"%s\" not in %s", s, tk->s);
+ return NULL;
+ }
+ }
+ else if (gl && (gle = storage_find(s, gl)))
+ {
+ const struct loc *loc = &gle->tk->loc;
+
+ errloc(tk, "parameter \"%s\" also in %s at %d:%d", s, gl->name,
+ loc->line, loc->col);
+ return NULL;
+ }
+ else if (param(tk, e, pr))
+ return NULL;
+ else
+ tk++;
+ }
+
+ return tk;
+}
+
+static int prey(const struct lex *l, struct prv *p)
+{
+ int ret;
+ const struct tk *tk = prparams(l, p, &ret);
+
+ if (!tk)
+ return -1;
+ else if (ret)
+ return prret(l, tk, p);
+
+ return finalize(l, p);
+}
+
+static int pren(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = prparams(l, p, NULL);
+
+ if (!tk)
+ return -1;
+
+ return finalize(l, p);
+}
+
+static int pryy(const struct lex *l, struct prv *p)
+{
+ int ret;
+ const struct tk *tk = prparams(l, p, &ret);
+
+ if (!tk)
+ return -1;
+ else if (ret)
+ return prret(l, tk, p);
+
+ return finalize(l, p);
+}
+
+static int pryn(const struct lex *l, struct prv *p)
+{
+ if (!prparams(l, p, NULL))
+ return -1;
+
+ return finalize(l, p);
+}
+
+static int prny(const struct lex *l, struct prv *p)
+{
+ return prret(l, p->stk + 1, p);
+}
+
+static int prnn(const struct lex *l, struct prv *p)
+{
+ return finalize(l, p);
+}
+
+void pr_free(struct pr *pr)
+{
+ if (pr)
+ {
+ free(pr->params);
+ free(pr->ret);
+ }
+
+ free(pr);
+}
+
+int pr(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = p->tk - 1, *next = p->tk;
+ struct fn *fn = fn_cur(p);
+ struct pos init =
+ {
+ .seq = seqs,
+ .stseq = seqs,
+ .step = seqs->steps
+ };
+
+ if (fn->pr)
+ {
+ const struct loc *loc = &fn->pr->tk->loc;
+
+ errloc(tk, "procedure already defined at %s:%d:%d", loc->f, loc->line,
+ loc->col);
+ return -1;
+ }
+ else if (!(fn->pr = malloc(sizeof *fn->pr)))
+ {
+ perror("malloc(3)");
+ return -1;
+ }
+
+ *fn->pr = (struct pr){.tk = tk};
+
+ /* on no parameters or return values, go directly to next sequences list. */
+ if (lex_eof(l, next)
+ || (strcmp(next->s, "using") && strcmp(next->s, "returning")))
+ return prnn(l, p);
+ else if (push(&init, p))
+ return -1;
+
+ p->stk = p->tk;
+ return 1;
+}
diff --git a/src/prc1/pr.h b/src/prc1/pr.h
new file mode 100644
index 0000000..e3d8bf8
--- /dev/null
+++ b/src/prc1/pr.h
@@ -0,0 +1,11 @@
+#ifndef PR_H
+#define PR_H
+
+#include "lex.h"
+#include "parse.h"
+#include "prv.h"
+
+int pr(const struct lex *l, struct prv *p);
+void pr_free(struct pr *pr);
+
+#endif
diff --git a/src/prc1/prc1.c b/src/prc1/prc1.c
new file mode 100644
index 0000000..69165ba
--- /dev/null
+++ b/src/prc1/prc1.c
@@ -0,0 +1,53 @@
+#include "cgen.h"
+#include "lex.h"
+#include "parse.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+ struct lex l = {0};
+ struct ast a = {0};
+ struct cgen c = {0};
+ FILE *f = stdin;
+ int opened = 0, ret = EXIT_FAILURE;
+
+ if (argc != 2)
+ {
+ fprintf(stderr, "%s: error: missing input file\n", *argv);
+ goto end;
+ }
+ else if (strcmp(argv[1], "-"))
+ {
+ if (!(f = fopen(argv[1], "rb")))
+ {
+ fprintf(stderr, "failed to open %s: %s\n", argv[1],
+ strerror(errno));
+ goto end;
+ }
+
+ opened = 1;
+ l.loc.f = argv[1];
+ }
+ else
+ l.loc.f = "stdin";
+
+ if (lex(&l, f) || parse(&l, &a) || cgen(&a, &c))
+ goto end;
+
+ ret = EXIT_SUCCESS;
+
+end:
+ if (opened && fclose(f))
+ {
+ perror("fclose(3)");
+ ret = EXIT_FAILURE;
+ }
+
+ cgen_free(&c);
+ ast_free(&a);
+ lex_free(&l);
+ return ret;
+}
diff --git a/src/prc1/print.c b/src/prc1/print.c
new file mode 100644
index 0000000..abdb149
--- /dev/null
+++ b/src/prc1/print.c
@@ -0,0 +1,103 @@
+#include "print.h"
+#include "errloc.h"
+#include "fn.h"
+#include "lit.h"
+#include "parse.h"
+#include "prv.h"
+#include "stmt.h"
+#include "storage.h"
+#include "type.h"
+#include <stdlib.h>
+#include <string.h>
+
+static int entry(const struct lex *l, const struct tk *tk, struct print *pr,
+ struct prv *p)
+{
+ size_t n = pr->nentries + 1;
+ struct prientry *entries;
+ const struct stentry *stentry = NULL;
+ const struct fn *fn = fn_cur(p);
+ const struct lit *lit = NULL;
+
+ if (tk->type == ID)
+ {
+ const struct type *t = NULL;
+
+ if ((t = type_find(fn, tk->s)))
+ {
+ if (t->type != C)
+ {
+ errloc(tk, "type \"%s\" not a constant", tk->s);
+ return -1;
+ }
+ else if (!(lit = lit_push(t->tk, p)))
+ return -1;
+ }
+ else if (!(stentry = fn_var(fn, tk)))
+ {
+ errloc(tk, "undefined reference to \"%s\"", tk->s);
+ return -1;
+ }
+ }
+ else if (tk->type == LIT && !(lit = lit_push(tk, p)))
+ return -1;
+
+ if (!(entries = realloc(pr->entries, n * sizeof *entries)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ pr->entries = entries;
+ pr->entries[pr->nentries++] = (struct prientry)
+ {
+ .tk = tk,
+ .lit = lit,
+ .entry = stentry
+ };
+
+ return 0;
+}
+
+void print_free(struct print *pr)
+{
+ free(pr->entries);
+}
+
+int print(const struct lex *l, struct prv *p,
+ int (*fn)(const struct lex *, const struct prv *, const struct print *))
+{
+ const struct tk *tk = p->stk + 1;
+ struct print pr = {0};
+ struct pos *pos = &p->pos[p->i];
+ int eof;
+
+ if (lex_eof(l, tk))
+ {
+ errloc(tk - 1, "incomplete %s statement", p->stk->s);
+ return -1;
+ }
+
+ for (;;)
+ if (entry(l, tk, &pr, p))
+ goto failure;
+ else if ((eof = lex_eof(l, ++tk)) || kw(tk->s))
+ break;
+
+ if (eof || strcmp(tk->s, "etc"))
+ pr.println = 1;
+ else if (!eof)
+ tk++;
+
+ if (fn(l, p, &pr))
+ goto failure;
+
+ pos->seq = pos->stseq = stmts;
+ pos->step = stmts->steps;
+ p->stk = tk;
+ return 1;
+
+failure:
+ print_free(&pr);
+ return -1;
+}
diff --git a/src/prc1/print.h b/src/prc1/print.h
new file mode 100644
index 0000000..950e0bc
--- /dev/null
+++ b/src/prc1/print.h
@@ -0,0 +1,26 @@
+#ifndef PRINT_H
+#define PRINT_H
+
+#include "cgen.h"
+#include "lex.h"
+#include <stddef.h>
+
+struct prientry
+{
+ const struct tk *tk;
+ const struct lit *lit;
+ const struct stentry *entry;
+};
+
+struct print
+{
+ struct prientry *entries;
+ size_t nentries;
+ int println;
+};
+
+int print(const struct lex *l, struct prv *p,
+ int (*fn)(const struct lex *, const struct prv *, const struct print *));
+void print_free(struct print *p);
+
+#endif
diff --git a/src/prc1/prv.h b/src/prc1/prv.h
new file mode 100644
index 0000000..c0c8930
--- /dev/null
+++ b/src/prc1/prv.h
@@ -0,0 +1,51 @@
+#ifndef PRV_H
+#define PRV_H
+
+#include "lex.h"
+#include <stddef.h>
+
+struct prv;
+
+struct step
+{
+ enum tktype type;
+ const char *id;
+ int chain;
+};
+
+struct pos
+{
+ const struct seq *seq, *stseq;
+ const struct step *step;
+ int (*end)(const struct lex *, struct prv *);
+};
+
+struct prv
+{
+ const struct tk *tk, *stk, *id;
+ struct tdconstant *tdc;
+ struct storage *st;
+ struct pos *pos;
+ struct ast *ast;
+ struct type *ag, *t;
+ struct fn *proto;
+ size_t n, i, block;
+ int (*entry)(const struct lex *, struct prv *);
+ int (*type)(const struct lex *, struct prv *p, const struct type *t);
+};
+
+struct seq
+{
+ const struct step *steps;
+ int (*fn)(const struct lex *l, struct prv *p);
+ int (*end)(const struct lex *l, struct prv *p);
+ const char *chain;
+};
+
+int kw(const char *s);
+int prev(const struct prv *p);
+int push(const struct pos *pos, struct prv *p);
+int pop(const struct lex *l, struct prv *p);
+char *sdup(const char *s);
+
+#endif
diff --git a/src/prc1/push.c b/src/prc1/push.c
new file mode 100644
index 0000000..1a9056d
--- /dev/null
+++ b/src/prc1/push.c
@@ -0,0 +1,27 @@
+#include "prv.h"
+#include <stdlib.h>
+
+int push(const struct pos *pos, struct prv *p)
+{
+ size_t n = p->n + 1;
+ struct pos *npos = realloc(p->pos, n * sizeof *npos);
+
+ if (!npos)
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+ else if (p->n)
+ {
+ struct pos *prev = &npos[p->n - 1];
+
+ prev->end = prev->seq->end;
+ prev->seq = prev->stseq;
+ prev->step = prev->seq->steps;
+ p->i++;
+ }
+
+ npos[p->n++] = *pos;
+ p->pos = npos;
+ return 0;
+}
diff --git a/src/prc1/sdup.c b/src/prc1/sdup.c
new file mode 100644
index 0000000..e990744
--- /dev/null
+++ b/src/prc1/sdup.c
@@ -0,0 +1,16 @@
+#include "prv.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *sdup(const char *s)
+{
+ char *n = malloc(strlen(s) + 1);
+
+ if (n)
+ strcpy(n, s);
+ else
+ perror("malloc(3)");
+
+ return n;
+}
diff --git a/src/prc1/set.c b/src/prc1/set.c
new file mode 100644
index 0000000..3058040
--- /dev/null
+++ b/src/prc1/set.c
@@ -0,0 +1,552 @@
+#include "set.h"
+#include "cgen.h"
+#include "errloc.h"
+#include "fn.h"
+#include "lex.h"
+#include "parse.h"
+#include "prv.h"
+#include "stmt.h"
+#include "storage.h"
+#include "type.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+static int mv(const struct lex *l, struct prv *p);
+static int maddr(const struct lex *l, struct prv *p);
+static int msz(const struct lex *l, struct prv *p);
+static int mimb(const struct lex *l, struct prv *p);
+
+static const struct seq seqs[] =
+{
+ /* size of elementary data types */
+ {(struct step[]){
+ {ID, NULL, 1},
+ {ID, "to"},
+ {ID, "size"},
+ {ID, "of"},
+ {ID, "void"},
+ {0}}, .fn = msz},
+ {(struct step[]){
+ {ID, NULL, 1},
+ {ID, "to"},
+ {ID, "size"},
+ {ID, "of"},
+ {ID, "byte"},
+ {0}}, .fn = msz},
+ {(struct step[]){
+ {ID, NULL, 1},
+ {ID, "to"},
+ {ID, "size"},
+ {ID, "of"},
+ {ID, "halfword"},
+ {0}}, .fn = msz},
+ {(struct step[]){
+ {ID, NULL, 1},
+ {ID, "to"},
+ {ID, "size"},
+ {ID, "of"},
+ {ID, "word"},
+ {0}}, .fn = msz},
+ {(struct step[]){
+ {ID, NULL, 1},
+ {ID, "to"},
+ {ID, "size"},
+ {ID, "of"},
+ {ID, "long"},
+ {0}}, .fn = msz},
+ {(struct step[]){
+ {ID, NULL, 1},
+ {ID, "to"},
+ {ID, "size"},
+ {ID, "of"},
+ {ID, "ubyte"},
+ {0}}, .fn = msz},
+ {(struct step[]){
+ {ID, NULL, 1},
+ {ID, "to"},
+ {ID, "size"},
+ {ID, "of"},
+ {ID, "uhalfword"},
+ {0}}, .fn = msz},
+ {(struct step[]){
+ {ID, NULL, 1},
+ {ID, "to"},
+ {ID, "size"},
+ {ID, "of"},
+ {ID, "uword"},
+ {0}}, .fn = msz},
+ {(struct step[]){
+ {ID, NULL, 1},
+ {ID, "to"},
+ {ID, "size"},
+ {ID, "of"},
+ {ID, "ulong"},
+ {0}}, .fn = msz},
+
+ /* address of variable */
+ {(struct step[]){
+ {ID, NULL, 1},
+ {ID, "to"},
+ {ID, "address"},
+ {ID, "of"},
+ {ID},
+ {0}}, .fn = maddr},
+
+ /* size of variable or type alias */
+ {(struct step[]){
+ {ID, NULL, 1},
+ {ID, "to"},
+ {ID, "size"},
+ {ID, "of"},
+ {ANY},
+ {0}}, .fn = msz},
+
+ /* array index or aggregate member */
+ {(struct step[]){
+ {ID, NULL, 1},
+ {ID, "to"},
+ {ANY},
+ {ID, "of"},
+ {ID},
+ {0}}, .fn = mimb},
+
+ /* simple set */
+ {(struct step[]){{ID, NULL, 1}, {ID, "to"}, {ANY}, {0}}, .fn = mv},
+ {0}
+};
+
+static unsigned long long tonum(const char *s)
+{
+ unsigned long long v;
+ char *end;
+
+ errno = 0;
+ v = strtoull(s, &end, 0);
+
+ if (*s == '-' || errno || *end)
+ return strtoll(s, NULL, 0);
+
+ return v;
+}
+
+static int dst(const struct lex *l, const struct tk *tk, struct set *s,
+ struct prv *p)
+{
+ const struct stentry *e = fn_var(fn_cur(p), tk);
+ size_t n = s->ndsts + 1;
+ struct setdst *dsts;
+
+ if (!e)
+ {
+ errloc(tk, "undefined reference to \"%s\"", tk->s);
+ return -1;
+ }
+ else if (!(dsts = realloc(s->dsts, n * sizeof *dsts)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ s->dsts = dsts;
+ s->dsts[s->ndsts++] = (struct setdst){.entry = e};
+ return 0;
+}
+
+static int checkzero(const struct tk *tk, const struct set *s,
+ unsigned long long v)
+{
+ if (!s->srcv)
+ return 0;
+
+ for (size_t i = 0; i < s->ndsts; i++)
+ {
+ const struct stentry *dst = s->dsts[i].entry;
+
+ if (dst->t->type != BUILTIN)
+ {
+ errloc(tk, "destination \"%s\" cannot be set to \"%s\"",
+ dst->tk->s, tk->s);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int checkcompat(const struct tk *tk, const struct set *s)
+{
+ for (size_t i = 0; i < s->ndsts; i++)
+ {
+ const struct stentry *dst = s->dsts[i].entry;
+ const struct type *st = s->src->t, *dt = dst->t;
+
+ /* TODO: integer promotions */
+ if (st->type != dt->type
+ || st->sz != dt->sz
+ || st->sign != dt->sign)
+ {
+ errloc(tk, "destination \"%s\" cannot be set to \"%s\"",
+ dst->tk->s, tk->s);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int mv(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = p->stk;
+ const struct stentry *e;
+ struct stmt *stmt = stmt_cur(p);
+ struct set *s = &stmt->u.set;
+
+ while (!lex_eof(l, tk) && !kw((tk)->s))
+ if (dst(l, tk++, s, p))
+ return -1;
+
+ /* skip "to" */
+ tk++;
+
+ if (tk->type == LIT)
+ {
+ /* this can only be applied to arrays of/pointers to chars */
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+ }
+ else if (tk->type == NUM)
+ {
+ s->type = SET_VAL;
+ s->srcv = tonum(tk->s);
+
+ if (checkzero(tk, s, s->srcv))
+ return -1;
+ }
+ else if (tk->type == ID)
+ {
+ const struct fn *fn = fn_cur(p);
+ const struct type *t = type_find(fn, tk->s);
+
+ if (t)
+ {
+ if (t->type != C)
+ {
+ errloc(tk, "type \"%s\" not a constant", tk->s);
+ return -1;
+ }
+
+ s->type = SET_VAL;
+ s->srcv = t->u.c.v;
+
+ if (checkzero(tk, s, s->srcv))
+ return -1;
+ }
+ else if ((e = fn_var(fn_cur(p), tk)))
+ {
+ s->type = SET_VAR;
+ s->src = e;
+
+ if (checkcompat(tk, s))
+ return -1;
+ }
+ else
+ {
+ errloc(tk, "undefined reference to \"%s\"", tk->s);
+ return -1;
+ }
+ }
+
+ fputs("\t\tadding set ", stderr);
+
+ for (size_t i = 0; i < s->ndsts; i++)
+ fprintf(stderr, "%s ", s->dsts[i].entry->tk->s);
+
+ fprintf(stderr, "to %s\n", tk->s);
+
+ if (pop(l, p))
+ return -1;
+
+ p->stk = ++tk;
+ return 1;
+}
+
+static int maddr(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = p->stk;
+ struct stmt *stmt = stmt_cur(p);
+ struct set *s = &stmt->u.set;
+ const struct stentry *e;
+
+ while (!lex_eof(l, tk) && !kw((tk)->s))
+ {
+ const struct stentry *e = fn_var(fn_cur(p), tk);
+
+ if (e && e->t->type != PTR)
+ {
+ errloc(tk, "destination \"%s\" not a pointer", tk->s);
+ return -1;
+ }
+ else if (dst(l, tk++, s, p))
+ return -1;
+ }
+
+ /* skip "to address of" */
+ tk += 3;
+
+ if (!(e = fn_var(fn_cur(p), tk)))
+ {
+ errloc(tk, "undefined reference to \"%s\"", tk->s);
+ return -1;
+ }
+ else if (pop(l, p))
+ return -1;
+
+ s->type = SET_ADDR;
+ s->src = e;
+ p->stk = ++tk;
+ fputs("\t\tadding set ", stderr);
+
+ for (size_t i = 0; i < s->ndsts; i++)
+ fprintf(stderr, "%s ", s->dsts[i].entry->tk->s);
+
+ fprintf(stderr, "to address of %s\n", s->src->tk->s);
+ return 1;
+}
+
+static int msz(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = p->stk;
+ const struct stentry *e;
+ struct stmt *stmt = stmt_cur(p);
+ struct set *s = &stmt->u.set;
+ const struct fn *fn = fn_cur(p);
+ const struct type *t;
+
+ while (!lex_eof(l, tk) && !kw(tk->s))
+ {
+ const struct stentry *e = fn_var(fn_cur(p), tk);
+
+ if (e && e->t->type != BUILTIN)
+ {
+ errloc(tk, "destination \"%s\" not an integer", tk->s);
+ return -1;
+ }
+ else if (dst(l, tk++, s, p))
+ return -1;
+ }
+
+ /* skip "to size of" */
+ tk += 3;
+
+ if (tk->type == NUM)
+ {
+ errloc(tk, "cannot compute size of a number");
+ return -1;
+ }
+ else if (tk->type == LIT)
+ s->srcv = strlen(tk->s) + 1;
+ else if ((t = type_find(fn, tk->s)))
+ {
+ if (!t->sz)
+ {
+ errloc(tk, "type \"%s\" has no size", tk->s);
+ return-1;
+ }
+
+ s->srcv = t->sz;
+ }
+ else if ((e = fn_var(fn, tk)))
+ s->srcv = e->t->sz;
+ else
+ return-1;
+
+ if (pop(l, p))
+ return -1;
+
+ s->type = SET_VAL;
+ p->stk = ++tk;
+ fputs("\t\tadding set ", stderr);
+
+ for (size_t i = 0; i < s->ndsts; i++)
+ fprintf(stderr, "%s ", s->dsts[i].entry->tk->s);
+
+ fprintf(stderr, "size=%llu\n", s->srcv);
+ return 1;
+}
+
+static int mimb(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = p->stk;
+ const struct fn *fn = fn_cur(p);
+ struct stmt *stmt = stmt_cur(p);
+ struct set *s = &stmt->u.set;
+
+ while (!lex_eof(l, tk) && !kw((tk)->s))
+ if (dst(l, tk++, s, p))
+ return -1;
+
+ /* skip "to" */
+ tk++;
+
+ fputs("\t\tadding set ", stderr);
+
+ for (size_t i = 0; i < s->ndsts; i++)
+ fprintf(stderr, "%s ", s->dsts[i].entry->tk->s);
+
+ if (tk->type == LIT)
+ {
+ errloc(tk, "unexpected literal \"%s\"", tk->s);
+ return -1;
+ }
+ else if (tk->type == ID && !(s->off = fn_var(fn, tk)))
+ {
+ errloc(tk, "undefined reference to \"%s\"", tk->s);
+ return -1;
+ }
+ else if (tk->type == NUM)
+ s->srcv = tonum(tk->s);
+
+ fprintf(stderr, "to [%s] ", tk->s);
+
+ /* skip index and "of" */
+ tk += 2;
+
+ if (!(s->src = fn_var(fn, tk)))
+ {
+ errloc(tk, "undefined reference to \"%s\"", tk->s);
+ return -1;
+ }
+ else if (pop(l, p))
+ return -1;
+
+ fprintf(stderr, " of %s\n", tk->s);
+ s->type = SET_IMB;
+ p->stk = ++tk;
+ return 1;
+}
+
+void set_free(struct set *m)
+{
+ if (m)
+ free(m->dsts);
+}
+
+static int cg_val(const struct set *s, const struct stentry *dst,
+ struct cgen *c)
+{
+ const char *szstr = cgen_sz(dst->t->sz), *ab = cgen_abity(dst->t);
+ const struct fn *fn = c->fn;
+
+ if (ab)
+ {
+ printf("store%s %llu, ", szstr, s->srcv);
+ cgen_printvar(fn, dst);
+ }
+ else
+ {
+ fputs("%r =l call $memset(l ", stdout);
+ cgen_printvar(fn, dst);
+ printf(", w 0, l %zu)", dst->t->sz);
+ }
+
+ putchar('\n');
+ return 0;
+}
+
+static int cg_addr(const struct set *s, const struct stentry *dst,
+ struct cgen *c)
+{
+ printf("store%s ", cgen_sz(dst->t->sz));
+ cgen_printvar(c->fn, s->src);
+ fputs(", ", stdout);
+ cgen_printvar(c->fn, dst);
+ putchar('\n');
+ return 0;
+}
+
+static int cg_var(const struct set *s, const struct stentry *dst,
+ struct cgen *c)
+{
+ const struct stentry *src = s->src;
+ const char *tmp = cgen_tmp(src->t->sz);
+
+ printf("%%%s =%s load%s ", tmp, cgen_sz(src->t->sz), cgen_load(src->t));
+ cgen_printvar(c->fn, src);
+ putchar('\n');
+ printf("store%s %%%s, ", cgen_sz(dst->t->sz), tmp);
+ cgen_printvar(c->fn, dst);
+ putchar('\n');
+ return 0;
+}
+
+static int cg_imb(const struct set *s, const struct stentry *e, struct cgen *c)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}
+
+int set_cgen(const struct set *s, struct cgen *c)
+{
+ for (size_t i = 0; i < s->ndsts; i++)
+ {
+ const struct stentry *dst = s->dsts[i].entry;
+
+ static int (*const f[])(const struct set *, const struct stentry *,
+ struct cgen *) =
+ {
+ [SET_VAL] = cg_val,
+ [SET_ADDR] = cg_addr,
+ [SET_VAR] = cg_var,
+ [SET_IMB] = cg_imb
+ };
+
+ if (f[s->type](s, dst, c))
+ return -1;
+ }
+
+ return 0;
+}
+
+int set(const struct lex *l, struct prv *p)
+{
+ const struct tk *next = p->tk;
+ const char *ns;
+ struct fn *fn = fn_cur(p);
+ size_t n = fn->nstmts + 1;
+ struct stmt *stmts;
+
+ if (lex_eof(l, next))
+ {
+ errloc(next - 1, "incomplete set statement");
+ return -1;
+ }
+ else if (kw((ns = next->s)))
+ {
+ errloc(next, "incomplete set statement");
+ return -1;
+ }
+ else if (!(stmts = realloc(fn->stmts, n * sizeof *stmts)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ fn->stmts = stmts;
+ fn->stmts[fn->nstmts++] = (struct stmt){.type = SET};
+
+ struct pos init =
+ {
+ .seq = seqs,
+ .stseq = seqs,
+ .step = seqs->steps
+ };
+
+ if (push(&init, p))
+ return -1;
+
+ p->stk = p->tk;
+ return 1;
+}
diff --git a/src/prc1/set.h b/src/prc1/set.h
new file mode 100644
index 0000000..fdd067a
--- /dev/null
+++ b/src/prc1/set.h
@@ -0,0 +1,29 @@
+#ifndef SET_H
+#define SET_H
+
+#include "cgen.h"
+#include "lex.h"
+#include "parse.h"
+#include "prv.h"
+#include "storage.h"
+#include <stddef.h>
+
+struct setdst
+{
+ const struct stentry *entry;
+};
+
+struct set
+{
+ enum {SET_VAL, SET_VAR, SET_ADDR, SET_IMB} type;
+ const struct stentry *off, *src;
+ unsigned long long srcv;
+ struct setdst *dsts;
+ size_t ndsts;
+};
+
+int set(const struct lex *l, struct prv *p);
+int set_cgen(const struct set *s, struct cgen *c);
+void set_free(struct set *s);
+
+#endif
diff --git a/src/prc1/stmt.c b/src/prc1/stmt.c
new file mode 100644
index 0000000..8d2fdd4
--- /dev/null
+++ b/src/prc1/stmt.c
@@ -0,0 +1,92 @@
+#include "stmt.h"
+#include "call.h"
+#include "fn.h"
+#include "from.h"
+#include "parse.h"
+#include "prv.h"
+#include "display.h"
+#include "exit.h"
+#include "set.h"
+#include "warn.h"
+
+void stmt_free(struct stmt *s)
+{
+ switch (s->type)
+ {
+ case DISPLAY:
+ display_free(&s->u.display);
+ break;
+ case SET:
+ set_free(&s->u.set);
+ break;
+ case CALL:
+ call_free(&s->u.call);
+ break;
+ case WARN:
+ warn_free(&s->u.warn);
+ break;
+ case FROM:
+ from_free(&s->u.from);
+ break;
+ case END:
+ end_free(&s->u.end);
+ break;
+ }
+}
+
+int stmt_cgen(const struct stmt *s, struct cgen *c)
+{
+ switch (s->type)
+ {
+ case DISPLAY:
+ return display_cgen(&s->u.display, c);
+ case SET:
+ return set_cgen(&s->u.set, c);
+ case CALL:
+ return call_cgen(&s->u.call, c);
+ case WARN:
+ return warn_cgen(&s->u.warn, c);
+ case FROM:
+ return from_cgen(&s->u.from, c);
+ case END:
+ return end_cgen(&s->u.end, c);
+ }
+
+ fprintf(stderr, "%s: unreachable\n", __func__);
+ return -1;
+}
+
+struct stmt *stmt_cur(const struct prv *p)
+{
+ const struct fn *fn = fn_cur(p);
+
+ return &fn->stmts[fn->nstmts - 1];
+}
+
+const struct seq stmts[] =
+{
+ {(struct step[]){
+ {ID, "display"}, {ANY, NULL, 1}, {ID, "etc"}, {0}}, .fn = display},
+ {(struct step[])
+ {{ID, "display"}, {ANY, NULL, 1}, {0}}, .fn = display},
+ {(struct step[]){
+ {ID, "warn"}, {ANY, NULL, 1}, {ID, "etc"}, {0}}, .fn = warn},
+ {(struct step[])
+ {{ID, "warn"}, {ANY, NULL, 1}, {0}}, .fn = warn},
+ {(struct step[])
+ {{ID, "from"}, {ANY}, {ID, "to"}, {ANY}, {ID, "using"}, {ID},
+ {ID, "by"}, {ANY}, {0}}, .fn = from_idby},
+ {(struct step[])
+ {{ID, "from"}, {ANY}, {ID, "to"}, {ANY}, {ID, "by"}, {ANY}, {0}},
+ .fn = from_by},
+ {(struct step[])
+ {{ID, "from"}, {ANY}, {ID, "to"}, {ANY}, {ID, "using"}, {ID}, {0}},
+ .fn = from_id},
+ {(struct step[])
+ {{ID, "from"}, {ANY}, {ID, "to"}, {ANY}, {0}}, .fn = from},
+ {(struct step[]){{ID, "exit"}, {0}}, .fn = s_exit},
+ {(struct step[]){{ID, "set"}, {0}}, .fn = set},
+ {(struct step[]){{ID, "call"}, {0}}, .fn = call},
+ {(struct step[]){{ID, "end"}, {0}}, .fn = end},
+ {0}
+};
diff --git a/src/prc1/stmt.h b/src/prc1/stmt.h
new file mode 100644
index 0000000..5cc650d
--- /dev/null
+++ b/src/prc1/stmt.h
@@ -0,0 +1,44 @@
+#ifndef STMTS_H
+#define STMTS_H
+
+#include "cgen.h"
+#include "prv.h"
+#include "call.h"
+#include "display.h"
+#include "end.h"
+#include "from.h"
+#include "set.h"
+#include "warn.h"
+
+struct stmt
+{
+ enum
+ {
+ DISPLAY,
+ SET,
+ CALL,
+ WARN,
+ FROM,
+ END
+ } type;
+
+ int (*end)(const struct stmt *, struct cgen *);
+
+ union
+ {
+ struct display display;
+ struct set set;
+ struct call call;
+ struct warn warn;
+ struct from from;
+ struct end end;
+ } u;
+};
+
+struct stmt *stmt_cur(const struct prv *p);
+int stmt_cgen(const struct stmt *s, struct cgen *c);
+void stmt_free(struct stmt *s);
+
+extern const struct seq stmts[];
+
+#endif
diff --git a/src/prc1/storage.c b/src/prc1/storage.c
new file mode 100644
index 0000000..62f3c1f
--- /dev/null
+++ b/src/prc1/storage.c
@@ -0,0 +1,219 @@
+#include "storage.h"
+#include "errloc.h"
+#include "fn.h"
+#include "lex.h"
+#include "prv.h"
+#include "type.h"
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int id(const struct lex *l, struct prv *p);
+
+static const struct seq seqs[] =
+{
+ {(struct step[]){{ID}, {0}}, .fn = id},
+ {0}
+};
+
+static int compat(const struct fn *fn, const struct prv *p,
+ const struct tk *name, const struct type *t, const struct type **ot)
+{
+ const struct stentry *oe;
+ const struct fn *top = fn_cur(p);
+
+ /* allow global entries if already found on linkage section
+ * and vice versa, as long as the types are compatible. */
+ if (p->st == top->gl)
+ {
+ for (size_t i = 0; i < fn->nimps; i++)
+ {
+ const struct im *im = &fn->imps[i];
+
+ if (compat(im->ast.fns, p, name, t, ot))
+ return 1;
+ }
+
+ if (fn->lk && (oe = storage_find(name->s, fn->lk)))
+ {
+ if (t == oe->t)
+ return 1;
+
+ *ot = oe->t;
+ return -1;
+ }
+ }
+ else if (p->st == top->lk)
+ {
+ if (fn->gl && (oe = storage_find(name->s, fn->gl)))
+ {
+ if (t == oe->t)
+ return 1;
+
+ *ot = oe->t;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int stpush(const struct lex *l, struct prv *p, const struct tk *name,
+ const struct type *t)
+{
+ int ret = -1;
+ struct stentry *entries;
+ struct storage *st = p->st;
+ size_t n = st->nentries + 1;
+ const struct fn *fn = fn_cur(p);
+ const struct loc *loc = &name->loc;
+ const struct stentry *e;
+ char *tname = type_name(t);
+
+ if (!tname)
+ goto end;
+ else if (!t->sz && p->st != fn->lk)
+ {
+ errloc(name + 1, "type \"%s\" has null size", tname);
+ goto end;
+ }
+ else if (st->check && (e = fn_var(fn, name)))
+ {
+ const struct loc *loc = &e->tk->loc;
+ const struct type *ot;
+ int ret = compat(fn, p, name, t, &ot);
+
+ if (ret < 0)
+ {
+ errloc(name, "variable \"%s\" (\"%s\") already defined at "
+ "%s:%d:%d with incompatible type (\"%s\")",
+ name->s, type_name(t), loc->f, loc->line, loc->col,
+ type_name(ot));
+ goto end;
+ }
+ else if (!ret)
+ {
+ errloc(name, "variable \"%s\" already defined at %s:%d:%d",
+ name->s, loc->f, loc->line, loc->col);
+ goto end;
+ }
+ }
+
+ /* find var in currently known symbols */
+ for (size_t i = 0; i < st->nentries; i++)
+ {
+ const struct stentry *e = &st->entries[i];
+ const struct tk *tk = e->tk;
+
+ if (!strcmp(name->s, tk->s))
+ {
+ const struct loc *loc = &tk->loc;
+
+ errloc(tk, "variable \"%s\" already defined at %s:%d:%d",
+ tk->s, loc->f, loc->line, loc->col);
+ goto end;
+ }
+ }
+
+ if (pop(l, p))
+ goto end;
+ else if (!(entries = realloc(st->entries, n * sizeof *entries)))
+ {
+ perror("realloc(3)");
+ goto end;
+ }
+
+ if (t->sz)
+ st->offset += st->offset % t->sz;
+
+ entries[st->nentries++] = (struct stentry)
+ {
+ .t = t,
+ .tk = name,
+ .offset = st->offset
+ };
+
+ st->entries = entries;
+ st->offset += t->sz;
+ p->stk = p->tk;
+
+ if (p->proto)
+ fputc('\t', stderr);
+
+ fprintf(stderr, "\tadding %s (%d:%d) to %s, type %s, offset %zu, "
+ "size %zu\n", name->s, loc->line, loc->col, st->name, tname,
+ entries[st->nentries - 1].offset, t->sz);
+ ret = 1;
+end:
+ free(tname);
+ return ret;
+}
+
+static int v(const struct lex *l, struct prv *p, const struct type *t)
+{
+ return stpush(l, p, p->id, t);
+}
+
+static int id(const struct lex *l, struct prv *p)
+{
+ p->id = p->stk;
+ return type(l, p, v);
+}
+
+void storage_free(struct storage *s)
+{
+ if (s)
+ free(s->entries);
+
+ free(s);
+}
+
+const struct stentry *storage_find(const char *name, const struct storage *s)
+{
+ for (size_t i = 0; i < s->nentries; i++)
+ {
+ const struct stentry *e = &s->entries[i];
+
+ if (!strcmp(name, e->tk->s))
+ return e;
+ }
+
+ return NULL;
+}
+
+int storage(const struct lex *l, struct prv *p, const char *name, int check,
+ struct storage **out)
+{
+ struct storage *st = NULL;
+ struct pos init =
+ {
+ .seq = seqs,
+ .stseq = seqs,
+ .step = seqs->steps
+ };
+
+ if (*out)
+ {
+ const struct loc *loc = &(*out)->tk->loc;
+
+ errloc(p->stk, "%s already defined at %s:%d:%d", name, loc->f,
+ loc->line, loc->col);
+ goto failure;
+ }
+ else if (!(st = malloc(sizeof *st)))
+ {
+ perror("malloc(3)");
+ goto failure;
+ }
+ else if (push(&init, p))
+ goto failure;
+
+ *st = (struct storage){.name = name, .tk = p->stk, .check = check};
+ *out = p->st = st;
+ p->stk = p->tk;
+ return 1;
+
+failure:
+ free(st);
+ return -1;
+}
diff --git a/src/prc1/storage.h b/src/prc1/storage.h
new file mode 100644
index 0000000..a3509c5
--- /dev/null
+++ b/src/prc1/storage.h
@@ -0,0 +1,29 @@
+#ifndef STORAGE_H
+#define STORAGE_H
+
+#include "lex.h"
+#include "prv.h"
+#include <stddef.h>
+
+struct stentry
+{
+ const struct tk *tk;
+ const struct type *t;
+ size_t offset;
+};
+
+struct storage
+{
+ const char *name;
+ const struct tk *tk;
+ struct stentry *entries;
+ size_t nentries, offset;
+ int check;
+};
+
+int storage(const struct lex *l, struct prv *p, const char *name, int check,
+ struct storage **out);
+const struct stentry *storage_find(const char *name, const struct storage *s);
+void storage_free(struct storage *s);
+
+#endif
diff --git a/src/prc1/td.c b/src/prc1/td.c
new file mode 100644
index 0000000..72c379f
--- /dev/null
+++ b/src/prc1/td.c
@@ -0,0 +1,587 @@
+#include "td.h"
+#include "div.h"
+#include "errloc.h"
+#include "fn.h"
+#include "lex.h"
+#include "parse.h"
+#include "prv.h"
+#include "storage.h"
+#include "type.h"
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int cid(const struct lex *, struct prv *);
+static int cnum(const struct lex *, struct prv *);
+static int end(const struct lex *, struct prv *);
+static int endp(const struct lex *, struct prv *);
+static int u(const struct lex *, struct prv *);
+static int s(const struct lex *, struct prv *);
+static int c(const struct lex *, struct prv *);
+static int a(const struct lex *, struct prv *);
+static int p(const struct lex *, struct prv *);
+
+static const struct seq seqs[] =
+{
+ {(struct step[]){{ID, "end"}, {0}}, .fn = end},
+ {(struct step[]){{ID}, {ID, "constant"}, {ID, "to"}, {ID}, {0}}, .fn = cid},
+ {(struct step[]){{ID}, {ID, "constant"}, {ID, "to"}, {NUM}, {0}}, .fn = cnum},
+ {(struct step[]){{ID}, {ID, "alias"}, {0}}, .fn = a},
+ {(struct step[]){{ID}, {ID, "union"}, {0}}, .fn = u},
+ {(struct step[]){{ID}, {ID, "struct"}, {0}}, .fn = s},
+ {(struct step[]){{ID}, {ID, "constant"}, {0}}, .fn = c},
+ {(struct step[]){{ID}, {ID, "prototype"}, {0}}, .fn = p, .end = endp},
+ {0}
+};
+
+static void reset(struct prv *p)
+{
+ struct pos *pos = &p->pos[p->i];
+
+ pos->seq = seqs;
+ pos->step = seqs->steps;
+ p->stk = p->tk;
+}
+
+static int bump(const struct tk *tk, const struct type *t, struct prv *p)
+{
+ struct tdconstant *tdc = p->tdc;
+ int neg = 0;
+
+ if (!t->sign)
+ {
+ unsigned long long uv = t->u.c.uv;
+
+ if (uv + 1 < uv)
+ {
+ errloc(tk, "constant value exceeds maximum (%llu)", ULLONG_MAX);
+ return -1;
+ }
+
+ tdc->u.uv = ++uv;
+ }
+ else
+ {
+ long long v = t->u.c.v;
+
+ if (++v < 0)
+ neg = 1;
+
+ tdc->u.v = v;
+ }
+
+ tdc->neg = neg;
+
+ for (size_t i = 0; i < p->block; i++)
+ fputc('\t', stderr);
+
+ fprintf(stderr, "\t\tnext %s constant value set to ",
+ neg ? "negative" : "positive");
+
+ if (neg)
+ fprintf(stderr, "%lld", tdc->u.v);
+ else
+ fprintf(stderr, "%llu", tdc->u.uv);
+
+ fputc('\n', stderr);
+ return 0;
+}
+
+static int cid(const struct lex *l, struct prv *p)
+{
+ const struct fn *fn = fn_cur(p);
+ const struct tk *id = p->stk, *v = p->stk + 3;
+ const struct stentry *e;
+ const struct type *t = type_find(fn, id->s);
+ struct tdconstant *tdc = p->tdc;
+ struct td *td = fn->td;
+ size_t n = td->ntypes + 1;
+ struct type *types, *nt;
+
+ if (t)
+ {
+ const struct loc *loc = &t->tk->loc;
+
+ errloc(id, "type \"%s\" already defined at %s:%d:%d", id->s, loc->f,
+ loc->line, loc->col);
+ return -1;
+ }
+ else if ((e = fn_var(fn, id)))
+ {
+ const struct loc *loc = &e->tk->loc;
+
+ errloc(id, "\"%s\" already defined as a variable at %s:%d:%d", id->s,
+ loc->f, loc->line, loc->col);
+ return -1;
+ }
+ else if ((e = fn_var(fn, v)))
+ {
+ const struct loc *loc = &e->tk->loc;
+
+ errloc(id, "value \"%s\" (defined at %s:%d:%d) is not a constant",
+ v->s, loc->f, loc->line, loc->col);
+ return -1;
+ }
+ else if (!(t = type_find(fn, v->s)))
+ {
+ errloc(id, "undefined reference to value \"%s\"", v->s);
+ return -1;
+ }
+ else if (t->type != C)
+ {
+ const struct loc *loc = &t->tk->loc;
+
+ errloc(id, "value \"%s\" (defined at %s:%d:%d) is not a constant",
+ v->s, loc->f, loc->line, loc->col);
+ return -1;
+ }
+ else if (!(types = realloc(td->types, n * sizeof *types)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ td->types = types;
+ nt = &td->types[td->ntypes++];
+ *nt = (struct type)
+ {
+ .type = C,
+ .tk = id,
+ .sign = t->sign,
+ .u.c = t->u.c
+ };
+
+ tdc->neg = t->sign;
+
+ if (bump(id, t, p))
+ return -1;
+
+ for (size_t i = 0; i < p->block; i++)
+ fputc('\t', stderr);
+
+ fprintf(stderr, "\tadding constant %s to %s (%lld)\n", id->s, v->s,
+ t->u.c.v);
+ reset(p);
+ return 1;
+}
+
+static int cnum(const struct lex *l, struct prv *p)
+{
+ const struct fn *fn = fn_cur(p);
+ const struct tk *id = p->stk, *num = p->stk + 3;
+ const char *name = id->s;
+ const struct stentry *e;
+ struct td *td = fn->td;
+ size_t n = td->ntypes + 1;
+ struct type *types, *nt;
+ struct tdconstant *tdc = p->tdc;
+ const struct type *t = type_find(fn, name);
+ int sign = *num->s == '-';
+ unsigned long long uv = 0;
+ long long v;
+ char *end;
+
+ errno = 0;
+ v = strtoll(num->s, &end, 0);
+
+ if (!sign || errno || *end)
+ {
+ errno = 0;
+ uv = strtoull(num->s, NULL, 0);
+ }
+
+ if (t)
+ {
+ const struct loc *loc = &t->tk->loc;
+
+ errloc(id, "type \"%s\" already defined at %s:%d:%d", name, loc->f,
+ loc->line, loc->col);
+ return -1;
+ }
+ else if ((e = fn_var(fn, id)))
+ {
+ const struct loc *loc = &e->tk->loc;
+
+ errloc(id, "\"%s\" already defined as a variable at %s:%d:%d", id->s,
+ loc->f, loc->line, loc->col);
+ return -1;
+ }
+ else if (!(types = realloc(td->types, n * sizeof *types)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ td->types = types;
+ nt = &td->types[td->ntypes++];
+ *nt = (struct type)
+ {
+ .type = C,
+ .tk = id,
+ .sign = sign
+ };
+
+ if (sign)
+ tdc->u.v = nt->u.c.v = v;
+ else
+ tdc->u.uv = nt->u.c.uv = uv;
+
+ tdc->neg = sign;
+
+ for (size_t i = 0; i < p->block; i++)
+ fputc('\t', stderr);
+
+ fprintf(stderr, "\tadding constant %s to %s\n", id->s, num->s);
+
+ if (bump(id, nt, p))
+ return -1;
+
+ reset(p);
+ return 1;
+}
+
+static int c(const struct lex *l, struct prv *p)
+{
+ const struct fn *fn = fn_cur(p);
+ const struct tk *id = p->stk;
+ const struct stentry *e;
+ const struct type *t = type_find(fn, id->s);
+ struct td *td = fn->td;
+ size_t n = td->ntypes + 1;
+ struct type *types, *nt;
+ const struct tdconstant *tdc = p->tdc;
+
+ if (t)
+ {
+ const struct loc *loc = &t->tk->loc;
+
+ errloc(id, "type \"%s\" already defined at %s:%d:%d", id->s, loc->f,
+ loc->line, loc->col);
+ return -1;
+ }
+ else if ((e = fn_var(fn, id)))
+ {
+ const struct loc *loc = &e->tk->loc;
+
+ errloc(id, "\"%s\" already defined as a variable at %s:%d:%d", id->s,
+ loc->f, loc->line, loc->col);
+ return -1;
+ }
+ else if (!(types = realloc(td->types, n * sizeof *types)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ td->types = types;
+ nt = &td->types[td->ntypes];
+ *nt = (struct type)
+ {
+ .type = C,
+ .tk = id,
+ .sign = tdc->neg,
+ };
+
+ if (nt->sign)
+ nt->u.c.v = tdc->u.v;
+ else
+ nt->u.c.uv = tdc->u.uv;
+
+ for (size_t i = 0; i < p->block; i++)
+ fputc('\t', stderr);
+
+ fprintf(stderr, "\tadding constant %s to ", id->s);
+
+ if (tdc->neg)
+ fprintf(stderr, "%lld", tdc->u.v);
+ else
+ fprintf(stderr, "%llu", tdc->u.uv);
+
+ fputc('\n', stderr);
+
+ if (bump(id, &td->types[td->ntypes++], p))
+ return -1;
+
+ reset(p);
+ return 1;
+}
+
+static int p(const struct lex *l, struct prv *p)
+{
+ struct fn *fn = fn_cur(p), *pt = NULL;
+ struct td *td = fn->td;
+ size_t n = td->ntypes + 1;
+ struct type *types;
+
+ if (!(pt = malloc(sizeof *pt)))
+ {
+ perror("malloc(3)");
+ goto failure;
+ }
+
+ *pt = (struct fn){.tk = p->stk, .parent = fn};
+
+ if (!(types = realloc(td->types, n * sizeof *types)))
+ {
+ perror("realloc(3)");
+ goto failure;
+ }
+
+ types[td->ntypes++] = (struct type)
+ {
+ .type = P,
+ .tk = p->stk,
+ .sz = sizeof (void *),
+ .align = sizeof (void *),
+ .u.p.fn = pt
+ };
+
+ td->types = types;
+ p->proto = pt;
+ fprintf(stderr, "\tadding prototype %s\n", p->stk->s);
+ return div_p(l, p);
+
+failure:
+ free(pt);
+ return -1;
+}
+
+static int ends(struct type *t)
+{
+ const struct s *s = &t->u.s;
+ const struct storage *st = s->storage;
+ const struct stentry *e;
+
+ if (!st->nentries)
+ {
+ errloc(t->tk, "struct \"%s\" has no members", t->tk->s);
+ return -1;
+ }
+
+ e = &st->entries[st->nentries - 1];
+ t->align = st->entries->t->align;
+ t->sz = e->offset + e->t->sz;
+ t->sz += t->align - (t->sz % t->align);
+ fprintf(stderr, "\tadding struct %s, size %zu, align %zu\n", t->tk->s,
+ t->sz, t->align);
+ return 0;
+}
+
+static int endu(struct type *t)
+{
+ const struct u *u = &t->u.u;
+
+ if (!u->storage->nentries)
+ {
+ errloc(t->tk, "union \"%s\" has no members", t->tk->s);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int end(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = p->tk - 1;
+ struct type *t;
+
+ if (!p->block)
+ {
+ errloc(tk, "unexpected \"end\" statement");
+ return -1;
+ }
+ else if (!(t = p->ag))
+ {
+ errloc(tk, "unreachable");
+ return -1;
+ }
+
+ switch (t->type)
+ {
+ case U:
+ if (endu(t))
+ return -1;
+
+ break;
+
+ case S:
+ if (ends(t))
+ return -1;
+
+ break;
+
+ default:
+ errloc(tk, "unreachable");
+ return -1;
+ }
+
+ p->ag = NULL;
+ p->block--;
+ reset(p);
+ return 1;
+}
+
+static int endp(const struct lex *l, struct prv *p)
+{
+ if (fn_end(l, p))
+ return -1;
+
+ p->proto = NULL;
+ return 0;
+}
+
+static int s(const struct lex *l, struct prv *p)
+{
+ const struct tk *id = p->stk;
+ struct fn *fn = fn_cur(p);
+ struct td *td = fn->td;
+ size_t nt = td->ntypes + 1;
+ struct type *types, *newt;
+ const struct type *t = type_find(fn, id->s);
+
+ if (t)
+ {
+ const struct loc *loc = &t->tk->loc;
+
+ errloc(id, "type \"%s\" already defined at %s:%d:%d", id->s, loc->f,
+ loc->line, loc->col);
+ return -1;
+ }
+ else if (!(types = realloc(td->types, nt * sizeof *types)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ td->types = types;
+ newt = &td->types[td->ntypes++];
+ *newt = (struct type)
+ {
+ .type = S,
+ .tk = id
+ };
+
+ p->ag = newt;
+ p->block++;
+ return storage(l, p, id->s, 0, &newt->u.s.storage);
+}
+
+static int u(const struct lex *l, struct prv *p)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}
+
+static int tt(const struct lex *l, struct prv *p, const struct type *t)
+{
+ int ret = -1;
+ const struct fn *fn = fn_cur(p);
+ const struct tk *id = p->id;
+ const struct type *tp = type_ufind(fn, id->s);
+ const struct loc *loc = &id->loc;
+ struct td *td = fn->td;
+ size_t n = td->ntypes + 1;
+ struct type *types, *nt;
+ char *s = type_name(t);
+
+ if (!s)
+ goto end;
+ else if (tp)
+ {
+ const struct loc *loc = &tp->tk->loc;
+
+ errloc(id, "type \"%s\" already defined at %s:%d:%d", id->s, loc->f,
+ loc->line, loc->col);
+ goto end;
+ }
+ else if (pop(l, p))
+ return -1;
+ else if (!(types = realloc(td->types, n * sizeof *types)))
+ {
+ perror("realloc(3)");
+ goto end;
+ }
+
+ td->types = types;
+ nt = &td->types[td->ntypes++];
+ *nt = (struct type)
+ {
+ .type = T,
+ .tk = id,
+ .u.t.alias = t
+ };
+
+ fprintf(stderr, "\tadding %s (%d:%d) alias %s\n", id->s, loc->line,
+ loc->col, s);
+ reset(p);
+ p->id = NULL;
+ ret = 1;
+end:
+ free(s);
+ return ret;
+}
+
+static int a(const struct lex *l, struct prv *p)
+{
+ p->id = p->stk;
+ return type(l, p, tt);
+}
+
+void td_free(struct td *td)
+{
+ if (td)
+ {
+ for (size_t i = 0; i < td->ntypes; i++)
+ type_free(&td->types[i]);
+
+ free(td->types);
+ }
+
+ free(td);
+}
+
+int td(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = p->stk;
+ struct fn *fn = fn_cur(p);
+ struct td *td = NULL, *prev = fn->td;
+ struct tdconstant *tdc = NULL;
+ struct pos init =
+ {
+ .seq = seqs,
+ .stseq = seqs,
+ .step = seqs->steps
+ };
+
+ if (prev)
+ {
+ const struct loc *loc = &prev->tk->loc;
+
+ errloc(tk, "types section already defined at %s:%d:%d", loc->f,
+ loc->line, loc->col);
+ goto failure;
+ }
+ else if (!(td = malloc(sizeof *td))
+ || !(tdc = malloc(sizeof *tdc)))
+ {
+ perror("malloc(3)");
+ goto failure;
+ }
+ else if (push(&init, p))
+ goto failure;
+
+ *tdc = (struct tdconstant){0};
+ *td = (struct td){.tk = tk};
+ fn->td = td;
+ p->tdc = tdc;
+ p->stk = p->tk;
+ return 1;
+
+failure:
+ free(tdc);
+ free(td);
+ return -1;
+}
diff --git a/src/prc1/td.h b/src/prc1/td.h
new file mode 100644
index 0000000..2e23348
--- /dev/null
+++ b/src/prc1/td.h
@@ -0,0 +1,22 @@
+#ifndef TD_H
+#define TD_H
+
+#include "lex.h"
+#include "parse.h"
+#include "prv.h"
+
+struct tdconstant
+{
+ int neg;
+
+ union
+ {
+ unsigned long long uv;
+ long long v;
+ } u;
+};
+
+int td(const struct lex *l, struct prv *prv);
+void td_free(struct td *td);
+
+#endif
diff --git a/src/prc1/tmp.c b/src/prc1/tmp.c
new file mode 100644
index 0000000..c93443f
--- /dev/null
+++ b/src/prc1/tmp.c
@@ -0,0 +1,69 @@
+#include "tmp.h"
+#include "type.h"
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+const struct stentry *tmp_create(struct fn *fn, const struct type *t)
+{
+ static const char fmt[] = "__tmp%zu";
+ int r = snprintf(NULL, 0, fmt, fn->ntmps);
+ char *s = NULL, *tname = NULL;
+ struct tmp *tmp;
+
+ if (!(tname = type_name(t)))
+ goto failure;
+ else if (r < 0)
+ {
+ fprintf(stderr, "%s: snprintf(3) failed\n", __func__);
+ goto failure;
+ }
+ else if (!(s = malloc(r + 1)))
+ {
+ perror("malloc(3)");
+ goto failure;
+ }
+
+ snprintf(s, r + 1, fmt, fn->ntmps++);
+
+ if (!(tmp = malloc(sizeof *tmp)))
+ {
+ perror("realloc(3)");
+ goto failure;
+ }
+
+ *tmp = (struct tmp)
+ {
+ .tk =
+ {
+ .type = ID,
+ .s = s,
+ .loc.f = "(temporary)"
+ },
+
+ .e =
+ {
+ .tk = &tmp->tk,
+ .t = t
+ }
+ };
+
+ if (!fn->tmps)
+ fn->tmps = tmp;
+ else
+ for (struct tmp *t = fn->tmps; t ; t = t->next)
+ if (!t->next)
+ {
+ t->next = tmp;
+ break;
+ }
+
+ fprintf(stderr, "\tcreating temporary variable %s, type %s\n", s, tname);
+ free(tname);
+ return &tmp->e;
+
+failure:
+ free(s);
+ free(tname);
+ return NULL;
+}
diff --git a/src/prc1/tmp.h b/src/prc1/tmp.h
new file mode 100644
index 0000000..b62487a
--- /dev/null
+++ b/src/prc1/tmp.h
@@ -0,0 +1,9 @@
+#ifndef TMP_H
+#define TMP_H
+
+#include "parse.h"
+#include "storage.h"
+
+const struct stentry *tmp_create(struct fn *fn, const struct type *t);
+
+#endif
diff --git a/src/prc1/type.c b/src/prc1/type.c
new file mode 100644
index 0000000..c7e43d0
--- /dev/null
+++ b/src/prc1/type.c
@@ -0,0 +1,459 @@
+#include "type.h"
+#include "errloc.h"
+#include "fn.h"
+#include "lex.h"
+#include "parse.h"
+#include "prv.h"
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int aetc(const struct lex *l, struct prv *p);
+static int aid(const struct lex *l, struct prv *p);
+static int anum(const struct lex *l, struct prv *p);
+static int p(const struct lex *l, struct prv *p);
+static int t(const struct lex *l, struct prv *p);
+
+static const struct seq seqs[] =
+{
+ {(struct step[])
+ {{ID, "array"}, {ID, "of"}, {ID, "etc"}, {0}}, .fn = aetc},
+ {(struct step[]){{ID, "array"}, {ID, "of"}, {NUM}, {0}}, .fn = anum},
+ {(struct step[]){{ID, "array"}, {ID, "of"}, {ID}, {0}}, .fn = aid},
+ {(struct step[]){{ID, "pointer"}, {ID, "to"}, {0}}, .fn = p},
+ {(struct step[]){{ID, "void"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "byte"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "halfword"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "word"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "long"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "ubyte"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "uhalfword"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "uword"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "ulong"}, {0}}, .fn = t},
+ {(struct step[]){{ID}, {0}}, .fn = t},
+ {0}
+};
+
+void type_free(struct type *t)
+{
+ switch (t->type)
+ {
+ case U:
+ {
+ struct u *u = &t->u.u;
+
+ storage_free(u->storage);
+ }
+ break;
+
+ case S:
+ {
+ struct s *s = &t->u.s;
+
+ storage_free(s->storage);
+ }
+ break;
+
+ case C:
+ break;
+
+ case P:
+ {
+ struct p *p = &t->u.p;
+
+ fn_free(p->fn);
+ free(p->fn);
+ }
+ break;
+
+ case T:
+ case ARY:
+ case PTR:
+ case BUILTIN:
+ break;
+ }
+}
+
+void type_lfree(struct type *t)
+{
+ if (t->child)
+ type_lfree(t->child);
+
+ free(t);
+}
+
+static int append(const char *s, char **out)
+{
+ size_t n = *out ? strlen(*out) : 0;
+ char *ns = realloc(*out, strlen(s) + n + 1);
+
+ if (!ns)
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ strcpy(ns + n, s);
+ *out = ns;
+ return 0;
+}
+
+char *type_name(const struct type *t)
+{
+ char *ret = NULL;
+
+ while (t)
+ {
+ int n = -1;
+
+ switch (t->type)
+ {
+ case BUILTIN:
+ n = append(t->u.b.name, &ret);
+ break;
+
+ case ARY:
+ {
+ const struct ary *ary = &t->u.ary;
+ char s[sizeof "18446744073709551615 "];
+
+ if (ary->etc)
+ snprintf(s, sizeof s, "etc ");
+ else
+ snprintf(s, sizeof s, "%llu ", t->u.ary.n);
+
+ n = append("array of ", &ret) || append(s, &ret);
+ break;
+ }
+
+ case PTR:
+ n = append("pointer to ", &ret);
+ break;
+
+ case U:
+ case S:
+ case C:
+ case P:
+ case T:
+ n = append(t->tk->s, &ret);
+ break;
+ }
+
+ if (n)
+ {
+ free(ret);
+ return NULL;
+ }
+
+ t = t->child;
+ }
+
+ return ret;
+}
+
+const struct type *type_ufind(const struct fn *fn, const char *s)
+{
+ static const struct type builtins[] =
+ {
+ {.type = BUILTIN, .u.b.name = "void"},
+ {.type = BUILTIN, .u.b.name = "byte", .sz = 1, .sign = 1, .align = 1},
+ {.type = BUILTIN, .u.b.name = "halfword", .sz = 2, .sign = 1, .align = 2},
+ {.type = BUILTIN, .u.b.name = "word", .sz = 4, .sign = 1, .align = 4},
+ {.type = BUILTIN, .u.b.name = "long", .sz = 8, .sign = 1, .align = 8},
+ {.type = BUILTIN, .u.b.name = "ubyte", .sz = 1, .align = 1},
+ {.type = BUILTIN, .u.b.name = "uhalfword", .sz = 2, .align = 2},
+ {.type = BUILTIN, .u.b.name = "uword", .sz = 4, .align = 4},
+ {.type = BUILTIN, .u.b.name = "ulong", .sz = 8, .align = 8}
+ };
+
+ const struct td *td = fn->td;
+
+ if (fn->parent)
+ {
+ const struct type *t = type_find(fn->parent, s);
+
+ if (t)
+ return t;
+ }
+
+ for (size_t i = 0; i < sizeof builtins / sizeof *builtins; i++)
+ {
+ const struct type *t = &builtins[i];
+
+ if (!strcmp(t->u.b.name, s))
+ return t;
+ }
+
+ if (td)
+ for (size_t i = 0; i < td->ntypes; i++)
+ {
+ const struct type *t = &td->types[i];
+
+ if (!strcmp(t->tk->s, s))
+ return t;
+ }
+
+ for (size_t i = 0; i < fn->nimps; i++)
+ {
+ const struct type *t = type_ufind(fn->imps[i].ast.fns, s);
+
+ if (t)
+ return t;
+ }
+
+ return NULL;
+}
+
+const struct type *type_find(const struct fn *fn, const char *s)
+{
+ const struct type *t = type_ufind(fn, s);
+
+ if (t && t->type == T)
+ return t->u.t.alias;
+
+ return t;
+}
+
+static int queue(struct prv *p, struct type *t)
+{
+ struct type *tt;
+ struct pos *pos = &p->pos[p->i];
+
+ if (!(tt = p->t))
+ p->t = t;
+ else
+ {
+ while (tt->child)
+ tt = tt->child;
+
+ tt->child = t;
+ }
+
+ pos->seq = pos->stseq;
+ pos->step = pos->seq->steps;
+ p->stk = p->tk;
+ return 1;
+}
+
+static int aetc(const struct lex *l, struct prv *p)
+{
+ struct type *t;
+ const struct type *tt = p->t;
+
+ while (tt)
+ {
+ if (tt->type == ARY && tt->u.ary.etc)
+ {
+ errloc(p->stk, "multi-dimensional arrays "
+ "with unknown size are not allowed");
+ return -1;
+ }
+
+ tt = tt->next;
+ }
+
+ if (!(t = malloc(sizeof *t)))
+ {
+ perror("malloc");
+ return -1;
+ }
+
+ *t = (struct type){.type = ARY, .u.ary.etc = 1};
+ return queue(p, t);
+}
+
+static int aid(const struct lex *l, struct prv *p)
+{
+ const struct tk *qty = p->stk + 2;
+ const struct type *ct = type_find(fn_cur(p), qty->s);
+ struct type *t;
+ unsigned long long v;
+
+ if (!ct)
+ {
+ errloc(qty, "undefined reference to type \"%s\"", qty->s);
+ return -1;
+ }
+ else if (ct->type != C)
+ {
+ errloc(qty, "type \"%s\" not a constant", qty->s);
+ return -1;
+ }
+ else if (ct->sign)
+ {
+ errloc(qty, "type \"%s\" defines a negative constant (%lld)",
+ qty->s, ct->u.c.v);
+ return -1;
+ }
+ else if (!(v = ct->u.c.uv))
+ {
+ errloc(qty, "constant \"%s\" equals zero and cannot be used "
+ "for array sizes", qty->s);
+ return -1;
+ }
+ else if (!(t = malloc(sizeof *t)))
+ {
+ perror("malloc");
+ return -1;
+ }
+
+ *t = (struct type){.type = ARY, .u.ary.n = v};
+ return queue(p, t);
+}
+
+static int anum(const struct lex *l, struct prv *p)
+{
+ const struct tk *qty = p->stk + 2;
+ unsigned long long n = strtoull(qty->s, NULL, 0);
+ struct type *t;
+
+ if (*qty->s == '-' || errno)
+ {
+ errloc(qty, "invalid negative size for array");
+ return -1;
+ }
+ else if (!n)
+ {
+ errloc(qty, "array size cannot be zero");
+ return -1;
+ }
+ else if (!(t = malloc(sizeof *t)))
+ {
+ perror("malloc");
+ return -1;
+ }
+
+ *t = (struct type){.type = ARY, .u.ary.n = n};
+ return queue(p, t);
+}
+
+static int p(const struct lex *l, struct prv *p)
+{
+ struct type *t = malloc(sizeof *t), *tt;
+
+ if (!t)
+ {
+ perror("malloc");
+ return -1;
+ }
+
+ *t = (struct type)
+ {
+ .type = PTR,
+ .sz = sizeof (void *),
+ .align = sizeof (void *)
+ };
+
+ if ((tt = p->t) && tt->type == ARY)
+ {
+ tt->sz = tt->u.ary.n * sizeof (void *);
+ tt->align = sizeof (void *);
+ }
+
+ return queue(p, t);
+}
+
+static int aryeq(const struct type *a, const struct type *b)
+{
+ const struct ary *aa = &a->u.ary, *ab = &b->u.ary;
+
+ return !memcmp(aa, ab, sizeof *aa);
+}
+
+static int eq(const struct type *a, const struct type *b)
+{
+ for (; a && b; a = a->child, b = b->child)
+ if (a->type != b->type)
+ return 0;
+ else if (a->type == ARY && !aryeq(a, b))
+ return 0;
+
+ return !a && !b;
+}
+
+static const struct type *insert(struct prv *p)
+{
+ struct ast *ast = p->ast;
+ struct type *a = p->t, *tt = ast->types;
+
+ for (const struct type *b = ast->types; b; b = b->next)
+ if (eq(a, b))
+ {
+ type_lfree(a);
+ return b;
+ }
+
+ if (!ast->types)
+ ast->types = a;
+ else
+ {
+ while (tt->next)
+ tt = tt->next;
+
+ tt->next = a;
+ }
+
+ return a;
+}
+
+static int t(const struct lex *l, struct prv *p)
+{
+ const struct fn *fn = fn_cur(p);
+ const struct tk *tk = p->stk;
+ const struct type *t = type_find(fn, tk->s);
+ struct type *tt = p->t;
+
+ if (!t)
+ {
+ errloc(tk, "undefined reference to type \"%s\"", tk->s);
+ return -1;
+ }
+ else if (tt)
+ {
+ struct type *nt = malloc(sizeof *nt);
+
+ if (!nt)
+ {
+ perror("malloc(3)");
+ return -1;
+ }
+
+ *nt = *t;
+
+ while (tt->child)
+ tt = tt->child;
+
+ tt->child = nt;
+ tt = p->t;
+
+ /* array size still undecided */
+ if (tt->type == ARY && !tt->sz)
+ {
+ tt->sz = tt->u.ary.n * nt->sz;
+ tt->align = t->align;
+ }
+
+ if (!(t = insert(p)))
+ return -1;
+
+ p->t = NULL;
+ }
+
+ return p->type(l, p, t);
+}
+
+int type(const struct lex *l, struct prv *p,
+ int (*fn)(const struct lex *, struct prv *, const struct type *))
+{
+ struct pos init =
+ {
+ .seq = seqs,
+ .stseq = seqs,
+ .step = seqs->steps
+ };
+
+ p->stk = p->tk;
+ p->type = fn;
+ return push(&init, p) ? -1 : 1;
+}
diff --git a/src/prc1/type.h b/src/prc1/type.h
new file mode 100644
index 0000000..7de6c28
--- /dev/null
+++ b/src/prc1/type.h
@@ -0,0 +1,16 @@
+#ifndef TYPE_H
+#define TYPE_H
+
+#include "lex.h"
+#include "prv.h"
+#include "parse.h"
+
+const struct type *type_ufind(const struct fn *fn, const char *s);
+const struct type *type_find(const struct fn *fn, const char *s);
+char *type_name(const struct type *t);
+int type(const struct lex *l, struct prv *p,
+ int (*fn)(const struct lex *, struct prv *p, const struct type *t));
+void type_free(struct type *t);
+void type_lfree(struct type *t);
+
+#endif
diff --git a/src/prc1/warn.c b/src/prc1/warn.c
new file mode 100644
index 0000000..456ce79
--- /dev/null
+++ b/src/prc1/warn.c
@@ -0,0 +1,146 @@
+#include "warn.h"
+#include "cgen.h"
+#include "parse.h"
+#include "prv.h"
+#include "stmt.h"
+#include "storage.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int finalize(const struct lex *l, const struct prv *p,
+ const struct print *pr)
+{
+ const struct ast *ast = p->ast;
+ struct fn *fn = &ast->fns[ast->nfns - 1];
+ size_t n = fn->nstmts + 1;
+ struct stmt *stmts = realloc(fn->stmts, n * sizeof *stmts);
+
+ if (!stmts)
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ fn->stmts = stmts;
+ fn->stmts[fn->nstmts++] = (struct stmt)
+ {
+ .type = WARN,
+ .u.warn.p = *pr
+ };
+
+ fprintf(stderr, "\t\tadding warn statement with %zu entries%s\n",
+ pr->nentries, pr->println ? "" : ", no lf");
+ return 0;
+}
+
+static int cg_val(const struct tk *tk)
+{
+ const char *num = tk->s;
+ int neg = *num == '-';
+ unsigned long long uv;
+ char *end;
+
+ fputs("call $printf(l $____fmt", stdout);
+ errno = 0;
+ uv = strtoull(num, &end, 16);
+
+ if (!neg && !errno && !*end)
+ printf("lx, ..., l %llu )\n", uv);
+ else
+ {
+ errno = 0;
+ uv = strtoull(num, &end, 0);
+
+ if (neg || errno || *end)
+ {
+ long long v = strtoll(num, NULL, 0);
+ printf("l, ..., l %lld )\n", v);
+ }
+ else
+ printf("l, ..., l %llu )\n", uv);
+ }
+
+ return 0;
+}
+
+static int cg_lit(const struct prientry *e)
+{
+ const struct lit *l = e->lit;
+ const char *tmp;
+
+ if (!l)
+ {
+ fprintf(stderr, "%s: unexpected null literal\n", __func__);
+ return -1;
+ }
+
+ printf("%%%s =l loadl $stderr\n", (tmp = cgen_tmp(sizeof (void *))));
+ printf("call $fputs(l $%s, w %%%s)\n", l->name, tmp);
+ return 0;
+}
+
+static int cg_id(const struct prientry *de, const struct fn *fn)
+{
+ const struct stentry *e = de->entry;
+ const struct type *t = e->t;
+ const char *id = e->tk->s;
+ int global = cgen_global(fn, e);
+ size_t sz = t->sz;
+ const char *asz = cgen_sz(sz < 4 ? 4 : sz), *tmp = cgen_tmp(sz);
+
+ if (global)
+ printf("%%%s =%s load%s $%s\n", tmp, asz, cgen_load(t), id);
+ else
+ printf("%%%s =%s load%s %%%s_\n", tmp, asz, cgen_load(t), id);
+
+ puts("%z =l loadl $stderr");
+ fputs("call $fprintf(w %z, l $____fmt", stdout);
+
+ if (t->type == PTR)
+ fputs("lx, ..., l ", stdout);
+ else
+ printf("%s, ..., %s ", cgen_type(t), asz);
+
+ printf("%%%s)\n", tmp);
+
+ return 0;
+}
+
+int warn_cgen(const struct warn *w, struct cgen *c)
+{
+ const struct print *p = &w->p;
+
+ for (size_t i = 0; i < p->nentries; i++)
+ {
+ const struct prientry *e = &p->entries[i];
+ const struct tk *tk = e->tk;
+
+ if (tk->type == LIT && cg_lit(e))
+ return -1;
+ else if (tk->type == NUM && cg_val(tk))
+ return -1;
+ else if (tk->type == ID && e->entry && cg_id(e, c->fn))
+ return -1;
+ else if (tk->type == ID && e->lit && cg_lit(e))
+ return -1;
+ }
+
+ if (p->println)
+ {
+ puts("%v =l loadl $stderr");
+ puts("call $fputc(l 10 , w %v)");
+ }
+
+ return 0;
+}
+
+void warn_free(struct warn *w)
+{
+ print_free(&w->p);
+}
+
+int warn(const struct lex *l, struct prv *p)
+{
+ return print(l, p, finalize);
+}
diff --git a/src/prc1/warn.h b/src/prc1/warn.h
new file mode 100644
index 0000000..5327592
--- /dev/null
+++ b/src/prc1/warn.h
@@ -0,0 +1,17 @@
+#ifndef WARN_H
+#define WARN_H
+
+#include "lex.h"
+#include "print.h"
+#include "prv.h"
+
+struct warn
+{
+ struct print p;
+};
+
+int warn(const struct lex *l, struct prv *p);
+int warn_cgen(const struct warn *w, struct cgen *c);
+void warn_free(struct warn *w);
+
+#endif
diff --git a/src/prc1/ws.c b/src/prc1/ws.c
new file mode 100644
index 0000000..59e1aec
--- /dev/null
+++ b/src/prc1/ws.c
@@ -0,0 +1,29 @@
+#include "ws.h"
+#include "fn.h"
+#include "prv.h"
+#include "parse.h"
+#include "storage.h"
+
+static int wspush(const struct stentry *e, struct cgen *c)
+{
+ const struct type *t = e->t;
+
+ printf("%%%s_ =l alloc%zu %zu\n", e->tk->s, t->align, t->sz);
+ return 0;
+}
+
+int ws_cgen(const struct storage *s, struct cgen *c)
+{
+ for (size_t i = 0; i < s->nentries; i++)
+ if (wspush(&s->entries[i], c))
+ return -1;
+
+ return 0;
+}
+
+int ws(const struct lex *l, struct prv *p)
+{
+ struct fn *fn = fn_cur(p);
+
+ return storage(l, p, "storage section", 1, &fn->ws);
+}
diff --git a/src/prc1/ws.h b/src/prc1/ws.h
new file mode 100644
index 0000000..fa44a37
--- /dev/null
+++ b/src/prc1/ws.h
@@ -0,0 +1,12 @@
+#ifndef WS_H
+#define WS_H
+
+#include "lex.h"
+#include "cgen.h"
+#include "prv.h"
+
+int ws(const struct lex *l, struct prv *p);
+int ws_cgen(const struct storage *s, struct cgen *c);
+extern const struct seq wsseq[];
+
+#endif