diff options
Diffstat (limited to 'src/main/java/com/seaofnodes/simple/parser/ControlFlow.java')
| -rw-r--r-- | src/main/java/com/seaofnodes/simple/parser/ControlFlow.java | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/src/main/java/com/seaofnodes/simple/parser/ControlFlow.java b/src/main/java/com/seaofnodes/simple/parser/ControlFlow.java new file mode 100644 index 00000000..caa73076 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/parser/ControlFlow.java @@ -0,0 +1,96 @@ +package com.seaofnodes.simple.parser; + +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.parser.Parser.ParseException; +import com.seaofnodes.simple.type.*; + +import static com.seaofnodes.simple.util.Utils.TODO; + +/** Shared guarded branch/merge construction for if, ?:, && and ||. */ +final class ControlFlow { + private final Parser P; + + ControlFlow(Parser P) { this.P = P; } + + // Parse a conditional expression, merging results. + Node conditional( Node pred, String fside ) { + // IfNode takes current control and predicate + pred.keep(); + Node ifNode = new IfNode(P.ctrl(), pred).peephole(); + // Setup projection nodes + CFGNode ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole().keep(); + CFGNode ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole().keep(); + // In if true branch, the ifT proj node becomes the ctrl + // But first clone the scope and set it as current + ScopeNode fScope = P._scope.dup(); // Duplicate current scope + fScope.ctrl(ifF); // The pending arm already starts at False + P._xScopes.push(fScope); // For graph visualization we need all scopes + + // Parse the true side + P.ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection + P._scope.addGuards(ifT,pred,false); // Up-cast predicate + // Parse true-side flavor + Node lhs = switch( fside ) { + case "else" -> P.statements.parseStatement(); // if( pred ) stmts; + case ":" -> P.expressions.parseAssignment(); // pred ? asgn; + case "&&" -> P.expressions.parseLogical(); // pred && expr + case "||" -> pred; // pred || expr_is_ignored + default -> throw TODO(); + }; + lhs.keep(); + P._scope.removeGuards(ifT); + + // See if a one-sided def was made: "if(pred) int x = 1;" and throw. + // See if any forward-refs were made, and copy them to the other side: + // "pred ? n*fact(n-1) : 1" + fScope.balanceIf(P._scope); + + ScopeNode tScope = P._scope; + + // Parse the false side + P._scope = fScope; // Restore scope, then parse else block if any + ifF.unkeep(); // fScope already owns the false control + // Up-cast predicate, even if not else clause, because predicate can + // remain true if the true clause exits: `if( !ptr ) return 0; return ptr.fld;` + P._scope.addGuards(ifF,pred,true); + // Parse false-side flavor + boolean doRHS = false; // RHS is optional for if/else and trinary + Node rhs = switch( fside ) { + case "else" -> (doRHS = P.match(fside)) ? P.statements.parseStatement() : P.con(lhs._type.makeZero()); + case ":" -> (doRHS = P.match(fside)) ? P.expressions.parseAssignment() : P.con(lhs._type.makeZero()); + case "&&" -> P.con(lhs._type.makeZero()); // pred && expr_is_ignored + case "||" -> P.expressions.parseLogical(); // pred || expr + default -> throw TODO(); + }; + rhs.keep(); + P._scope.removeGuards(ifF); + // The RHS of `||` is parsed on the false side, so retain its updated + // control, memory and locals just as an explicit else arm does. + if( doRHS || fside.equals("||") ) + fScope = P._scope; + pred.unkeep(); + + // Check the trinary widening int/flt + if( !fside.equals("else") ) { + rhs = P.conversions.widenInt( rhs.unkeep(), lhs._type ).keep(); + lhs = P.conversions.widenInt( lhs.unkeep(), rhs._type ).keep(); + } + + P._scope = tScope; + P._xScopes.pop(); // Discard pushed from graph display + // See if a one-sided def was made: "if(pred) int x = 1;" and throw. + // See if any forward-refs were made, and copy them to the other side: + // "pred ? n*fact(n-1) : 1" + tScope.balanceIf(fScope); + + // Merge results + RegionNode r = P.ctrl(tScope.mergeScopes(fScope,P.loc())); + Node ret = P.peep(new PhiNode("",r,lhs.unkeep(),rhs.unkeep())); + // Immediately fail e.g. `arg ? 7 : ptr` + ParseException err; + if( !fside.equals("else") && (err = ret.err()) !=null ) throw err; + ret.keep(); + r.peephole(); + return ret.unkeep(); + } +} |
