aboutsummaryrefslogtreecommitdiff
path: root/src/parse.cff
diff options
context:
space:
mode:
Diffstat (limited to 'src/parse.cff')
-rw-r--r--src/parse.cff500
1 files changed, 469 insertions, 31 deletions
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 {