/**
* @param CompilationContext $compilationContext
* @param boolean $unreachable
* @param int $branchType
* @return Branch
*/
public function compile(CompilationContext $compilationContext, $unreachable = false, $branchType = Branch::TYPE_UNKNOWN)
{
$compilationContext->codePrinter->increaseLevel();
$compilationContext->currentBranch++;
/**
* Create a new branch
*/
$currentBranch = new Branch();
$currentBranch->setType($branchType);
$currentBranch->setUnreachable($unreachable);
/**
* Activate branch in the branch manager
*/
$compilationContext->branchManager->addBranch($currentBranch);
$this->_unreachable = $unreachable;
$statements = $this->_statements;
foreach ($statements as $statement) {
/**
* Generate GDB hints
*/
if ($this->_debug) {
if (isset($statement['file'])) {
if ($statement['type'] != 'declare' && $statement['type'] != 'comment') {
$compilationContext->codePrinter->outputNoIndent('#line ' . $statement['line'] . ' "' . $statement['file'] . '"');
}
}
}
/**
* Show warnings if code is generated when the 'unreachable state' is 'on'
*/
if ($this->_unreachable === true) {
switch ($statement['type']) {
case 'echo':
$compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement['expressions'][0]);
break;
case 'let':
$compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement['assignments'][0]);
break;
case 'fetch':
case 'fcall':
case 'mcall':
case 'scall':
case 'if':
case 'while':
case 'do-while':
case 'switch':
case 'for':
case 'return':
case 'c-block':
if (isset($statement['expr'])) {
$compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement['expr']);
} else {
$compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement);
}
break;
default:
$compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement);
}
}
switch ($statement['type']) {
case 'let':
$letStatement = new LetStatement($statement);
$letStatement->compile($compilationContext);
break;
case 'echo':
$echoStatement = new EchoStatement($statement);
$echoStatement->compile($compilationContext);
break;
case 'declare':
$declareStatement = new DeclareStatement($statement);
$declareStatement->compile($compilationContext);
break;
case 'if':
$ifStatement = new IfStatement($statement);
$ifStatement->compile($compilationContext);
break;
case 'while':
$whileStatement = new WhileStatement($statement);
$whileStatement->compile($compilationContext);
break;
case 'do-while':
$doWhileStatement = new DoWhileStatement($statement);
$doWhileStatement->compile($compilationContext);
break;
case 'switch':
$switchStatement = new SwitchStatement($statement);
$switchStatement->compile($compilationContext);
break;
case 'for':
$forStatement = new ForStatement($statement);
$forStatement->compile($compilationContext);
break;
case 'return':
$returnStatement = new ReturnStatement($statement);
$returnStatement->compile($compilationContext);
$this->_unreachable = true;
break;
case 'require':
$requireStatement = new RequireStatement($statement);
$requireStatement->compile($compilationContext);
break;
case 'loop':
$loopStatement = new LoopStatement($statement);
$loopStatement->compile($compilationContext);
break;
case 'break':
$breakStatement = new BreakStatement($statement);
$breakStatement->compile($compilationContext);
$this->_unreachable = true;
break;
case 'continue':
$continueStatement = new ContinueStatement($statement);
$continueStatement->compile($compilationContext);
$this->_unreachable = true;
break;
case 'unset':
$unsetStatement = new UnsetStatement($statement);
$unsetStatement->compile($compilationContext);
break;
case 'throw':
$throwStatement = new ThrowStatement($statement);
$throwStatement->compile($compilationContext);
$this->_unreachable = true;
break;
case 'try-catch':
$throwStatement = new TryCatchStatement($statement);
$throwStatement->compile($compilationContext);
$this->_unreachable = false;
break;
case 'fetch':
$expr = new Expression($statement['expr']);
$expr->setExpectReturn(false);
$compiledExpression = $expr->compile($compilationContext);
$compilationContext->codePrinter->output($compiledExpression->getCode() . ';');
break;
case 'mcall':
$methodCall = new MethodCall();
$expr = new Expression($statement['expr']);
$expr->setExpectReturn(false);
$methodCall->compile($expr, $compilationContext);
break;
case 'fcall':
$functionCall = new FunctionCall();
$expr = new Expression($statement['expr']);
$expr->setExpectReturn(false);
$compiledExpression = $functionCall->compile($expr, $compilationContext);
switch ($compiledExpression->getType()) {
case 'int':
case 'double':
case 'uint':
case 'long':
case 'ulong':
case 'char':
case 'uchar':
case 'bool':
$compilationContext->codePrinter->output($compiledExpression->getCode() . ';');
break;
}
break;
case 'scall':
$methodCall = new StaticCall();
$expr = new Expression($statement['expr']);
$expr->setExpectReturn(false);
$methodCall->compile($expr, $compilationContext);
break;
case 'cblock':
$compilationContext->codePrinter->output($statement['value']);
break;
case 'empty':
break;
default:
throw new Exception('Unsupported statement: ' . $statement['type']);
}
if ($statement['type'] != 'comment') {
$this->_lastStatement = $statement;
}
}
/**
* Traverses temporal variables created in a specific branch
* marking them as idle
*/
$compilationContext->symbolTable->markTemporalVariablesIdle($compilationContext);
$compilationContext->branchManager->removeBranch($currentBranch);
$compilationContext->currentBranch--;
$compilationContext->codePrinter->decreaseLevel();
return $currentBranch;
}