aboutsummaryrefslogtreecommitdiff
path: root/src/parse.cff
diff options
context:
space:
mode:
Diffstat (limited to 'src/parse.cff')
-rw-r--r--src/parse.cff172
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,