<?php
namespace Tlf;
/**
* Used to process a string into an Asymmetrical Syntax Tree (AST)
*
*/
class Lexer {
use LexerTrait\Internals;
use LexerTrait\Cache;
Use LexerTrait\Directives;
Use LexerTrait\InstructionProcessor;
Use LexerTrait\Instructions;
use LexerTrait\MappedMethods;
/**
* set true to show debug messages
*/
public bool $debug = false;
/**
* The loop we're on. 0 means not started. 1 means we're executing the first loop. 2 means 2nd loop, etc.
*/
public int $loop_count = 0;
/**
* Whether to load from & save to the cache or not.
*/
public bool $useCache = true;
/**
* The token currently being processed
*/
public $token = null;
/**
* array of grammar class names & their file's last mtime
*/
protected $grammarListForCache = null;
/**
* the loop to stop processing on. Used for debugging, when writing a grammar.
*/
public $stop_loop = -1;
/**
* stop lexing
*/
public function abort(){
$this->stop_loop = $this->loop_count;
}
public function haltAll(){
$this->haltAll = true;
}
public function continueAll(){
$this->haltAll = false;
}
public function haltInstructions(){
$this->haltInstructions = true;
}
public function continueInstructions(){
$this->haltInstructions = false;
}
public function previous($key){
return $this->previous[$key] ?? null;
}
public function setPrevious($key, $value){
$old = $this->previous($key);
$this->previous[$key] = $value;
return $old;
}
public function appendToPrevious($key, $value){
$old = $this->previous($key);
if (is_array($this->previous[$key]??null))$this->previous[$key][] = $value;
else $this->previous[$key] = ($this->previous[$key]??'') . $value;
return $old;
}
public function unsetPrevious($key){
$prev = $this->previous($key);
unset($this->previous[$key]);
return $prev;
}
public function clearBuffer(){
$buffer = $this->buffer;
$this->buffer = '';
return $buffer;
}
public function getToken(){
return $this->token;
}
/**
* @param $grammar A grammar
* @param $name pass to override default namespace
* @param $executeOnAdd `false` to skip calling `onGrammarAdded`
*/
public function addGrammar(object $grammar, string $namespace=null, $executeOnAdd=true){
$grammar->setLexer($this);
$this->grammars[strtolower($namespace ?? $grammar->getNamespace())] = $grammar;
if ($executeOnAdd)$grammar->onGrammarAdded($this);
}
public function getGrammar(string $namespace){
return $this->grammars[strtolower($namespace)];
}
public function setHead($ast){
$this->head[] = $ast;
}
public function popHead(){
if (count($this->head)>1)
return array_pop($this->head);
return $this->head[0];
}
public function getHead(){
return $this->head[count($this->head)-1];
}
public function rootAst(){
return $this->head[0];
}
/*
* Create a 'file' ast & call 'lexAst'. Asts generated by this function are cached. Chache is invalidated when the source of the active grammars or the file being processed changes.
*
* @param $file an absolute file path
*/
public function lexFile($file){
$debug = true;
if ($ast=$this->getCachedAst($file)){
echo "\nAst loaded from cache for file '$file'\n";
// exit;
return $ast;
}
if ($debug){
echo "\n\n\n\n/////////////////////////////////";
echo "\n/////////////////////////////////";
echo "\nParse File '$file'";
echo "\n/////////////////////////////////";
echo "\n/////////////////////////////////";
echo "\n\n\n\n";
}
$ast = new Lexer\Ast('file');
// $ast->set('source', file_get_contents($file));
$ast->set('ext', pathinfo($file,PATHINFO_EXTENSION));
$ast->set('name', pathinfo($file,PATHINFO_FILENAME));
$ast->set('path', $file);
// $this->setHead($ast);
$str = file_get_contents($ast->get('path'));
$ast = $this->lex($str, $ast);
$this->cacheFileAst($file, $ast);
return $ast;
}
}