<?php
require_once 'Parser.class.php';
class AnnotationParser extends Parser {
/*
START -> e | ANNOTATION START
ANNOTATION -> "@" <QUALIFIER> PARAMS
PARAMS -> e | "(" PARAMLIST ")"
PARAMLIST -> PARAM PARAMLISTCONT
PARAMLISTCONT -> e | "," PARAMLIST
PARAM -> VALUE | <VARIABLE> "=" VALUE
VALUE -> <STRING> | ARRAY
ARRAY -> "{" PARAMLIST "}"
FIRST(PARAMS) = { "(" }
FIRST(PARAMLISTCONT) = { "," }
FIRST(PARAM) = { <STRING>, "$", "{" }
FIRST(VALUE) = { <STRING>, "{" }
FOLLOW(PARAMS) = { e | "@" }
FOLLOW(PARAMLISTCONT) = { ")", "}" }
*/
protected $first = array('PARAMS' => array('('), 'PARAMLISTCONT' => array(','));
protected $follow = array('PARAMS' => array() , 'PARAMLISTCONT' => array(')', '}'));
public $annotations = array();
protected function read_START() {
if ($this->isSymbol(''))
return;
$this->read('ANNOTATION');
$this->read('START');
}
protected function read_ANNOTATION() {
$this->assertSymbol('@');
$this->read('QUALIFIER');
$this->read('PARAMS');
}
protected function read_PARAMS() {
if ($this->isSymbol('')) {
if (count($this->follow['PARAMS']) > 0)
throw new ParsingException("Parsing-Error: unexpected end of input.");
return;
}
if ($this->isSymbol('@')) {
return;
}
$this->assertSymbol('(');
$this->read('PARAMLIST');
$this->assertSymbol(')');
}
protected function read_PARAMLIST() {
$this->read('PARAM');
$this->read('PARAMLISTCONT');
}
protected function read_PARAMLISTCONT() {
if ($this->testFollowOf('PARAMLISTCONT'))
return;
$this->assertSymbol(',');
$this->read('PARAMLIST');
}
protected function read_STRING() {
$this->nextSymbol();
}
protected function read_VALUE() {
if ($this->isSymbol("{")) {
$this->read('ARRAY');
} else {
$this->read('STRING');
}
}
protected function read_PARAM() {
if ($this->symbol[0] == '$') {
$this->read('VARIABLE');
$this->assertSymbol('=');
$this->read('VALUE');
} else {
$this->read('VALUE');
}
}
protected function read_ARRAY() {
$this->assertSymbol('{');
$this->read('PARAMLIST');
$this->assertSymbol('}');
}
protected function read_QUALIFIER() {
if (is_numeric($this->symbol[0]))
throw new ParsingException("Parsing-Error: qualifiers must not begin with a number.");
//TODO: Gültige Zeichen testen...
$this->nextSymbol();
}
protected function read_VARIABLE() {
$this->nextSymbol();
}
protected function handle_VARIABLE($pos, $stckCount) {
array_push($this->stack, $this->symbols[$pos]);
}
protected function handle_QUALIFIER($pos, $stckCount) {
array_push($this->stack, $this->symbols[$pos]);
}
protected function handle_STRING($pos, $stckCount) {
array_push($this->stack, $this->symbols[$pos]);
}
protected function handle_ARRAY($pos, $stckCount) {
$array = array();
$keysOnStack = array_keys($this->stack);
for($i=0; $i<$stckCount; $i++) {
$k = array_pop($keysOnStack);
if ($k[0] == '$')
$array = array_merge(array(substr($k, 1) => array_pop($this->stack)), $array);
else
array_unshift($array, array_pop($this->stack));
}
array_push($this->stack, $array);
}
protected function handle_PARAM($pos, $stckCount) {
if ($this->symbols[$pos][0] == '$') {
$val = array_pop($this->stack);
$key = array_pop($this->stack);
$this->stack[$key] = $val;
}
}
protected function handle_ANNOTATION($pos, $stckCount) {
if (!class_exists($this->stack[0]))
throw new AnnotationException('Can\'t instatiate annotation. Class not found: ' . $this->stack[0]);
$annot = new $this->stack[0]();
if (!$annot instanceof DocAnnotation)
throw new AnnotationException('Incompatible classes: \'' . $this->stack[0] . '\' is not a subclass of Annotation.');
$k = strtolower($this->stack[0]);
for($i=1; isset($this->annotations[$k]);$i++)
$k = strtolower($this->stack[0]) . "($i)";
$this->annotations[$k] = $annot;
$vars = array();
$keysOnStack = array_keys($this->stack);
for($i=0; $i<$stckCount-1; $i++) {
$k = array_pop($keysOnStack);
if ($k[0] == '$')
$vars = array_merge(array($k => array_pop($this->stack)), $vars);
else
array_unshift($vars, array_pop($this->stack));
}
$annotVars = array_keys(get_object_vars($annot));
if (count($annotVars) < count($vars))
throw new AnnotationException('Too many parameters given for annotation \''
. get_class($annot) . '\'. ' . count($annotVars) . ' expected, but '
. count($vars) . ' given.');
foreach($vars as $k => $v) {
if ($k[0] == '$') {
$k = substr($k, 1);
unset($annotVars[array_search($k, $annotVars)]);
$annot->$k = $v;
} else {
$annot->{array_shift($annotVars)} = $v;
}
}
foreach($annotVars as $k) {
if (!$annot->$k && $annot->$k !== 0 && $annot->$k !== false && $annot->$k !== '')
throw new AnnotationException('Failed to create Annotation '.get_class($annot).': no value given for variable \''.$k.'\'.');
}
array_pop($this->stack);
}
}
class AnnotationException extends RuntimeException {
}
?>