168 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php declare(strict_types=1);
 | |
| 
 | |
| namespace Picea;
 | |
| 
 | |
| use Picea\Event\Compiler\CompileTokenExtension;
 | |
| use Picea\Event\Compiler\CompileTokenExtensionEvent;
 | |
| use Picea\Event\Compiler\CompileTokenTagEvent;
 | |
| use Picea\Exception\RegisterExtensionToken;
 | |
| use Picea\Extension\FunctionExtension;
 | |
| use Picea\Language\LanguageRegistration;
 | |
| 
 | |
| class Compiler
 | |
| {
 | |
|     use EventTrait;
 | |
| 
 | |
|     protected string $sourceCode = "";
 | |
| 
 | |
|     protected array $syntaxObjectList = [];
 | |
| 
 | |
|     protected array $tagList = [];
 | |
| 
 | |
|     protected string $tagTokenOpen = "\{%";
 | |
| 
 | |
|     protected string $tagTokenClose = "%\}";
 | |
| 
 | |
|     public array $extensionList = [];
 | |
| 
 | |
|     public function __construct(
 | |
|         public LanguageRegistration $languageRegistration
 | |
|     )
 | |
|     {
 | |
|         $this->languageRegistration->registerAll($this);
 | |
|     }
 | |
| 
 | |
|     public function compile(Compiler\Context $context) : string
 | |
|     {
 | |
|         $context->compiler = $this;
 | |
| 
 | |
|         /**
 | |
|          * Syntaxes such as {{ echo }} & {{= echo raw }} && {# comment #}
 | |
|          */
 | |
|         foreach($this->syntaxObjectList as $syntax) {
 | |
|             $syntax->parse($context, $this->sourceCode);
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * Control structures & functions {% functionName arguments %}
 | |
|          */
 | |
|         $replace = "#({$this->tagTokenOpen})(.*?)({$this->tagTokenClose})#s";
 | |
| 
 | |
|         $this->sourceCode = preg_replace_callback($replace, function ($matches) use (&$context) {
 | |
|             $matches[2] = trim($matches[2]);
 | |
| 
 | |
|             list($token, $arguments) = array_pad(array_filter(explode(' ', $matches[2], 2), 'strlen'), 2, null);
 | |
| 
 | |
|             $token = strtolower(trim($token));
 | |
|             $tokenName = $context->tokenName($token);
 | |
|             $tokenOptions = $context->tokenOptions($token);
 | |
| 
 | |
|             if ( $this->tagList[$tokenName] ?? false ) {
 | |
|                 $this->eventExecute(CompileTokenTagEvent::class, $context, $arguments, $tokenName, $tokenOptions);
 | |
| 
 | |
|                 return $this->tagList[$tokenName]->parse($context, $arguments, $tokenName, $tokenOptions);
 | |
|             }
 | |
|             elseif ( $this->extensionList[$tokenName] ?? false ) {
 | |
|                 $this->eventExecute(CompileTokenExtensionEvent::class, $context, $arguments, $tokenName, $tokenOptions);
 | |
| 
 | |
|                 return $this->extensionList[$tokenName]->parse($context, $arguments, $tokenName, $tokenOptions);
 | |
|             }
 | |
|             else {
 | |
|                 throw new \LogicException("Impossible to find token `$tokenName` declared in `{$matches[2]}`. Perhapse you forgot to add a custom token to Picea's engine ?");
 | |
|             }
 | |
| 
 | |
|         }, $this->sourceCode);
 | |
| 
 | |
|         return $this->sourceCode;
 | |
|     }
 | |
| 
 | |
|     public function loadSourceCode($html) : self
 | |
|     {
 | |
|         $this->sourceCode = $html;
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     public function registerCaching(CachingStrategy $cachingStrategy) : self
 | |
|     {
 | |
|     	return $this;
 | |
|     }
 | |
| 
 | |
|     public function registerSyntax(Syntax\Syntax $syntaxObject) : self
 | |
|     {
 | |
|         $this->syntaxObjectList[get_class($syntaxObject)] = $syntaxObject;
 | |
| 
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     public function registerControlStructure(ControlStructure\ControlStructure $controlStructureObject) : self
 | |
|     {
 | |
|         foreach($controlStructureObject->tokens ?? (array) ( $controlStructureObject->token ?? [] ) as $token) {
 | |
|             if ( strpos($token, '.') !== false ) {
 | |
|                 throw new RegisterExtensionToken(sprintf("Could not register token '%s' from control structure '%s' in this version. Options are now delt by extensions themselve instead.", $token, $controlStructureObject::class));
 | |
|             }
 | |
| 
 | |
|             $this->tagList[strtolower($token)] = $controlStructureObject;
 | |
|         }
 | |
| 
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     public function registerExtension(Extension\Extension $extension) : self
 | |
|     {
 | |
|         $tokens = $extension->tokens ?? (array) ( $extension->token ?? [] );
 | |
| 
 | |
|         foreach($tokens as $token) {
 | |
|             if ( strpos($token, '.') !== false ) {
 | |
|                 throw new RegisterExtensionToken(sprintf("Could not register token '%s' from extension '%s' in this version. Options are now delt by extensions themselve instead.", $token, $extension::class));
 | |
|             }
 | |
| 
 | |
|             $this->extensionList[strtolower($token)] = $extension;
 | |
|         }
 | |
| 
 | |
|         if (! $tokens ) {
 | |
|             $this->extensionList[] = $extension;
 | |
|         }
 | |
| 
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     public function registerExtensionsFunctions(Compiler\Context $context) : void
 | |
|     {
 | |
|         foreach($this->extensionList as $ext) {
 | |
|             if ($ext instanceof FunctionExtension) {
 | |
|                 foreach ($ext->exportFunctions() as $name => $value) {
 | |
|                     $callable = is_string($value) ? fn(...$args) => call_user_func_array([ $ext, $value ], $args) : null;
 | |
| 
 | |
|                     $context->pushFunction(is_numeric($name) ? $value : $name, $callable ?? $value);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     public function __toString() : string
 | |
|     {
 | |
|         return "???";
 | |
|     }
 | |
| 
 | |
|     public function getExtensionFromToken(string $name) : Extension\Extension
 | |
|     {
 | |
|         $tokenName = explode('.', $name)[0];
 | |
| 
 | |
|         if ( ! isset($this->extensionList[$tokenName]) ) {
 | |
|             throw new \InvalidArgumentException(<<<MSG
 | |
|                 Requested extension from token `$tokenName` could not be found from loaded Picea compiler's extension.
 | |
|             MSG);
 | |
|         }
 | |
| 
 | |
|         return $this->extensionList[$tokenName];
 | |
|     }
 | |
| 
 | |
|     public function exportFunctions() : void
 | |
|     {
 | |
|         static $caching = [];
 | |
|         eval( $this->context->renderFunctions() );
 | |
|     }
 | |
| 
 | |
| }
 |