/** * @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; }
/** * Resolves an expression * * @param CompilationContext $compilationContext * @return bool|CompiledExpression|mixed * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { $expression = $this->_expression; $type = $expression['type']; $compilableExpression = null; switch ($type) { case 'null': return new LiteralCompiledExpression('null', null, $expression); case 'int': case 'integer': return new LiteralCompiledExpression('int', $expression['value'], $expression); case 'long': case 'double': case 'bool': return new LiteralCompiledExpression($type, $expression['value'], $expression); case 'string': if (!$this->_stringOperation) { if (ctype_digit($expression['value'])) { return new CompiledExpression('int', $expression['value'], $expression); } } return new LiteralCompiledExpression('string', str_replace(PHP_EOL, '\\n', $expression['value']), $expression); case 'istring': return new LiteralCompiledExpression('istring', str_replace(PHP_EOL, '\\n', $expression['value']), $expression); case 'char': if (!strlen($expression['value'])) { throw new CompilerException("Invalid empty char literal", $expression); } if (strlen($expression['value']) > 2) { if (strlen($expression['value']) > 10) { throw new CompilerException("Invalid char literal: '" . substr($expression['value'], 0, 10) . "...'", $expression); } else { throw new CompilerException("Invalid char literal: '" . $expression['value'] . "'", $expression); } } return new LiteralCompiledExpression('char', $expression['value'], $expression); case 'variable': $var = $compilationContext->symbolTable->getVariable($expression['value']); if ($var) { if ($var->getRealName() == 'this') { $var = 'this'; } else { $var = $var->getName(); } } else { $var = $expression['value']; } return new CompiledExpression('variable', $var, $expression); case 'constant': $compilableExpression = new Constants(); break; case 'empty-array': return $this->emptyArray($expression, $compilationContext); case 'array-access': $compilableExpression = new NativeArrayAccess(); $compilableExpression->setNoisy($this->isNoisy()); break; case 'property-access': $compilableExpression = new PropertyAccess(); $compilableExpression->setNoisy($this->isNoisy()); break; case 'property-string-access': case 'property-dynamic-access': $compilableExpression = new PropertyDynamicAccess(); $compilableExpression->setNoisy($this->isNoisy()); break; case 'static-constant-access': $compilableExpression = new StaticConstantAccess(); break; case 'static-property-access': $compilableExpression = new StaticPropertyAccess(); break; case 'fcall': $functionCall = new FunctionCall(); return $functionCall->compile($this, $compilationContext); case 'mcall': $methodCall = new MethodCall(); return $methodCall->compile($this, $compilationContext); case 'scall': $staticCall = new StaticCall(); return $staticCall->compile($this, $compilationContext); case 'isset': $compilableExpression = new IssetOperator(); break; case 'fetch': $compilableExpression = new FetchOperator(); break; case 'empty': $compilableExpression = new EmptyOperator(); break; case 'array': $compilableExpression = new NativeArray(); break; case 'new': $compilableExpression = new NewInstanceOperator(); break; case 'new-type': $compilableExpression = new NewInstanceTypeOperator(); break; case 'not': $compilableExpression = new NotOperator(); break; case 'bitwise_not': $compilableExpression = new BitwiseNotOperator(); break; case 'equals': $compilableExpression = new EqualsOperator(); break; case 'not-equals': $compilableExpression = new NotEqualsOperator(); break; case 'identical': $compilableExpression = new IdenticalOperator(); break; case 'not-identical': $compilableExpression = new NotIdenticalOperator(); break; case 'greater': $compilableExpression = new GreaterOperator(); break; case 'less': $compilableExpression = new LessOperator(); break; case 'less-equal': $compilableExpression = new LessEqualOperator(); break; case 'greater-equal': $compilableExpression = new GreaterEqualOperator(); break; case 'add': $compilableExpression = new AddOperator(); break; case 'minus': $compilableExpression = new MinusOperator(); break; case 'sub': $compilableExpression = new SubOperator(); break; case 'mul': $compilableExpression = new MulOperator(); break; case 'div': $compilableExpression = new DivOperator(); break; case 'mod': $compilableExpression = new ModOperator(); break; case 'and': $compilableExpression = new AndOperator(); break; case 'or': $compilableExpression = new OrOperator(); break; case 'bitwise_and': $compilableExpression = new BitwiseAndOperator(); break; case 'bitwise_or': $compilableExpression = new BitwiseOrOperator(); break; case 'bitwise_xor': $compilableExpression = new BitwiseXorOperator(); break; case 'bitwise_shiftleft': $compilableExpression = new ShiftLeftOperator(); break; case 'bitwise_shiftright': $compilableExpression = new ShiftRightOperator(); break; case 'concat': $expr = new ConcatOperator(); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'irange': $compilableExpression = new RangeInclusiveOperator(); break; case 'erange': $compilableExpression = new RangeExclusiveOperator(); break; case 'list': if ($expression['left']['type'] == 'list') { $compilationContext->logger->warning("Unnecessary extra parentheses", "extra-parentheses", $expression); } $numberPrints = $compilationContext->codePrinter->getNumberPrints(); $expr = new Expression($expression['left']); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); $resolved = $expr->compile($compilationContext); if ($compilationContext->codePrinter->getNumberPrints() - $numberPrints <= 1) { if (strpos($resolved->getCode(), ' ') !== false) { return new CompiledExpression($resolved->getType(), '(' . $resolved->getCode() . ')', $expression); } } return $resolved; case 'cast': $compilableExpression = new CastOperator(); break; case 'type-hint': return $this->compileTypeHint($expression, $compilationContext); case 'instanceof': $compilableExpression = new InstanceOfOperator(); break; case 'clone': $compilableExpression = new CloneOperator(); break; case 'ternary': $compilableExpression = new TernaryOperator(); break; case 'short-ternary': $expr = new ShortTernaryOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'likely': if (!$this->_evalMode) { throw new CompilerException("'likely' operator can only be used in evaluation expressions", $expression); } $expr = new LikelyOperator(); $expr->setReadOnly($this->isReadOnly()); return $expr->compile($expression, $compilationContext); case 'unlikely': if (!$this->_evalMode) { throw new CompilerException("'unlikely' operator can only be used in evaluation expressions", $expression); } $expr = new UnlikelyOperator(); $expr->setReadOnly($this->isReadOnly()); return $expr->compile($expression, $compilationContext); case 'typeof': $compilableExpression = new TypeOfOperator(); break; case 'require': $compilableExpression = new RequireOperator(); break; case 'closure': $compilableExpression = new Closure(); break; case 'closure-arrow': $compilableExpression = new ClosureArrow(); break; case 'reference': $compilableExpression = new Reference(); break; default: throw new CompilerException("Unknown expression: " . $type, $expression); } if (!$compilableExpression) { throw new CompilerException("Unknown expression passed as compilableExpression", $expression); } $compilableExpression->setReadOnly($this->isReadOnly()); $compilableExpression->setExpectReturn($this->_expecting, $this->_expectingVariable); return $compilableExpression->compile($expression, $compilationContext); }
/** * Generates the C sources from Zephir without compiling them * * @param CommandInterface $command * @return bool * @throws Exception */ public function generate(CommandInterface $command) { /** * Get global namespace */ $namespace = $this->checkDirectory(); /** * Check whether there are external dependencies */ $externalDependencies = $this->config->get('external-dependencies'); if (is_array($externalDependencies)) { foreach ($externalDependencies as $dependencyNs => $location) { if (!file_exists($location)) { throw new CompilerException('Location of dependency "' . $dependencyNs . '" does not exist. Check the config.json for more information'); } $this->addExternalDependency($dependencyNs, $location); } } /** * Check if there are module/request/global destructors */ $destructors = $this->config->get('destructors'); if (is_array($destructors)) { $invokeDestructors = $this->processCodeInjection($destructors); $includes = $invokeDestructors[0]; $destructors = $invokeDestructors[1]; } /** * Check if there are module/request/global initializers */ $initializers = $this->config->get('initializers'); if (is_array($initializers)) { $invokeInitializers = $this->processCodeInjection($initializers); $includes = $invokeInitializers[0]; $initializers = $invokeInitializers[1]; } /** * Round 1. pre-compile all files in memory */ $this->recursivePreCompile(str_replace('\\', DIRECTORY_SEPARATOR, $namespace)); if (!count($this->files)) { throw new Exception("Zephir files to compile couldn't be found. Did you add a first class to the extension?"); } /** * Round 2. Check 'extends' and 'implements' dependencies */ foreach ($this->files as $compileFile) { $compileFile->checkDependencies($this); } /** * Sort the files by dependency ranking */ $files = array(); $rankedFiles = array(); $this->calculateDependencies($this->files); foreach ($this->files as $rankFile) { $rank = $rankFile->getClassDefinition()->getDependencyRank(); $rankedFiles[$rank][] = $rankFile; } krsort($rankedFiles); foreach ($rankedFiles as $rank => $rankFiles) { $files = array_merge($files, $rankFiles); } $this->files = $files; /** * Convert C-constants into PHP constants */ $constantsSources = $this->config->get('constants-sources'); if (is_array($constantsSources)) { $this->loadConstantsSources($constantsSources); } /** * Set extension globals */ $globals = $this->config->get('globals'); if (is_array($globals)) { $this->setExtensionGlobals($globals); } /** * Load function optimizers */ if (self::$loadedPrototypes === false) { FunctionCall::addOptimizerDir(ZEPHIRPATH . 'Library/Optimizers/FunctionCall'); $optimizerDirs = $this->config->get('optimizer-dirs'); if (is_array($optimizerDirs)) { foreach ($optimizerDirs as $directory) { FunctionCall::addOptimizerDir(realpath($directory)); } } if (is_dir(ZEPHIRPATH . 'prototypes') && is_readable(ZEPHIRPATH . 'prototypes')) { /** * Load additional extension prototypes * @var $file \DirectoryIterator */ foreach (new \DirectoryIterator(ZEPHIRPATH . 'prototypes') as $file) { if (!$file->isDir()) { $extension = str_replace('.php', '', $file); if (!extension_loaded($extension)) { require $file->getRealPath(); } } } } self::$loadedPrototypes = true; } /** * Round 3. Compile all files to C sources */ $files = array(); $hash = ""; foreach ($this->files as $compileFile) { /** * Only compile classes in the local extension, ignore external classes */ if (!$compileFile->isExternal()) { $compileFile->compile($this, $this->stringManager); $compiledFile = $compileFile->getCompiledFile(); $methods = array(); $classDefinition = $compileFile->getClassDefinition(); foreach ($classDefinition->getMethods() as $method) { $methods[] = '[' . $method->getName() . ':' . join('-', $method->getVisibility()) . ']'; if ($method->isInitializer() && $method->isStatic()) { $this->internalInitializers[] = "\t" . $method->getName() . '(TSRMLS_C);'; } } $files[] = $compiledFile; $hash .= '|' . $compiledFile . ':' . $classDefinition->getClassEntry() . '[' . join('|', $methods) . ']'; } } /** * Round 3.2. Compile anonymous classes */ foreach ($this->anonymousFiles as $compileFile) { $compileFile->compile($this, $this->stringManager); $compiledFile = $compileFile->getCompiledFile(); $methods = array(); $classDefinition = $compileFile->getClassDefinition(); foreach ($classDefinition->getMethods() as $method) { $methods[] = '[' . $method->getName() . ':' . join('-', $method->getVisibility()) . ']'; } $files[] = $compiledFile; $hash .= '|' . $compiledFile . ':' . $classDefinition->getClassEntry() . '[' . join('|', $methods) . ']'; } $hash = md5($hash); $this->compiledFiles = $files; /** * Round 3.3. Load extra C-sources */ $extraSources = $this->config->get('extra-sources'); if (is_array($extraSources)) { $this->extraFiles = $extraSources; } else { $this->extraFiles = array(); } /** * Round 3.4. Load extra classes sources */ $extraClasses = $this->config->get('extra-classes'); if (is_array($extraClasses)) { foreach ($extraClasses as $value) { if (isset($value['source'])) { $this->extraFiles[] = $value['source']; } } } /** * Round 4. Create config.m4 and config.w32 files / Create project.c and project.h files */ $namespace = str_replace('\\', '_', $namespace); $extensionName = $this->config->get('extension-name'); if (empty($extensionName) || !is_string($extensionName)) { $extensionName = $namespace; } $needConfigure = $this->createConfigFiles($extensionName); $needConfigure |= $this->createProjectFiles($extensionName); $needConfigure |= $this->checkIfPhpized(); /** * When a new file is added or removed we need to run configure again */ if (!$command instanceof CommandGenerate) { if (!$this->fileSystem->exists(self::VERSION . '/compiled-files-sum')) { $needConfigure = true; $this->fileSystem->write(self::VERSION . '/compiled-files-sum', $hash); } else { if ($this->fileSystem->read(self::VERSION . '/compiled-files-sum') != $hash) { $needConfigure = true; $this->fileSystem->write(self::VERSION . '/compiled-files-sum', $hash); } } } /** * Round 5. Generate concatenation functions */ $this->stringManager->genConcatCode(); $this->fcallManager->genFcallCode(); if ($this->config->get('stubs-run-after-generate', 'stubs')) { $this->stubs($command, true); } return $needConfigure; }
/** * 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 CommandInterface $command * * @return bool * @throws Exception */ public function generate(CommandInterface $command) { /** * Get global namespace */ $namespace = $this->checkDirectory(); /** * Round 1. pre-compile all files in memory */ $this->recursivePreCompile(str_replace('\\', DIRECTORY_SEPARATOR, $namespace)); if (!count($this->files)) { throw new Exception("Zephir files to compile weren't found"); } /** * Round 2. Check 'extends' and 'implements' dependencies */ foreach ($this->files as $compileFile) { $compileFile->checkDependencies($this); } /** * Convert C-constants into PHP constants */ $constantsSources = $this->config->get('constants-sources'); if (is_array($constantsSources)) { $this->loadConstantsSources($constantsSources); } /** * Set extension globals */ $globals = $this->config->get('globals'); if (is_array($globals)) { $this->setExtensionGlobals($globals); } /** * Load function optimizers */ if (self::$loadedPrototypes == false) { FunctionCall::addOptimizerDir(ZEPHIRPATH . 'Library/Optimizers/FunctionCall'); $optimizerDirs = $this->config->get('optimizer-dirs'); if (is_array($optimizerDirs)) { foreach ($optimizerDirs as $directory) { FunctionCall::addOptimizerDir(realpath($directory)); } } if (is_dir(ZEPHIRPATH . 'prototypes') && is_readable(ZEPHIRPATH . 'prototypes')) { /** * Load additional extension prototypes * @var $file \DirectoryIterator */ foreach (new \DirectoryIterator(ZEPHIRPATH . 'prototypes') as $file) { if (!$file->isDir()) { $extension = str_replace('.php', '', $file); if (!extension_loaded($extension)) { require $file->getRealPath(); } } } } self::$loadedPrototypes = true; } /** * Round 3. Compile all files to C sources */ $files = array(); $hash = ""; foreach ($this->files as $compileFile) { $compileFile->compile($this, $this->stringManager); $compiledFile = $compileFile->getCompiledFile(); $methods = array(); $classDefinition = $compileFile->getClassDefinition(); foreach ($classDefinition->getMethods() as $method) { $methods[] = '[' . $method->getName() . ':' . join('-', $method->getVisibility()) . ']'; } $files[] = $compiledFile; $hash .= '|' . $compiledFile . ':' . $classDefinition->getClassEntry() . '[' . join('|', $methods) . ']'; } /** * Round 3.2. Compile anonymous classes */ foreach ($this->anonymousFiles as $compileFile) { $compileFile->compile($this, $this->stringManager); $compiledFile = $compileFile->getCompiledFile(); } $hash = md5($hash); $this->compiledFiles = $files; /** * Round 3.3. Load extra C-sources */ $extraSources = $this->config->get('extra-sources'); if (is_array($extraSources)) { $this->extraFiles = $extraSources; } else { $this->extraFiles = array(); } /** * Round 4. Create config.m4 and config.w32 files / Create project.c and project.h files */ $namespace = str_replace('\\', '_', $namespace); $needConfigure = $this->createConfigFiles($namespace); $needConfigure |= $this->createProjectFiles($namespace); $needConfigure |= $this->checkIfPhpized(); /** * When a new file is added or removed we need to run configure again */ if (!$command instanceof CommandGenerate) { if (!$this->fileSystem->exists(self::VERSION . '/compiled-files-sum')) { $needConfigure = true; $this->fileSystem->write(self::VERSION . '/compiled-files-sum', $hash); } else { if ($this->fileSystem->read(self::VERSION . '/compiled-files-sum') != $hash) { $needConfigure = true; $this->fileSystem->write(self::VERSION . '/compiled-files-sum', $hash); } } } /** * Round 5. Generate the concatenation */ $this->stringManager->genConcatCode(); if ($this->config->get('stubs-run-after-generate', 'stubs')) { $this->stubs($command, true); } return $needConfigure; }