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 = 0z, 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 = 0z, 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 const keyword2str [NUM_KEYWORDS]*const u8 = { "alignof", "and", "as", "bitfield", "break", "case", "const", "continue", "def", "defer", "defmacro", "do", "else", "enum", "extern", "fn", "for", "if", "import", "let", "offsetof", "or", "return", "sizeof", "static", "struct", "switch", "typedef", "typeof", "union", "while", }; fn str2keyword(s *const u8) int { let i isize = 0, j isize = 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 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; } 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; break; } ++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; 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; if !ep.tepl { 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; if ep.tepl { arg.toks[0].loc = tok.loc; } let old = P.curexpan; P.curexpan = xcalloc(sizeof Expan, 1); *P.curexpan = { old, {}, arg.toks }; 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, .ty: ty_int, .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 P.is_comment { readtilsep(P, s[0::], #t); return tok; } 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 P.is_comment { readtilsep(P, s[0::], #t); return tok; } 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 P.is_comment { readtilhsep(P, s[0::], #t); return tok; } if readtilhsep(P, s[0::], #f) < 0 { fatal(P, P.tokloc, "invalid #keyword"); } switch { case streq(s, "#") and chrpeek(P) == '{'; chr(P); let is_comment = P.is_comment; P.is_comment = #t; let bal = 1; while bal != 0 { switch lex(P).t { case '{'; ++bal; case '}'; --bal; case :eof; fatal(P, tok.loc, "unterminated comment"); } } P.is_comment = is_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, "#ptr"); tok.t = '#ptr'; case streq(s, "#tag"); tok.t = '#tag'; case streq(s, "#raw"); tok.t = '#raw'; case streq(s, "#FILE"); tok.t = '#FIL'; case streq(s, "#LINE"); tok.t = '#LIN'; case streq(s, "#?"); tok.t = '#?'; case s[0] == '#' and s[1] == '\''; tok.t = :label; tok.u.ident = 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 != '\\' { if !P.is_comment { str->push(c); } continue; } if P.is_comment { 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 'e'; str->push('\e'); 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 P.is_comment { return tok; } if delim == '"' { tok.t = :str; str->push('\0'); tok.u.str = str->move(P.tlalloc); tok.u.str = tok.u.str[0::tok.u.str.#len - 1]; } 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; } tok.ty = ty4intX(tok.u.uint); } 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; } if !P.is_comment { 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; } fn putdecl_alloc(P *Parser, alloc *Allocator, 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_alloc(P.curenv, alloc, 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 pexbitarith(P *Parser) Expr; fn parsetype(P *Parser) *const Type; typedef DeclYielder *fn(*Decl, *void) void; fn parsedecls(P *Parser, loc Loc, yield DeclYielder, yarg *void, toplevel bool) void; fn parseenum(P *Parser, name *const u8, lax bool) *const Type { let intty *const Type = #null; let tok Tok #?; if lexmatch(P, &tok, ':') { intty = parsetype(P); if !intty->is(:Int) { fatal(P, tok.loc, "enum backing type must be integral (%t)", intty); } } 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 = intty ?? ( max < 1i64 << ((g_targ.intsize * 8) - 1) ? ty_int : max < 1i64 << 31 ? ty_i32 : ty_i64); static id int = 0; return interntype(&Type{ intty.size, intty.align, .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, :kw_bitfield, :kw_typedef; return #t; } return #f; } fn parseagg(P *Parser, loc Loc, kind AggKind, name *const u8, retdecl **Decl) *const Type { 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 or ty.u.Agg.kind != kind or !streq(ty.u.Agg.name, name) { decl = #null; } case else decl = #null; } } let ty = decl ? decl.u.Ty : interntype(&Type{ .u: :Agg { kind, name, id++ }}); let ty = as(*Type)ty; let constty = as(*Type)constify(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 = 0z, align = 1z; let flds Vec = {}; let havedecls = #f; while !lexmatch(P, &tok, '}') { if isdecltokt(lexpeek(P).t) { havedecls = #t; break; } let off = kind == :Struct ? size : 0; let name = (tok = lexexpects(P, :ident, "field name")).u.ident; let type *const Type = #null; if !(kind == :EUnion and lexpeek(P).t == ',') { type = parsetype(P); } if type != #null and !completetype(type) { err(P, tok.loc, "field `%s' is of incomplete type (%t)", name, type); } 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; } } size = ALIGNUP(size, align); ty.size = size; ty.align = align; agg.flds = flds.dat[0::flds.len]; *constty = *ty; constty.konst = #t; if kind == :EUnion { // add enum tag field on top and offset every other field down static id int = 0; let enumname [200]u8 = {}; snprintf(enumname, sizeof(enumname) - 1, "%s#tag",name); let enumty Type = { .u: :Enum { #null, internstr(enumname), .id: --id }}; let n = flds.len; enumty.u.Enum.vals = (as(*EnumVal)malloc(n * sizeof EnumVal))[0::n]; if n < 256 { enumty.u.Enum.intty = ty_u8; } else if n < 65536 { enumty.u.Enum.intty = ty_u16; } else { assert(#f, "wow"); } enumty.size = enumty.u.Enum.intty.size; enumty.align = enumty.u.Enum.intty.align; vec_each(fld, i, flds) { let ev = &enumty.u.Enum.vals[i]; ev.name = fld.name; ev.i = i; } agg.enumty = interntype(&enumty); let off = ALIGNUP(enumty.size, ty.align); ty.size += off; align = MAX(align, enumty.align); ty.size = ALIGNUP(ty.size, align); *constty = *ty; constty.konst = #t; foreach_ptr(fld, _, agg.flds) { fld.off += off; } } llvm_addtype(ty); if havedecls { agg.decls = mkenv(P.curenv, P.alloc); constty.u.Agg.decls = agg.decls; pushenv(P, agg.decls); while !lexmatch(P, #null, '}') { parsedecls(P, tok.loc, #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 findbitffield(ty *const Type, name *const u8) *BitFField { foreach_ptr(fld, i, ty.u.BitF.flds) { if streq(fld.name, name) { return fld; } } return #null; } fn parsebitfield(P *Parser, name *const u8) *const Type { let ty Type = { .u: :BitF{} }; let bitf = &ty.u.BitF; let tok = lexexpect(P, ':'); bitf.name = name; bitf.intty = parsetype(P); if !bitf.intty->is(:Int) or bitf.intty.u.Int.sgn { fatal(P, tok.loc, "bitfield backing type must be unsigned integer (got %t)", bitf.intty); } ty.size = bitf.intty.size; ty.align = bitf.intty.align; lexexpect(P, '{'); static id int = {}; bitf.id = id++; let flds Vec = {}; let iota = 0u16; while !lexmatch(P, &tok, '}') { let name = (tok = lexexpects(P, :ident, "field name")).u.ident; let off = 0u, size = 0u; let ty = bitf.intty; if lexmatch(P, &tok, '(') { // `(offset, size)` let off_ex = parseexpr(P); lexexpect(P, ','); let size_ex = parseexpr(P); lexmatch(P, &tok, ','); lexexpect(P, ')'); if !fold(&off_ex) or off_ex.u.#tag != :IntLit or off_ex.u.IntLit.i < 0 { fatal(P, off_ex.loc, "expected non-negative constant integer for field offset"); } if !fold(&size_ex) or size_ex.u.#tag != :IntLit or size_ex.u.IntLit.i <= 0 { fatal(P, size_ex.loc, "expected positive constant integer for field size"); } off = off_ex.u.IntLit.i; size = size_ex.u.IntLit.i; } else { // `size` (offset is right after previous field) let size_ex = parseexpr(P); if !fold(&size_ex) or size_ex.u.#tag != :IntLit or size_ex.u.IntLit.i <= 0 { fatal(P, size_ex.loc, "expected positive constant integer for field size"); } off = iota; size = size_ex.u.IntLit.i; } if (tok = lexpeek(P)).t == :ident and streq(tok.u.ident, "bool") { lex(P); ty = ty_bool; } else if (tok = lexpeek(P)).t == :ident and streq(tok.u.ident, "signed") { lex(P); let signedty = *bitf.intty; signedty.u.Int.sgn = #t; ty = interntype(&signedty); } iota += size; if off + size > bitf.intty.size * 8 { err(P, tok.loc, "field `%s' outside %t bit range", name, bitf.intty); } flds->push({ name, ty, off, size }); if !lexmatch(P, #null, ',') { lexexpect(P, '}'); break; } } bitf.flds = flds->move(P.alloc); return interntype(&ty); } 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 parseexpandtepl(P *Parser, tepl *Tepl) *const Type { let nparam = tepl.params.#len; let tname = container_of(tepl, Decl, u.Tepl).name; let args *ExpanArg = xcalloc(nparam, sizeof ExpanArg); let ty *const Type = #null; let i = 0z; let tok Tok #?; lexexpect(P, '<'); let loc = container_of(tepl, Decl, u.Tepl).loc; while !lexmatch(P, &tok, '>') { let par = &tepl.params[i]; let toks [#]Tok = (as(*Tok)P.tlalloc->alloc(sizeof Tok, alignof Tok))[0::1]; switch par.u { case Ty; toks[0] = { :typearg, P.curloc, parsetype(P), .u: { .ident: par.name }}; case Val ty; let ex = pexbitarith(P); if !typematchestarg(ty, ex.ty) { fatal(P, ex.loc, "template value argument mismatch (%t, expected %t)", ex.ty, ty); } fold(&ex); switch ex.u { case IntLit i; toks[0] = { :int, ex.loc, ty, .u: { .int: i.i }}; case FloLit f; toks[0] = { :flo, ex.loc, ty, .u: { .flo: f }}; case BoolLit b; toks[0] = { :bool, ex.loc, ty, .u: { .bool: b }}; case NullLit; toks[0] = { :null, ex.loc, ty}; case else fatal(P, ex.loc, "expected constant expression"); } case else assert(#f, "unreachable %d", par.u.#tag); } args[i].name = par.name; args[i].toks = toks; ++i; if !lexmatch(P, &tok, ',') { lexexpect(P, '>'); break; } } if i != nparam { fatal(P, tok.loc, "invalid argument count for template `%s' (%d, expected %d)", tname, i, nparam); } let tpargs [#]TeplArg = (as(*TeplArg)xcalloc(nparam, sizeof TeplArg))[0::nparam]; foreach(par, i, tepl.params) { switch par.u { case Ty; tpargs[i] = :Ty(args[i].toks[0].ty); case Val; tpargs[i] = :Val(args[i].toks[0]); } } let cache *TeplCache #?; #'search for cache = tepl.cache; cache; cache = cache.next { for let i = 0z; i < nparam; ++i { let arg = &tpargs[i]; switch cache.args[i] { case Ty ty; if ty != arg.Ty { continue #'search; } case Val tok; let tok2 = arg.Val; switch tok.t { case :int; if tok.u.int != tok2.u.int { continue #'search; } case :flo; if tok.u.flo != tok2.u.flo { continue #'search; } case :bool; if tok.u.bool != tok2.u.bool { continue #'search; } case else assert(#f, "unreachable"); } } } free(args); free(tpargs.#ptr); return cache.ty; } let env = mkenv(tepl.env, P.tlalloc); defer envfree(env); with_tmpchange(P.curenv, env) { let decl *Decl #?; let expan Expan = { P.curexpan, args[0::nparam], tepl.toks, tname, tok.loc, #{tepl?} #t, }; ++P.expanno; P.curexpan = xcalloc(sizeof Expan,1); *P.curexpan = expan; ty = parseagg(P, loc, tepl.kind, tname, &decl); } P.peektok = :None; (as(*Type)ty).u.Agg.tpargs = tpargs; cache = P.tlalloc->alloc(sizeof(*cache), alignof(*cache)); cache.next = tepl.cache; cache.args = tpargs; cache.ty = ty; tepl.cache = cache; return ty; } 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); } switch decl.u { case Ty ty; return ty; case Tepl *tepl; return parseexpandtepl(P, tepl); case else fatal(P, eloc, "`%s' is not a type", name); } } fn parsefnparams(P *Parser, variadic *bool, paramnames *Vec<*const u8>, paramtys *Vec<*const Type>) void { lexexpect(P, '('); let tok Tok #?; *variadic = #f; 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(&Type{ .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.#tag == :EnumIni and ex.ty.u.Enum.lax)) 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, :typearg); return tok.ty; 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 lexmatch(P, &tok, :kw_struct); let decl *Decl; return parseagg(P, tok.loc, :Struct, #null, &decl); case lexmatch(P, &tok, :kw_union); let decl *Decl; return parseagg(P, tok.loc, :Union, #null, &decl); case lexmatch(P, &tok, :kw_enum); let decl *Decl; if lexmatch(P, #null, :kw_union) { return parseagg(P, tok.loc, :EUnion, #null, &decl); } else { return parseenum(P, #{name} #null, #{lax?} #f); } case lexmatch(P, &tok, :kw_bitfield); return parsebitfield(P, #{name} #null); case else; fatal(P, tok.loc, "expected type (near %qT)", &tok); } } ///////////////////////// // Expressions Parsing // ///////////////////////// fn exprdup(alloc *Allocator, ex Expr) *Expr { return memcpy(alloc->alloc(sizeof(ex), alignof(ex)), &ex, sizeof(ex)); } fn parseaggini(P *Parser, loc Loc, ty *const Type) Expr { let loc = loc; let tok Tok #?; let flds Vec<*const AggField> = {}; let exs Vec = {}; let iota = 0u64; 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, tok.loc, "excess elements in %t struct initializer", ty); } fld = &ty.u.Agg.flds[iota++]; } P.targty = fld.ty; ex = parseexpr(P); if !typematchestarg(fld.ty, ex.ty) { err(P, ex.loc, "incompatible element `%s' type in %t initializer (%t, expected %t)", fld.name, ty, ex.ty, fld.ty); } 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, loc Loc, ty *const Type) Expr { 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 parsebitfini(P *Parser, loc Loc, ty *const Type) Expr { let loc = loc; let tok Tok #?; let flds Vec<*const BitFField> = {}; let exs Vec = {}; let iota = 0u64; while !lexmatch(P, &tok, '}') { let fld *BitFField #?; let ex Expr #?; if lexmatch(P, &tok, '.') { fld = findbitffield(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.BitF.flds.#ptr) + 1; } else { if iota >= ty.u.BitF.flds.#len { fatal(P, tok.loc, "excess elements in %t struct initializer", ty); } fld = &ty.u.BitF.flds[iota++]; } P.targty = fld.ty; ex = parseexpr(P); if !typematchestarg(fld.ty, ex.ty) { err(P, ex.loc, "incompatible element `%s' type in %t initializer (%t, expected %t)", fld.name, ty, ex.ty, fld.ty); } flds->push(fld); exs->push(ex); if !lexmatch(P, &tok, ',') { lexexpects(P, '}', "`,' or `}'"); break; } } return { loc, ty, :BitFIni { flds->move(P.alloc), exs->move(P.alloc) }}; } 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, alignof 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, loc Loc, mac *Macro) void { let tok Tok #?; let expan Expan = {}; let args Vec<[#]Tok> = {}; let c MacroCase #?; 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 = xmalloc(sizeof Expan); *P.curexpan = expan; } fn parseblock0(P *Parser) Block; fn pexprimary(P *Parser) Expr { let tok Tok = lex(P); let ex Expr #?; let fakedecl Decl #?; fn resolvedecl(P *Parser, tok Tok, decl *Decl, ex *Expr) void { let fakedecl Decl #?; 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, tok.loc, mac); *ex = parseexpr(P); case Ty ty; if (tok = lexpeek(P)).t != ':' and tok.t != '{' { lexexpects(P, ':', "`:' 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) and !ty->is(:BitF) { fatal(P, tok.loc, "type is not aggregate or enum"); } else {; P.targty = ty; *ex = parseexpr(P); return; } case Tepl *tepl; fakedecl = { .u: :Ty(parseexpandtepl(P, tepl)) }; decl = &fakedecl; continue; } break; } } 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 str = tok.u.str; let s Vec = {}; if lexpeek(P).t == :str { foreach (c, _, tok.u.str) { s->push(c); } while lexmatch(P, &tok, :str) { foreach (c, _, tok.u.str) { s->push(c); } } s->push('\0'); --s.len; str = s->move(P.tlalloc); } let ty = mkarrtype(str.#len + 1, #t, ty_u8); ex = { tok.loc, ty, .u: :StrLit(str) }; case :ident; let ident = tok.u.ident; let decl = finddecl(P, ident); resolvedecl(P, tok, decl, &ex); case :typearg; fakedecl = { .u: :Ty(tok.ty) }; resolvedecl(P, tok, &fakedecl, &ex); 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 :kw_alignof; 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, "alignof incomplete type (%t)", ty); } ex = { tok.loc, ty_usize, :IntLit { ty.align }}; case :kw_offsetof; lexexpect(P, '('); let ty = parsetype(P); lexexpect(P, ','); let off = 0z; for ;; { let name = (tok = lexexpect(P, :ident)).u.ident; let fld *AggField = ty->is(:Agg) ? findaggfield(ty, name) : #null; if fld == #null { fatal(P, tok.loc, "%t has no such field %qT", ty, &tok); } off += fld.off; ty = fld.ty; if !lexmatch(P, #null, '.') { lexmatch(P, #null, ','); lexexpect(P, ')'); break; } } ex = { tok.loc, ty_usize, :IntLit { off }}; case '('; if lexmatch(P, &tok, :kw_do) { let block = parseblock0(P); let st = block.sts; lexexpect(P, ')'); if st.#len == 0 or st[st.#len - 1].u.#tag != :Expr { ex = { tok.loc, ty_void, :Stmt(block) }; } else { ex = { tok.loc, st[st.#len - 1].u.Expr.ty, :Stmt(block) }; } } else { ex = parseexpr(P); lexexpect(P, ')'); } case ':'; if P.targty == #null { fatal(P, tok.loc, "cannot infer type for variant"); } let ty = P.targty; P.targty = #null; 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(i) }; } else if ty->is(:Agg) and ty.u.Agg.kind == :EUnion { let name = (tok = lexexpects(P, :ident, "variant name")).u.ident; let fld = findaggfield(ty, name); if fld == #null { fatal(P, tok.loc, "%t has no such variant %qT", ty, &tok); } let iex *Expr = #null; if lexpeek(P).t == '(' or lexpeek(P).t == '{' { P.targty = fld.ty; iex = exprdup(P.alloc, parseexpr(P)); if fld.ty == #null { fatal(P, iex.loc, "%t variant %qT is empty", ty, &tok); } if !typematchestarg(fld.ty, iex.ty) { fatal(P, iex.loc, "bad enum union initializer (%t, expected %t)", iex.ty, fld.ty); } } else if fld.ty { fatal(P, tok.loc, "%t variant %qT must be initialized", ty, &tok); } ex = { tok.loc, ty, :EUnionIni { fld, iex }}; } 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, tok.loc, ty); } else if ty->is(:Arr) { ex = parsearrini(P, tok.loc, ty); } else if ty->is(:BitF) { ex = parsebitfini(P, tok.loc, 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, ')') { P.targty = args.len + 1 <= params.#len ? *param : #null; let ex = parseexpr(P); 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 (ex.ty->is(:Agg) or ex.ty->is(:Slice) or ex.ty->is(:VaList)) { err(P, ex.loc, "cannot pass aggregate value as variadic argument"); } args->push(ex); 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) and !(rhs.ty->is(:Enum) and rhs.ty.u.Enum.lax){ 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, ']'); let ty = childtype(lhs.ty); if lhs.ty->is(:Arr) and lhs.ty.konst { ty = constify(ty); } ex = { tok.loc, 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; } let konst = ty.konst; switch ty.u { case Agg *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 BitF *bitf; let fld *BitFField = #null; foreach(f, i, bitf.flds) { if streq(name, f.name) { fld = &bitf.flds[i]; break; } } if fld == #null { fatal(P, tok.loc, "%t has no such field %qT", ty, &tok); } ex = { tok.loc, konst ? constify(fld.ty) : fld.ty, :BitDot { exprdup(P.alloc, ex), fld }}; case else fatal(P, tok.loc, "left-hand-side is not an aggregate (%t)", ty); } case lexmatch(P, &tok, '#ptr'); let ty = ex.ty; switch ty.u { case Slice sl; ex = { tok.loc, mkptrtype(sl), :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; 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'); let ty = ex.ty; if ty->is(:Ptr) { ty = ty.u.Ptr; } if !ty->is(:Agg) or ty.u.Agg.kind != :EUnion { fatal(P, ex.loc, "invalid operand to #tag (%t)", ex.ty); } let enumty = ty.u.Agg.enumty; ex = { tok.loc, ty.konst ? constify(enumty) : enumty, :EUTag(exprdup(P.alloc, ex)) }; case lexmatch(P, &tok, '#raw'); let ty = ex.ty; if ty->is(:Ptr) { ty = ty.u.Ptr; } if !ty->is(:BitF) { fatal(P, ex.loc, "invalid operand to #raw (%t)", ex.ty); } let intty = ty.u.BitF.intty; ex = { tok.loc, ty.konst ? constify(intty) : intty, :BitRaw(exprdup(P.alloc, ex)) }; case lexpeek(P).t == '['; // sugar: expr.[idx] -> (*expr)[idx] if !ex.ty->is(:Ptr) { fatal(P, ex.loc, "`.[]' operator takes a pointer (got %t)", ex.ty); } ex = { ex.loc, ex.ty.u.Ptr, .u: :UnOp { :deref, exprdup(P.alloc, ex) }}; case else; lexexpects(P, :ident, "field name"); } case lexmatch(P, &tok, '->'); let ty = ex.ty; let exptr = #f; if (exptr = ty->is(:Ptr)) { ty = ty.u.Ptr; } let name = (tok = lexexpects(P, :ident, "method name")).u.ident; switch ty.u { case Agg *agg; let decl *Decl = agg.decls ? envfind_noparent(agg.decls, name) : #null; 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 = {}; let recv0 = Fn.params[0], recv = recv0; let metptr = #f; if (metptr = recv->is(:Ptr)) { recv = recv.u.Ptr; } if unconstify(ty) != unconstify(recv) { err(P, tok.loc, "method receiver type mismatch for `->%s' (%t, expected %t)", name, ty, recv0); } switch { case !exptr and !metptr; case exptr and !metptr; ex = { ex.loc, ty, :UnOp{:deref, exprdup(P.alloc, ex)}}; case !exptr and metptr; if !islvalue(&ex) { err(P, tok.loc, "cannot call `->%s' by reference, lhs is not an lvalue", name); } else if ty.konst and !recv.konst { err(P, tok.loc, "constness mismatch: method takes %t but got %t", recv0, ex.ty); } ex = { ex.loc, mkptrtype(ty), :UnOp{:addrof, exprdup(P.alloc, ex)}}; case exptr and metptr; if ty.konst and !recv.konst { err(P, tok.loc, "constness mismatch: method takes %t but got %t", recv0, ex.ty); } } args->push(ex); let param = &Fn.params[1]; while !lexmatch(P, #null, ')') { P.targty = *param++; 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 VaList; if !islvalue(&ex) { err(P, ex.loc, "variadic list receiver must be lvalue"); } if exptr { ex = { ex.loc, ty, :UnOp{:deref, exprdup(P.alloc, ex)}}; } switch { case streq(name, "start"); lexexpect(P, '('); if !lexmatch(P, #null, ')') { let _ = parseexpr(P); lexmatch(P, #null, ','); warn(P, ex.loc, "variadic list start doesn't need an argument"); lexexpect(P, ')'); } ex = { tok.loc, ty_void, :VaStart(exprdup(P.alloc, ex)) }; case streq(name, "arg"); lexexpect(P, '('); let type = parsetype(P); lexmatch(P, #null, ','); lexexpect(P, ')'); if type->is(:Agg) { err(P, tok.loc, "unsupported use of variadic arg of aggregate type"); } ex = { tok.loc, type, :VaArg(exprdup(P.alloc, ex)) }; case streq(name, "end"); lexexpect(P, '('); lexmatch(P, #null, ','); lexexpect(P, ')'); ex = { tok.loc, ty_void, :VaEnd(exprdup(P.alloc, ex)) }; case else fatal(P, tok.loc, "`%s' is not a valid method for variadic list", name); } case else fatal(P, tok.loc, "left-hand-side is not an aggregate (%t)", ty); } 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(&Type{ 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"); } if ex.u.#tag == :BitDot { err(P, ex.loc, "cannot take address of bitfield value"); } return { tok.loc, ty2, .u: :UnOp { :addrof, exprdup(P.alloc, ex) }}; case lexmatch(P, &tok, :kw_as); let loc = tok.loc; lexexpect(P, '('); let to = parsetype(P); lexexpect(P, ')'); let ex = pexprefix(P); let from = ex.ty; switch { case to->is(:Arr) and to.u.Arr.length < 0 and ex.u.#tag == :ArrIni; ex.ty = (to = mkarrtype(ex.u.ArrIni.maxn + 1, #f, to.u.Arr.child)); case typeof2(to, from); case to->is(:Int) and from->is(:Ptr) and to.size == from.size; case to->is(:Ptr) and from->is(:Int) and to.size == from.size; case to->is(:Ptr) and from->is(:Ptr); case to->is(:Flo) and from->is(:Int); case to->is(:Int) and from->is(:Flo); case to->is(:Int) and from->is(:Bool); case to->is(:Bool) and from->is(:Int); case to->is(:Int) and from->is(:Enum); case to->is(:Enum) and from->is(:Int); case else err(P, loc, "invalid cast from %t to %t", from, to); } return { loc, to, :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) { if tok.t == '==' or tok.t == '!=' { P.targty = ex.ty; } let rhs = pexbitarith(P); P.targty = #null; 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); } fn ispositiveintlit(ex Expr) bool { return ex.u.#tag == :IntLit and ex.u.IntLit.i >= 0; } if ty->is(:Int) and ty != ty_int and ex.ty.u.Int.sgn != rhs.ty.u.Int.sgn and !(ispositiveintlit(ex) or ispositiveintlit(rhs)) { 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 targty = P.targty; let ex = pexlog(P); if (lexmatch(P, &tok, '?')) { P.targty = targty; 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, ':'); P.targty = targty; 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; } if tok.t == '=' { P.targty = ex.ty; } 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, tok.loc, "invalid operands %t and %t for assignment operator %qT", ex.ty, rhs.ty, &tok); case okind == :IntFlo and (!isnumtype(ex.ty) or !isnumtype(rhs.ty)); err(P, tok.loc, "invalid operands %t and %t for assignment operator %qT", ex.ty, rhs.ty, &tok); case okind == :Int and (!ex.ty->is(:Int) or !rhs.ty->is(:Int)); err(P, tok.loc, "invalid operands %t and %t for assignment operator %qT", 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), alignof(st)), &st, sizeof(st)); } typedef StmtYielder *fn(Stmt, *void) void; fn parseblock(P *Parser) Block; 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, loc Loc) Stmt { 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); let tok Tok #?; if lexmatch(P, &tok, :kw_else) { if lexmatch(P, &tok, :kw_if) { let f = stmtdup(P.alloc, pstif(P, tok.loc)); 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, loc Loc, deferage int) Stmt { 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{ :None, deferage} }; } else { P.targty = retty; 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), deferage} }; } } fn pstwhile(P *Parser, loc Loc, label *const u8) Stmt { 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 body Block #?; let loopid = ++P.loopid; with_tmpchange(P.curloop, loopid) { let env = mkenv(P.curenv, P.alloc); defer envfree(env); pushenv(P, env); if label { putdecl(P, loc, { label, loc, .u: :Label(loopid) }); } body = parseblock(P); popenv(P); } return { loc, :While { test, body, loopid }}; } fn pstdowhile(P *Parser, loc Loc, label *const u8) Stmt { lexexpect(P, '{'); let body Block #?; let loopid = ++P.loopid; with_tmpchange(P.curloop, loopid) { let env = mkenv(P.curenv, P.alloc); defer envfree(env); pushenv(P, env); if label { putdecl(P, loc, { label, loc, .u: :Label(loopid) }); } body = parseblock(P); popenv(P); } lexexpect(P, :kw_while); let test = parseexpr(P); if !test.ty->is(:Bool) and !test.ty->is(:Ptr) { err(P, test.loc, "do-while condition must be bool or pointer (%t)", test.ty); } return { loc, :DoWhile { test, body, loopid }}; } fn pstfor(P *Parser, loc Loc, label *const u8) Stmt { let ini Block = {}; let test Expr #?; let next = Option:None; let tok Tok #?; 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] }; lexexpect(P, ';'); } if lexmatch(P, &tok, ';') { // for (...; ; ...;) -> for (...; #t; ...;) test = { tok.loc, 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 Block #?; let loopid = ++P.loopid; with_tmpchange(P.curloop, loopid) { let env = mkenv(P.curenv, P.alloc); defer envfree(env); pushenv(P, env); if label { putdecl(P, loc, { label, loc, .u: :Label(loopid) }); } body = parseblock(P); popenv(P); } return { loc, :For { ini, test, next, body, loopid }}; } fn pstiswitch(P *Parser, loc Loc, ex Expr) Stmt { let tok Tok #?; let cs Vec = {}; let f Block = {}; let elsep = #f; let seen Map = {}; defer seen->clear(); 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 { P.targty = ex.ty; 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); } if seen->get(e.u.IntLit.i) { err(P, e.loc, "duplicate case expression (previously seen at %l)", seen->get(e.u.IntLit.i)); } seen->put(e.u.IntLit.i, e.loc); 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 psteuswitch(P *Parser, loc Loc, test Expr) Stmt { let cs Vec = {}; let tok Tok #?; let f Block = {}; let elsep = #f; let seen Map = {}; defer seen->clear(); while !lexmatch(P, &tok, '}') { let c EUSwitchCase = {}; lexexpect(P, :kw_case); if lexmatch(P, &tok, :kw_else) { if elsep { err(P, tok.loc, "duplicate 'case else' block"); } elsep = #t; f = parseblock0(P); continue; } let vname = (tok = lexexpect(P, :ident)).u.ident; c.fld = findaggfield(test.ty, vname); if c.fld == #null { fatal(P, tok.loc, "%t has no such variant %qT", test.ty, &tok); } c.variant = c.fld - test.ty.u.Agg.flds.#ptr; if seen->get(c.variant) { err(P, tok.loc, "duplicate case expression (previously seen at %l)", seen->get(c.variant)); } seen->put(c.variant, tok.loc); if c.fld.ty != #null and lexmatch(P, &tok, '*') { if !islvalue(&test) { fatal(P, tok.loc, "cannot capture by pointer, test expression is not lvalue"); } c.captptr = #t; if lexpeek(P).t != :ident { lexexpect(P, :ident); } } let env = mkenv(P.curenv, P.alloc); defer envfree(env); if c.fld.ty != #null and lexmatch(P, &tok, :ident) { let ty = c.fld.ty; c.capt = tok.u.ident; if c.captptr { ty = mkptrtype(test.ty.konst ? constify(ty) : ty); } pushenv(P, env); c.captid = P.varid++; putdecl(P, tok.loc, { c.capt, tok.loc, .u: :Let { c.captty = ty, :None, #f, P.curfn.id, c.captid } }); } lexexpect(P, ';'); c.t = parseblock0(P); if c.capt { popenv(P); } cs->push(c); } return { loc, :EUSwitch { islvalue(&test), test, cs->move(P.alloc), f }}; } fn pstcswitch(P *Parser, loc Loc) Stmt { let cs Vec = {}; let tok Tok #?; let f Block = {}; let elsep = #f; while !lexmatch(P, &tok, '}') { let c CSwitchCase = {}; lexexpect(P, :kw_case); if lexmatch(P, &tok, :kw_else) { if elsep { err(P, tok.loc, "duplicate 'case else' block"); } elsep = #t; f = parseblock0(P); continue; } c.test = parseexpr(P); lexexpect(P, ';'); c.t = parseblock0(P); cs->push(c); } return { loc, :CSwitch { cs->move(P.alloc), f }}; } fn pstswitch(P *Parser, loc Loc) Stmt { let tok Tok #?; if lexmatch(P, &tok, '{') { return pstcswitch(P, loc); } 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 { return psteuswitch(P, loc, ex); } else { fatal(P, tok.loc, "invalid switch test expression type (%t)", ex.ty); } } } fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void { static deferage int = {}; 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, tok.loc), yarg); case lexmatch(P, &tok, :kw_return); return yield(pstreturn(P, tok.loc, deferage), yarg); case lexmatch(P, &tok, :label); let label = tok.u.ident; switch { case lexmatch(P, &tok, :kw_while); return yield(pstwhile(P, tok.loc, label), yarg); case lexmatch(P, &tok, :kw_for); return yield(pstfor(P, tok.loc, label), yarg); case lexmatch(P, &tok, :kw_do); return yield(pstdowhile(P, tok.loc, label), yarg); case else fatal(P, tok.loc, "label can only precede looping statement"); } case lexmatch(P, &tok, :kw_while); return yield(pstwhile(P, tok.loc, #null), yarg); case lexmatch(P, &tok, :kw_for); return yield(pstfor(P, tok.loc, #null), yarg); case lexmatch(P, &tok, :kw_do); return yield(pstdowhile(P, tok.loc, #null), yarg); case lexmatch(P, &tok, :kw_break) or lexmatch(P, &tok, :kw_continue); if P.curloop == 0 { err(P, tok.loc, "%s outside loop", tok.t == :kw_break ? "break" : "continue"); } let tok2 Tok #?; let loop = P.curloop; if lexmatch(P, &tok2, :label) { let decl = finddecl(P, tok2.u.ident); if decl == #null { fatal(P, tok2.loc, "no such label %qT", &tok2); } assert(decl.u.#tag == :Label, "label?"); loop = decl.u.Label; } lexexpect(P, ';'); return yield({ tok.loc, tok.t == :kw_break ? :Break(loop) : :Continue(loop) }, yarg); case lexmatch(P, &tok, :kw_defer); if P.curblock == #null { fatal(P, tok.loc, "defer outside function"); } let ex = parseexpr(P); lexexpect(P, ';'); let defr *Defers = anew(P.alloc, Defers); defr.age = deferage++; defr.ex = ex; defr.blockid = P.curblock.id; defr.next = P.curblock.defers; P.curblock.defers = defr; return; case lexmatch(P, &tok, :kw_switch); return yield(pstswitch(P, tok.loc), 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, tok.loc, &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, tok.loc, &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) Block { static id int = 1; let block Block = { .id: id++, .defers: P.curblock ? P.curblock.defers : #null }; let sts Vec = {}; let tok Tok = {}; let env = mkenv(P.curenv, P.alloc); defer envfree(env); pushenv(P, env); with_tmpchange(P.curblock, &block) { 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); block.sts = sts->move(P.alloc); return block; } fn parseblock(P *Parser) Block { 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, toplevel, :Let { ty, ini, fwd, P.curfn ? P.curfn.id : 0, } }; if letp { decl.u.Let.id = P.varid++; } else { static id int = 0; decl.u.Static.id = id++; decl.u.#tag = :Static; } let decll *Decl #?; yield(decll = putdecl(P, tok.loc, decl), yarg); if !letp and !fwd and !P.error { llvm_genstatic(decl.externp, decl.name, &decl.u.Static); } if !lexmatch(P, &tok, ',') { lexexpects(P, ';', "`,' or `;'"); break; } } while !lexmatch(P, &tok, ';'); } extern fn BREAK() void {} fn parsefn(P *Parser, loc Loc, toplevel bool, externp bool, name *const u8) *Decl { let decl Decl = { name, loc, externp, .u: :Fn {} }; let Fn = &decl.u.Fn, paramnames Vec<*const u8> = {}, paramtys Vec<*const Type> = {}; parsefnparams(P, &Fn.variadic, ¶mnames, ¶mtys); Fn.paramnames = paramnames->move(P.alloc); let retty = parsetype(P); Fn.ty = interntype(&Type{ .size: 0, .align: 1, .u: :Fn { paramtys->move(P.alloc), Fn.variadic, retty }}); static id int = 0; Fn.id = ++id; // TODO tlalloc necessary for templates. but it leaks some memory let decl = putdecl_alloc(P, P.tlalloc, loc, decl); let Fn = &decl.u.Fn; decl.toplevel = toplevel; if !lexmatch(P, #null, '{') { lexexpects(P, ';', "';' or '{'"); if toplevel or externp { llvm_addglobl(decl, #f); } return decl; } let varid = P.varid, loopid = P.loopid; P.varid = 0; P.loopid = 0; with_tmpchange(P.curfn, Fn) { let env = mkenv(P.curenv, P.alloc); defer envfree(env); 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, P.varid++ }}); } } with_tmpchange(P.alloc, &Allocator { &Arena {}, &Arena:allocf }) { let block = P.curblock; P.curblock = #null; Fn.body = :Some(parseblock(P)); P.curblock = block; popenv(P); // ir_genfn(P.irctx, Fn); if !P.error { llvm_genfn(externp, name, Fn); } (as(*Arena)P.alloc.a)->destroy(); } } if toplevel or externp { llvm_addglobl(decl, #f); } P.varid = varid; P.loopid = loopid; return decl; } fn parse4import(P *Parser) [#]*Decl; fn doimport(P *Parser, loc Loc, 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, loc, "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 e = seen->put(rpath, { {}, #t }); let P2 Parser #?; parser_init(&P2, rpath); P2.is_header = #t; P2.tlalloc = P.tlalloc; P2.alloc = P.alloc; let decls = parse4import(&P2); *e = { decls, #f }; return decls; } fn parsemacro(P *Parser, loc Loc, 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, ".gensym.%s.%d", 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 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 parsetepl(P *Parser, loc Loc, kind AggKind, name *const u8) Tepl { if envparent(P.curenv) { fatal(P, loc, "templates can only defined be at top-level"); } let params Vec = {}; let toks Vec = {}; let tok Tok #?; while (!lexmatch(P, &tok, '>')) { let p TeplParam #?; let name = lexexpects(P, :ident, "parameter name").u.ident; p.name = name; if (tok = lexpeek(P)).t == '>' or tok.t == ',' { p.u = :Ty; } else { p.u = :Val(parsetype(P)); } params->push(p); if !lexmatch(P, #null, ',') { lexexpect(P, '>'); break; } } tok = lexexpect(P, '{'); toks->push({'{'}); let bal = 1; while bal != 0 { tok = lex(P); switch tok.t { case '{'; ++bal; case '}'; --bal; case :eof; fatal(P, tok.loc, "unterminated template definiton"); case :ident; vec_each(par, _, params) { if streq(par.name, tok.u.ident) { tok.t = :macident; } } } toks->push(tok); } return { kind, P.curenv, params->move(P.alloc), toks->move(P.alloc), #null }; } fn parsedecls(P *Parser, loc Loc, yield DeclYielder, yarg *void, toplevel bool) void { let tok = Tok { .loc: loc }, decl *Decl = {}, externp = #f, name *const u8 = #null; if lexmatch(P, &tok, ';') or lexmatch(P, &tok, :eof) { // no-op return; } let attr u32 = 0; if lexmatch(P, &tok, '#') { lexexpect(P, '['); while !lexmatch(P, #null, ']') { let a = (tok = lexexpect(P, :ident)).u.ident; if streq(a, "lax") { attr |= ATTR_LAX; } else { err(P, tok.loc, "unknown attribute %qT", &tok); } } } 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, tok.loc, toplevel, externp, name); case lexmatch(P, &tok, :kw_import); let path = (tok = lexexpects(P, :str, "import path")).u.str.#ptr; let decls = doimport(P, tok.loc, path); foreach(decl, _, decls) { let decl = putdecl(P, decl.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; if !lexmatch(P, #null, '<') { parseagg(P, tok.loc, :EUnion, name, &decl); } else { decl = putdecl(P, tok.loc, { name, tok.loc, .u: :Tepl(parsetepl(P, tok.loc, :EUnion, name)) }); } } else { let name = lexexpect(P, :ident).u.ident; let ty = parseenum(P, name, (attr & ATTR_LAX) != 0); decl = putdecl(P, loc, { name, loc, .u: :Ty(ty) }); } case lexmatch(P, &tok, :kw_struct); let name = lexexpect(P, :ident).u.ident; if !lexmatch(P, #null, '<') { parseagg(P, tok.loc, :Struct, name, &decl); } else { decl = putdecl(P, tok.loc, { name, tok.loc, .u: :Tepl(parsetepl(P, tok.loc, :Struct, name)) }); } case lexmatch(P, &tok, :kw_union); let name = lexexpect(P, :ident).u.ident; if !lexmatch(P, #null, '<') { parseagg(P, tok.loc, :Union, name, &decl); } else { decl = putdecl(P, tok.loc, { name, tok.loc, .u: :Tepl(parsetepl(P, tok.loc, :Union, name)) }); } case lexmatch(P, &tok, :kw_bitfield); let name = lexexpect(P, :ident).u.ident; let ty = parsebitfield(P, name); decl = putdecl(P, loc, { name, loc, .u: :Ty(ty) }); 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); } // ir_genstatic(a.P.irctx, decl); if a.toplevel or a.externp { llvm_addglobl(decl, #t); } } parsevardecl(P, toplevel, externp, #{let?} #f, &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); } if ty != ini.ty { ini = { ini.loc, ty, :Cast(exprdup(P.alloc, ini)) }; } if !fold(&ini) { err(P, ini.loc, "cannot evaluate expression at compile time"); } decl = putdecl(P, tok.loc, { name, tok.loc, .u: :Def(ini) }); if yield { yield(decl, 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; if lexmatch(P, &tok, '=') { // defmacro x = expr let ini = parseexpr(P); decl = putdecl(P, tok.loc, { name, tok.loc, .u: :Def(ini) }); lexexpect(P, ';'); } else { decl = parsemacro(P, tok.loc, name); } case lexmatch(P, &tok, :kw_typedef); let name = lexexpects(P, :ident, "typedef name").u.ident; decl = putdecl(P, tok.loc, { name, tok.loc, .u: :Ty(parsetype(P)) }); lexexpect(P, ';'); 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, tok.loc, &decl.u.Macro); return parsedecls(P, tok.loc, yield, yarg, toplevel); } } fatal(P, tok.loc, "expected declaration (near %qT)", &tok); } if yield != #null and decl != #null { yield(decl, yarg); } } fn parse4import(P *Parser) [#]*Decl { let decls Vec<*Decl> = {}; P.curenv = mkenv(#null, P.alloc); while !P.eof { fn yield(decl *Decl, arg *void) void { let decls *Vec<*Decl> = arg; decls->push(decl); } parsedecls(P, P.tokloc, &yield, &decls, #{toplevel} #t); if lexmatch(P, #null, :eof) { break; } } return decls.dat[0::decls.len]; } extern fn parse(P *Parser) [#]Decl { let aralloc = Arena {}; let alloc = Allocator { &aralloc, &Arena:allocf, #null }; let decls Vec = {}; P.alloc = (P.tlalloc = &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, P.tokloc, &yield, &decls, #{toplevel} #t); if lexmatch(P, #null, :eof) { break; } } if !P.error { llvm_fini(); } 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; }