import "mem.hff"; import "util.hff"; import "vec.hff"; import "map.hff"; import "cffc.hff"; /////////// // Lexer // /////////// fn chr(P *Parser) int { let c int #?; switch P.peekchr { case Some pc; c = pc; P.peekchr = :None; case None; c = fgetc(P.fp); } ++P.curloc.idx; ++P.curloc.col; if c == '\n' { P.curloc.col = 1; ++P.curloc.line; } if c == EOF { P.eof = #t; } return c; } fn chrpeek(P *Parser) int { switch P.peekchr { case Some c; return c; } let c = fgetc(P.fp); if c == EOF { P.eof = #t; } P.peekchr = :Some(c); return c; } fn chrmatch(P *Parser, c int) bool { if chrpeek(P) == c { chr(P); return #t; } return #f; } fn isspace(c u8) bool { switch c { case ' ', '\t', '\n', '\r', '\v', '\f'; return #t; } return #f; } fn isdigit(c u8) bool { return c >= '0' and c <= '9'; } fn isxdigit(c u8) bool { return isdigit(c) or (c >= 'A' and c <= 'F') or (c >= 'a' and c <= 'f'); } fn isalpha(c u8) bool { return (c >= 'a' and c <= 'z') or (c >= 'A' and c <= 'Z'); } fn issep(c u8) bool { if isspace(c) { return #t; } switch (c) { case '(', ')', '[', ']', '{', '}', '.', ',', ';', '?', '+', '-', '*', '/', '&', '|', '^', '~', '=', '\'', '"', '<', '>', ':', '@', '#', '\\', '`'; return #t; } return #f; } fn ishsep(c u8) bool { if isspace(c) { return #t; } switch (c) { case '(', ')', '[', ']', '{', '}', '.', ',', ';', '"'; return #t; } return #f; } fn readtilsep(P *Parser, buf [#]u8, dot bool) int { let i = 0, c u8 #?; while (!issep(c = chrpeek(P))) or (dot and c == '.') { chr(P); if i >= buf.#len - 1 { return -1; } buf[i++] = c; } buf[i++] = 0; return i; } fn readtilhsep(P *Parser, buf [#]u8, dot bool) int { let i = 0, c u8 #?, pred = &ishsep; while (!pred(c = chrpeek(P))) or (dot and c == '.') { chr(P); if !issep(c) { pred = &issep; } if i >= buf.#len - 1 { return -1; } buf[i++] = c; } buf[i++] = 0; return i; } fn eatspaces(P *Parser) void { for ;;chr(P) { if !isspace(chrpeek(P)) { break; } } } // !sorted extern static keyword2str [NUM_KEYWORDS]*const u8 = { "and", "as", "break", "case", "const", "continue", "def", "defmacro", "do", "else", "enum", "extern", "fn", "for", "if", "import", "let", "or", "return", "sizeof", "static", "struct", "switch", "typedef", "typeof", "union", "while", }; fn str2keyword(s *const u8) int { let i = 0zs, j = keyword2str.#len - 1; while i <= j { let k = (j + i) / 2; let cmp = strcmp(keyword2str[k], s); if cmp == 0 { return k; } else if cmp < 0 { i = k + 1; } else { j = k - 1; } } return -1; } fn xdigit2num(c u8) int { switch { case isdigit(c); return c - '0'; case c >= 'a' and c <= 'f'; return (c - 'a') + 10; case c >= 'A' and c <= 'F'; return (c - 'A') + 10; } return -1; } fn readnumber(s *const u8) Option { let c u8 #?, acc = 0u64, accf = 0.0f64, fmul = 0.1, base = 10, flt = #f, nused = 0, suffix *const u8 = #null; for let i = 0; (c = s[i]) != 0; ++i { if i == 0 and c == '0' { --nused; } if i == 1 and tolower(c) == 'x' { base = 16; continue; } if !flt and c == '.' and base == 10 { flt = #t; accf = acc; continue; } if nused > 0 and c == '_' { continue; } if (base == 16 and !isxdigit(c)) or (base != 16 and (c < '0' or c > ('0' + base) - 1)) { suffix = s + i; } ++nused; if flt { accf = accf + ((c - '0') * fmul); fmul *= 0.1; } else { acc = (acc * base) + xdigit2num(c); } } let tok = Tok {}; if flt { tok.t = :flo; tok.u.flo = accf; switch { case suffix == #null; tok.ty = ty_f64; case strcieq(suffix, "f"); tok.ty = ty_f32; case strcieq(suffix, "f32"); tok.ty = ty_f32; case strcieq(suffix, "f64"); tok.ty = ty_f64; case else; return :None; } return :Some(tok); } else { tok.t = :int; tok.u.uint = acc; fn ty4intX(i u64) *const Type { if i < 1u64 << ((g_targ.intsize * 8) - 1) { return ty_int; } if i < 1u64 << 31 { return ty_i32; } if i < 1u64 << 63 { return ty_i64; } return ty_u64; } fn ty4uintX(i u64) *const Type { if i < 1u64 << (g_targ.intsize * 8) { return ty_uint; } if i < 1u64 << 32 { return ty_u32; } return ty_u64; } switch { case suffix == #null; tok.ty = ty4intX(tok.u.uint); case strcieq(suffix, "u"); tok.ty = ty4uintX(tok.u.uint); case strcieq(suffix, "u8"); tok.ty = ty_u8; case strcieq(suffix, "i8"); tok.ty = ty_i8; case strcieq(suffix, "u16"); tok.ty = ty_u16; case strcieq(suffix, "i16"); tok.ty = ty_i16; case strcieq(suffix, "u32"); tok.ty = ty_u32; case strcieq(suffix, "i32"); tok.ty = ty_i32; case strcieq(suffix, "u64"); tok.ty = ty_u64; case strcieq(suffix, "i64"); tok.ty = ty_i64; case strcieq(suffix, "z"); tok.ty = ty_usize; case strcieq(suffix, "zs"); tok.ty = ty_isize; case strcieq(suffix, "p"); tok.ty = ty_uptrint; case strcieq(suffix, "ps"); tok.ty = ty_iptrint; case else; return :None; } return :Some(tok); } } def MAX_MACROEXPAND_ITER = 100; fn lex(P *Parser) Tok { let c int #?; let tok Tok = {}; switch P.peektok { case Some pt; P.peektok = :None; 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)) { let s [80]u8 = {}; if readtilsep(P, s[0::], #t) < 0 { fatal(P, tok.loc, "bad number literal"); } switch readnumber(s) { case None; fatal(P, tok.loc, "bad number literal %qs", s); case Some tok; tok.loc = P.tokloc; return tok; } } if isalpha(c) or c == '_' or c == '$' or c > 0x7F { let s [120]u8; if readtilsep(P, s[0::], #f) < 0 { fatal(P, tok.loc, "identifier too long"); } let kw = str2keyword(s); if kw >= 0 { tok.t = kw; tok.u.ident = keyword2str[kw]; } else if (c == '$') { tok.t = :gensym; tok.u.ident = internstr(&s[1]); } else { tok.t = :ident; tok.u.ident = internstr(s); } return tok; } if c == '#' { let s [100]u8 = {}; if readtilhsep(P, s[0::], #f) < 0 { fatal(P, P.tokloc, "invalid #keyword"); } switch { case streq(s, "#") and chrpeek(P) == '{'; chr(P); let bal = 1; while bal != 0 { switch lex(P).t { case '{'; ++bal; case '}'; --bal; case :eof; fatal(P, tok.loc, "unterminated comment"); } } return lex(P); case streq(s, "#"); tok.t = '#'; case streq(s, "#t") or streq(s, "#f"); tok.t = :bool; tok.u.bool = s[1] == 't'; case streq(s, "#null"); tok.t = :null; case streq(s, "##"); tok.t = '##'; case streq(s, "#len"); tok.t = '#len'; case streq(s, "#tag"); tok.t = '#tag'; case streq(s, "#?"); tok.t = '#?'; case s[0] == '#' and s[1] == '\''; tok.t = :label; tok.u.str = spanz(internstr(s)); case else fatal(P, P.tokloc, "invalid #keyword `%s'", s); } return tok; } if c == '"' or c == '\'' { chr(P); let delim = c, str Vec = {}, c u8 #?, i = 0z; while (c = chr(P)) != delim { if c == 0 or c == '\n' { fatal(P, P.tokloc, "unterminated %s literal", delim == '"' ? "string" : "character"); } if c != '\\' { str->push(c); continue; } switch ((c = chr(P))) { case 0, '\n'; fatal(P, P.tokloc, "unterminated %s literal", delim == '"' ? "string" : "character"); case '\''; str->push('\''); case '\\'; str->push('\\'); case '"'; str->push('"'); case 'n'; str->push('\n'); case 'r'; str->push('\r'); case 't'; str->push('\t'); case 'v'; str->push('\v'); case 'f'; str->push('\f'); case '0'; str->push('\0'); case 'x'; let x0 = xdigit2num(chr(P)), x1 = xdigit2num(chr(P)); if x0 < 0 or x1 < 0 { fatal(P, P.tokloc, "not a hex byte escape sequence"); } str->push((x0 << 4) | x1); case else fatal(P, P.tokloc, "unknown escape sequence '\\%c'", c); } } if delim == '"' { tok.t = :str; tok.u.str = str->move(P.alloc); } else { tok.t = :chr; if str.len == 0 { fatal(P, P.tokloc, "empty char literal"); } else if str.len > 8 { fatal(P, P.tokloc, "too long multichar literal %qs", str.dat); } tok.u.uint = 0; vec_each(c0, i, str) { tok.u.uint = (tok.u.uint << 8) | c0; } } return tok; } switch c = chr(P) { case '(', ')', '[', ']', '{', '}', ',', ';', '~'; tok.t = c; return tok; case '?'; if chrmatch(P, '?') { tok.t = '??'; } else { tok.t = '?'; } return tok; case '.'; if chrmatch(P, '.') { if chrmatch(P, '.') { tok.t = '...'; } else { tok.t = '..'; } } else { tok.t = '.'; } return tok; case '*'; if chrmatch(P, '=') { tok.t = '*='; } else { tok.t = '*'; } return tok; case '/'; if chrmatch(P, '=') { tok.t = '/='; } else if chrmatch(P, '/') { while (c = chr(P)) != 0 and c != '\n' { } return lex(P); } else { tok.t = '/'; } return tok; case '%'; if chrmatch(P, '=') { tok.t = '%='; } else { tok.t = '%'; } return tok; case '+'; if chrmatch(P, '=') { tok.t = '+='; } else if chrmatch(P, '+') { tok.t = '++'; } else { tok.t = '+'; } return tok; case '-'; if chrmatch(P, '=') { tok.t = '-='; } else if chrmatch(P, '-') { tok.t = '--'; } else if chrmatch(P, '>') { tok.t = '->'; } else { tok.t = '-'; } return tok; case '&'; if chrmatch(P, '=') { tok.t = '&='; } else { tok.t = '&'; } return tok; case '|'; if chrmatch(P, '=') { tok.t = '|='; } else { tok.t = '|'; } return tok; case '^'; if chrmatch(P, '=') { tok.t = '^='; } else { tok.t = '^'; } return tok; case ':'; if chrmatch(P, ':') { tok.t = '::'; } else { tok.t = ':'; } return tok; case '='; if chrmatch(P, '=') { tok.t = '=='; } else { tok.t = '='; } return tok; case '!'; if chrmatch(P, '=') { tok.t = '!='; } else { tok.t = '!'; } return tok; case '<'; if chrmatch(P, '=') { tok.t = '<='; } else if chrmatch(P, '<') { if chrmatch(P, '=') { tok.t = '<<='; } else { tok.t = '<<'; } } else { tok.t = '<'; } return tok; case '>'; if chrmatch(P, '=') { tok.t = '>='; } else if chrmatch(P, '>') { if chrmatch(P, '=') { tok.t = '>>='; } else { tok.t = '>>'; } } else { tok.t = '>'; } return tok; case EOF, 0; tok.t = :eof; return tok; } fatal(P, tok.loc, "stray %qc in program", c); } fn lexpeek(P *Parser) Tok { switch P.peektok { case Some t; return t; } let tok = lex(P); P.peektok = :Some(tok); return tok; } fn lexmatch(P *Parser, tokp *Tok, t TokT) bool { let tok = lexpeek(P); if tokp { *tokp = tok; } if tok.t == t { lex(P); return #t; } return #f; } fn lexexpects(P *Parser, t TokT, what *const u8) Tok { let tok = Tok {}; if !lexmatch(P, &tok, t) { if what { fatal(P, tok.loc, "expected %s (near %qT)", what, tok); } else { fatal(P, tok.loc, "expected %qk (near %qT)", t, tok); } } return tok; } defmacro lexexpect(P, t) [lexexpects(P, t, #null)] //////////// // Parser // //////////// static primenv *Env = {}; fn pushenv(P *Parser, env *Env) void { assert(P.curenv == envparent(env), "pushenv"); P.curenv = env; } fn popenv(P *Parser) void { P.curenv = envparent(P.curenv); assert(P.curenv != #null, "popenv"); } fn finddecl(P *Parser, name *const u8) *Decl { let p = envfind(primenv, name); return p ?? envfind(P.curenv, name); } fn finddecl_noparent(P *Parser, name *const u8) *Decl { let p = envfind(primenv, name); return p ?? envfind_noparent(P.curenv, name); } fn putdecl(P *Parser, eloc Loc, decl Decl) *Decl { if envfind(primenv, decl.name) { fatal(P, eloc, "cannot shadow primitive `%s'", decl.name); } let d0 *const Decl; let d = envput(P.curenv, decl, &d0); if d == #null { fatal(P, eloc, "attempt to redefine `%s' (previously defined at %l)", decl.name, d0.loc); } return d; } /////////////////// // Types Parsing // /////////////////// fn parseexpr(P *Parser) Expr; fn parsetype(P *Parser) *const Type; typedef DeclYielder *fn(*Decl, *void) void; fn parsedecls(P *Parser, yield DeclYielder, yarg *void, toplevel bool) void; fn parseenum(P *Parser, name *const u8, lax bool) *const Type { lexexpect(P, '{'); let iota = 0i64; let max = iota; let vals Vec = {}; while !lexmatch(P, #null, '}') { let name = lexexpects(P, :ident, "variant name").u.ident; if lexmatch(P, #null, '=') { let ex = parseexpr(P); if !fold(&ex) or !ex.ty->is(:Int) { err(P, ex.loc, "expected constant integer expression"); } iota = ex.u.IntLit.i; } vals->push({ name, iota++, }); if !lexmatch(P, #null, ',') { lexexpect(P, '}'); break; } } let intty *const Type = max < 1u64 << ((g_targ.intsize * 8) - 1) ? ty_int : max < 1u64 << 31 ? ty_i32 : ty_i64; static id int = 0; return interntype({ .u: :Enum { intty, name, lax, id++, vals->move(P.alloc) }}); } fn isdecltokt(t TokT) bool { switch t { case :kw_fn, :kw_static, :kw_def, :kw_defmacro, :kw_struct, :kw_union, :kw_enum, :kw_extern; return #t; } return #f; } fn parseagg(P *Parser, kind AggKind, name *const u8, retdecl **Decl) *const Type { let loc = P.tokloc; let tok Tok #?; static id int = 0; let decl *Decl = name ? finddecl_noparent(P, name) : #null; if decl { switch decl.u { case Ty ty; if !ty->is(:Agg) or !ty.u.Agg.fwd { decl = #null; } } } let ty = decl ? decl.u.Ty : interntype({ .u: :Agg { kind, name, id++ }}); let ty = as(*Type)ty; let agg = &ty.u.Agg; if name != #null { agg.fwd = #t; if decl == #null { decl = putdecl(P, loc, { name, loc, .u: :Ty(ty)}); if retdecl { *retdecl = decl; } } } if !lexmatch(P, &tok, ';') { agg.fwd = #f; lexexpect(P, '{'); let size = 0, align = 1; let flds Vec = {}; let havedecls = #f; while !lexmatch(P, #null, '}') { if isdecltokt(lexpeek(P).t) { havedecls = #t; break; } let off = size; let name = lexexpects(P, :ident, "field name").u.ident; let type = parsetype(P); if type { off = ALIGNUP(off, type.align); align = MAX(align, type.align); size = kind == :Struct ? off + type.size : MAX(size, type.size); } flds->push({ name, type, off }); if !lexmatch(P, #null, ',') { lexexpect(P, '}'); break; } } ty.size = size; ty.align = align; agg.flds = flds.dat[0::flds.len]; if havedecls { agg.decls = mkenv(P.curenv, P.alloc); pushenv(P, agg.decls); while !lexmatch(P, #null, '}') { parsedecls(P, #null, #null, #{toplevel} #f); } popenv(P); } } 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 { fatal(P, eloc, "`%s' is not defined", name); } if decl.u.#tag != :Ty { fatal(P, eloc, "`%s' is not a type", name); } return decl.u.Ty; } fn parsefnparams(P *Parser, variadic *bool, paramnames *Vec<*const u8>, paramtys *Vec<*const Type>) void { lexexpect(P, '('); let tok Tok #?; *variadic = #t; while !lexmatch(P, &tok, ')') { if lexmatch(P, #null, '...') { *variadic = #t; lexmatch(P, #null, ','); lexexpect(P, ')'); break; } let name *const u8 = #null; let type *const Type #?; if lexmatch(P, &tok, :ident) { let tok2 Tok #?; if (tok2 = lexpeek(P)).t == ',' or tok2.t == ')' { type = xident2type(P, tok.loc, tok.u.ident); } else { name = tok.u.ident; type = parsetype(P); } } else { type = parsetype(P); } if paramnames { paramnames->push(name); } paramtys->push(type); if !lexmatch(P, &tok, ',') { lexexpect(P, ')'); break; } } } fn parsefntype(P *Parser) *const Type { let variadic = #f; let paramtys Vec<*const Type> = {}; parsefnparams(P, &variadic, #null, ¶mtys); let ret = parsetype(P); return interntype({ .u: :Fn { paramtys->move(P.alloc), variadic, ret, }}); } fn parsetype(P *Parser) *const Type { let tok Tok = {}; switch { case lexmatch(P, &tok, :kw_const); return constify(parsetype(P)); case lexmatch(P, &tok, '*'); return mkptrtype(parsetype(P)); case lexmatch(P, &tok, '['); let len = -1i64; if lexmatch(P, &tok, '#') { lexexpect(P, ']'); return mkslicetype(parsetype(P)); } else if !lexmatch(P, &tok, ']') { let ex = parseexpr(P); lexexpect(P, ']'); if !fold(&ex) or ex.u.#tag != :IntLit or ex.u.IntLit.i < 0 { fatal(P, ex.loc, "expected constant non-negative integer expression for array length"); } len = ex.u.IntLit.i; } let child = parsetype(P); return mkarrtype(len, #f, child); 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); case else; fatal(P, tok.loc, "expected type (near %qT)", tok); } } ///////////////////////// // Expressions Parsing // ///////////////////////// fn typematchestarg(targ *const Type, ty *const Type) bool { if targ == ty { return #t; } if typeof2(targ, ty) == #null { return #f; } if targ->is(:Ptr) or ty->is(:Ptr) { if !targ.u.Ptr.konst and ty.u.Ptr.konst { return #f; } } return #t; } fn exprdup(alloc *Allocator, ex Expr) *Expr { return memcpy(alloc->alloc(sizeof(ex)), &ex, sizeof(ex)); } fn islvalue(ex Expr) bool { switch ex.u { case Symbol decl; return decl.u.#tag == :Let or decl.u.#tag == :Static; case UnOp u; return u.op == :deref; case Index; return #t; case Dot; return #t; } 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 = {}; 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 = {}; let exs Vec = {}; 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 = {}; #'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 = {}; 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 #?; switch tok.t { case :int, :chr; ex = { tok.loc, tok.ty, .u: :IntLit { tok.u.int }}; case :flo; ex = { tok.loc, tok.ty, .u: :FloLit(tok.u.flo) }; case :bool; ex = { tok.loc, ty_bool, .u: :BoolLit(tok.u.bool) }; case :null; ex = { tok.loc, ty_voidptr, .u: :NullLit }; case :str; let ty = mkarrtype(tok.u.str.#len + 1, #t, ty_u8); ex = { tok.loc, ty, .u: :StrLit(tok.u.str) }; case :ident; let ident = tok.u.ident; let decl = finddecl(P, ident); if decl == #null { fatal(P, tok.loc, "%qT is not defined", tok); } 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 #?; if lexmatch(P, &tok, '(') { let ex = parseexpr(P); lexexpect(P, ')'); ty = ex.ty; } else { ty = parsetype(P); } if !completetype(ty) { err(P, tok.loc, "sizeof incomplete type (%t)", ty); } ex = { tok.loc, ty_usize, :IntLit { ty.size }}; case '('; 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 { 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); } P.targty = #null; return ex; } fn pexpostfix(P *Parser) Expr { let tok Tok = {}; let ex = pexprimary(P); for ;; { switch { case lexmatch(P, &tok, '('); let lhs = exprdup(P.alloc, ex), ty = lhs.ty->is(:Ptr) ? lhs.ty.u.Ptr : lhs.ty; if !ty->is(:Fn) { fatal(P, lhs.loc, "not callable (%t)", lhs.ty); } let Fn = &ty.u.Fn; let args Vec = {}; let params = Fn.params; let param = ¶ms[0]; while !lexmatch(P, #null, ')') { 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; } } if args.len < Fn.params.#len { err(P, lhs.loc, "too few args (%z, expected %z)", args.len, Fn.params.#len); } ex = { tok.loc, .ty: Fn.ret, .u: :Call { lhs, args->move(P.alloc) }}; case lexmatch(P, &tok, '['); let lhs = exprdup(P.alloc, ex), rhs = exprdup(P.alloc, parseexpr(P)); if !lhs.ty->is(:Ptr) and !lhs.ty->is(:Arr) and !lhs.ty->is(:Slice) { fatal(P, lhs.loc, "indexee is not array or pointer type (%t)", lhs.ty); } if !rhs.ty->is(:Int) { err(P, lhs.loc, "index expression type is not integral (%t)", rhs.ty); } if lexmatch(P, #null, '::') { let end *Expr #?; if !lexmatch(P, #null, ']') { end = exprdup(P.alloc, parseexpr(P)); if !end.ty->is(:Int) { err(P, lhs.loc, "slice range end expression type is not integral (%t)", end.ty); } lexexpect(P, ']'); } else if lhs.ty->is(:Arr) { end = exprdup(P.alloc, { tok.loc, ty_usize, .u: :IntLit{lhs.ty.u.Arr.length} }); } else { fatal(P, tok.loc, "missing end of slice range"); } ex = { tok.loc, mkslicetype(childtype(lhs.ty)), .u: :Slice { lhs, rhs, end }}; } else { lexexpect(P, ']'); ex = { tok.loc, childtype(lhs.ty), .u: :Index { lhs, rhs }}; } case lexmatch(P, &tok, '++') or lexmatch(P, &tok, '--'); if !isnumtype(ex.ty) and !ex.ty->is(:Ptr) { fatal(P, ex.loc, "invalid operand to unary operator %qT (%t)", tok, ex.ty); } if !islvalue(ex) { err(P, ex.loc, "left operand to prefix %qT operator is not lvalue", tok); } if ex.ty.konst { err(P, ex.loc, "left operand to prefix %qT operator is const", tok); } ex = { tok.loc, ex.ty, .u: :UnOp { tok.t == '++' ? :postinc : :postdec, exprdup(P.alloc, ex) } }; case lexmatch(P, &tok, '.'); 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; } if !ty->is(:Agg) { fatal(P, tok.loc, "left-hand-side is not an aggregate (%t)", ty); } let agg = &ty.u.Agg; let decl = envfind_noparent(agg.decls, name); if decl == #null { fatal(P, tok.loc, "%t has no such method %qT", ty, 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 = {}; 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); } case else; break; } } return ex; } fn pexprefix(P *Parser) Expr { let tok Tok #?; switch { case lexmatch(P, &tok, '-') or lexmatch(P, &tok, '~'); let ex = pexprefix(P); let ty = typeof2(ex.ty, ty_int); if ty == #null { fatal(P, ex.loc, "invalid operand to unary operator %qT (%t)", tok, ex.ty); } if !ty->is(:Int) and tok.t == '~' { fatal(P, ex.loc, "invalid operand to unary operator %qT (%t)", tok, ex.ty); } return { tok.loc, ty, .u: :UnOp { tok.t == '-' ? :neg : :compl, exprdup(P.alloc, ex) } }; case lexmatch(P, &tok, '++') or lexmatch(P, &tok, '--'); let ex = pexprefix(P); if !isnumtype(ex.ty) and !ex.ty->is(:Ptr) { fatal(P, ex.loc, "invalid operand to unary operator %qT (%t)", tok, ex.ty); } if !islvalue(ex) { err(P, ex.loc, "left operand to prefix %qT operator is not lvalue", tok); } if ex.ty.konst { err(P, ex.loc, "left operand to prefix %qT operator is const", tok); } return { tok.loc, ex.ty, .u: :UnOp { tok.t == '++' ? :preinc : :predec, exprdup(P.alloc, ex) } }; case lexmatch(P, &tok, '!'); let ex = pexprefix(P); if !ex.ty->is(:Bool) { err(P, ex.loc, "invalid operand to unary operator %qT (%t)", tok, ex.ty); } return { tok.loc, ty_bool, .u: :UnOp { :not, exprdup(P.alloc, ex) }}; case lexmatch(P, &tok, '*'); let ex = pexprefix(P); if !ex.ty->is(:Ptr) { fatal(P, ex.loc, "invalid operand to dereference, not pointer (%t)", ex.ty); } let child = ex.ty.u.Ptr; if !completetype(child) and !child->is(:Fn) { fatal(P, ex.loc, "invalid operand to dereference, incomplete (%t)", ex.ty); } return { tok.loc, child, .u: :UnOp { :deref, exprdup(P.alloc, ex) }}; case lexmatch(P, &tok, '&'); 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 == :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) }}; case lexmatch(P, &tok, :kw_as); let loc = tok.loc; lexexpect(P, '('); let ty = parsetype(P); lexexpect(P, ')'); let ex = pexprefix(P); // TODO check valid cast return { loc, ty, :Cast(exprdup(P.alloc, ex)) }; } return pexpostfix(P); } fn pexbitarith(P *Parser) Expr { enum Kind { None, IntFlo, Int, StrLit } fn peeksop(P *Parser, tokp *Tok) Kind { let tok = lexpeek(P); if tokp { *tokp = tok; } switch tok.t { case '+', '-', '*', '/'; return :IntFlo; case '%', '&', '|', '^', '<<', '>>'; return :Int; case '##'; return :StrLit; } return :None; } let ex = pexprefix(P); let tok Tok = {}; let okind = peeksop(P, &tok); if okind == :None { return ex; } let tokt = tok.t; while lexmatch(P, &tok, tokt) { let rhs = pexprefix(P); let ty = typeof2(ex.ty, rhs.ty); switch { case ty == #null and tokt == '+' and ((ex.ty->is(:Int) and rhs.ty->is(:Ptr)) or (rhs.ty->is(:Int) and ex.ty->is(:Ptr))); ty = ex.ty->is(:Ptr) ? ex.ty : rhs.ty; case ty == #null and tokt == '-' and rhs.ty->is(:Int) and ex.ty->is(:Ptr); ty = ex.ty->is(:Ptr) ? ex.ty : rhs.ty; case ty == #null; fatal(P, tok.loc, "invalid operands %t and %t to binary operator %k", ex.ty, rhs.ty, tokt); case tokt == '-' and ex.ty->is(:Ptr) and rhs.ty->is(:Ptr); ty = ty_isize; case okind == :Int and ty->is(:Flo); err(P, tok.loc, "invalid operands %t and %t to binary operator %k", ex.ty, rhs.ty, tokt); case okind != :StrLit and !isnumtype(ty); fatal(P, tok.loc, "invalid operands %t and %t to binary operator %k", ex.ty, rhs.ty, tokt); } ex = { tok.loc, ty, .u: :BinOp { tokt, exprdup(P.alloc, ex), exprdup(P.alloc, rhs) }}; } return ex; } fn pexcmp(P *Parser) Expr { fn matchcmpop(P *Parser, tokp *Tok) bool { let tok = lexpeek(P); if tokp { *tokp = tok; } switch tok.t { case '==', '<=', '>=', '<', '>', '!='; lex(P); return #t; } return #f; } let ex = pexbitarith(P); let tok Tok = {}; if matchcmpop(P, &tok) { let rhs = pexbitarith(P); if typeof2(ex.ty, rhs.ty) == #null { fatal(P, tok.loc, "incompatible operands %t and %t to binary operator %qT", ex.ty, rhs.ty, tok); } let ty = typeof2(ex.ty, rhs.ty); if tok.t != '==' and tok.t != '!=' { if !isnumtype(ty) and !ty->is(:Ptr) { fatal(P, tok.loc, "invalid non-numeric operands %t and %t to relational operator %qT", ex.ty, rhs.ty, tok); } if ty->is(:Int) and ty != ty_int and ex.ty.u.Int.sgn != rhs.ty.u.Int.sgn { warn(P, tok.loc, "comparing integers of different signedness (%t and %t)", ex.ty, rhs.ty); } } ex = { tok.loc, ty_bool, .u: :BinOp { tok.t, exprdup(P.alloc, ex), exprdup(P.alloc, rhs) } }; } return ex; } fn pexlog(P *Parser) Expr { let ex = pexcmp(P); let tok Tok = lexpeek(P); let tokt = lexpeek(P).t; if tokt != :kw_and and tokt != :kw_or and tokt != '??' { return ex; } while lexmatch(P, &tok, tokt) { let rhs = pexcmp(P); if tokt == '??' { let ty = typeof2(ex.ty, rhs.ty); if !ex.ty->is(:Ptr) or !rhs.ty->is(:Ptr) or ty == #null { fatal(P, tok.loc, "invalid operands %t and %t to binary operator %k", ex.ty, rhs.ty, tok.t); } ex = { tok.loc, ty, .u: :BinOp { '??', exprdup(P.alloc, ex), exprdup(P.alloc, rhs) } }; } else { if !ex.ty->is(:Bool) or !rhs.ty->is(:Bool) { fatal(P, tok.loc, "invalid operands %t and %t to binary operator %k", ex.ty, rhs.ty, tok.t); } ex = { tok.loc, ty_bool, .u: :BinOp { tokt == :kw_and ? 'and' : 'or' , exprdup(P.alloc, ex), exprdup(P.alloc, rhs) } }; } } return ex; } fn pexcond(P *Parser) Expr { let tok Tok = {}; let ex = pexlog(P); if (lexmatch(P, &tok, '?')) { let ex2 = parseexpr(P); if !ex.ty->is(:Bool) and !ex.ty->is(:Ptr) { fatal(P, ex.loc, "invalid test operand to conditional operator (%t)", ex.ty); } lexexpect(P, ':'); let ex3 = pexcond(P); let ty = typeof2(ex2.ty, ex3.ty); if ty == #null { fatal(P, tok.loc, "conditional operator branches have incompatible types %t and %t", ex2.ty, ex3.ty); } ex = { tok.loc, ty, .u: :Cond { exprdup(P.alloc, ex), exprdup(P.alloc, ex2), exprdup(P.alloc, ex3) } }; } return ex; } fn pexassign(P *Parser) Expr { enum Kind { None, Set, IntFlo, Int } fn matchop(P *Parser, tokp *Tok) Kind { let tok = lexpeek(P); if tokp { *tokp = tok; } switch tok.t { case '='; lex(P); return :Set; case '+=', '-=', '*=', '/='; lex(P); return :IntFlo; case '%=', '&=', '|=', '^=', '<<=', '>>='; lex(P); return :Int; } return :None; } let tok Tok = {}; let ex = pexcond(P); let okind = matchop(P, &tok); if okind == :None { return ex; } let rhs = pexcond(P); switch { case !islvalue(ex); err(P, ex.loc, "left operand to assignment is not lvalue"); case (typeof2(ex.ty, rhs.ty) == #null) and !((ex.ty->is(:Ptr) and rhs.ty->is(:Int) and (tok.t == '+=' or tok.t == '-='))); err(P, ex.loc, "operands %t and %t to assignment operator %qT have incompatible types", ex.ty, rhs.ty, tok); case ex.ty.konst; err(P, ex.loc, "left operand to assignment is const"); } return { tok.loc, ex.ty, .u: :BinOp { tok.t, exprdup(P.alloc, ex), exprdup(P.alloc, rhs) }}; } fn parseexpr(P *Parser) Expr { return pexassign(P); } //////////////////////// // Statements Parsing // //////////////////////// fn stmtdup(alloc *Allocator, st Stmt) *Stmt { return memcpy(alloc->alloc(sizeof(st)), &st, sizeof(st)); } typedef StmtYielder *fn(Stmt, *void) void; fn parseblock(P *Parser) [#]Stmt; fn parsevardecl(P *Parser, toplevel bool, externp bool, letp bool, DeclYielder, *void) void; fn pstlet(P *Parser, yield StmtYielder, yarg *void) void { struct Arg { P *Parser, yield StmtYielder, yarg *void, } fn varyield(decl *Decl, arg *void) void { let a *Arg = arg; a.yield({ decl.loc, :Decl(decl) }, a.yarg); } parsevardecl(P, #{toplevel?} #f, #{extern?} #f, #{let?} #t, &varyield, &Arg { P, yield, yarg }); } fn pstif(P *Parser) Stmt { let loc = P.tokloc; let test = parseexpr(P); if !test.ty->is(:Bool) and !test.ty->is(:Ptr) { err(P, test.loc, "if condition must be bool or pointer (%t)", test.ty); } lexexpect(P, '{'); let t = parseblock(P); if lexmatch(P, #null, :kw_else) { if lexmatch(P, #null, :kw_if) { let f = stmtdup(P.alloc, pstif(P)); return { loc, :If { test, t, f[0::1] }}; } else { lexexpect(P, '{'); let f = parseblock(P); return { loc, :If { test, t, f }}; } } return { loc, :If { test, t }}; } fn pstreturn(P *Parser) Stmt { let loc = P.tokloc; if P.curfn == #null { fatal(P, loc, "return outside function"); } let retty = P.curfn.ty.u.Fn.ret; if lexmatch(P, #null, ';') { if retty != ty_void { err(P, loc, "return with no value in function returning %t", retty); } return { loc, :Return{} }; } else { let ex = parseexpr(P); lexexpect(P, ';'); if !typematchestarg(retty, ex.ty) { err(P, ex.loc, "incompatible type in return statement (%t, expected %t)", ex.ty, retty); } return { loc, :Return(:Some(ex)) }; } } fn pstwhile(P *Parser) Stmt { let loc = P.tokloc; let test = parseexpr(P); if !test.ty->is(:Bool) and !test.ty->is(:Ptr) { err(P, test.loc, "while condition must be bool or pointer (%t)", test.ty); } lexexpect(P, '{'); let t [#]Stmt #?; with_tmpchange(P.curloop, ++P.loopid, t = parseblock(P); ) return { loc, :While { test, t, P.loopid }}; } fn pstfor(P *Parser) Stmt { let loc = P.tokloc; let ini [#]Stmt = {}; let test Expr #?; let next = Option:None; if lexmatch(P, #null, :kw_let) { let sts Vec = {}; fn yieldlet(st Stmt, arg *void) void { let sts *Vec = arg; sts->push(st); } pstlet(P, &yieldlet, &sts); ini = sts->move(P.alloc); } else if lexmatch(P, #null, ';') { // pass } else { let ex = parseexpr(P); ini = stmtdup(P.alloc, { ex.loc, :Expr(ex) })[0::1]; } if lexmatch(P, #null, ';') { // for (...; ; ...;) -> for (...; #t; ...;) test = { P.tokloc, ty_bool, :BoolLit(#t) }; } else { test = parseexpr(P); lexexpect(P, ';'); } if !test.ty->is(:Bool) and !test.ty->is(:Ptr) { err(P, test.loc, "for condition must be bool or pointer (%t)", test.ty); } if !lexmatch(P, #null, '{') { next = :Some(parseexpr(P)); lexexpect(P, '{'); } let body [#]Stmt #?; with_tmpchange(P.curloop, ++P.loopid, body = parseblock(P); ) return { loc, :For { ini, test, next, body, P.loopid }}; } fn pstiswitch(P *Parser, loc Loc, ex Expr) Stmt { let tok Tok #?; let cs Vec = {}; let f [#]Stmt = {}; let elsep = #f; while !lexmatch(P, &tok, '}') { lexexpect(P, :kw_case); if lexmatch(P, &tok, :kw_else) { if elsep { err(P, tok.loc, "duplicate else case"); } elsep = #t; f = parseblock0(P); continue; } let es Vec = {}; do { let e = parseexpr(P); if !fold(&e) { err(P, e.loc, "expected compile-time expression"); } if !typematchestarg(ex.ty, e.ty) { err(P, e.loc, "bad case expression (%t, expected %t)", e.ty, ex.ty); } es->push(e); if !lexmatch(P, #null, ',') { lexexpect(P, ';'); break; } } while !lexmatch(P, #null, ';'); cs->push({ es->move(P.alloc), parseblock0(P) }); } return { loc, :ISwitch { ex, cs->move(P.alloc), f }}; } fn pstswitch(P *Parser) Stmt { let loc = P.tokloc; let tok Tok #?; if lexmatch(P, &tok, '{') { } else { let ex = parseexpr(P); lexexpect(P, '{'); if ex.ty->is(:Int) or ex.ty->is(:Enum) { return pstiswitch(P, loc, ex); } else if ex.ty->is(:Agg) and ex.ty.u.Agg.kind == :EUnion { } else { fatal(P, tok.loc, "invalid switch test expression type (%t)", ex.ty); } } } fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void { let tok Tok = {}; switch { case lexmatch(P, &tok, '{'); return yield({ tok.loc, .u: :Block(parseblock(P)) }, yarg); case lexmatch(P, &tok, :kw_let); return pstlet(P, yield, yarg); case lexmatch(P, &tok, :kw_if); return yield(pstif(P), yarg); case lexmatch(P, &tok, :kw_return); return yield(pstreturn(P), yarg); case lexmatch(P, &tok, :kw_while); return yield(pstwhile(P), yarg); case lexmatch(P, &tok, :kw_for); return yield(pstfor(P), yarg); case lexmatch(P, &tok, :kw_break); if P.curloop == 0 { err(P, tok.loc, "break outside loop"); } lexexpect(P, ';'); return yield({ tok.loc, :Break(P.curloop) }, yarg); case lexmatch(P, &tok, :kw_continue); if P.curloop == 0 { err(P, tok.loc, "continue outside loop"); } lexexpect(P, ';'); return yield({ tok.loc, :Continue(P.curloop) }, yarg); case lexmatch(P, &tok, :kw_switch); return yield(pstswitch(P), yarg); case isdecltokt(tok.t); struct Arg { P *Parser, yield StmtYielder, yarg *void, } fn declyield(decl *Decl, arg *void) void { let a *Arg = arg; a.yield({ decl.loc, :Decl(decl) }, a.yarg); } 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); } } fn parseblock0(P *Parser) [#]Stmt { let sts Vec = {}; 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 != ')' { if lexmatch(P, #null, ';') { continue; } fn pushstmt(st Stmt, arg *void) void { let sts *Vec = arg; sts->push(st); } parsestmts(P, &pushstmt, &sts); } popenv(P); envfree(env); return sts->move(P.alloc); } fn parseblock(P *Parser) [#]Stmt { let b = parseblock0(P); lexexpect(P, '}'); return b; } /////////////////// // Decls Parsing // /////////////////// fn parsevardecl(P *Parser, toplevel bool, externp bool, letp bool, yield DeclYielder, yarg *void) void { let tok Tok #?; do { let konst = lexmatch(P, #null, :kw_const), tok = lexexpect(P, :ident), name = tok.u.ident, ini Option #?, ty *const Type = #null, fwd = #f; if lexmatch(P, #null, '=') { ini = :Some(parseexpr(P)); ty = unconstify(ini.Some.ty); } else { ty = parsetype(P); if lexmatch(P, #null, '=') { P.targty = ty; ini = :Some(parseexpr(P)); } else if lexmatch(P, #null, '#?') { ini = :None; } else { ini = :None; fwd = #t; } } 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); } if konst { ty = constify(ty); } let decl Decl = { name, tok.loc, externp, :Let { ty, ini, fwd, 0 } }; if !letp { decl.u.#tag = :Static; } yield(putdecl(P, tok.loc, decl), yarg); if !lexmatch(P, &tok, ',') { lexexpects(P, ';', "`,' or `;'"); break; } } while !lexmatch(P, &tok, ';'); } fn parsefn(P *Parser, externp bool, name *const u8) *Decl { let decl Decl = { name, P.tokloc, externp, .u: :Fn {} }; let Fn = &decl.u.Fn, paramnames Vec<*const u8> = {}, paramtys Vec<*const Type> = {}; let loc = P.tokloc; parsefnparams(P, &Fn.variadic, ¶mnames, ¶mtys); Fn.paramnames = paramnames->move(P.alloc); let retty = parsetype(P); Fn.ty = interntype({ .size: 0, .align: 1, .u: :Fn { paramtys->move(P.alloc), Fn.variadic, retty }}); static id int = 0; Fn.id = ++id; let decl = putdecl(P, loc, decl); if !lexmatch(P, #null, '{') { lexexpects(P, ';', "';' or '{'"); return decl; } with_tmpchange(P.alloc, &Allocator { &Arena {}, &Arena:allocf }, with_tmpchange(P.curfn, Fn, let env = mkenv(P.curenv, P.alloc); pushenv(P, env); foreach(name, i, Fn.paramnames) { if name { putdecl(P, loc, { name, loc, .u: :Let { Fn.ty.u.Fn.params[i], :None, #f, Fn.id, }}); } } Fn.body = :Some(parseblock(P)); popenv(P); envfree(env); (as(*Arena)P.alloc.a)->destroy(); ); ); return decl; } fn doimport(P *Parser, path *const u8) [#]Decl { struct Entry { decls [#]Decl, wip bool, } static seen Map<*const u8, Entry, struct { fn hash(s *const u8) u32 { return fnv1a_s(FNV1A_INI, s); } fn eq(a *const u8, b *const u8) bool { return streq(a, b); } }> = {}; // imports are cached based on their realpath fn getbasedir(path *const u8) *const u8 { static res [PATH_MAX]u8 = {}; let i isize #?; for i = strlen(path); i > 0 and path[i] != '/'; --i { } if i == 0 { return "."; } memcpy(res, path, i); res[i] = 0; return res; } let basedir = getbasedir(P.curfile); let path2 [PATH_MAX]u8 #?; let _rpath [PATH_MAX]u8 #?; snprintf(path2, sizeof(path2) - 1, "%s/%s", basedir, path); let rpath *const u8 = realpath(path2, _rpath); if rpath == #null { fatal(P, P.tokloc, "import %qs: %s", path, strerror(errno)); } let d0 = seen->get(rpath); if d0 != #null { return d0.decls; } rpath = strcpy(malloc(strlen(rpath) + 1), rpath); let P2 Parser #?; parser_init(&P2, rpath); P2.is_header = #t; let e = seen->put(rpath, { {}, #t }); let decls = parse(&P2); *e = { decls, #f }; return decls; } fn parsemacro(P *Parser, name *const u8) *Decl { fn parsemacrocase(P *Parser) MacroCase { let tok Tok #?; let c MacroCase = {}; let params Vec<*const u8> = {}; let body Vec = {}; static gensymid int = 0; struct Gensyms { next *Gensyms, name *const u8, tok Tok, }; let gensyms *Gensyms = #null; lexexpect(P, '('); while !lexmatch(P, &tok, ')') { if lexmatch(P, &tok, '...') { params->push(lexexpects(P, :ident, "parameter name").u.ident); c.variadic = #t; lexmatch(P, &tok, ','); lexexpect(P, ')'); break; } else if lexmatch(P, &tok, '&') { params->push(lexexpects(P, :ident, "parameter name").u.ident); c.bodyarg = #t; lexmatch(P, &tok, ','); lexexpect(P, ')'); break; } else { params->push(lexexpects(P, :ident, "parameter name").u.ident); if !lexmatch(P, &tok, ',') { lexexpect(P, ')'); break; } } } let eloc = tok.loc; lexexpect(P, '['); let bal = 1; while bal != 0 { tok = lex(P); switch tok.t { case '['; ++bal; case ']'; --bal; case :eof; fatal(P, eloc, "unterminated macro definition"); } if tok.t == :ident { vec_each(par, _, params) { if streq(par, tok.u.ident) { tok.t = :macident; } } } else if tok.t == :gensym { let gs *Gensyms #?; let found = #f; for gs = gensyms; gs; gs = gs.next { if streq(gs.name, tok.u.ident) { tok = gs.tok; found = #t; break; } } if !found { gs = xcalloc(1, sizeof(*gs)); gs.name = tok.u.ident; gs.tok.t = :ident; gs.tok.loc = tok.loc; let s [300]u8 = {}; snprintf(s, sizeof(s) - 1, "#", tok.u.ident, gensymid++); gs.tok.u.ident = internstr(s); tok = gs.tok; gs.next = gensyms; gensyms = gs; } } body->push(tok); } body->pop(); for let gs = gensyms, next *Gensyms #?; gs; gs = next { next = gs.next; free(gs); } c.params = params->move(P.alloc); c.body = body->move(P.alloc); return c; } let loc = P.tokloc; let tok Tok #?; let cs Vec = {}; if lexpeek(P).t == '(' { cs->push(parsemacrocase(P)); } else { lexexpect(P, '{'); while !lexmatch(P, &tok, '}') { cs->push(parsemacrocase(P)); if !lexmatch(P, &tok, ',') { lexexpect(P, '}'); break; } } } return putdecl(P, loc, { name, loc, .u: :Macro { cs->move(P.alloc) }}); } fn parsedecls(P *Parser, yield DeclYielder, yarg *void, toplevel bool) void { let tok = Tok { .loc: P.tokloc }, decl *Decl = {}, externp = #f, name *const u8 = #null; if lexmatch(P, &tok, :kw_extern) { externp = #t; if (tok = lexpeek(P)).t != :kw_fn and tok.t != :kw_static { err(P, tok.loc, "expected `fn' or `static' after `extern'"); } } switch { case lexmatch(P, &tok, :kw_fn); let name = lexmatch(P, &tok, :ident) ? tok.u.ident : #null; decl = parsefn(P, externp, name); case lexmatch(P, &tok, :kw_import); let path = (tok = lexexpects(P, :str, "import path")).u.str.#ptr; let decls = doimport(P, path); foreach(decl, _, decls) { let decl = putdecl(P, tok.loc, decl); if yield { yield(decl, yarg); } } lexexpect(P, ';'); return; case lexmatch(P, &tok, :kw_enum); if lexmatch(P, &tok, :kw_union) { let name = lexexpect(P, :ident).u.ident; parseagg(P, :EUnion, name, &decl); } else { let name = lexexpect(P, :ident).u.ident; let loc = P.tokloc; let ty = parseenum(P, name, #{lax} #f); decl = putdecl(P, loc, { name, loc, .u: :Ty(ty) }); } case lexmatch(P, &tok, :kw_struct); let name = lexexpect(P, :ident).u.ident; parseagg(P, :Struct, name, &decl); case lexmatch(P, &tok, :kw_union); let name = lexexpect(P, :ident).u.ident; parseagg(P, :Union, name, &decl); case lexmatch(P, &tok, :kw_static); struct Arg { P *Parser, externp bool, toplevel bool, yield DeclYielder, yarg *void, } fn varyield(decl *Decl, arg *void) void { let a *Arg = arg; let Var = decl.u.Static; if !a.toplevel and !a.externp and Var.fwd { err(a.P, decl.loc, "static variable inside function cannot be forward-declared"); } if !a.toplevel and a.externp and !Var.fwd { err(a.P, decl.loc, "extern variable inside function cannot be initialized"); } if a.P.is_header and a.externp and !Var.fwd { err(a.P, decl.loc, "extern variable inside header cannot be initialized"); } if a.yield { a.yield(decl, a.yarg); } } parsevardecl(P, toplevel, externp, #{let?} #t, &varyield, &Arg { P, externp, toplevel, yield, yarg }); return; case lexmatch(P, &tok, :kw_def); do { let tok = lexexpect(P, :ident), name = tok.u.ident, ini Expr #?, ty *const Type = #null; if lexmatch(P, #null, '=') { ini = parseexpr(P); ty = unconstify(ini.ty); } else { ty = parsetype(P); lexexpect(P, '='); ini = parseexpr(P); } if !typematchestarg(ty, ini.ty) { err(P, ini.loc, "incompatible initializer (%t, expected %t)", ini.ty, ty); } fold(&ini); yield(putdecl(P, tok.loc, { name, tok.loc, .u: :Def(ini) }), yarg); if !lexmatch(P, &tok, ',') { lexexpects(P, ';', "`,' or `;'"); break; } } while !lexmatch(P, &tok, ';'); return; case lexmatch(P, &tok, :kw_defmacro); let name = lexexpects(P, :ident, "macro name").u.ident; decl = parsemacro(P, name); case else; fatal(P, tok.loc, "expected declaration (near %qT)", tok); } if yield != #null and decl != #null { yield(decl, yarg); } } extern fn parse(P *Parser) [#]Decl { let aralloc = Arena {}; let alloc = Allocator { &aralloc, &Arena:allocf, #null }; let decls Vec = {}; P.alloc = &alloc; P.curenv = mkenv(#null, P.alloc); if primenv == #null { primenv = mkenv(#null, P.alloc); putprimtypes(primenv); } while !P.eof { fn yield(decl *Decl, arg *void) void { let decls *Vec = arg; decls->push(*decl); } parsedecls(P, &yield, &decls, #{toplevel} #t); if lexmatch(P, #null, :eof) { break; } } envfree(P.curenv); aralloc->destroy(); return decls.dat[0::decls.len]; } extern fn parser_init(P *Parser, path *const u8) void { assert(NUM_KEYWORDS - 1 < '!', "2manykw"); *P = {}; P.curfile = path; if (P.fp = fopen(path, "r")) == #null { perror(path); exit(1); } P.tokloc = (P.curloc = { addfilepath(path), 0, 1, 1 }); P.peekchr = :None; }