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() );
|
|
}
|
|
|
|
}
|