153 lines
3.3 KiB
PHP
153 lines
3.3 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @author Tassos.gr <info@tassos.gr>
|
|
* @link https://www.tassos.gr
|
|
* @copyright Copyright © 2024 Tassos All Rights Reserved
|
|
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
|
*/
|
|
|
|
namespace NRFramework\Parser;
|
|
|
|
defined('_JEXEC') or die;
|
|
|
|
use NRFramework\Parser\Lexer;
|
|
use NRFramework\Parser\RingBuffer;
|
|
|
|
/**
|
|
* Parser base class
|
|
* LL(k) recursive-decent parser with backtracking support
|
|
*/
|
|
abstract class Parser
|
|
{
|
|
/**
|
|
* Lexer instance (feeds the parser with tokens)
|
|
*
|
|
* @var NRFramework\Parser\Lexer
|
|
*/
|
|
protected $input = null;
|
|
|
|
/**
|
|
* Ring buffer of the next k tokens
|
|
* from the input stream
|
|
*
|
|
* @var RingBuffer
|
|
*/
|
|
protected $lookahead = null;
|
|
|
|
/**
|
|
* k: Number of lookahead tokens
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $k;
|
|
|
|
/**
|
|
* Array(stack) containing the current
|
|
* contents of the lookahead buffer when
|
|
* marking the position of the stream
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $lookahead_history = null;
|
|
|
|
/**
|
|
* Lexer constructor
|
|
*
|
|
* @param Lexer $input
|
|
* @param integer $k, number of lookahead tokens
|
|
*/
|
|
public function __construct(Lexer $input, $k = 1)
|
|
{
|
|
if (!is_integer($k) || ($k < 1))
|
|
{
|
|
throw new \InvalidArgumentException('Parser: $k must be greater than 0!');
|
|
}
|
|
$this->k = $k;
|
|
$this->input = $input;
|
|
$this->lookahead_history = [];
|
|
|
|
// initialize lookahead buffer
|
|
$this->resetBuffer();
|
|
}
|
|
|
|
/**
|
|
* Checks the type of the next token.
|
|
* Advances the position in the token stream.
|
|
*
|
|
* @param string $type
|
|
* @return void
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
public function match($type)
|
|
{
|
|
if ($this->lookahead[0]->type === $type)
|
|
{
|
|
$this->consume();
|
|
return;
|
|
}
|
|
|
|
throw new Exceptions\SyntaxErrorException('Expecting token ' . $type . ', found ' . $this->lookahead[0]);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the next token from the input stream
|
|
* and add it to the buffer.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function consume()
|
|
{
|
|
$this->lookahead[] = $this->input->nextToken();
|
|
}
|
|
|
|
/**
|
|
* Marks the position in the token stream
|
|
*
|
|
* @return void
|
|
*/
|
|
public function mark()
|
|
{
|
|
array_push($this->lookahead_history, $this->lookahead);
|
|
$this->input->mark();
|
|
}
|
|
|
|
/**
|
|
* Reset to a previously marked position
|
|
* in the token stream
|
|
*
|
|
* @return void
|
|
*/
|
|
public function reset()
|
|
{
|
|
$this->input->reset();
|
|
|
|
// reset lookahead buffer if not marked
|
|
if (empty($this->lookahead_history))
|
|
{
|
|
$this->resetBuffer();
|
|
}
|
|
// normal reset
|
|
else
|
|
{
|
|
$this->lookahead = array_pop($this->lookahead_history);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets and refills the lookahead buffer starting
|
|
* from the current position in the token stream
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function resetBuffer()
|
|
{
|
|
$this->lookahead = new RingBuffer($this->k);
|
|
for ($i=0; $i < $this->k; $i++)
|
|
{
|
|
$this->consume();
|
|
}
|
|
}
|
|
}
|