/** * 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 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); }
/** * @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); }
/** * 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('}'); }
/** * @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; }
/** * 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 * @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(); } } }
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); }
/** * @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(); } }
/** * 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); }