aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2022-08-19 12:52:29 +0200
committerlemon <lsof@mailbox.org>2022-08-19 12:52:29 +0200
commit5f1827beb3199a9f4cec44de5001d599be55cfc8 (patch)
treec42ddaf91c2ebef9efdb81ac07818b42a5377449
parentb1934f206b3590cf6c7436f50e2bf6dba653c04c (diff)
EUSwitch and #tag
-rw-r--r--bootstrap/test2.cff2
-rw-r--r--src/cffc.hff13
-rw-r--r--src/parse.cff66
3 files changed, 75 insertions, 6 deletions
diff --git a/bootstrap/test2.cff b/bootstrap/test2.cff
index c120d05..c4aedd9 100644
--- a/bootstrap/test2.cff
+++ b/bootstrap/test2.cff
@@ -45,7 +45,6 @@ extern fn main() void {
x = X + 1 + Y;
let const v= Value:None;
-#{
switch v {
case None;
case Int i;
@@ -80,4 +79,3 @@ extern fn main() void {
Bit<i32>:foo(3);
Bit<i64>:foo(3);
}
-}
diff --git a/src/cffc.hff b/src/cffc.hff
index c8c90ac..131b3d9 100644
--- a/src/cffc.hff
+++ b/src/cffc.hff
@@ -184,6 +184,16 @@ struct ISwitchCase {
t [#]Stmt
}
+struct EUSwitchCase {
+ capt *const u8,
+ captptr bool,
+ captid int,
+ variant int,
+ captty *const Type,
+ fld *AggField,
+ t [#]Stmt
+}
+
struct Stmt {
loc Loc,
u enum union {
@@ -202,7 +212,8 @@ struct Stmt {
Return Option<Expr>,
Expr Expr,
Decl *Decl,
- ISwitch struct { ex Expr, cs [#]ISwitchCase, f [#]Stmt }
+ ISwitch struct { ex Expr, cs [#]ISwitchCase, f [#]Stmt },
+ EUSwitch struct { ex Expr, cs [#]EUSwitchCase, f [#]Stmt },
}
}
diff --git a/src/parse.cff b/src/parse.cff
index e237a90..4f78b21 100644
--- a/src/parse.cff
+++ b/src/parse.cff
@@ -1548,7 +1548,15 @@ fn pexpostfix(P *Parser) Expr {
case else;
fatal(P, ex.loc, "invalid operand to `#len' (%t)", ex.ty);
}
- // case lexmatch(P, &tok, '#tag');
+ 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);
+ }
+ ex = { tok.loc, ty.u.Agg.enumty, :EUTag(exprdup(P.alloc, ex)) };
case else;
lexexpects(P, :ident, "field name");
@@ -2027,8 +2035,60 @@ fn pstiswitch(P *Parser, loc Loc, ex Expr) Stmt {
return { loc, :ISwitch { ex, cs->move(P.alloc), f }};
}
-fn psteuswitch(P *Parser, loc Loc, ex Expr) Stmt {
- let ty = ex.ty;
+fn psteuswitch(P *Parser, loc Loc, test Expr) Stmt {
+ let cs Vec<EUSwitchCase> = {};
+ let tok Tok #?;
+ let f [#]Stmt = {};
+ let elsep = #f;
+
+ 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 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);
+ 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);
+ putdecl(P, tok.loc, {
+ c.capt, tok.loc, .u: :Let { c.captty = ty, :None, #f, P.curfn.id }
+ });
+ }
+ lexexpect(P, ';');
+ c.t = parseblock0(P);
+ if c.capt {
+ popenv(P);
+ }
+ envfree(env);
+ }
+
+
+ return { loc, :EUSwitch { test, cs->move(P.alloc), f }};
}
fn pstswitch(P *Parser, loc Loc) Stmt {