Пример #1
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);
 }
Пример #2
0
 /**
  * @param array $expression
  * @param Call $call
  * @param CompilationContext $context
  * @return bool|CompiledExpression|mixed
  */
 public function optimize(array $expression, Call $call, CompilationContext $context)
 {
     if (!isset($expression['parameters'])) {
         return false;
     }
     $context->headersManager->add('kernel/variables');
     $resolvedParams = $call->getResolvedParamsAsExpr($expression['parameters'], $context, $expression);
     foreach ($resolvedParams as $resolvedParam) {
         $variable = $context->symbolTable->getVariable($resolvedParam->getCode());
         if (!$variable || !$variable->isVariable()) {
             /**
              * Complex expressions require a temporary variable
              */
             switch ($resolvedParam->getType()) {
                 case 'array':
                     $type = 'array';
                     break;
                 default:
                     $type = 'variable';
                     break;
             }
             $variable = $context->symbolTable->addTemp($type, $context);
             $variable->initVariant($context);
             $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => $type, 'variable' => $variable->getName(), 'operator' => 'assign', 'expr' => array('type' => $resolvedParam->getType(), 'value' => $resolvedParam->getCode(), 'file' => $expression['file'], 'line' => $expression['line'], 'char' => $expression['char']), 'file' => $expression['file'], 'line' => $expression['line'], 'char' => $expression['char']))));
             $statement->compile($context);
         } else {
             /**
              * This mark the variable as used
              */
             $variable = $context->symbolTable->getVariableForRead($resolvedParam->getCode(), $context, $expression);
         }
         $context->codePrinter->output('zephir_var_dump(&' . $variable->getName() . ' TSRMLS_CC);');
     }
     return new CompiledExpression('null', 'null', $expression);
 }
Пример #3
0
 /**
  * @param array $expression
  * @param Call $call
  * @param CompilationContext $context
  * @return mixed|CompiledExpression
  * @throws CompilerException
  */
 public function optimize(array $expression, Call $call, CompilationContext $context)
 {
     if (!isset($expression['parameters'])) {
         return false;
     }
     /**
      * Process the expected symbol to be returned
      */
     $call->processExpectedReturn($context);
     $symbolVariable = $call->getSymbolVariable();
     if ($symbolVariable) {
         if (!$symbolVariable->isVariable()) {
             throw new CompilerException("Returned values by functions can only be assigned to variant variables", $expression);
         }
     }
     $context->headersManager->add('kernel/variables');
     $resolvedParams = $call->getResolvedParamsAsExpr($expression['parameters'], $context, $expression);
     $resolvedParam = $resolvedParams[0];
     if (!$symbolVariable || !$symbolVariable->isVariable()) {
         /**
          * Complex expressions require a temporary variable
          */
         switch ($resolvedParam->getType()) {
             case 'array':
                 $type = 'array';
                 break;
             default:
                 $type = 'variable';
                 break;
         }
         $variable = $context->symbolTable->addTemp($type, $context);
         $variable->initVariant($context);
         $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => $type, 'variable' => $variable->getName(), 'operator' => 'assign', 'expr' => array('type' => $resolvedParam->getType(), 'value' => $resolvedParam->getCode(), 'file' => $expression['file'], 'line' => $expression['line'], 'char' => $expression['char']), 'file' => $expression['file'], 'line' => $expression['line'], 'char' => $expression['char']))));
         $statement->compile($context);
     } else {
         /**
          * This mark the variable as used
          */
         $variable = $context->symbolTable->getVariableForRead($resolvedParam->getCode(), $context, $expression);
     }
     $variableSymbol = $context->backend->getVariableCodePointer($variable);
     /**
      * let a = var_export(val);
      */
     if ($symbolVariable) {
         if ($symbolVariable->getName() == 'return_value') {
             $symbolVariable = $context->symbolTable->getTempVariableForWrite('variable', $context);
         } else {
             $symbolVariable->initVariant($context);
         }
         $symbol = $context->backend->getVariableCode($symbolVariable);
         $context->codePrinter->output('zephir_var_export_ex(' . $symbol . ', ' . $variableSymbol . ' TSRMLS_CC);');
         return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
     }
     $context->codePrinter->output('zephir_var_export(' . $variableSymbol . ' TSRMLS_CC);');
     return new CompiledExpression('null', 'null', $expression);
 }
Пример #4
0
 /**
  * Compiles {"var"} = {expr}
  *
  * @param string $variable
  * @param ZephirVariable $symbolVariable
  * @param CompiledExpression $resolvedExpr
  * @param CompilationContext $compilationContext,
  * @param array $statement
  */
 public function assign($variable, ZephirVariable $symbolVariable = null, CompiledExpression $resolvedExpr = null, CompilationContext $compilationContext = null, $statement = null)
 {
     $codePrinter = $compilationContext->codePrinter;
     $variable = $compilationContext->symbolTable->getTempVariable('variable', $compilationContext, $statement);
     $variable->setMustInitNull(true);
     $letStatement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $variable->getName(), 'operator' => 'assign', 'expr' => array('type' => $resolvedExpr->getType(), 'value' => $resolvedExpr->getCode(), 'file' => $statement['file'], 'line' => $statement['line'], 'char' => $statement['char']), 'file' => $statement['file'], 'line' => $statement['line'], 'char' => $statement['char']))));
     $letStatement->compile($compilationContext);
     $codePrinter->output('if (zephir_set_symbol_str(SS("' . $statement["variable"] . '"), ' . $variable->getName() . ' TSRMLS_CC) == FAILURE){');
     $codePrinter->output('  return;');
     $codePrinter->output('}');
 }
Пример #5
0
 /**
  * @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;
 }
Пример #6
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('}');
 }
Пример #7
0
 /**
  * @param CompilationContext $compilationContext
  * @throws CompilerException
  */
 public function compile(CompilationContext $compilationContext)
 {
     $statement = $this->_statement;
     if (!isset($statement['data-type'])) {
         throw new CompilerException("Data type is required", $this->_statement);
     }
     $typeInference = $compilationContext->typeInference;
     $symbolTable = $compilationContext->symbolTable;
     foreach ($statement['variables'] as $variable) {
         $varName = $variable['variable'];
         if ($symbolTable->hasVariableInBranch($varName, $compilationContext->branchManager->getCurrentBranch())) {
             throw new CompilerException("Variable '" . $varName . "' is already defined", $variable);
         }
         $currentType = $statement['data-type'];
         /**
          * Replace original data type by the pre-processed infered type
          */
         if ($typeInference) {
             if ($currentType == 'variable') {
                 $type = $typeInference->getInferedType($varName);
                 if (is_string($type)) {
                     $currentType = $type;
                 }
             }
         }
         switch ($currentType) {
             case 'variable':
             case 'array-access':
             case 'property-access':
             case 'static-property-access':
             case 'fcall':
             case 'mcall':
             case 'scall':
                 $currentType = 'variable';
                 break;
         }
         /**
          * Variables are added to the symbol table
          */
         if (isset($variable['expr'])) {
             $symbolVariable = $symbolTable->addVariable($currentType, $varName, $compilationContext, $variable['expr']);
         } else {
             $symbolVariable = $symbolTable->addVariable($currentType, $varName, $compilationContext);
         }
         $varName = $symbolVariable->getName();
         /**
          * Set the node where the variable is declared
          */
         $symbolVariable->setOriginal($variable);
         $symbolVariable->setIsInitialized(true, $compilationContext);
         //$symbolVariable->increaseMutates();
         if ($currentType == 'variable') {
             $symbolVariable->setMustInitNull(true);
             $symbolVariable->setLocalOnly(false);
         }
         //$symbolVariable->increaseVariantIfNull();
         if (isset($variable['expr'])) {
             $builder = BuilderFactory::getInstance();
             $letBuilder = $builder->statements()->let(array($builder->operators()->assignVariable($varName, $builder->raw($variable['expr']))));
             $letStatement = new LetStatement($letBuilder->build());
             $letStatement->compile($compilationContext);
         } else {
             $symbolVariable->enableDefaultAutoInitValue();
         }
     }
 }
Пример #8
0
 public function compile($expression, CompilationContext $compilationContext)
 {
     if (!isset($expression['left'])) {
         throw new \Exception("Missing left part of the expression");
     }
     if (!isset($expression['right'])) {
         throw new \Exception("Missing right part of the expression");
     }
     $leftExpr = new Expression($expression['left']);
     $leftExpr->setReadOnly($this->_readOnly);
     $left = $leftExpr->compile($compilationContext);
     /**
      * This variable is used to check if the compound and expression is evaluated as true or false
      */
     $flagVariable = $compilationContext->symbolTable->getTempVariableForWrite('bool', $compilationContext);
     switch ($left->getType()) {
         case 'int':
         case 'bool':
         case 'char':
         case 'double':
         case 'uint':
         case 'uchar':
             $assignExprLeft = array('type' => $left->getType(), 'value' => $left->getCode());
             break;
         case 'variable':
             $assignExprLeft = array('type' => 'variable', 'value' => $left->getCode());
             break;
         case 'null':
             $assignExprLeft = array('type' => 'null', 'value' => null);
             break;
     }
     if (!isset($assignExprLeft)) {
         throw new CompilerException($left->getType(), $expression['left']);
     }
     /**
      * Create an implicit 'let' operation to update the evaluated left operator
      */
     $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $flagVariable->getName(), 'operator' => 'assign', 'expr' => $assignExprLeft, 'file' => $expression['left']['file'], 'line' => $expression['left']['line'], 'char' => $expression['left']['char']))));
     $statement->compile($compilationContext);
     $compilationContext->codePrinter->output('if (' . $flagVariable->getName() . ') {');
     $compilationContext->codePrinter->increaseLevel();
     $rightExpr = new Expression($expression['right']);
     $rightExpr->setReadOnly($this->_readOnly);
     $right = $rightExpr->compile($compilationContext);
     switch ($right->getType()) {
         case 'int':
         case 'bool':
         case 'char':
         case 'double':
         case 'uint':
         case 'uchar':
             $assignExprRight = array('type' => $right->getType(), 'value' => $right->getCode());
             break;
         case 'variable':
             $assignExprRight = array('type' => 'variable', 'value' => $right->getCode());
             break;
         case 'null':
             $assignExprRight = array('type' => 'null', 'value' => null);
             break;
     }
     if (!isset($assignExprRight)) {
         throw new CompilerException($right->getType(), $expression['right']);
     }
     /**
      * Create an implicit 'let' operation to update the evaluated right operator
      */
     $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $flagVariable->getName(), 'operator' => 'assign', 'expr' => $assignExprRight, 'file' => $expression['right']['file'], 'line' => $expression['right']['line'], 'char' => $expression['right']['char']))));
     $statement->compile($compilationContext);
     $compilationContext->codePrinter->decreaseLevel();
     $compilationContext->codePrinter->output('}');
     return new CompiledExpression('bool', $flagVariable->getName(), $expression);
 }
Пример #9
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();
     }
 }
Пример #10
0
 /**
  * Resolves the access to a property in an object
  *
  * @param array $expression
  * @param CompilationContext $compilationContext
  * @return \CompiledExpression
  */
 public function compile($expression, CompilationContext $compilationContext)
 {
     $codePrinter = $compilationContext->codePrinter;
     $propertyAccess = $expression;
     $expr = new Expression($propertyAccess['left']);
     $exprVariable = $expr->compile($compilationContext);
     switch ($exprVariable->getType()) {
         case 'variable':
             $variableVariable = $compilationContext->symbolTable->getVariableForRead($exprVariable->getCode(), $compilationContext, $expression);
             switch ($variableVariable->getType()) {
                 case 'variable':
                     break;
                 default:
                     throw new CompilerException("Variable type: " . $variableVariable->getType() . " cannot be used as object", $propertyAccess['left']);
             }
             break;
         default:
             throw new CompilerException("Cannot use expression: " . $exprVariable->getType() . " as an object", $propertyAccess['left']);
     }
     switch ($propertyAccess['right']['type']) {
         case 'variable':
             $propertyVariable = $compilationContext->symbolTable->getVariableForRead($propertyAccess['right']['value'], $compilationContext, $expression);
             break;
         case 'string':
             $propertyVariable = $compilationContext->symbolTable->getTempVariableForWrite('string', $compilationContext);
             $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $propertyVariable->getName(), 'operator' => 'assign', 'expr' => $expression['right']))));
             $statement->compile($compilationContext);
             break;
         default:
             throw new CompilerException("Variable type: " . $propertyAccess['right']['type'] . " cannot be used as object", $propertyAccess['left']);
     }
     /**
      * Resolves the symbol that expects the value
      */
     if ($this->_expecting) {
         if ($this->_expectingVariable) {
             $symbolVariable = $this->_expectingVariable;
             if ($symbolVariable->getName() != 'return_value') {
                 $symbolVariable->observeVariant($compilationContext);
             } else {
                 $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserve('variable', $compilationContext, $expression);
             }
         } else {
             $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserve('variable', $compilationContext, $expression);
         }
     }
     /**
      * Variable that receives a property value must be polymorphic
      */
     if ($symbolVariable->getType() != 'variable') {
         throw new CompilerException("Cannot use variable: " . $symbolVariable->getType() . " to assign property value", $expression);
     }
     /**
      * At this point, we don't know the exact dynamic type fetched from the property
      */
     $symbolVariable->setDynamicTypes('undefined');
     $compilationContext->headersManager->add('kernel/object');
     $codePrinter->output('zephir_read_property_zval(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $propertyVariable->getName() . ', PH_NOISY_CC);');
     return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
 }