<?php
/**
* Parsec
* Copyright (c) 2010 Maxime Bouroumeau-Fuseau
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author Maxime Bouroumeau-Fuseau
* @copyright 2010 (c) Maxime Bouroumeau-Fuseau
* @license http://www.opensource.org/licenses/mit-license.php
* @link http://github.com/maximebf/parsec
*/
namespace Parsec;
/**
* String parser using AbstractParser and Lexer
*/
class StringParser extends AbstractParser
{
const TOKEN_EOS = 'eos';
const TOKEN_TEXT = 'text';
/** @var Lexer */
protected $lexer;
/** @var array */
protected $tokens = array();
/** @var int */
protected $cursor = 0;
/** @var int */
protected $count = 0;
/**
* @param Lexer $lexer
* @param ContextFactory $contextFactory
*/
public function __construct(Lexer $lexer = null, ContextFactory $contextFactory = null)
{
$this->lexer = $lexer;
$this->contextFactory = $contextFactory;
}
/**
* @param Lexer $lexer
*/
public function setLexer(Lexer $lexer)
{
$this->lexer = $lexer;
}
/**
* @return Lexer
*/
public function getLexer()
{
return $this->lexer;
}
/**
* Parse the specified string.
* Must specify the starting context
*
* @param string $data
* @param string $context
* @param array $params Context parameters
* @param string $filename
* @return mixed Data returned by the context
*/
public function parse($string, $context, array $params = array(), $filename = null)
{
$this->data = $string;
$this->tokens = $this->lexer->tokenize($string, $filename);
$this->cursor = -1;
$this->count = count($this->tokens);
return $this->enterContext($context, $params);
}
/**
* @param string $context
* @param array $params Context parameters
* @return mixed Data returned by context
*/
public function enterContext($contextName, array $params = array(), Context $parentContext = null)
{
$context = $this->createContextInstance($contextName, array($this, $params, $parentContext));
do {
$this->cursor++;
$token = null; $value = null; $position = null;
if ($this->cursor >= $this->count) {
$token = self::TOKEN_EOS;
} else if (is_string($this->tokens[$this->cursor])) {
$token = self::TOKEN_TEXT;
$value = $this->tokens[$this->cursor];
} else {
$token = $this->tokens[$this->cursor]['token'];
$value = $this->tokens[$this->cursor]['value'];
$position = $this->tokens[$this->cursor]['position'];
}
if ($context->execute($token, $value, $position)) {
break;
}
} while ($this->cursor < $this->count);
return $context->getExitData();
}
/**
* @return array
*/
public function getCurrentToken()
{
return $this->tokens[$this->cursor];
}
/**
* @return mixed
*/
public function getCurrentTokenValue()
{
return $this->tokens[$this->cursor]['value'];
}
/**
* Checks if two tokens are equal
*
* @param mixed $token1
* @param string $token2
* @return bool
*/
public function isToken($token, $tokenName)
{
if (!is_array($token) && $tokenName == self::TOKEN_TEXT) {
return true;
}
return $token['token'] == $tokenName;
}
/**
* Returns the name of a token
*
* @param mixed $token
* @return string
*/
public function getTokenName($token)
{
if (!is_array($token)) {
return self::TOKEN_TEXT;
}
return $token['token'];
}
/**
* @return bool
*/
public function hasMoreTokens()
{
return $this->cursor < $this->count;
}
/**
* Skips the next token
*
* @param int $howMany
* @return StringParser
*/
public function skipNext($howMany = 1)
{
$this->cursor += $howMany;
return $this;
}
/**
* Skips tokens until the specified one
*
* @param string $tokenName
* @param int $step
* @return StringParser
*/
public function skipUntil($tokenName, $step = 1)
{
do {
$this->cursor += $step;
} while($this->cursor >= 0 && $this->cursor < $this->count &&
!$this->isToken($this->tokens[$this->cursor], $tokenName));
return $this;
}
/**
* Rewinds the parser position
*
* @param int $howMany
* @return StringParser
*/
public function rewind($howMany = 1)
{
$this->cursor -= $howMany;
return $this;
}
/**
* Rewinds tokens until the specified one
*
* @param string $tokenName
* @return StringParser
*/
public function rewindUntil($tokenName)
{
return $this->skipUntil($tokenName, -1);
}
/**
* Checks if the next token matches the specified one
*
* @param string $token
* @param array $ignore
* @param int $direction
* @return bool
*/
public function isNextToken($tokenName, $ignore = array(), $direction = 1)
{
$i = $this->cursor;
do {
$i += $direction;
if (!in_array($this->getTokenName($this->tokens[$i]), $ignore)) {
break;
}
} while ($i >= 0 && $i < $this->count);
return $this->isToken($this->tokens[$i], $tokenName);
}
/**
* Checks if the previous token matches the specified one
*
* @param string $token
* @param array $ignore
* @return bool
*/
public function isPreviousToken($tokenName, $ignore = array())
{
return $this->isNextToken($tokenName, $ignore, -1);
}
/**
* @param bool $skip
* @return array
*/
public function getNextToken($skip = false)
{
$token = $this->tokens[$this->cursor + 1];
if ($skip) {
$this->cursor++;
}
return $token;
}
/**
* @param bool $skip
* @return string
*/
public function getNextTokenValue($skip = false)
{
$token = $this->getNextToken($skip);
return is_array($token) ? $token['value'] : $token;
}
/**
* @param bool $rewind
* @return array
*/
public function getPreviousToken($rewind = false)
{
$token = $this->tokens[$this->cursor - 1];
if ($rewind) {
$this->cursor--;
}
return $token;
}
/**
* @param bool $rewind
* @return string
*/
public function getPreviousTokenValue($rewind = false)
{
$token = $this->getPreviousToken($rewind);
return is_array($token) ? $token['value'] : $token;
}
public function findNextTokenValue($tokenName, $direction = 1)
{
$i = $this->cursor;
do {
$i += $direction;
if ($this->isToken($this->tokens[$i], $tokenName)) {
break;
}
} while ($i >= 0 && $i < $this->count);
return is_array($this->tokens[$i]) ? $this->tokens[$i]['value'] : $this->tokens[$i];
}
public function findPreviousTokenValue($tokenName)
{
return $this->findNextTokenValue($tokenName, -1);
}
public function getCursorPosition()
{
return $this->cursor;
}
public function seek($position)
{
$this->cursor = $position;
return $this;
}
}