3 class LalrGrammarParser
{
4 protected $expr; /* Protected, just so we can add custom acceptors... */
7 /* ********************************************
9 *********************************************/
11 public function parse( $expr ) {
15 $result = $this->run();
17 $curptr = $this->ptr
; /* Keep $this->ptr for error message */
18 while( ctype_space( substr( $this->expr
, $curptr, 1 ) ) ) $this->ptr++
;
19 if( $curptr < strlen( $this->expr
) ) {
20 $this->error( 'Expected end' );
26 /* ********************************************
27 * Lexer-internal helpers
28 *********************************************/
30 protected function trimLeft() {
31 while( false !== strpos( " \t", substr( $this->expr
, $this->ptr
, 1 ) ) ) $this->ptr++
;
34 protected function trimLine() {
35 $c = substr( $this->expr
, $this->ptr
, 1 );
36 while( $c != "" && $c != "\n" ) {
38 $c = substr( $this->expr
, $this->ptr
, 1 );
42 /* ********************************************
44 *********************************************/
46 protected function acceptSym( $tokenlist ) {
50 foreach( $tokenlist as $token ) {
51 if( substr( $this->expr
, $this->ptr
, strlen( $token ) ) == $token ) {
52 $this->ptr +
= strlen( $token );
59 protected function expectSym( $tokenlist ) {
60 $sym = $this->acceptSym( $tokenlist );
62 $this->error('Unexpected token, expected '.implode(',',$tokenlist));
66 protected function acceptKeyword( $keywordlist = false, $case_insensitive = false, $numeric = false ) {
69 /* Peek at next keyword */
72 $c = substr( $this->expr
, $curptr, 1 );
73 while( ctype_alpha( $c ) ||
$c == '_' ||
($buffer !== '' && ctype_digit($c)) ) {
76 $c = substr( $this->expr
, $curptr, 1 );
78 if( $case_insensitive )
79 $buffer = strtolower( $buffer );
80 if( $keywordlist === false ||
in_array( $buffer, $keywordlist ) ) {
87 protected function expectKeyword( $keywordlist = false, $case_insensitive = false, $numeric = false ) {
88 $sym = $this->acceptKeyword( $keywordlist, $case_insensitive, $numeric );
90 $this->error('Unexpected token, expected '.(($keywordlist===false)?
('keyword'):implode(',',$keywordlist)));
95 public function acceptLinebreak() {
97 $c = substr( $this->expr
, $this->ptr
, 1 );
98 if( $c == "" ||
$c == "\n" ) {
105 protected function expectLinebreak() {
106 $sym = $this->acceptLinebreak();
108 $this->error('Unexpected token, expected linebreak');
112 public function acceptRegexp() {
115 $c = substr( $this->expr
, $this->ptr
, 1 );
120 while( $c != "" && $c != "\n" ) {
123 $c = substr( $this->expr
, $this->ptr
, 1 );
130 /* ********************************************
132 *********************************************/
134 public function run() {
136 while( false !== ($line = $this->accept_line()) ) {
137 /* Todo: conflicting lines */
138 $result = array_merge_recursive($result, $line);
143 public function accept_line() {
146 /* Match end-of-file */
147 if( $this->ptr
>= strlen( $this->expr
) ) {
151 /* Trim empty lines */
152 if( $this->acceptLinebreak() ) {
156 if( $this->acceptSym(array('--')) ) {
161 $name = $this->expectKeyword(false, false, true);
164 if( false !== ($re = $this->acceptRegexp()) ){
165 $result['tokens'] = array($name => $re);
166 $this->expectLinebreak();
171 if( $this->acceptSym(array(':')) ) {
174 $items[] = $this->expectKeyword(false, false, true);
175 $this->expectSym(array('='));
176 if( $this->acceptKeyword(array('error'), false, true) ) {
178 $result['errors'] = array($name => $items);
179 $this->expectLinebreak();
182 /* Regular grammar rule */
183 while( $item = $this->acceptKeyword(false, false, true) ) {
186 $result['rules'] = array($name => $items);
187 $this->expectLinebreak();
195 private function error($message) {
196 throw new Exception("Error while parsing grammar file: ". $message);