picea/src/Compiler.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() );
}
}