Resolves evaluation of expressions returning a C-int expression that can be used by 'if'/'while'/'do-while' statements
Ejemplo n.º 1
0
 /**
  * @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 . ');');
 }
Ejemplo n.º 2
0
 /**
  * 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);
 }
Ejemplo n.º 3
0
 /**
  * @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('}');
 }
Ejemplo n.º 4
0
 /**
  * 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('}');
 }
Ejemplo n.º 5
0
 /**
  * @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();
     }
 }