diff options
Diffstat (limited to 'src/parse.cff')
| -rw-r--r-- | src/parse.cff | 172 |
1 files changed, 143 insertions, 29 deletions
diff --git a/src/parse.cff b/src/parse.cff index d4b8b7a..364b23b 100644 --- a/src/parse.cff +++ b/src/parse.cff @@ -141,8 +141,8 @@ fn eatspaces(P *Parser) void { // !sorted extern static keyword2str [NUM_KEYWORDS]*const u8 = { - "alignof", "and", "as", "break", "case", "const", - "continue", "def", "defer", "defmacro", "do", + "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", @@ -400,6 +400,8 @@ fn lex(P *Parser) Tok { 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"); @@ -693,7 +695,7 @@ fn parseenum(P *Parser, name *const u8, lax bool) *const Type { 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_extern, :kw_bitfield; return #t; } return #f; @@ -824,6 +826,79 @@ fn findaggfield(ty *const Type, name *const u8) *AggField { 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<BitFField> = {}; + let iota = 0u16; + while !lexmatch(P, &tok, '}') { + let name = (tok = lexexpects(P, :ident, "field name")).u.ident; + let off u16 = 0, size u16 = 0; + 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; @@ -1077,9 +1152,12 @@ fn parsetype(P *Parser) *const Type { if lexmatch(P, #null, :kw_union) { return parseagg(P, tok.loc, :EUnion, #null, &decl); } else { - return parseenum(P, #null, #{lax?} #f); + 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); } @@ -1093,7 +1171,7 @@ fn exprdup(alloc *Allocator, ex Expr) *Expr { return memcpy(alloc->alloc(sizeof(ex), alignof(ex)), &ex, sizeof(ex)); } -fn islvalue(ex Expr) bool { +fn islvalue(ex *Expr) bool { switch ex.u { case Symbol decl; return decl.u.#tag == :Let or decl.u.#tag == :Static; @@ -1103,6 +1181,10 @@ fn islvalue(ex Expr) bool { return #t; case Dot; return #t; + case BitDot bdot; + return islvalue(bdot.lhs); + case BitRaw ex; + return islvalue(ex); case EUTag; return #t; } @@ -1588,12 +1670,16 @@ fn pexpostfix(P *Parser) Expr { 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) { + 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); } + if ex.u.#tag == :BitDot { + err(P, ex.loc, "cannot apply post-%srement operator to bitfield value", + tok.t == '++' ? "inc" : "dec"); + } ex = { tok.loc, ex.ty, .u: :UnOp { tok.t == '++' ? :postinc : :postdec, exprdup(P.alloc, ex) @@ -1607,26 +1693,32 @@ fn pexpostfix(P *Parser) Expr { 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; + 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); } - 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; @@ -1658,6 +1750,17 @@ fn pexpostfix(P *Parser) Expr { 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) { @@ -1709,7 +1812,7 @@ fn pexpostfix(P *Parser) Expr { ex = { ex.loc, ty, :UnOp{:deref, exprdup(P.alloc, ex)}}; case !exptr and metptr; - if !islvalue(ex) { + 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); @@ -1773,12 +1876,16 @@ fn pexprefix(P *Parser) Expr { 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) { + 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); } + if ex.u.#tag == :BitDot { + err(P, ex.loc, "cannot apply pre-%srement operator to bitfield value", + tok.t == '++' ? "inc" : "dec"); + } return { tok.loc, ex.ty, .u: :UnOp { tok.t == '++' ? :preinc : :predec, exprdup(P.alloc, ex) @@ -1805,7 +1912,7 @@ fn pexprefix(P *Parser) Expr { 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) + 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"); } @@ -2027,7 +2134,7 @@ fn pexassign(P *Parser) Expr { } let rhs = pexcond(P); switch { - case !islvalue(ex); + 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) @@ -2035,6 +2142,8 @@ fn pexassign(P *Parser) Expr { err(P, ex.loc, "operands %t and %t to assignment operator %qT have incompatible types", ex.ty, rhs.ty, tok); + case okind != :Set and ex.u.#tag == :BitDot; + err(P, ex.loc, "cannot apply compound assignment operator to bitfield value"); case ex.ty.konst; err(P, ex.loc, "left operand to assignment is const"); } @@ -2289,7 +2398,7 @@ fn psteuswitch(P *Parser, loc Loc, test Expr) Stmt { seen->put(c.variant, tok.loc); if c.fld.ty != #null and lexmatch(P, &tok, '*') { - if !islvalue(test) { + if !islvalue(&test) { fatal(P, tok.loc, "cannot capture by pointer, test expression is not lvalue"); } c.captptr = #t; @@ -2320,7 +2429,7 @@ fn psteuswitch(P *Parser, loc Loc, test Expr) Stmt { } - return { loc, :EUSwitch { islvalue(test), test, cs->move(P.alloc), f }}; + return { loc, :EUSwitch { islvalue(&test), test, cs->move(P.alloc), f }}; } fn pstcswitch(P *Parser, loc Loc) Stmt { @@ -2914,6 +3023,11 @@ fn parsedecls(P *Parser, loc Loc, yield DeclYielder, yarg *void, toplevel bool) { 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, |