aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bootstrap/all.h1
-rw-r--r--bootstrap/cgen.c50
-rw-r--r--bootstrap/test.cff5
-rw-r--r--src/cffc.hff28
-rw-r--r--src/common.hff17
-rw-r--r--src/fmt.cff24
-rw-r--r--src/fold.cff71
-rw-r--r--src/parse.cff500
8 files changed, 653 insertions, 43 deletions
diff --git a/bootstrap/all.h b/bootstrap/all.h
index bebdaf5..34bab40 100644
--- a/bootstrap/all.h
+++ b/bootstrap/all.h
@@ -210,6 +210,7 @@ struct type {
};
// for cgen.c hack (mutated later, not hashed or involved in equality)
const char *_cname;
+ bool _defined;
};
struct fnparam {
diff --git a/bootstrap/cgen.c b/bootstrap/cgen.c
index d7b91b0..e047f08 100644
--- a/bootstrap/cgen.c
+++ b/bootstrap/cgen.c
@@ -730,7 +730,7 @@ gendecl(struct decl *decl, bool toplevel) {
}
static void
-defctype(const struct type *ty, void *_) {
+declctype(const struct type *ty, void *_) {
static int id;
char **cname = (char **)&ty->_cname;
const char *kind;
@@ -739,9 +739,43 @@ defctype(const struct type *ty, void *_) {
switch (ty->t) {
case TYvoid: case TYbool: case TYvalist:
case TYenum: case TYint: case TYfloat:
+ case TYptr: case TYarr: case TYslice:
+ case TYfn:
+ *(bool *)&ty->_defined = 1;
+ break;
+ case TYstruct: case TYeunion:
+ kind = "struct";
+ goto agg;
+ case TYunion:
+ kind = "union";
+ agg:
+ if (ty->konst) {
+ declctype(unconstify(ty), NULL);
+ *cname = (char *)unconstify(ty)->_cname;
+ break;
+ }
+ *(bool *)&ty->_defined = 0;
+ *cname = xasprintf("__ta%s%d", ty->agg.name ? ty->agg.name : "", id++);
+ pri("typedef %s %s %s;\n", kind, *cname, *cname);
+ }
+}
+
+#define isagg(ty) ((ty)->t == TYstruct || (ty)->t == TYunion || (ty)->t == TYenum)
+
+static void
+defctype(const struct type *ty, void *_) {
+ static int id;
+ char **cname = (char **)&ty->_cname;
+ const char *kind;
+
+ if (!ty->_cname || (!ty->_defined))
+ switch (ty->t) {
+ case TYvoid: case TYbool: case TYvalist:
+ case TYenum: case TYint: case TYfloat:
break;
case TYptr:
- defctype(ty->child, NULL);
+ if (!isagg(ty->child))
+ defctype(ty->child, NULL);
break;
case TYarr:
if (ty->length >= 0) {
@@ -757,9 +791,10 @@ defctype(const struct type *ty, void *_) {
*cname = (char *)ty2->_cname;
return;
}
- defctype(ty->child, NULL);
if (ty->_cname) break;
*cname = xasprintf("__ty%d", id++);
+ if (!isagg(ty->child))
+ defctype(ty->child, NULL);
pri("typedef struct { %t *ptr; size_t len; } %s;\n",
ty->child, *cname);
@@ -789,13 +824,13 @@ defctype(const struct type *ty, void *_) {
case TYunion:
kind = "union";
agg:
+ *(bool *)&ty->_defined = 1;
if (ty->konst) {
defctype(unconstify(ty), NULL);
*cname = (char *)unconstify(ty)->_cname;
break;
}
- *cname = xasprintf("__ty%s%d", ty->agg.name ? ty->agg.name : "", id++);
- pri("typedef %s %s %s;\n", kind, *cname, *cname);
+
if (!ty->agg.fwd) {
for (int i = 0; i < ty->agg.flds.n; ++i) {
struct aggfield fld = ty->agg.flds.d[i];
@@ -816,18 +851,18 @@ defctype(const struct type *ty, void *_) {
}
break;
case TYeunion:
+ *(bool *)&ty->_defined = 1;
if (ty->konst) {
defctype(unconstify(ty), NULL);
*cname = (char *)unconstify(ty)->_cname;
break;
}
+
for (int i = 0; i < ty->agg.flds.n; ++i) {
struct aggfield *fld = &ty->agg.flds.d[i];
if (fld->ty)
defctype(fld->ty, NULL);
}
- *cname = xasprintf("__ty%s%d", ty->agg.name ? ty->agg.name : "", id++);
- pri("typedef struct %s %s;\n", *cname, *cname);
if (!ty->agg.fwd) {
pri("struct %s {\n", *cname);
pri("%t t;\n", ty->agg.enumty);
@@ -861,6 +896,7 @@ cgen(FILE *fp, const struct comfile *cf) {
outfp = fp;
prelude();
+ visittypes(declctype, NULL);
visittypes(defctype, NULL);
for (int i = 0; i < cf->decls.n; ++i) {
diff --git a/bootstrap/test.cff b/bootstrap/test.cff
index 6f5242d..c7309a9 100644
--- a/bootstrap/test.cff
+++ b/bootstrap/test.cff
@@ -72,7 +72,7 @@ fn spanz(cstr *const u8) [#]const u8 {
defmacro transmute(Type, x) [
(do
union T { from typeof(x), to Type };
- T{x}.to;
+ (T{x}).to;
)
]
@@ -88,6 +88,9 @@ extern fn main (argc int, argv **u8) int {
let is []int = { [4] = 1, 2, [1 - 1] = 3 };
isort(is, is.#len);
+ each(i, x, is,
+ printf("%d\n", x);
+ )
each(i, x, is) {
printf("%d\n", x);
}
diff --git a/src/cffc.hff b/src/cffc.hff
index ad6a8aa..2af06d6 100644
--- a/src/cffc.hff
+++ b/src/cffc.hff
@@ -111,6 +111,7 @@ struct Type {
}
struct Fn;
+struct Expan;
struct Parser {
fp *FILE,
alloc *Allocator,
@@ -118,6 +119,8 @@ struct Parser {
curfn *Fn,
curenv *Env,
curloop int,
+ curexpan *Expan,
+ expanno int,
loopid int,
targty *const Type,
tokloc Loc,
@@ -125,7 +128,6 @@ struct Parser {
eof bool,
is_header bool,
peekchr Option<int>,
-
peektok Option<Tok>,
}
@@ -141,6 +143,7 @@ enum UnOp {
predec,
}
+struct Stmt;
struct Expr {
loc Loc,
ty *const Type,
@@ -156,16 +159,21 @@ struct Expr {
Cond struct { test *Expr, t *Expr, f *Expr },
Cast *Expr,
Dot struct { lhs *Expr, fld *const AggField },
+ SLen *Expr,
+ SPtr *Expr,
+ EUTag *Expr,
Index struct { lhs *Expr, rhs *Expr },
Slice struct { lhs *Expr, begin *Expr, end *Expr },
Call struct { lhs *Expr, args [#]Expr },
ZeroIni,
- Ini struct { },
+ EnumIni struct { name *const u8, val i64 },
+ AggIni struct { flds [#]*const AggField, exs [#]Expr },
+ ArrIni struct { idxs [#]u32, exs [#]Expr, maxn i64 },
+ Stmt [#]Stmt,
}
}
-struct Stmt;
struct Stmt {
loc Loc,
u enum union {
@@ -228,6 +236,20 @@ struct Decl {
myenv *Env,
}
+struct ExpanArg {
+ name *const u8,
+ toks [#]Tok,
+}
+struct Expan {
+ prev *Expan,
+ args [#]ExpanArg,
+ toks [#]Tok,
+ name *const u8,
+ loc Loc,
+ idx int,
+ peektok Option<Tok>
+}
+
struct DeclList {
link *DeclList,
decl Decl,
diff --git a/src/common.hff b/src/common.hff
index 4da1398..6643dda 100644
--- a/src/common.hff
+++ b/src/common.hff
@@ -25,6 +25,16 @@ defmacro foreach(x, i, a, &body) [
}
]
+defmacro foreach_ptr(x, i, a, &body) [
+ {
+ let $a = a;
+ for let i = 0; i < $a.#len; ++i {
+ let x = &$a[i];
+ { body }
+ }
+ }
+]
+
defmacro streq(a,b) [ (strcmp(a,b) == 0) ]
defmacro strcieq(a,b) [ (strcasecmp(a,b) == 0) ]
@@ -42,6 +52,13 @@ defmacro with_tmpchange(var,x,&body) [
defmacro MAX(a,b) [((a) > (b) ? (a) : (b))]
+defmacro offsetof_(T, fld) [
+ (as(isize)(&(as(*T)#null).fld))
+]
+defmacro container_of(x, T, fld) [
+ (as(*T)(as(*void)(x) - offsetof_(T, fld)))
+]
+
// Inline functions
fn bswap32(x u32) u32 {
return (x >> 24)
diff --git a/src/fmt.cff b/src/fmt.cff
index 0dce9d3..111809f 100644
--- a/src/fmt.cff
+++ b/src/fmt.cff
@@ -150,7 +150,11 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list)
case Ptr child;
pfmt(proc, parg, "*%t", child);
case Arr arr;
- pfmt(proc, parg, "[%I]%t", arr.length, arr.child);
+ if arr.length < 0 {
+ pfmt(proc, parg, "[]%t", arr.child);
+ } else {
+ pfmt(proc, parg, "[%I]%t", arr.length, arr.child);
+ }
case Slice child;
pfmt(proc, parg, "[#]%t", child);
case Fn f;
@@ -169,6 +173,13 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list)
if agg.name {
ps(agg.name);
} else {
+ ps("(anonymous)");
+ }
+ case Enum e;
+ if e.name {
+ ps(e.name);
+ } else {
+ ps("(anonymous)");
}
}
}
@@ -326,6 +337,17 @@ extern fn vdiag(P *Parser, loc Loc, kind *const u8, fmt *const u8, ap va_list) v
vefmt(fmt, ap);
efmt("\n");
eprifileline(loc);
+ let i = 0;
+ for let ep = P.curexpan; ep; ep = ep.prev {
+ if ep.name != #null and (i++ < 8 or ep.prev == #null or ep.prev.prev == #null) {
+ efmt("* while expanding %s `%s' at %l\n",
+ "macro", ep.name, ep.loc);
+ eprifileline(ep.loc);
+ } else if ep.name != #null and i == 8 {
+ efmt(" ... (some expansions omitted)\n");
+ }
+
+ }
}
extern fn warn(P *Parser, loc Loc, fmt *const u8, ...) void {
diff --git a/src/fold.cff b/src/fold.cff
index 7b393e5..476e860 100644
--- a/src/fold.cff
+++ b/src/fold.cff
@@ -52,15 +52,86 @@ fn numcast(ex *Expr, to *const Type) void {
: :BoolLit;
}
+fn funary(ex *Expr) void {
+}
+
+fn fbinary(ex *Expr) void {
+ let l = ex.u.BinOp.lhs,
+ r = ex.u.BinOp.rhs;
+ let li = l.u.IntLit,
+ lf = l.u.FloLit,
+ ri = r.u.IntLit,
+ rf = r.u.FloLit,
+ ei = &ex.u.IntLit,
+ ef = &ex.u.FloLit,
+ eb = &ex.u.BoolLit;
+ let ty = unconstify(ex.ty);
+ let op = ex.u.BinOp.op;
+
+ if !fold(l) or !fold(r) {
+ return;
+ }
+ if ty->is(:Bool) {
+ ty = typeof2(l.ty, r.ty);
+ }
+ numcast(l, ty);
+ numcast(r, ty);
+
+ if op == '/' and ty->is(:Int) and ri.i == 0 { // div/0
+ return;
+ }
+ switch {
+ case op == '+' and ty->is(:Int); ei.i = li.i + ri.i;
+ case op == '+' and ty->is(:Flo); *ef = lf + rf;
+ case op == '-' and ty->is(:Int); ei.i = li.i - ri.i;
+ case op == '-' and ty->is(:Flo); *ef = lf - rf;
+ case op == '*' and ty == ty_u64; ei.u = li.u * ri.u;
+ case op == '*' and ty->is(:Int); ei.i = li.i * ri.i;
+ case op == '*' and ty->is(:Flo); *ef = lf * rf;
+ case op == '/' and ty == ty_u64; ei.u = li.u / ri.u;
+ case op == '/' and ty->is(:Int); ei.i = li.i / ri.i;
+ case op == '/' and ty->is(:Flo); *ef = lf / rf;
+ case op == '%' and ty == ty_u64; ei.u = li.u % ri.u;
+ case op == '&' and ty->is(:Int); ei.i = li.i & ri.i;
+ case op == '|' and ty->is(:Int); ei.i = li.i | ri.i;
+ case op == '^' and ty->is(:Int); ei.i = li.i ^ ri.i;
+ case op == '<<' and ty->is(:Int); ei.i = li.i << ri.i;
+ case op == '>>' and ty->is(:Int) and !ty.u.Int.sgn; ei.u = li.u >> ri.u;
+ case op == '>>' and ty->is(:Int); ei.i = li.i >> ri.i;
+ }
+
+ if !ex.ty->is(:Bool) {
+ numcast(ex, ty);
+ } else {
+ ex.u.#tag = :BoolLit;
+ }
+}
+
extern fn fold(ex *Expr) bool {
switch ex.u.#tag {
case :IntLit, :FloLit, :BoolLit;
numcast(ex, ex.ty);
return #t;
+
case :StrLit, :NullLit;
return #t;
+
+ case :UnOp;
+ funary(ex);
+
+ case :BinOp;
+ fbinary(ex);
+
case else;
return #f;
}
+ switch ex.u.#tag {
+ case :IntLit, :FloLit, :BoolLit;
+ numcast(ex, ex.ty);
+ return #t;
+
+ case else;
+ return #f;
+ }
}
diff --git a/src/parse.cff b/src/parse.cff
index 7d083fb..613b4ae 100644
--- a/src/parse.cff
+++ b/src/parse.cff
@@ -277,6 +277,8 @@ fn readnumber(s *const u8) Option<Tok> {
}
}
+def MAX_MACROEXPAND_ITER = 100;
+
fn lex(P *Parser) Tok {
let c int #?;
let tok Tok = {};
@@ -287,6 +289,46 @@ fn lex(P *Parser) Tok {
return pt;
}
+ if P.expanno > MAX_MACROEXPAND_ITER {
+ fatal(P, P.curloc, "maximum number of nested macro expansions reached (infinite recursion?)");
+ }
+ if P.curexpan {
+ let ep = P.curexpan;
+ if ep.idx == ep.toks.#len {
+ // end macro expansion
+ --P.expanno;
+ free(ep.args.#ptr);
+ P.curexpan = ep.prev;
+ P.peektok = ep.peektok;
+ free(ep);
+ return lex(P);
+ }
+ tok = ep.toks[ep.idx++];
+ // expand macro arg?
+ if tok.t == :macident {
+ for let ep = P.curexpan; ep; ep = ep.prev {
+ foreach(arg, i, ep.args) {
+ if streq(tok.u.ident, arg.name) {
+ ++P.expanno;
+ P.curexpan = memcpy(xmalloc(sizeof Expan), &Expan {
+ P.curexpan, {}, arg.toks,
+ }, sizeof Expan);
+ return lex(P);
+ }
+ }
+ }
+ } else if tok.t == '#FIL' or tok.t == '#LIN' {
+ let ep *Expan #?;
+ for ep = P.curexpan; ep.prev; ep = ep.prev { }
+ if tok.t == '#FIL' {
+ return { :str, tok.loc, .u: { .str: spanz(fileid2path(ep.loc.fileid)) }};
+ } else {
+ return { :int, tok.loc, .u: { .int: ep.loc.line }};
+ }
+ }
+ return tok;
+ }
+
eatspaces(P);
tok.loc = (P.tokloc = P.curloc);
if isdigit(c = chrpeek(P)) {
@@ -683,6 +725,15 @@ fn parseagg(P *Parser, kind AggKind, name *const u8, retdecl **Decl) *const Type
return ty;
}
+fn findaggfield(ty *const Type, name *const u8) *AggField {
+ foreach_ptr(fld, i, ty.u.Agg.flds) {
+ if streq(fld.name, name) {
+ return fld;
+ }
+ }
+ return #null;
+}
+
fn xident2type(P *Parser, eloc Loc, name *const u8) *const Type {
let decl = finddecl(P, name);
if decl == #null {
@@ -769,6 +820,13 @@ fn parsetype(P *Parser) *const Type {
case lexmatch(P, &tok, :ident);
return xident2type(P, tok.loc, tok.u.ident);
+ case lexmatch(P, &tok, :kw_typeof);
+ lexexpect(P, '(');
+ let ex = parseexpr(P);
+ lexexpect(P, ')');
+ return ex.ty;
+
+
case lexmatch(P, &tok, :kw_fn);
return parsefntype(P);
@@ -782,6 +840,9 @@ fn parsetype(P *Parser) *const Type {
/////////////////////////
fn typematchestarg(targ *const Type, ty *const Type) bool {
+ if targ == ty {
+ return #t;
+ }
if typeof2(targ, ty) == #null {
return #f;
}
@@ -811,6 +872,208 @@ fn islvalue(ex Expr) bool {
return #f;
}
+fn parseaggini(P *Parser, ty *const Type) Expr {
+ let loc = P.tokloc;
+ let tok Tok #?;
+ let flds Vec<*const AggField> = {};
+ let exs Vec<Expr> = {};
+ let iota = 0;
+ while !lexmatch(P, &tok, '}') {
+ let fld *AggField #?;
+ let ex Expr #?;
+ if lexmatch(P, &tok, '.') {
+ fld = findaggfield(ty, (tok = lexexpects(P, :ident, "field name")).u.ident);
+ lexexpect(P, ':');
+ if fld == #null {
+ fatal(P, tok.loc, "%t has no such field %qT", ty, tok);
+ }
+ iota = (fld - ty.u.Agg.flds.#ptr) + 1;
+ } else {
+ if iota >= ty.u.Agg.flds.#len {
+ fatal(P, P.tokloc, "excess elements in struct initializer");
+ }
+ fld = &ty.u.Agg.flds[iota++];
+ }
+ P.targty = fld.ty;
+ ex = parseexpr(P);
+ flds->push(fld);
+ exs->push(ex);
+ if !lexmatch(P, &tok, ',') {
+ lexexpects(P, '}', "`,' or `}'");
+ break;
+ }
+ }
+ return { loc, ty, :AggIni { flds->move(P.alloc), exs->move(P.alloc) }};
+}
+
+fn parsearrini(P *Parser, ty *const Type) Expr {
+ let loc = P.tokloc;
+ let tok Tok #?;
+ let idxs Vec<u32> = {};
+ let exs Vec<Expr> = {};
+ let iota = 0;
+ let maxn = 0;
+ while !lexmatch(P, &tok, '}') {
+ P.targty = ty.u.Arr.child;
+ let ex Expr #?;
+ if lexmatch(P, #null, '[') {
+ let iex = parseexpr(P);
+ lexexpect(P, ']');
+ lexexpect(P, '=');
+ if !fold(&iex) or iex.u.#tag != :IntLit {
+ fatal(P, iex.loc, "expected integer constant expression for array index");
+ }
+ iota = iex.u.IntLit.i;
+ if iota < 0 or (ty.u.Arr.length < 0 ? #f : iota >= ty.u.Arr.length) {
+ fatal(P, iex.loc, "index out of bounds");
+ }
+ ex = parseexpr(P);
+ } else {
+ ex = parseexpr(P);
+ }
+ idxs->push(iota++);
+ exs->push(ex);
+ maxn = MAX(maxn, iota);
+
+ if !lexmatch(P, &tok, ',') {
+ lexexpects(P, '}', "`,' or `}'");
+ break;
+ }
+ }
+ return { loc, ty, :ArrIni { idxs->move(P.alloc), exs->move(P.alloc), maxn }};
+}
+
+
+fn mergetoktrees(alloc *Allocator, src *[#]Tok, n int) [#]Tok {
+ let total = 0z;
+ for let i = 0; i < n; ++i {
+ total += src[i].#len;
+ if i > 0 {
+ ++total;
+ }
+ }
+ let d *Tok = alloc->alloc(total * sizeof Tok);
+ let len = 0;
+ for let i = 0; i < n; ++i {
+ if i > 0 {
+ d[len++] = { ',' };
+ }
+ memcpy(d + len, src[i].#ptr, src[i].#len * sizeof Tok);
+ len += src[i].#len;
+ }
+ assert(len == total, "mergetoktrees");
+ return d[0::len];
+}
+
+fn parseexpandmacro(P *Parser, mac *Macro) void {
+ let tok Tok #?;
+ let expan Expan = {};
+ let args Vec<[#]Tok> = {};
+ let c MacroCase #?;
+ let loc = P.tokloc;
+ let mname = container_of(mac, Decl, u.Macro).name;
+ lexexpect(P, '(');
+
+ while !lexmatch(P, &tok, ')') {
+ let pabal = 0, // ( ) parens balance
+ bkbal = 0, // [ ] brackets
+ bcbal = 0; // { } braces
+ let eloc = tok.loc;
+ let toks Vec<Tok> = {};
+ #'arg for ;; {
+ tok = lex(P);
+ switch tok.t {
+ case '['; ++bkbal; case ']'; --bkbal;
+ case '{'; ++bcbal; case '}'; --bcbal;
+ case '('; ++pabal;
+ case ')';
+ if --pabal < 0 {
+ break #'arg;
+ }
+ case ',';
+ if pabal == 0 and bkbal == 0 and bcbal == 0 {
+ break #'arg;
+ }
+ case :eof;
+ fatal(P, eloc, "unterminated macro `%s' invocation", mname);
+ }
+ toks->push(tok);
+ }
+
+ args->push(toks->move(P.alloc));
+ if tok.t != ',' {
+ assert(tok.t == ')', "ok");
+ break;
+ }
+ }
+ let matches = #f, bodyarg = #f;
+ if lexpeek(P).t == '{' {
+ foreach(cas, _, mac.cs) {
+ c = cas;
+ if args.len == c.params.#len - 1 and c.bodyarg {
+ bodyarg = #t;
+ break;
+ }
+ }
+ if bodyarg {
+ let bal = 0;
+ let eloc = tok.loc;
+ let toks Vec<Tok> = {};
+ do {
+ tok = lex(P);
+ switch tok.t {
+ case '{'; ++bal; case '}'; --bal;
+ case :eof;
+ fatal(P, eloc, "unterminated macro `%s' invokation", mname);
+ }
+ toks->push(tok);
+ } while bal != 0;
+ args->push(toks->move(P.alloc));
+ matches = #t;
+ }
+ }
+ if !bodyarg {
+ foreach(cas, _, mac.cs) {
+ c = cas;
+ if !c.variadic and args.len == c.params.#len {
+ matches = #t;
+ break;
+ } else if c.variadic and args.len >= c.params.#len - 1 {
+ let n = args.len - (c.params.#len - 1);
+ if n == 0 {
+ args->push({});
+ } else {
+ let arg = mergetoktrees(P.alloc, args.dat + (c.params.#len - 1), n);
+ args.dat[c.params.#len - 1] = arg;
+ }
+ matches = #t;
+ break;
+ }
+ }
+ }
+ if !matches {
+ fatal(P, tok.loc, "no match for invoking macro `%s' with %d arguments", mname, args.len);
+ }
+ let expanargs *ExpanArg = xcalloc(c.params.#len, sizeof ExpanArg);
+ foreach(param, i, c.params) {
+ expanargs[i].name = param;
+ expanargs[i].toks = args.dat[i];
+ }
+ args->clear();
+ expan.prev = P.curexpan;
+ expan.loc = loc;
+ expan.name = mname;
+ expan.args = expanargs[0::c.params.#len];
+ expan.peektok = P.peektok;
+ P.peektok = :None;
+ expan.toks = c.body;
+ ++P.expanno;
+ P.curexpan = memcpy(xmalloc(sizeof Expan), &expan, sizeof Expan);
+
+}
+
+fn parseblock0(P *Parser) [#]Stmt;
+
fn pexprimary(P *Parser) Expr {
let tok Tok = lex(P);
let ex Expr #?;
@@ -837,16 +1100,40 @@ fn pexprimary(P *Parser) Expr {
if decl == #null {
fatal(P, tok.loc, "%qT is not defined", tok);
}
- switch decl.u {
- case Fn f;
- ex = { tok.loc, f.ty, .u: :Symbol(decl) };
- case Let var;
- ex = { tok.loc, var.ty, .u: :Symbol(decl) };
- case Static var;
- ex = { tok.loc, var.ty, .u: :Symbol(decl) };
- case Def dex;
- dex.loc = tok.loc;
- ex = dex;
+ while #t {
+ switch decl.u {
+ case Fn f;
+ ex = { tok.loc, f.ty, .u: :Symbol(decl) };
+ case Let var;
+ ex = { tok.loc, var.ty, .u: :Symbol(decl) };
+ case Static var;
+ ex = { tok.loc, var.ty, .u: :Symbol(decl) };
+ case Def dex;
+ dex.loc = tok.loc;
+ ex = dex;
+ case Macro *mac;
+ parseexpandmacro(P, mac);
+ ex = parseexpr(P);
+ case Ty ty;
+ if (tok = lexpeek(P)).t != ':' and tok.t != '{' {
+ fatal(P, tok.loc, "expected `:' or `{' after type name");
+ }
+ if tok.t == ':' and ty->is(:Agg) and ty.u.Agg.kind != :EUnion {
+ lex(P);
+ let name = (tok = lexexpect(P, :ident)).u.ident;
+ decl = envfind_noparent(ty.u.Agg.decls, name);
+ if decl == #null {
+ fatal(P, tok.loc, "%t has no such decl %qT", ty, tok);
+ }
+ continue;
+ } else if !ty->is(:Agg) and !ty->is(:Enum) {
+ fatal(P, tok.loc, "type is not aggregate or enum");
+ } else {
+ P.targty = ty;
+ return parseexpr(P);
+ }
+ }
+ break;
}
case :kw_sizeof;
let ty *const Type #?;
@@ -863,18 +1150,59 @@ fn pexprimary(P *Parser) Expr {
ex = { tok.loc, ty_usize, :IntLit { ty.size }};
case '(';
- ex = parseexpr(P);
- lexexpect(P, ')');
+ if lexmatch(P, &tok, :kw_do) {
+ let st = parseblock0(P);
+ lexexpect(P, ')');
+ if st.#len == 0 or st[st.#len - 1].u.#tag != :Expr {
+ ex = { tok.loc, ty_void, :Stmt(st) };
+ } else {
+ ex = { tok.loc, st[st.#len - 1].u.Expr.ty, :Stmt(st) };
+ }
+ } else {
+ ex = parseexpr(P);
+ lexexpect(P, ')');
+ }
+
+ case ':';
+ if P.targty == #null {
+ fatal(P, tok.loc, "cannot infer type for variant");
+ }
+ let ty = P.targty;
+ if ty->is(:Enum) {
+ let name = (tok = lexexpects(P, :ident, "variant name")).u.ident;
+ let i i64 #?;
+ #'search do {
+ foreach(val, _, ty.u.Enum.vals) {
+ if streq(val.name, name) {
+ i = val.i;
+ break #'search;
+ }
+ }
+ fatal(P, tok.loc, "%t has no such variant %qT", ty, tok);
+ } while #f;
+ ex = { tok.loc, ty, :EnumIni { name, i }};
+ } else if ty->is(:Agg) and ty.u.Agg.kind == :EUnion {
+ assert(#f, "NYI");
+ } else {
+ fatal(P, tok.loc, "cannot infer type for variant");
+ }
case '{';
if P.targty == #null {
fatal(P, tok.loc, "cannot infer type for initializer");
}
let ty = P.targty;
+ P.targty = #null;
if lexmatch(P, #null, '}') {
ex = { tok.loc, ty, .u: :ZeroIni };
} else {
- assert(#f, "NYI");
+ if ty->is(:Agg) {
+ ex = parseaggini(P, ty);
+ } else if ty->is(:Arr) {
+ ex = parsearrini(P, ty);
+ } else {
+ fatal(P, tok.loc, "excess elements in initializer");
+ }
}
case else;
fatal(P, tok.loc, "expected expression (near %qT)", tok);
@@ -896,11 +1224,19 @@ fn pexpostfix(P *Parser) Expr {
}
let Fn = &ty.u.Fn;
let args Vec<Expr> = {};
+ let params = Fn.params;
+ let param = &params[0];
while !lexmatch(P, #null, ')') {
- args->push(parseexpr(P));
- if args.len > Fn.params.#len and !Fn.variadic {
- fatal(P, args->last().loc, "too many args (%z, expected %z)", args.len, Fn.params.#len);
+ let ex = parseexpr(P);
+ args->push(ex);
+ if args.len >= params.#len and !Fn.variadic {
+ fatal(P, ex.loc, "too many args (%z, expected %z)", args.len, params.#len);
+ }
+ if args.len < params.#len and !typematchestarg(*param, ex.ty) {
+ err(P, ex.loc, "function call argument type mismatch (%t, expected %t)",
+ ex.ty, *param);
}
+ param++;
if !lexmatch(P, #null, ',') {
lexexpect(P, ')');
break;
@@ -955,7 +1291,67 @@ fn pexpostfix(P *Parser) Expr {
}
};
case lexmatch(P, &tok, '.');
- let name = (tok = lexexpects(P, :ident, "field name")).u.ident;
+ switch {
+ case lexmatch(P, &tok, :ident);
+ let name = tok.u.ident;
+ let ty = ex.ty;
+ if ty->is(:Ptr) {
+ ty = ty.u.Ptr;
+ }
+ if !ty->is(:Agg) {
+ fatal(P, tok.loc, "left-hand-side is not an aggregate (%t)", ty);
+ }
+ let konst = ty.konst;
+ ty = unconstify(ty);
+ let agg = &ty.u.Agg;
+ let fld *AggField = #null;
+ foreach(f, i, agg.flds) {
+ if streq(name, f.name) {
+ fld = &agg.flds[i];
+ break;
+ }
+ }
+ if fld == #null {
+ fatal(P, tok.loc, "%t has no such field %qT", ty, tok);
+ }
+ if fld.ty == #null {
+ fatal(P, tok.loc, "field %qT has no type", tok);
+ }
+ ex = { tok.loc, konst ? constify(fld.ty) : fld.ty, :Dot { exprdup(P.alloc, ex), fld }};
+
+ case lexmatch(P, &tok, '#ptr');
+ let ty = ex.ty;
+ if ty->is(:Ptr) {
+ ty = ty.u.Ptr;
+ }
+ switch ty.u {
+ case Slice;
+ ex = { tok.loc, ty_usize, :SPtr(exprdup(P.alloc, ex)) };
+ case else;
+ fatal(P, ex.loc, "invalid operand to `#len' (%t)", ex.ty);
+ }
+ case lexmatch(P, &tok, '#len');
+ let ty = ex.ty;
+ if ty->is(:Ptr) {
+ ty = ty.u.Ptr;
+ }
+ switch ty.u {
+ case Arr arr;
+ assert(arr.length >= 0, "arr len");
+ ex = { tok.loc, ty_usize, :IntLit { arr.length }};
+ case Slice;
+ ex = { tok.loc, ty_usize, :SLen(exprdup(P.alloc, ex)) };
+ case else;
+ fatal(P, ex.loc, "invalid operand to `#len' (%t)", ex.ty);
+ }
+ // case lexmatch(P, &tok, '#tag');
+
+ case else;
+ lexexpects(P, :ident, "field name");
+ }
+
+ case lexmatch(P, &tok, '->');
+ let name = (tok = lexexpects(P, :ident, "method name")).u.ident;
let ty = ex.ty;
if ty->is(:Ptr) {
ty = ty.u.Ptr;
@@ -964,20 +1360,38 @@ fn pexpostfix(P *Parser) Expr {
fatal(P, tok.loc, "left-hand-side is not an aggregate (%t)", ty);
}
let agg = &ty.u.Agg;
- let fld *AggField = #null;
- foreach(f, i, agg.flds) {
- if streq(name, f.name) {
- fld = &agg.flds[i];
- break;
- }
+ let decl = envfind_noparent(agg.decls, name);
+ if decl == #null {
+ fatal(P, tok.loc, "%t has no such method %qT", ty, tok);
}
- if fld == #null {
- fatal(P, tok.loc, "%t has no such field %qT", ty, tok);
- }
- if fld.ty == #null {
- fatal(P, tok.loc, "field %qT has no type", tok);
+ lexexpect(P, '(');
+ switch decl.u {
+ case Fn f;
+ let Fn = &f.ty.u.Fn;
+ if Fn.params.#len == 0 {
+ fatal(P, tok.loc, "function takes no arguments");
+ }
+ let args Vec<Expr> = {};
+ args->push(ex);
+ while !lexmatch(P, #null, ')') {
+ args->push(parseexpr(P));
+ if args.len > Fn.params.#len and !Fn.variadic {
+ fatal(P, args->last().loc, "too many args (%z, expected %z)", args.len, Fn.params.#len);
+ }
+ if !lexmatch(P, #null, ',') {
+ lexexpect(P, ')');
+ break;
+ }
+ }
+ if args.len < Fn.params.#len {
+ err(P, ex.loc, "too few args (%z, expected %z)", args.len, Fn.params.#len);
+ }
+ let met Expr = { tok.loc, f.ty, :Symbol(decl) };
+ ex = { tok.loc, .ty: Fn.ret, .u: :Call { exprdup(P.alloc, met), args->move(P.alloc) }};
+
+ case else;
+ fatal(P, tok.loc, "cannot call `->%s': not a function or macro", name);
}
- ex = { tok.loc, fld.ty, :Dot { exprdup(P.alloc, ex), fld }};
case else;
break;
@@ -1041,7 +1455,7 @@ fn pexprefix(P *Parser) Expr {
let ex = pexprefix(P);
let ty2 = interntype({ g_targ.ptrsize, .u: :Ptr(ex.ty) });
if !islvalue(ex) and !(ex.u.#tag == :Symbol and ex.u.Symbol.u.#tag == :Fn)
- and !(ex.u.#tag == :ZeroIni or ex.u.#tag == :Ini) {
+ and !(ex.u.#tag == :ZeroIni or ex.u.#tag == :AggIni or ex.u.#tag == :ArrIni) {
err(P, ex.loc, "invalid operand to `&': not an lvalue");
}
return { tok.loc, ty2, .u: :UnOp { :addrof, exprdup(P.alloc, ex) }};
@@ -1424,6 +1838,14 @@ fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void {
parsedecls(P, &declyield, &Arg { P, yield, yarg }, #{toplevel?} #f);
case else;
+ if (tok = lexpeek(P)).t == :ident {
+ let decl = finddecl(P, tok.u.ident);
+ if decl != #null and decl.u.#tag == :Macro {
+ lex(P);
+ parseexpandmacro(P, &decl.u.Macro);
+ return parsestmts(P, yield, yarg);
+ }
+ }
let ex = parseexpr(P);
lexexpect(P, ';');
return yield({ tok.loc, .u: :Expr(ex) }, yarg);
@@ -1435,7 +1857,7 @@ fn parseblock0(P *Parser) [#]Stmt {
let tok Tok = {};
let env = mkenv(P.curenv, P.alloc);
pushenv(P, env);
- while (tok = lexpeek(P)).t != '}' and tok.t != :kw_case and tok.t != '}' {
+ while (tok = lexpeek(P)).t != '}' and tok.t != :kw_case and tok.t != ')' {
if lexmatch(P, #null, ';') { continue; }
fn pushstmt(st Stmt, arg *void) void {
let sts *Vec<Stmt> = arg;
@@ -1444,6 +1866,7 @@ fn parseblock0(P *Parser) [#]Stmt {
parsestmts(P, &pushstmt, &sts);
}
popenv(P);
+ envfree(env);
return sts->move(P.alloc);
}
@@ -1484,6 +1907,18 @@ fn parsevardecl(P *Parser, toplevel bool, externp bool, letp bool, yield DeclYie
}
}
+ if ty->is(:Arr) and ty.u.Arr.length < 0 and !ini->empty() and ini.Some.u.#tag == :ZeroIni {
+ ty = mkarrtype(0, #f, ty.u.Arr.child);
+ }
+
+ if ty->is(:Arr) and ty.u.Arr.length < 0 and !ini->empty() and ini.Some.u.#tag == :ArrIni {
+ ty = mkarrtype(ini.Some.u.ArrIni.maxn, #f, ty.u.Arr.child);
+ }
+
+ if !completetype(ty) and !fwd {
+ err(P, tok.loc, "incomplete type in initializer (%t)", ty);
+ }
+
if !ini->empty() and !typematchestarg(ty, ini.Some.ty) {
err(P, ini.Some.loc, "incompatible initializer (%t, expected %t)", ini.Some.ty, ty);
}
@@ -1687,6 +2122,7 @@ fn parsemacro(P *Parser, name *const u8) *Decl {
return c;
}
+ let loc = P.tokloc;
let tok Tok #?;
let cs Vec<MacroCase> = {};
if lexpeek(P).t == '(' {
@@ -1701,6 +2137,8 @@ fn parsemacro(P *Parser, name *const u8) *Decl {
}
}
}
+
+ return putdecl(P, loc, { name, loc, .u: :Macro { cs->move(P.alloc) }});
}
fn parsedecls(P *Parser, yield DeclYielder, yarg *void, toplevel bool) void {