diff options
| -rw-r--r-- | bootstrap/all.h | 1 | ||||
| -rw-r--r-- | bootstrap/cgen.c | 50 | ||||
| -rw-r--r-- | bootstrap/test.cff | 5 | ||||
| -rw-r--r-- | src/cffc.hff | 28 | ||||
| -rw-r--r-- | src/common.hff | 17 | ||||
| -rw-r--r-- | src/fmt.cff | 24 | ||||
| -rw-r--r-- | src/fold.cff | 71 | ||||
| -rw-r--r-- | src/parse.cff | 500 |
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 = ¶ms[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 { |