3 require_once( 'LalrItem.php' );
4 require_once( 'LalrError.php' );
11 public function __construct( $grammar ) {
12 $this->tokens
= array_map( 'trim', $grammar['tokens'] );
13 $this->rules
= array();
15 foreach( $grammar['rules'] as $name => $rule ) {
16 $rule = array_map( 'trim', $rule );
17 $generates = array_shift( $rule );
18 $this->rules
[$name] = new LalrItem( $name, $generates, $rule );
21 $this->errors
= array();
22 if( isset($grammar['errors']) ) {
23 foreach( $grammar['errors'] as $name => $error ) {
24 $error = array_map( 'trim', $error );
25 $generates = array_shift( $error );
26 if(isset($this->errors
[$generates])) {
27 /* FIXME: conflicting error handlers */
30 foreach($this->rules
as $rule ) {
31 foreach( $this->follow($generates) as $next ) {
32 if( !in_array( $next, $error ) ) {
38 $this->errors
[$generates] = new LalrError( $name, $generates, $error );
42 /* If no default error handler exists, inject it */
43 $program_element = $this->rules
['entry']->next();
44 if(!isset($this->errors
[$program_element])) {
45 /* As the default error handler is to kill the parser without recover, we only end at the end token, and not in the middle of the parse */
46 $error = array('end');
47 $this->errors
[$program_element] = new LalrError( 'default_error_handler', $program_element, array('end'));
51 public function get_tokens() {
55 public function get_rules() {
59 public function get_errors() {
63 public function get( $name ) {
64 if( !isset( $this->rules
[$name] ) )
66 return $this->rules
[$name];
69 public function productions( $symbol ) {
71 foreach( $this->rules
as $item ) {
72 if( $item->produces( $symbol ) ) {
79 public function is_terminal( $symbol ) {
80 if( $symbol == 'end' ) return true;
81 return isset( $this->tokens
[$symbol] );
84 public function terminals() {
85 $symbols = array('end');
86 foreach( $this->tokens
as $sym => $re ) {
87 if( $sym[0] != '.' ) {
94 public function non_terminals() {
96 foreach( $this->rules
as $rule ) {
97 if( !in_array( $rule->generates(), $symbols ) ) {
98 $symbols[] = $rule->generates();
104 public function symbols() {
105 return array_merge( $this->terminals(), $this->non_terminals() );
108 public function follow( $symbol ) {
110 $search_list = array($symbol);
113 for( $i=0; $i < count( $search_list ); $i++
) {
114 $cur_symbol = $search_list[$i];
115 foreach( $this->rules
as $rule ) {
116 foreach( $rule->follow( $cur_symbol ) as $sym ) {
117 if( $sym === false ) {
118 $gen = $rule->generates();
119 if( !in_array( $gen, $search_list ) ) {
120 $search_list[] = $gen;
129 /* Reduce to next terminal */
130 $next_term = array();
131 for( $i=0; $i<count($next);$i++
) {
132 if( $this->is_terminal($next[$i]) ) {
133 if( !in_array( $next[$i], $next_term ) ) {
134 $next_term[] = $next[$i];
137 foreach( $this->productions($next[$i]) as $rule ) {
138 $next_sym = $rule->next();
139 if( $next_sym === false ) {
142 if( !in_array( $next_sym, $next ) ) {
153 public function errors($sym) {
154 if(!isset($this->errors
[$sym]))
156 return $this->errors
[$sym];
159 public function __toString() {
161 foreach( $this->tokens
as $name => $el ) {
162 $outp .= $name . ' = ' . strval($el)."\n";
164 foreach( $this->rules
as $el ) {
165 $outp .= strval($el)."\n";
167 foreach( $this->errors
as $el ) {
168 $outp .= strval($el)."\n";