Beispiel #1
0
 /**
  * Creates a closure
  *
  * @param array $expression
  * @param CompilationContext $compilationContext
  * @return CompiledExpression
  * @throws \Zephir\CompilerException
  */
 public function compile(array $expression, CompilationContext $compilationContext)
 {
     $classDefinition = new ClassDefinition($compilationContext->config->get('namespace'), self::$id . '__closure');
     $classDefinition->setIsFinal(true);
     $compilerFile = new CompilerFileAnonymous($classDefinition, $compilationContext->config, $compilationContext->logger);
     $compilationContext->compiler->addClassDefinition($compilerFile, $classDefinition);
     /**
      * Builds parameters using the only parameter available
      */
     $parameters = new ClassMethodParameters(array(array('type' => 'parameter', 'name' => $expression['left']['value'], 'const' => 0, 'data-type' => 'variable', 'mandatory' => 0, 'reference' => 0, 'file' => $expression['left']['file'], 'line' => $expression['left']['line'], 'char' => $expression['left']['char'])));
     $exprBuilder = BuilderFactory::getInstance();
     $statementBlockBuilder = $exprBuilder->statements()->block(array($exprBuilder->statements()->returnX($exprBuilder->raw($expression['right']))));
     $block = $statementBlockBuilder->build();
     $classMethod = new ClassMethod($classDefinition, array('public', 'final'), '__invoke', $parameters, new StatementsBlock($block), null, null, $expression);
     $classDefinition->addMethod($classMethod, $block);
     $compilationContext->headersManager->add('kernel/object');
     if ($this->expecting) {
         if ($this->expectingVariable) {
             $symbolVariable = $this->expectingVariable;
         } else {
             $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression);
         }
     } else {
         $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression);
     }
     $symbolVariable->initVariant($compilationContext);
     $compilationContext->backend->createClosure($symbolVariable, $classDefinition, $compilationContext);
     self::$id++;
     return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
 }
Beispiel #2
0
 /**
  * Transforms calls to method "toHex" to sprintf('%X') call
  *
  * @param object $caller
  * @param CompilationContext $compilationContext
  * @param Call $call
  * @param array $expression
  * @return bool|mixed|\Zephir\CompiledExpression
  */
 public function toHex($caller, CompilationContext $compilationContext, Call $call, array $expression)
 {
     $exprBuilder = BuilderFactory::getInstance();
     $functionCall = $exprBuilder->statements()->functionCall('sprintf', array($exprBuilder->literal(Types::STRING, '%X'), $caller))->setFile($expression['file'])->setLine($expression['line'])->setChar($expression['char']);
     $expression = new Expression($functionCall->build());
     return $expression->compile($compilationContext);
 }
Beispiel #3
0
 /**
  * @param CompilationContext $compilationContext
  */
 public function compile(CompilationContext $compilationContext)
 {
     $codePrinter = $compilationContext->codePrinter;
     $compilationContext->insideTryCatch++;
     $currentTryCatch = ++$compilationContext->currentTryCatch;
     $codePrinter->outputBlankLine();
     $codePrinter->output('/* try_start_' . $currentTryCatch . ': */');
     $codePrinter->outputBlankLine();
     if (isset($this->_statement['statements'])) {
         $st = new StatementsBlock($this->_statement['statements']);
         $st->compile($compilationContext);
     }
     $codePrinter->outputBlankLine();
     $codePrinter->output('try_end_' . $currentTryCatch . ':');
     /**
      * If 'try' is the latest statement add a 'dummy' statement to avoid compilation errors
      */
     $codePrinter->outputBlankLine();
     $compilationContext->insideTryCatch--;
     if (isset($this->_statement['catches'])) {
         /**
          * Check if there was an exception
          */
         $codePrinter->output('if (EG(exception)) {');
         $codePrinter->increaseLevel();
         $exprBuilder = BuilderFactory::getInstance();
         foreach ($this->_statement['catches'] as $catch) {
             if (isset($catch['variable'])) {
                 $variable = $compilationContext->symbolTable->getVariableForWrite($catch['variable']['value'], $compilationContext, $catch['variable']);
                 if ($variable->getType() != 'variable') {
                     throw new CompilerException('Only dynamic variables can be used to catch exceptions', $catch['exception']);
                 }
             } else {
                 $variable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $compilationContext);
             }
             $compilationContext->backend->copyOnWrite($variable, 'EG(exception)', $compilationContext);
             /**
              * @TODO, use a builder here
              */
             $variable->setIsInitialized(true, $compilationContext, $catch);
             $variable->setMustInitNull(true);
             /**
              * Check if any of the classes in the catch block match the thrown exception
              */
             foreach ($catch['classes'] as $class) {
                 $ifCheck = $exprBuilder->statements()->ifX()->setCondition($exprBuilder->operators()->binary(BinaryOperator::OPERATOR_INSTANCEOF, $exprBuilder->variable($variable->getName()), $exprBuilder->variable($class['value'])))->setStatements($exprBuilder->statements()->block(array_merge(array($exprBuilder->statements()->rawC('zend_clear_exception(TSRMLS_C);')), isset($catch['statements']) ? $catch['statements'] : array())));
                 $ifStatement = new IfStatement($ifCheck->build());
                 $ifStatement->compile($compilationContext);
             }
             if ($variable->isTemporal()) {
                 $variable->setIdle(true);
             }
         }
         $codePrinter->decreaseLevel();
         $codePrinter->output('}');
     } else {
         $codePrinter->output('zend_clear_exception(TSRMLS_C);');
     }
 }
Beispiel #4
0
 /**
  * @param $expression
  * @param CompilationContext $compilationContext
  * @return bool|CompiledExpression
  * @throws CompilerException
  */
 public function compile($expression, CompilationContext $compilationContext)
 {
     if (!isset($expression['left'])) {
         throw new CompilerException("Invalid 'left' operand for 'typeof' expression", $expression['left']);
     }
     $functionCall = BuilderFactory::getInstance()->statements()->functionCall('gettype', array($expression['left']));
     $expression = new Expression($functionCall->build());
     return $expression->compile($compilationContext);
 }
 /**
  * @param array $expression
  * @param CompilationContext $compilationContext
  * @return CompiledExpression
  * @throws CompilerException
  */
 public function compile(array $expression, CompilationContext $compilationContext)
 {
     if (!isset($expression['left'])) {
         throw new CompilerException("Invalid 'left' operand for 'irange' expression", $expression['left']);
     }
     if (!isset($expression['right'])) {
         throw new CompilerException("Invalid 'right' operand for 'irange' expression", $expression['right']);
     }
     $exprBuilder = Expression\Builder\BuilderFactory::getInstance();
     /**
      * Implicit type coercing
      */
     $castBuilder = $exprBuilder->operators()->cast(Types::ARRAY_, $exprBuilder->statements()->functionCall('range', array($expression['left'], $expression['right'])));
     $expression = new Expression($castBuilder->build());
     $expression->setReadOnly($this->_readOnly);
     return $expression->compile($compilationContext);
 }
Beispiel #6
0
 /**
  * Intercepts calls to built-in methods
  *
  * @param string $methodName
  * @param object $caller
  * @param CompilationContext $compilationContext
  * @param Call $call
  * @param array $expression
  * @throws CompilerException
  * @return bool|\Zephir\CompiledExpression
  */
 public function invokeMethod($methodName, $caller, CompilationContext $compilationContext, Call $call, array $expression)
 {
     /**
      * Checks first whether the method exist in the array type definition
      */
     if (method_exists($this, $methodName)) {
         return $this->{$methodName}($caller, $compilationContext, $call, $expression);
     }
     /**
      * Check the method map
      */
     if (isset($this->methodMap[$methodName])) {
         $paramNumber = $this->getNumberParam($methodName);
         if ($paramNumber == 0) {
             if (isset($expression['parameters'])) {
                 $parameters = $expression['parameters'];
                 array_unshift($parameters, array('parameter' => $caller));
             } else {
                 $parameters = array(array('parameter' => $caller));
             }
         } else {
             if (isset($expression['parameters'])) {
                 $parameters = array();
                 foreach ($expression['parameters'] as $number => $parameter) {
                     if ($number == $paramNumber) {
                         $parameters[] = null;
                     }
                     $parameters[] = $parameter;
                 }
                 $parameters[$paramNumber] = array('parameter' => $caller);
             } else {
                 $parameters = array(array('parameter' => $caller));
             }
         }
         $functionCall = BuilderFactory::getInstance()->statements()->functionCall($this->methodMap[$methodName], $parameters)->setFile($expression['file'])->setLine($expression['line'])->setChar($expression['char']);
         $expression = new Expression($functionCall->build());
         return $expression->compile($compilationContext);
     }
     throw new CompilerException(sprintf('Method "%s" is not a built-in method of type "%s"', $methodName, $this->getTypeName()), $expression);
 }
Beispiel #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();
         }
     }
 }
Beispiel #8
0
 /**
  * @return $this|ExpressionLetStatement
  */
 protected function getLetStatement()
 {
     $exprBuilder = BuilderFactory::getInstance();
     if ($this->isStatic()) {
         $className = '\\' . $this->classDefinition->getCompleteName();
         $expr = $exprBuilder->raw($this->original['default']);
         return $exprBuilder->statements()->let(array($exprBuilder->operators()->assignStaticProperty($className, $this->name, $expr)->setFile($this->original['default']['file'])->setLine($this->original['default']['line'])->setChar($this->original['default']['char'])));
     }
     $lsb = $exprBuilder->statements()->let(array($exprBuilder->operators()->assignProperty('this', $this->name, $exprBuilder->raw($this->original['default']))->setFile($this->original['default']['file'])->setLine($this->original['default']['line'])->setChar($this->original['default']['char'])));
     return $exprBuilder->statements()->ifX()->setCondition($exprBuilder->operators()->binary(BinaryOperator::OPERATOR_EQUALS, $exprBuilder->operators()->binary(BinaryOperator::OPERATOR_ACCESS_PROPERTY, $exprBuilder->variable('this'), $exprBuilder->literal(Types::STRING, $this->name)), $exprBuilder->literal(Types::NULL_)))->setStatements($exprBuilder->statements()->block(array($lsb)));
 }
Beispiel #9
0
 /**
  * Transforms calls to method "join" to function calls to "join"
  *
  * @param object $caller
  * @param CompilationContext $compilationContext
  * @param Call $call
  * @param array $expression
  * @return bool|\Zephir\CompiledExpression
  */
 public function join($caller, CompilationContext $compilationContext, Call $call, array $expression)
 {
     $functionCall = BuilderFactory::getInstance()->statements()->functionCall('join', $expression['parameters'])->addArgument($caller)->setFile($expression['file'])->setLine($expression['line'])->setChar($expression['char']);
     $expression = new Expression($functionCall->build());
     return $expression->compile($compilationContext);
 }
Beispiel #10
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;
     $exprBuilder = BuilderFactory::getInstance();
     /**
      * 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
      */
     $builderLet = $exprBuilder->statements()->let();
     $builderLet->setAssignments(array($exprBuilder->operators()->assignVariable($upperBoundVariable->getName(), $exprBuilder->literal($parameters[1]->getType(), $parameters[1]->getCode())->setFile($this->_statement['file'])->setLine($this->_statement['line'])->setChar($this->_statement['char']))->setFile($this->_statement['file'])->setLine($this->_statement['line'])->setChar($this->_statement['char'])));
     $statement = new LetStatement($builderLet->build());
     $statement->compile($compilationContext);
     if ($this->_statement['reverse']) {
         /**
          * Create an implicit 'let' operation for the initialize expression
          */
         $builderLet->setAssignments(array($exprBuilder->operators()->assignVariable($tempVariable->getName(), $exprBuilder->variable($upperBoundVariable->getName())->setFile($this->_statement['file'])->setLine($this->_statement['line'])->setChar($this->_statement['char']))->setFile($this->_statement['file'])->setLine($this->_statement['line'])->setChar($this->_statement['char'])));
         $statement = new LetStatement($builderLet->build());
     } else {
         /**
          * Create an implicit 'let' operation for the initialize expression
          */
         $builderLet->setAssignments(array($exprBuilder->operators()->assignVariable($tempVariable->getName(), $exprBuilder->literal($parameters[0]->getType(), $parameters[0]->getCode())->setFile($this->_statement['file'])->setLine($this->_statement['line'])->setChar($this->_statement['char']))->setFile($this->_statement['file'])->setLine($this->_statement['line'])->setChar($this->_statement['char'])));
         $statement = new LetStatement($builderLet->build());
     }
     $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('}');
 }