package com.seaofnodes.simple.parser; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.node.ScopeNode.Kind; import com.seaofnodes.simple.type.*; /** Blocks, statements, and loops; builds their control-flow scopes. */ final class Statements { private final Parser P; // Merge all the while-breaks here ScopeNode _breakScope; // Merge all loop continues here. ScopeNode _continueScope; Statements(Parser P) { this.P = P; } /** * Parses a statement * *
* returnStatement | declStatement | blockStatement | ifStatement | expressionStatement
*
* @return a {@link Node} or {@code null}
*/
Node parseStatement() {
if( P.matchx("return") ) return parseReturn();
else if (P.matchx("if") ) return parseIf();
else if (P.matchx("while") ) return parseWhile();
else if (P.matchx("for") ) return parseFor();
else if (P.matchx("break") ) return parseBreak();
else if (P.matchx("continue")) return parseContinue();
else if (P.matchx("struct") ) return P.structs.parseStruct();
else if (P.structs.inStructDeclaration() && P.matchx("new")) return P.structs.parseConstructorDeclaration();
else if (P.matchx(";") ) return P._code.ZERO; // Empty statement
// Break ambiguity around leading function types and starting a block
else if (P.peek('{') && !P.types.isTypeFun() ) {
P.match("{");
return P.require(parseBlock(new Kind.Block()),"}");
}
// Declaration or normal assignment/expression
else return P.declarations.parseDeclarationStatement();
}
/**
* Parses a block
*
*
* '{' statements '}'
*
* Does not parse the opening or closing '{}'
* @return a {@link Node} or {@code null}
*/
private Node parseBlock(Kind kind) {
// Enter a new scope
P._scope.push(kind);
Node last = P._code.ZERO;
while (!P.peek('}') && !P._lexer.isEOF()) {
P._code.add(last);
last = parseStatement();
}
// Exit scope
last.keep();
P._scope.pop();
return last.unkeep();
}
/**
* Parses a return statement; "return" already parsed.
* The $ctrl edge is killed.
*
*
* 'return' expr ;
*
* @return an expression {@link Node}, never {@code null}
*/
private Node parseReturn() {
var expr = P.require(P.expressions.parseAssignment(), ";");
return P.functions.addReturn(expr);
}
/**
* Parses a statement
*
*
* if ( expression ) statement [else statement]
*
* @return a {@link Node}, never {@code null}
*/
private Node parseIf() {
// Parse predicate
P.require("(");
var pred = P.require(P.expressions.parseAssignment(), ")");
return P.controlflow.conditional(pred,"else");
}
/**
* Parses a while statement
*
*
* while ( expression ) statement
*
* @return a {@link Node}, never {@code null}
*/
private Node parseWhile() {
P.require("(");
return parseLooping(false);
}
/**
* Parses a for statement
*
*
* for( var x=init; test; incr ) body
*
* @return a {@link Node}, never {@code null}
*/
private Node parseFor() {
// { var x=init,y=init,...;
// while( test ) {
// body;
// next;
// }
// }
P.require("(");
P._scope.push(new Kind.Block()); // Scope for the index variables
if( !P.match(";") ) // Can be empty init "for(;test;next) body"
P.declarations.parseDeclarationStatement(); // Non-empty init
Node rez = parseLooping(true);
P._scope.pop(); // Exit index variable scope
return rez;
}
// Shared by `for` and `while`
private Node parseLooping( boolean doFor ) {
var savedContinueScope = _continueScope;
var savedBreakScope = _breakScope;
// Loop region has two control inputs, the first is the entry
// point, and second is back edge that is set after loop is parsed
// (see end_loop() call below). Note that the absence of back edge is
// used as an indicator to switch off peepholes of the region and
// associated phis; see {@code inProgress()}.
P.ctrl(new LoopNode(P.loc(),null,P.ctrl()).peephole()); // Note we set back edge to null here
// At loop head, we clone the current Scope (this includes all
// names in every nesting level within the Scope).
// We create phis eagerly for all the names we find, see dup().
// Save the current scope as the loop head
ScopeNode head = P._scope.keep();
// Clone the head Scope to create a new Scope for the body.
// Create phis eagerly as part of cloning
P._xScopes.push(P._scope = P._scope.dup(true)); // The true argument triggers creating phis
// Parse predicate
var pred = P.peek(';') ? P.con(1) : P.expressions.parseAssignment();
P.require( doFor ? ";" : ")" );
// IfNode takes current control and predicate
Node ifNode = new IfNode(P.ctrl(), pred.keep()).peephole();
// Setup projection nodes
CFGNode ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole().keep();
CFGNode ifF = (CFGNode)new CProjNode(ifNode.unkeep(), 1, "False").peephole();
// for( ;;next ) body
int nextPos = -1, nextEnd = -1;
if( doFor ) {
// Skip the next expression and parse it later
nextPos = P.pos();
P._lexer.skipTo('(',')',"for update");
nextEnd = P.pos();
P.require(")");
}
// Clone the body Scope to create the break/exit Scope which accounts for any
// side effects in the predicate. The break/exit Scope will be the final
// scope after the loop, and its control input is the False branch of
// the loop predicate. Note that body Scope is still our current scope.
P.ctrl(ifF);
P._xScopes.push(_breakScope = P._scope.dup());
_breakScope.addGuards(ifF,pred,true); // Up-cast predicate
// No continues yet
_continueScope = null;
// Parse the true side, which corresponds to loop body
// Our current scope is the body Scope
P.ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection
P._scope.addGuards(ifT,pred.unkeep(),false); // Up-cast predicate
parseStatement().isKill(); // Parse loop body
P._scope.removeGuards(ifT);
// Merge the loop bottom into other continue statements
if (_continueScope != null) {
_continueScope = jumpTo(_continueScope);
P._scope.kill();
P._scope = _continueScope;
}
// Now append the next code onto the body code
if( doFor ) {
int old = P.pos(nextPos);
if( !P.peek(')') )
P.expressions.parseAssignment();
if( P.pos() != nextEnd )
throw P.errorSyntax( "Unexpected code after expression" );
P.pos(old);
}
// The true branch loops back, so whatever is current _scope.ctrl gets
// added to head loop as input. endLoop() updates the head scope, and
// goes through all the phis that were created earlier. For each phi,
// it sets the second input to the corresponding input from the back
// edge. If the phi is redundant, it is replaced by its sole input.
var exit = _breakScope;
head.balanceLoopFRefs(P._scope);
exit.balanceLoopFRefs(P._scope);
head.endLoop(P._scope, exit);
head.unkeep().kill();
P._xScopes.pop(); // Cleanup
P._xScopes.pop(); // Cleanup
_continueScope = savedContinueScope;
_breakScope = savedBreakScope;
// At exit the false control is the current control, and
// the scope is the exit scope after the exit test.
// During sys parsing, there is no xscope here.
if( !P._xScopes.isEmpty() ) {
P._xScopes.pop();
P._xScopes.push( exit );
}
P._scope = exit;
return P._code.ZERO;
}
private Node parseBreak () {
checkLoopActive();
// At the time of the break, and loop-exit conditions are only valid if
// they are ALSO valid at the break. It is the intersection of
// conditions here, not the union.
_breakScope.removeGuards(_breakScope.ctrl());
_breakScope = P.require(jumpTo(_breakScope ),";");
_breakScope.addGuards((CFGNode)_breakScope.ctrl(), null, false);
return P._code.ZERO;
}
private Node parseContinue() { checkLoopActive(); _continueScope = P.require(jumpTo( _continueScope ),";"); return P._code.ZERO; }
private ScopeNode jumpTo(ScopeNode toScope) {
ScopeNode cur = P._scope.dup();
P.ctrl(P._code.XCTRL); // Kill current scope
// Prune nested lexical scopes that have depth > than the loop head
// We use _breakScope as a proxy for the loop head scope to obtain the depth
while( cur.depth() > _breakScope.depth() )
cur.pop();
// If this is a continue then first time the target is null
// So we just use the pruned current scope as the base for the
// "continue"
if( toScope == null )
return cur;
// toScope is either the break scope, or a scope that was created here
assert toScope.depth() <= _breakScope.depth();
toScope.ctrl(toScope.mergeScopes(cur,P.loc()).peephole());
return toScope;
}
private void checkLoopActive() { if (_breakScope == null) throw P.error("No active loop for a break or continue"); }
}