/** * Pre-compiles a Zephir file. Generates the IR and perform basic validations * * @param Compiler $compiler * @throws ParseException * @throws CompilerException * @throws Exception */ public function preCompile(Compiler $compiler) { $ir = $this->genIR($compiler); if (!is_array($ir)) { throw new Exception("Cannot parse file: " . realpath($this->_filePath)); } if (isset($ir['type']) && $ir['type'] == 'error') { throw new ParseException($ir['message'], $ir); } /** * Compilation context stores common objects required by compilation entities */ $compilationContext = new CompilationContext(); /** * Set global compiler in the compilation context */ $compilationContext->compiler = $compiler; /** * Set global config in the compilation context */ $compilationContext->config = $this->_config; /** * Set global logger in the compilation context */ $compilationContext->logger = $this->_logger; /** * Alias manager */ $compilationContext->aliasManager = $this->_aliasManager; $compilationContext->backend = $compiler->backend; /** * Traverse the top level statements looking for the namespace */ $namespace = null; foreach ($ir as $topStatement) { switch ($topStatement['type']) { case 'namespace': if ($namespace !== null) { throw new CompilerException("The namespace must be defined just one time", $topStatement); } $namespace = $topStatement['name']; $this->_namespace = $namespace; if (!preg_match('/^[A-Z]/', $namespace)) { throw new CompilerException("Namespace '" . $namespace . "' must be in camelized-form", $topStatement); } break; case 'cblock': $this->_headerCBlocks[] = $topStatement['value']; break; case 'function': /* Just do the precompilation of the function */ $functionDefinition = new FunctionDefinition($namespace, $topStatement['name'], isset($topStatement['parameters']) ? new ClassMethodParameters($topStatement['parameters']) : null, isset($topStatement['statements']) ? new StatementsBlock($topStatement['statements']) : null, isset($topStatement['return-type']) ? $topStatement['return-type'] : null, $topStatement); $functionDefinition->preCompile($compilationContext); $this->addFunction($compiler, $functionDefinition, $topStatement); break; } } if (!$namespace) { throw new CompilerException("A namespace is required", $topStatement); } /* Set namespace and flag as global, if before namespace declaration */ foreach ($this->_functionDefinitions as $funcDef) { if ($funcDef->getNamespace() == null) { $funcDef->setGlobal(true); $funcDef->setNamespace($compiler->getConfig()->get('namespace')); } } $class = false; $interface = false; $lastComment = null; foreach ($ir as $topStatement) { switch ($topStatement['type']) { case 'class': if ($class || $interface) { throw new CompilerException("More than one class/interface defined in the same file", $topStatement); } $class = true; $name = $topStatement['name']; $this->preCompileClass($compilationContext, $namespace, $topStatement, $lastComment); $this->_originalNode = $topStatement; $lastComment = null; break; case 'interface': if ($class || $interface) { throw new CompilerException("More than one class/interface defined in the same file", $topStatement); } $interface = true; $name = $topStatement['name']; $this->preCompileInterface($namespace, $topStatement, $lastComment); $this->_originalNode = $topStatement; $lastComment = null; break; case 'use': if ($interface || $class) { throw new CompilerException("Aliasing must be done before declaring any class or interface", $topStatement); } $this->_aliasManager->add($topStatement); break; case 'comment': $lastComment = $topStatement; break; } } if (!$class && !$interface) { throw new CompilerException("Every file must contain at least a class or an interface", $topStatement); } if (!$this->_external) { $expectedPath = strtolower(str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR . $name) . '.zep'; if (strtolower($this->_filePath) != $expectedPath) { $className = str_replace('\\', '/', $namespace) . '\\' . $name; $message = 'Unexpected class name ' . $className . ' in file: ' . $this->_filePath . ', expected: ' . $expectedPath; throw new CompilerException($message); } } if ($compilationContext->classDefinition) { if ($extendsClass = $compilationContext->classDefinition->getExtendsClass()) { $compiler->isClass($extendsClass); } if ($interfaces = $compilationContext->classDefinition->getImplementedInterfaces()) { foreach ($interfaces as $interface) { $compiler->isInterface($interface); } } } $this->_ir = $ir; }
/** * Adds a function to the function definitions * * @param FunctionDefinition $func * @param array $statement */ public function addFunction(FunctionDefinition $func, $statement = null) { $funcName = strtolower($func->getInternalName()); if (isset($this->functionDefinitions[$funcName])) { throw new CompilerException("Function '" . $func->getCompleteName() . "' was defined more than one time", $statement); } $this->functionDefinitions[$funcName] = $func; }