/** * @param CompilationContext $compilationContext */ public function compile(CompilationContext $compilationContext) { $exprRaw = $this->_statement['expr']; $codePrinter = $compilationContext->codePrinter; $codePrinter->output('do {'); /** * Variables are initialized in a different way inside cycle */ $compilationContext->insideCycle++; /** * Compile statements in the 'while' block */ if (isset($this->_statement['statements'])) { $st = new StatementsBlock($this->_statement['statements']); $st->compile($compilationContext); } $compilationContext->codePrinter->increaseLevel(); $expr = new EvalExpression(); $condition = $expr->optimize($exprRaw, $compilationContext); $compilationContext->codePrinter->decreaseLevel(); /** * Restore the cycle counter */ $compilationContext->insideCycle--; /** * Compound conditions can be evaluated in a single line of the C-code */ $codePrinter->output('} while (' . $condition . ');'); }
/** * Compile ternary operator * * @param $expression * @param CompilationContext $compilationContext * @return CompiledExpression */ public function compile($expression, CompilationContext $compilationContext) { /** * This variable is used to check if the compound and expression is evaluated as true or false */ $returnVariable = $this->getExpected($compilationContext, $expression, false); $returnVariable->setLocalOnly(false); if ($returnVariable->getType() != 'variable' || $returnVariable->getName() == 'return_value') { $returnVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); if ($returnVariable->isTemporal()) { $returnVariable->skipInitVariant(2); } } $expr = new EvalExpression(); $condition = $expr->optimize($expression['left'], $compilationContext); $compilationContext->codePrinter->output('if (' . $condition . ') {'); $compilationContext->codePrinter->increaseLevel(); /** * Create an implicit 'let' operation to update the evaluated left operator */ $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $returnVariable->getName(), 'operator' => 'assign', 'expr' => $expression['right'])))); $statement->compile($compilationContext); $compilationContext->codePrinter->decreaseLevel(); $compilationContext->codePrinter->output('} else {'); $compilationContext->codePrinter->increaseLevel(); /** * Create an implicit 'let' operation to update the evaluated left operator */ $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $returnVariable->getName(), 'operator' => 'assign', 'expr' => $expression['extra'])))); $statement->compile($compilationContext); $compilationContext->codePrinter->decreaseLevel(); $compilationContext->codePrinter->output('}'); return new CompiledExpression('variable', $returnVariable->getName(), $expression); }
/** * @param CompilationContext $compilationContext */ public function compile(CompilationContext $compilationContext) { $exprRaw = $this->_statement['expr']; $expr = new EvalExpression(); $condition = $expr->optimize($exprRaw, $compilationContext); /** * This pass tries to move dynamic variable initialization out of the if/else branch */ if (isset($this->_statement['statements']) && isset($this->_statement['else_statements'])) { $skipVariantInit = new SkipVariantInit(); $skipVariantInit->setVariablesToSkip(0, $expr->getUsedVariables()); $skipVariantInit->setVariablesToSkip(1, $expr->getUsedVariables()); $skipVariantInit->pass(0, new StatementsBlock($this->_statement['statements'])); $skipVariantInit->pass(1, new StatementsBlock($this->_statement['else_statements'])); $symbolTable = $compilationContext->symbolTable; foreach ($skipVariantInit->getVariables() as $variable) { if ($symbolTable->hasVariable($variable)) { $symbolVariable = $symbolTable->getVariable($variable); if ($symbolVariable->getType() == 'variable') { $symbolVariable->initVariant($compilationContext); $symbolVariable->skipInitVariant(2); } } } } $compilationContext->codePrinter->output('if (' . $condition . ') {'); $this->_evalExpression = $expr; /** * Try to mark lastest temporary variable used as idle */ $evalVariable = $expr->getEvalVariable(); if (is_object($evalVariable)) { if ($evalVariable->isTemporal()) { $evalVariable->setIdle(true); } } /** * Compile statements in the 'if' block */ if (isset($this->_statement['statements'])) { $st = new StatementsBlock($this->_statement['statements']); $branch = $st->compile($compilationContext, $expr->isUnreachable(), Branch::TYPE_CONDITIONAL_TRUE); $branch->setRelatedStatement($this); } /** * Compile statements in the 'else' block */ if (isset($this->_statement['else_statements'])) { $compilationContext->codePrinter->output('} else {'); $st = new StatementsBlock($this->_statement['else_statements']); $branch = $st->compile($compilationContext, $expr->isUnreachableElse(), Branch::TYPE_CONDITIONAL_FALSE); $branch->setRelatedStatement($this); } $compilationContext->codePrinter->output('}'); }
/** * Compiles a for statement that use a 'range' as expression * * @param array $exprRaw * @param \CompilationContext $compilationContext * @return boolean */ public function compileRange($exprRaw, CompilationContext $compilationContext) { if (!count($exprRaw['parameters'])) { return false; } if (count($exprRaw['parameters']) > 3) { return false; } $functionCall = new FunctionCall(); $parameters = $functionCall->getResolvedParamsAsExpr($exprRaw['parameters'], $compilationContext, $exprRaw); if (count($parameters) != 2 && count($parameters) != 3) { throw new CompilerException("Wrong number of parameters", $this->_statement['expr']); } if ($parameters[0]->getType() != 'variable') { if (!$parameters[0]->isIntCompatibleType()) { return false; } } if ($parameters[1]->getType() != 'variable') { if (!$parameters[1]->isIntCompatibleType()) { return false; } } $codePrinter = $compilationContext->codePrinter; /** * Initialize 'key' variable */ if (isset($this->_statement['key'])) { /** * This variable is used to check if the loop is in its first iteration */ $keyVariable = $compilationContext->symbolTable->getTempVariableForWrite('long', $compilationContext, $this->_statement); $keyVariable->increaseUses(); } /** * This variable is used to check if the loop is in its first iteration */ $flagVariable = $compilationContext->symbolTable->getTempVariableForWrite('bool', $compilationContext, $this->_statement); if ($parameters[0]->getType() != 'variable') { $tempVariable = $compilationContext->symbolTable->addTemp($parameters[0]->getType(), $compilationContext); } else { $rangeVariable = $compilationContext->symbolTable->getVariableForRead($parameters[0]->getCode(), $compilationContext, $this->_statement['expr']); $tempVariable = $compilationContext->symbolTable->addTemp($rangeVariable->getType(), $compilationContext); } /** * Create a copy of the current value in the end of the range to avoid modify the range * inside the cycle */ if ($parameters[1]->getType() != 'variable') { $upperBoundVariable = $compilationContext->symbolTable->getTempVariable($parameters[1]->getType(), $compilationContext); } else { $rangeVariable = $compilationContext->symbolTable->getVariableForRead($parameters[1]->getCode(), $compilationContext, $this->_statement['expr']); $upperBoundVariable = $compilationContext->symbolTable->getTempVariable($rangeVariable->getType(), $compilationContext); } /** * Create an implicit 'let' operation to set the current value in the upper bound of the range */ $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $upperBoundVariable->getName(), 'operator' => 'assign', 'expr' => array('type' => $parameters[1]->getType(), 'value' => $parameters[1]->getCode(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char'])))); $statement->compile($compilationContext); if ($this->_statement['reverse']) { /** * Create an implicit 'let' operation for the initialize expression, @TODO use a builder */ $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $tempVariable->getName(), 'operator' => 'assign', 'expr' => array('type' => 'variable', 'value' => $upperBoundVariable->getName(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char'])))); } else { /** * Create an implicit 'let' operation for the initialize expression, @TODO use a builder */ $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $tempVariable->getName(), 'operator' => 'assign', 'expr' => array('type' => $parameters[0]->getType(), 'value' => $parameters[0]->getCode(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char'])))); } $statement->compile($compilationContext); /** * Initialize 'key' variable */ if (isset($this->_statement['key'])) { $codePrinter->output($keyVariable->getName() . ' = 0;'); } $codePrinter->output($flagVariable->getName() . ' = 0;'); if ($this->_statement['reverse']) { $conditionExpr = array('type' => 'greater-equal', 'left' => array('type' => 'variable', 'value' => $tempVariable->getName()), 'right' => array('type' => $parameters[0]->getType(), 'value' => $parameters[0]->getCode())); } else { $conditionExpr = array('type' => 'less-equal', 'left' => array('type' => 'variable', 'value' => $tempVariable->getName()), 'right' => array('type' => 'variable', 'value' => $upperBoundVariable->getName())); } $expr = new EvalExpression(); $condition = $expr->optimize($conditionExpr, $compilationContext); $codePrinter->output('if (' . $condition . ') {'); $codePrinter->increaseLevel(); /** * Inside a cycle */ $compilationContext->insideCycle++; $codePrinter->output('while (1) {'); $codePrinter->increaseLevel(); $codePrinter->output('if (' . $flagVariable->getName() . ') {'); $codePrinter->increaseLevel(); if (isset($this->_statement['key'])) { $codePrinter->output($keyVariable->getName() . '++;'); } if ($this->_statement['reverse']) { if (!isset($parameters[2])) { $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'decr', 'variable' => $tempVariable->getName(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char'])))); } else { $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'operator' => 'sub-assign', 'variable' => $tempVariable->getName(), 'expr' => array('type' => $parameters[2]->getType(), 'value' => $parameters[2]->getCode(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char'])))); } } else { if (!isset($parameters[2])) { $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'incr', 'variable' => $tempVariable->getName(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char'])))); } else { $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'operator' => 'add-assign', 'variable' => $tempVariable->getName(), 'expr' => array('type' => $parameters[2]->getType(), 'value' => $parameters[2]->getCode(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char'])))); } } $statement->compile($compilationContext); /** * Multi-line conditions would need to be regenerated here */ $condition = $expr->optimize($conditionExpr, $compilationContext); $codePrinter->output('if (!(' . $condition . ')) {'); $codePrinter->output("\t" . "break;"); $codePrinter->output('}'); $codePrinter->decreaseLevel(); $codePrinter->output('} else {'); $codePrinter->output("\t" . $flagVariable->getName() . ' = 1;'); $codePrinter->output('}'); /** * Initialize 'key' variable */ if (isset($this->_statement['key'])) { /** * Check for anonymous variables */ if ($this->_statement['key'] != '_') { $keyVariableName = $this->_statement['key']; } else { $keyVariableName = $keyVariable->getName(); } /** * Create an implicit 'let' operation, @TODO use a builder */ $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $keyVariableName, 'operator' => 'assign', 'expr' => array('type' => 'variable', 'value' => $keyVariable->getName(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char'])))); $statement->compile($compilationContext); } /** * Initialize 'value' variable */ if (isset($this->_statement['value'])) { /** * Check for anonymous variables */ if ($this->_statement['value'] != '_') { $valueVariable = $this->_statement['value']; } else { $valueVariable = $tempVariable->getName(); } /** * Create an implicit 'let' operation, @TODO use a builder */ $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $valueVariable, 'operator' => 'assign', 'expr' => array('type' => 'variable', 'value' => $tempVariable->getName(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char'])))); $statement->compile($compilationContext); } $codePrinter->decreaseLevel(); /** * Compile statements in the 'for' block */ if (isset($this->_statement['statements'])) { $st = new StatementsBlock($this->_statement['statements']); $st->isLoop(true); if (isset($this->_statement['key'])) { $st->getMutateGatherer()->increaseMutations($this->_statement['key']); } $st->getMutateGatherer()->increaseMutations($this->_statement['value']); $st->compile($compilationContext); } /** * Restore the cycle counter */ $compilationContext->insideCycle--; $codePrinter->output('}'); $codePrinter->decreaseLevel(); $codePrinter->output('}'); }
/** * @param CompilationContext $compilationContext */ public function compile(CompilationContext $compilationContext) { $exprRaw = $this->_statement['expr']; $codePrinter = $compilationContext->codePrinter; $compilationContext->insideSwitch++; $exprEval = new Expression($exprRaw); $exprEval->setReadOnly(true); $resolvedExpr = $exprEval->compile($compilationContext); if (isset($this->_statement['clauses'])) { $evalExpr = new EvalExpression(); $codePrinter->output('do {'); $compilationContext->codePrinter->increaseLevel(); if ($resolvedExpr->getType() != 'variable') { /** * Create a temporary variable */ $tempVariable = $compilationContext->symbolTable->getTempVariable($resolvedExpr->getType(), $compilationContext); /** * Simulate an assignment to the temporary variable */ $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'operator' => 'assign', 'variable' => $tempVariable->getName(), 'expr' => array('type' => $resolvedExpr->getType(), 'value' => $resolvedExpr->getCode(), 'file' => $exprRaw['file'], 'line' => $exprRaw['line'], 'char' => $exprRaw['char']), 'file' => $exprRaw['file'], 'line' => $exprRaw['line'], 'char' => $exprRaw['char'])))); $statement->compile($compilationContext); } else { $tempVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $exprRaw); } $clauses = $this->normalizeClauses($this->_statement['clauses']); $tempLeft = array('type' => 'variable', 'value' => $tempVariable->getRealName()); /** * In the first round we group case clauses that have block statements * with the ones that does not have one */ $blocks = array(); $exprStack = array(); $defaultBlock = null; foreach ($clauses as $clause) { if ($clause['type'] == 'case') { $expr = array('type' => 'equals', 'left' => $tempLeft, 'right' => $clause['expr']); if (!isset($clause['statements'])) { $exprStack[] = $expr; } else { $exprStack[] = $expr; $blocks[] = array('expr' => $exprStack, 'block' => $clause['statements']); $exprStack = array(); } } else { if ($clause['type'] == 'default') { $defaultBlock = $clause['statements']; } } } /** * In the second round we generate the conditions with their blocks * grouping 'cases' without a statement block using an 'or' */ foreach ($blocks as $block) { $expressions = $block['expr']; if (count($expressions) == 1) { $condition = $evalExpr->optimize($expressions[0], $compilationContext); $codePrinter->output('if (' . $condition . ') {'); } else { $orConditions = array(); foreach ($expressions as $expression) { $orConditions[] = $evalExpr->optimize($expression, $compilationContext); } $codePrinter->output('if (' . join(' || ', $orConditions) . ') {'); } if (isset($block['block'])) { $st = new StatementsBlock($block['block']); $branch = $st->compile($compilationContext, false, Branch::TYPE_SWITCH); $branch->setRelatedStatement($this); } $codePrinter->output('}'); } $compilationContext->codePrinter->decreaseLevel(); /** * The default block is resolved at the end of the 'switch' */ if ($defaultBlock) { $st = new StatementsBlock($defaultBlock); $st->compile($compilationContext); } $compilationContext->insideSwitch--; $codePrinter->output('} while(0);'); $codePrinter->outputBlankLine(); } }