summaryrefslogtreecommitdiff
path: root/src/main/java/com/seaofnodes/simple/parser/ControlFlow.java
blob: caa730767a0bf7ef46a00332870e445d988c98ed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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();
    }
}