fix Tokenizer

This commit is contained in:
Nikita 2013-07-04 03:14:50 +04:00
parent dd95df8eb6
commit cf6bae47cb

View File

@ -35,12 +35,12 @@ defined('T_YIELD') || define('T_YIELD', 370);
* @package Fenom
* @author Ivan Shalganov <a.cobest@gmail.com>
*/
class Tokenizer {
class Tokenizer
{
const TOKEN = 0;
const TEXT = 1;
const WHITESPACE = 2;
const LINE = 3;
/**
* Some text value: foo, bar, new, class ...
*/
@ -77,13 +77,6 @@ class Tokenizer {
* Condition operation
*/
const MACRO_COND = 1008;
public $tokens;
public $p = 0;
public $quotes = 0;
private $_max = 0;
private $_last_no = 0;
/**
* @see http://docs.php.net/manual/en/tokens.php
* @var array groups of tokens
@ -135,7 +128,6 @@ class Tokenizer {
\T_LNUMBER => 1, \T_DNUMBER => 1, \T_CONSTANT_ENCAPSED_STRING => 1
)
);
/**
* Special tokens
* @var array
@ -143,11 +135,17 @@ class Tokenizer {
private static $spec = array(
'true' => 1, 'false' => 1, 'null' => 1, 'TRUE' => 1, 'FALSE' => 1, 'NULL' => 1
);
public $tokens;
public $p = 0;
public $quotes = 0;
private $_max = 0;
private $_last_no = 0;
/**
* @param $query
*/
public function __construct($query) {
public function __construct($query)
{
$tokens = array(-1 => array(\T_WHITESPACE, '', '', 1));
$_tokens = token_get_all("<?php " . $query);
$line = 1;
@ -185,12 +183,32 @@ class Tokenizer {
$this->_last_no = $this->tokens[$this->_max][3];
}
/**
* Get token name
* @static
* @param int|string $token
* @return string
*/
public static function getName($token)
{
if (is_string($token)) {
return $token;
} elseif (is_integer($token)) {
return token_name($token);
} elseif (is_array($token)) {
return token_name($token[0]);
} else {
return null;
}
}
/**
* Is incomplete mean some string not closed
*
* @return int
*/
public function isIncomplete() {
public function isIncomplete()
{
return ($this->quotes % 2) || ($this->tokens[$this->_max][0] === T_ENCAPSED_AND_WHITESPACE);
}
@ -200,7 +218,8 @@ class Tokenizer {
* @link http://php.net/manual/en/iterator.current.php
* @return mixed Can return any type.
*/
public function current() {
public function current()
{
return $this->curr[1];
}
@ -210,45 +229,25 @@ class Tokenizer {
* @link http://php.net/manual/en/iterator.next.php
* @return Tokenizer
*/
public function next() {
public function next()
{
if ($this->p > $this->_max) {
return $this;
}
$this->p++;
unset($this->prev, $this->curr, $this->next);
return $this;
}
/**
* Check token type. If token type is one of expected types return true. Otherwise return false
*
* @param array $expects
* @param string|int $token
* @return bool
*/
private function _valid($expects, $token) {
foreach($expects as $expect) {
if(is_string($expect) || $expect < 1000) {
if($expect === $token) {
return true;
}
} else {
if(isset(self::$_macros[ $expect ][ $token ])) {
return true;
}
}
}
return false;
}
/**
* If the next token is a valid one, move the position of cursor one step forward. Otherwise throws an exception.
* @param array $tokens
* @throws UnexpectedTokenException
* @return mixed
*/
public function _next($tokens) {
public function _next($tokens)
{
$this->next();
if (!$this->curr) {
throw new UnexpectedTokenException($this, $tokens);
@ -267,19 +266,21 @@ class Tokenizer {
* Fetch next specified token or throw an exception
* @return mixed
*/
public function getNext(/*int|string $token1, int|string $token2, ... */) {
public function getNext( /*int|string $token1, int|string $token2, ... */)
{
$this->_next(func_get_args());
return $this->current();
}
/**
* Return substring. This method doesn't move pointer.
* @param int $offset
* @param int $limit
* @return string
*/
public function getSubstr($offset, $limit = 0) {
public function getSubstr($offset, $limit = 0)
{
$str = '';
if (!$limit) {
$limit = $this->_max;
@ -289,6 +290,7 @@ class Tokenizer {
for ($i = $offset; $i <= $limit; $i++) {
$str .= $this->tokens[$i][1] . $this->tokens[$i][2];
}
return $str;
}
@ -297,10 +299,12 @@ class Tokenizer {
* @return mixed
* @throws UnexpectedTokenException
*/
public function getAndNext() {
public function getAndNext()
{
if ($this->curr) {
$cur = $this->curr[1];
$this->next();
return $cur;
} else {
throw new UnexpectedTokenException($this, func_get_args());
@ -312,7 +316,8 @@ class Tokenizer {
* @param $token1
* @return bool
*/
public function isNext($token1/*, ...*/) {
public function isNext($token1 /*, ...*/)
{
return $this->next && $this->_valid(func_get_args(), $this->next[0]);
}
@ -321,7 +326,8 @@ class Tokenizer {
* @param $token1
* @return bool
*/
public function is($token1/*, ...*/) {
public function is($token1 /*, ...*/)
{
return $this->curr && $this->_valid(func_get_args(), $this->curr[0]);
}
@ -330,7 +336,8 @@ class Tokenizer {
* @param $token1
* @return bool
*/
public function isPrev($token1/*, ...*/) {
public function isPrev($token1 /*, ...*/)
{
return $this->prev && $this->_valid(func_get_args(), $this->prev[0]);
}
@ -341,7 +348,8 @@ class Tokenizer {
* @throws UnexpectedTokenException
* @return mixed
*/
public function get($token1 /*, $token2 ...*/) {
public function get($token1 /*, $token2 ...*/)
{
if ($this->curr && $this->_valid(func_get_args(), $this->curr[0])) {
return $this->curr[1];
} else {
@ -353,12 +361,14 @@ class Tokenizer {
* Step back
* @return Tokenizer
*/
public function back() {
public function back()
{
if ($this->p === 0) {
return $this;
}
$this->p--;
unset($this->prev, $this->curr, $this->next);
return $this;
}
@ -368,7 +378,8 @@ class Tokenizer {
* @param string $key
* @return mixed
*/
public function __get($key) {
public function __get($key)
{
switch ($key) {
case 'curr':
return $this->curr = ($this->p <= $this->_max) ? $this->tokens[$this->p] : null;
@ -381,7 +392,8 @@ class Tokenizer {
}
}
public function count() {
public function count()
{
return $this->_max;
}
@ -389,7 +401,8 @@ class Tokenizer {
* Return the key of the current element
* @return mixed scalar on success, or null on failure.
*/
public function key() {
public function key()
{
return $this->curr ? $this->curr[0] : null;
}
@ -398,44 +411,30 @@ class Tokenizer {
* @return boolean The return value will be casted to boolean and then evaluated.
* Returns true on success or false on failure.
*/
public function valid() {
public function valid()
{
return (bool)$this->curr;
}
/**
* Get token name
* @static
* @param int|string $token
* @return string
*/
public static function getName($token) {
if(is_string($token)) {
return $token;
} elseif(is_integer($token)) {
return token_name($token);
} elseif(is_array($token)) {
return token_name($token[0]);
} else {
return null;
}
}
/**
* Skip specific token or throw an exception
*
* @throws UnexpectedTokenException
* @return Tokenizer
*/
public function skip(/*$token1, $token2, ...*/) {
public function skip( /*$token1, $token2, ...*/)
{
if (func_num_args()) {
if ($this->_valid(func_get_args(), $this->curr[0])) {
$this->next();
return $this;
} else {
throw new UnexpectedTokenException($this, func_get_args());
}
} else {
$this->next();
return $this;
}
}
@ -446,10 +445,12 @@ class Tokenizer {
* @param int|string $token1
* @return Tokenizer
*/
public function skipIf($token1/*, $token2, ...*/) {
public function skipIf($token1 /*, $token2, ...*/)
{
if ($this->_valid(func_get_args(), $this->curr[0])) {
$this->next();
}
return $this;
}
@ -460,7 +461,8 @@ class Tokenizer {
* @return Tokenizer
* @throws UnexpectedTokenException
*/
public function need($token1/*, $token2, ...*/) {
public function need($token1 /*, $token2, ...*/)
{
if ($this->_valid(func_get_args(), $this->curr[0])) {
return $this;
} else {
@ -474,7 +476,8 @@ class Tokenizer {
* @param int $after count tokens after current token
* @return array
*/
public function getSnippet($before = 0, $after = 0) {
public function getSnippet($before = 0, $after = 0)
{
$from = 0;
$to = $this->p;
if ($before > 0) {
@ -516,11 +519,13 @@ class Tokenizer {
* @param int $after
* @return string
*/
public function getSnippetAsString($before = 0, $after = 0) {
public function getSnippetAsString($before = 0, $after = 0)
{
$str = "";
foreach ($this->getSnippet($before, $after) as $token) {
$str .= $token[1] . $token[2];
}
return trim(str_replace("\n", '↵', $str));
}
@ -528,7 +533,8 @@ class Tokenizer {
* Check if current is special value: true, TRUE, false, FALSE, null, NULL
* @return bool
*/
public function isSpecialVal() {
public function isSpecialVal()
{
return isset(self::$spec[$this->current()]);
}
@ -536,14 +542,16 @@ class Tokenizer {
* Check if the token is last
* @return bool
*/
public function isLast() {
public function isLast()
{
return $this->p === $this->_max;
}
/**
* Move pointer to the end
*/
public function end() {
public function end()
{
$this->p = $this->_max;
}
@ -551,16 +559,44 @@ class Tokenizer {
* Return line number of the current token
* @return mixed
*/
public function getLine() {
public function getLine()
{
return $this->curr ? $this->curr[3] : $this->_last_no;
}
/**
* Check token type. If token type is one of expected types return true. Otherwise return false
*
* @param array $expects
* @param string|int $token
* @return bool
*/
private function _valid($expects, $token)
{
foreach ($expects as $expect) {
if (is_string($expect) || $expect < 1000) {
if ($expect === $token) {
return true;
}
} else {
if (isset(self::$_macros[$expect][$token])) {
return true;
}
}
}
return false;
}
}
/**
* Unexpected token
*/
class UnexpectedTokenException extends \RuntimeException {
public function __construct(Tokenizer $tokens, $expect = null, $where = null) {
class UnexpectedTokenException extends \RuntimeException
{
public function __construct(Tokenizer $tokens, $expect = null, $where = null)
{
if ($expect && count($expect) == 1 && is_string($expect[0])) {
$expect = ", expect '" . $expect[0] . "'";
} else {
@ -574,4 +610,6 @@ class UnexpectedTokenException extends \RuntimeException {
$this->message = "Unexpected token '" . $tokens->current() . "' in " . ($where ? : "expression") . "$expect";
}
}
};
}
;