<?php
abstract class Parser {
public $string;
public $symbol = '';
public $symbols = array();
public $forwardSymbol = '';
public $stack = array();
protected $pos = 0;
/**
* s the input string and returns the next symbol.
*
* @return string
*/
protected function sieve() {
$buffer = '';
$len = strlen($this->string);
// For all chars, do:
while($this->pos < $len) {
$whitespaces = 0;
// eat whitespace
for($char = $this->string[$this->pos];
($this->pos < $len) &&
($char == ' ' || $char == "\n" || $char == "\t" || $char == '*' || $char == '/');
$char = $this->string[++$this->pos], $whitespaces++);
if ($whitespaces != 0 && $buffer != '')
return $buffer;
// for all no whitespace chars to:
switch($this->string[$this->pos]) {
case '@': /*case '$':*/
if ($buffer != '') {
if ($whitespaces == 0)
throw new ParsingException('Syntaxmissmatch: need a space between "' . $buffer . '" and "' . $this->string[$this->pos] .'"');
return $buffer;
}
case '=': case '(': case ')':
case '{': case '}': case ',':
// if there is no buffer to return,
// return the delimeter/char
if ($buffer == '')
return $this->string[$this->pos++];
return $buffer;
break;
case '$':
// if there are no whitespaces
// between the last collected string
// and the $, thats a syntax missmatch.
if ($buffer != '') {
if ($whitespaces == 0)
throw new ParsingException('Syntaxmissmatch: need a space between "' . $buffer . '" and "$"');
return $buffer;
}
// collect the quoted string
$this->pos++;
while($this->pos < $len) {
$char = $this->string[$this->pos];
$ch = ord(strtolower($char));
if ($char != '_'
&& (($ch < ord('0') || $ch > ord('9'))
&& ($ch < ord('a') || $ch > ord('z'))))
break;
$buffer .= $char;
$this->pos++;
}
if ($buffer == '')
throw new ParsingException('Invalid variable name: Must not be empty.');
if (is_numeric($buffer[0]))
throw new ParsingException('Invalid variable name: "' . $buffer . '". Variablenames must not begin with a number.');
return '$'.$buffer;
break;
case "'": case '"': case '`':
// remember the quoting char
$quote = $this->string[$this->pos];
// if there are no whitespaces
// between the last collected string
// and the quote, thats a syntax missmatch.
if ($buffer != '') {
if ($whitespaces == 0)
throw new ParsingException('Syntaxmissmatch: need a space between "' . $buffer . '" and "' . $quote .'"');
return $buffer;
}
// collect the quoted string
while($this->pos < $len) {
$this->pos++;
if ($this->string[$this->pos] == $quote) {
// if quote appears, this means the end
// of collecting the string.
$this->pos++;
return $buffer;
} else if ($this->string[$this->pos] == '\\') {
// if a \ appears, we have a escape sequence
$this->pos++;
if ($this->string[$this->pos] == '\\')
// a second \ means that we have to
// collect that \
$buffer .= '\\';
else if ($this->string[$this->pos] == $quote)
// collect the quote
$buffer .= $quote;
else
throw new ParsingException('Invalid escape sequence');
} else {
// just collect
$buffer .= $this->string[$this->pos];
}
}
break;
default:
// just collect
$buffer .= $this->string[$this->pos++];
}
}
return $buffer;
}
public function parse($string) {
$this->string = $string;
$this->pos = 0;
$this->symbol = '';
$this->symbols = array();
$this->nextSymbol();
$this->nextSymbol();
$this->read('START');
return true;
}
abstract protected function read_START();
protected function testFollowOf($name) {
if (in_array($this->symbol, $this->follow[$name])) {
return true;
}
return false;
}
protected function isSymbol($expected) {
if ($expected == $this->symbol) {
return true;
}
return false;
}
protected function assertSymbol($expected) {
if ($this->symbol != $expected)
throw new ParsingException("Parsing-Error: '$expected' expected, but '$this->symbol' found.");
$this->nextSymbol();
}
protected function nextSymbol() {
$this->symbol = $this->forwardSymbol;
$this->symbols[] = $this->forwardSymbol = $this->sieve();
return $this->symbol;
}
protected function read($symbol) {
$osp = count($this->stack);
$pos = count($this->symbols) - 2;
$this->{'read_'.$symbol}();
if (method_exists($this, 'handle_'.$symbol))
$this->{'handle_'.$symbol}($pos, max(0, count($this->stack) - $osp));
}
}
class ParsingException extends RuntimeException {
}
?>