1<?php 2/** 3 * PHP_ParserGenerator, a php 5 parser generator. 4 * 5 * This is a direct port of the Lemon parser generator, found at 6 * {@link http://www.hwaci.com/sw/lemon/} 7 * 8 * PHP version 5 9 * 10 * LICENSE: 11 * 12 * Copyright (c) 2006, Gregory Beaver <cellog@php.net> 13 * All rights reserved. 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions 17 * are met: 18 * 19 * * Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * * Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in 23 * the documentation and/or other materials provided with the distribution. 24 * * Neither the name of the PHP_ParserGenerator nor the names of its 25 * contributors may be used to endorse or promote products derived 26 * from this software without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 29 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 30 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 31 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 32 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 33 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 34 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 35 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 36 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 37 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 38 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 * 40 * @category PHP 41 * @package PHP_ParserGenerator 42 * @author Gregory Beaver <cellog@php.net> 43 * @copyright 2006 Gregory Beaver 44 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 45 * @version CVS: $Id: Config.php 302382 2010-08-17 06:08:09Z jespino $ 46 * @link http://pear.php.net/package/PHP_ParserGenerator 47 * @since File available since Release 0.1.0 48 */ 49/** 50/** A configuration is a production rule of the grammar together with 51 * a mark (dot) showing how much of that rule has been processed so far. 52 * 53 * Configurations also contain a follow-set which is a list of terminal 54 * symbols which are allowed to immediately follow the end of the rule. 55 * Every configuration is recorded as an instance of the following class. 56 * 57 * @category PHP 58 * @package PHP_ParserGenerator 59 * @author Gregory Beaver <cellog@php.net> 60 * @copyright 2006 Gregory Beaver 61 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 62 * @version Release: @package_version@ 63 * @link http://pear.php.net/package/PHP_ParserGenerator 64 * @since Class available since Release 0.1.0 65 */ 66class PHP_ParserGenerator_Config 67{ 68 const COMPLETE = 1; 69 const INCOMPLETE = 2; 70 /** 71 * The parser rule upon with the configuration is based. 72 * 73 * A parser rule is something like: 74 * <pre> 75 * blah ::= FOO bar. 76 * </pre> 77 * @var PHP_ParserGenerator_Rule 78 */ 79 public $rp; 80 /** 81 * The parse point. 82 * 83 * This is the index into the right-hand side of a rule that is 84 * represented by this configuration. In other words, possible 85 * dots for this rule: 86 * 87 * <pre> 88 * blah ::= FOO bar. 89 * </pre> 90 * 91 * are (represented by "[here]"): 92 * 93 * <pre> 94 * blah ::= [here] FOO bar. 95 * blah ::= FOO [here] bar. 96 * blah ::= FOO bar [here]. 97 * </pre> 98 * @var int 99 */ 100 public $dot; 101 /** 102 * Follow-set for this configuration only 103 * 104 * This is the list of terminals and non-terminals that 105 * can follow this configuration. 106 * @var array 107 */ 108 public $fws; 109 /** 110 * Follow-set forward propagation links. 111 * @var PHP_ParserGenerator_PropagationLink 112 */ 113 public $fplp; 114 /** 115 * Follow-set backwards propagation links 116 * @var PHP_ParserGenerator_PropagationLink 117 */ 118 public $bplp; 119 /** 120 * State that contains this configuration 121 * @var PHP_ParserGenerator_State 122 */ 123 public $stp; 124 /* enum { 125 COMPLETE, /* The status is used during followset and 126 INCOMPLETE /* shift computations 127 } */ 128 /** 129 * Status during followset and shift computations. 130 * 131 * One of PHP_ParserGenerator_Config::COMPLETE or 132 * PHP_ParserGenerator_Config::INCOMPLETE. 133 * @var int 134 */ 135 public $status; 136 /** 137 * Next configuration in the state. 138 * 139 * Index of next PHP_ParserGenerator_Config object. 140 * @var int 141 */ 142 public $next; 143 /** 144 * Index of the next basis configuration PHP_ParserGenerator_Config object 145 * @var int 146 */ 147 public $bp; 148 149 /** 150 * Top of the list of configurations for the current state. 151 * @var PHP_ParserGenerator_Config 152 */ 153 static public $current; 154 /** 155 * Last on the list of configurations for the current state. 156 * @var PHP_ParserGenerator_Config 157 */ 158 static public $currentend; 159 160 /** 161 * Top of the list of basis configurations for the current state. 162 * @var PHP_ParserGenerator_Config 163 */ 164 static public $basis; 165 /** 166 * Last on the list of basis configurations for the current state. 167 * @var PHP_ParserGenerator_Config 168 */ 169 static public $basisend; 170 171 /** 172 * Associative array representation of the linked list of configurations 173 * found in {@link $current} 174 * 175 * @var array 176 */ 177 static public $x4a = array(); 178 179 /** 180 * Return a pointer to a new configuration 181 * @return PHP_ParserGenerator_Config 182 */ 183 private static function newconfig() 184 { 185 return new PHP_ParserGenerator_Config; 186 } 187 188 /** 189 * Display the current configuration for the .out file 190 * 191 * @param PHP_ParserGenerator_Config $cfp 192 * @see PHP_ParserGenerator_Data::ReportOutput() 193 */ 194 static function Configshow(PHP_ParserGenerator_Config $cfp) 195 { 196 $fp = fopen('php://output', 'w'); 197 while ($cfp) { 198 if ($cfp->dot == $cfp->rp->nrhs) { 199 $buf = sprintf('(%d)', $cfp->rp->index); 200 fprintf($fp, ' %5s ', $buf); 201 } else { 202 fwrite($fp,' '); 203 } 204 $cfp->ConfigPrint($fp); 205 fwrite($fp, "\n"); 206 if (0) { 207 //SetPrint(fp,cfp->fws,$this); 208 //PlinkPrint(fp,cfp->fplp,"To "); 209 //PlinkPrint(fp,cfp->bplp,"From"); 210 } 211 $cfp = $cfp->next; 212 } 213 fwrite($fp, "\n"); 214 fclose($fp); 215 } 216 217 /** 218 * Initialize the configuration list builder for a new state. 219 */ 220 static function Configlist_init() 221 { 222 self::$current = 0; 223 self::$currentend = &self::$current; 224 self::$basis = 0; 225 self::$basisend = &self::$basis; 226 self::$x4a = array(); 227 } 228 229 /** 230 * Remove all data from the table. 231 * 232 * Pass each data to the function $f as it is removed if 233 * $f is a valid callback. 234 * @param callback|null 235 * @see Configtable_clear() 236 */ 237 static function Configtable_reset($f) 238 { 239 self::$current = 0; 240 self::$currentend = &self::$current; 241 self::$basis = 0; 242 self::$basisend = &self::$basis; 243 self::Configtable_clear(0); 244 } 245 246 /** 247 * Remove all data from the associative array representation 248 * of configurations. 249 * 250 * Pass each data to the function $f as it is removed if 251 * $f is a valid callback. 252 * @param callback|null 253 */ 254 static function Configtable_clear($f) 255 { 256 if (!count(self::$x4a)) { 257 return; 258 } 259 if ($f) { 260 for ($i = 0; $i < count(self::$x4a); $i++) { 261 call_user_func($f, self::$x4a[$i]->data); 262 } 263 } 264 self::$x4a = array(); 265 } 266 267 /** 268 * Reset the configuration list builder for a new state. 269 * @see Configtable_clear() 270 */ 271 static function Configlist_reset() 272 { 273 self::Configtable_clear(0); 274 } 275 276 /** 277 * Add another configuration to the configuration list for this parser state. 278 * @param PHP_ParserGenerator_Rule the rule 279 * @param int Index into the right-hand side of the rule where the dot goes 280 * @return PHP_ParserGenerator_Config 281 */ 282 static function Configlist_add($rp, $dot) 283 { 284 $model = new PHP_ParserGenerator_Config; 285 $model->rp = $rp; 286 $model->dot = $dot; 287 $cfp = self::Configtable_find($model); 288 if ($cfp === 0) { 289 $cfp = self::newconfig(); 290 $cfp->rp = $rp; 291 $cfp->dot = $dot; 292 $cfp->fws = array(); 293 $cfp->stp = 0; 294 $cfp->fplp = $cfp->bplp = 0; 295 $cfp->next = 0; 296 $cfp->bp = 0; 297 self::$currentend = $cfp; 298 self::$currentend = &$cfp->next; 299 self::Configtable_insert($cfp); 300 } 301 return $cfp; 302 } 303 304 /** 305 * Add a basis configuration to the configuration list for this parser state. 306 * 307 * Basis configurations are the root for a configuration. This method also 308 * inserts the configuration into the regular list of configurations for this 309 * reason. 310 * @param PHP_ParserGenerator_Rule the rule 311 * @param int Index into the right-hand side of the rule where the dot goes 312 * @return PHP_ParserGenerator_Config 313 */ 314 static function Configlist_addbasis($rp, $dot) 315 { 316 $model = new PHP_ParserGenerator_Config; 317 $model->rp = $rp; 318 $model->dot = $dot; 319 $cfp = self::Configtable_find($model); 320 if ($cfp === 0) { 321 $cfp = self::newconfig(); 322 $cfp->rp = $rp; 323 $cfp->dot = $dot; 324 $cfp->fws = array(); 325 $cfp->stp = 0; 326 $cfp->fplp = $cfp->bplp = 0; 327 $cfp->next = 0; 328 $cfp->bp = 0; 329 self::$currentend = $cfp; 330 self::$currentend = &$cfp->next; 331 self::$basisend = $cfp; 332 self::$basisend = &$cfp->bp; 333 self::Configtable_insert($cfp); 334 } 335 return $cfp; 336 } 337 338 /** 339 * Compute the closure of the configuration list. 340 * 341 * This calculates all of the possible continuations of 342 * each configuration, ensuring that each state accounts 343 * for every configuration that could arrive at that state. 344 */ 345 static function Configlist_closure(PHP_ParserGenerator_Data $lemp) 346 { 347 for ($cfp = self::$current; $cfp; $cfp = $cfp->next) { 348 $rp = $cfp->rp; 349 $dot = $cfp->dot; 350 if ($dot >= $rp->nrhs) { 351 continue; 352 } 353 $sp = $rp->rhs[$dot]; 354 if ($sp->type == PHP_ParserGenerator_Symbol::NONTERMINAL) { 355 if ($sp->rule === 0 && $sp !== $lemp->errsym) { 356 PHP_ParserGenerator::ErrorMsg($lemp->filename, $rp->line, 357 "Nonterminal \"%s\" has no rules.", $sp->name); 358 $lemp->errorcnt++; 359 } 360 for ($newrp = $sp->rule; $newrp; $newrp = $newrp->nextlhs) { 361 $newcfp = self::Configlist_add($newrp, 0); 362 for ($i = $dot + 1; $i < $rp->nrhs; $i++) { 363 $xsp = $rp->rhs[$i]; 364 if ($xsp->type == PHP_ParserGenerator_Symbol::TERMINAL) { 365 $newcfp->fws[$xsp->index] = 1; 366 break; 367 } elseif ($xsp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) { 368 for ($k = 0; $k < $xsp->nsubsym; $k++) { 369 $newcfp->fws[$xsp->subsym[$k]->index] = 1; 370 } 371 break; 372 } else { 373 $a = array_diff_key($xsp->firstset, $newcfp->fws); 374 $newcfp->fws += $a; 375 if ($xsp->lambda === false) { 376 break; 377 } 378 } 379 } 380 if ($i == $rp->nrhs) { 381 PHP_ParserGenerator_PropagationLink::Plink_add($cfp->fplp, $newcfp); 382 } 383 } 384 } 385 } 386 } 387 388 /** 389 * Sort the configuration list 390 * @uses Configcmp() 391 */ 392 static function Configlist_sort() 393 { 394 $a = 0; 395 //self::Configshow(self::$current); 396 self::$current = PHP_ParserGenerator::msort(self::$current,'next', array('PHP_ParserGenerator_Config', 'Configcmp')); 397 //self::Configshow(self::$current); 398 self::$currentend = &$a; 399 self::$currentend = 0; 400 } 401 402 /** 403 * Sort the configuration list 404 * @uses Configcmp 405 */ 406 static function Configlist_sortbasis() 407 { 408 $a = 0; 409 self::$basis = PHP_ParserGenerator::msort(self::$current,'bp', array('PHP_ParserGenerator_Config', 'Configcmp')); 410 self::$basisend = &$a; 411 self::$basisend = 0; 412 } 413 414 /** 415 * Return a pointer to the head of the configuration list and 416 * reset the list 417 * @see $current 418 * @return PHP_ParserGenerator_Config 419 */ 420 static function Configlist_return() 421 { 422 $old = self::$current; 423 self::$current = 0; 424 self::$currentend = &self::$current; 425 return $old; 426 } 427 428 /** 429 * Return a pointer to the head of the basis list and 430 * reset the list 431 * @see $basis 432 * @return PHP_ParserGenerator_Config 433 */ 434 static function Configlist_basis() 435 { 436 $old = self::$basis; 437 self::$basis = 0; 438 self::$basisend = &self::$basis; 439 return $old; 440 } 441 442 /** 443 * Free all elements of the given configuration list 444 * @param PHP_ParserGenerator_Config 445 */ 446 static function Configlist_eat($cfp) 447 { 448 for (; $cfp; $cfp = $nextcfp) { 449 $nextcfp = $cfp->next; 450 if ($cfp->fplp !=0) { 451 throw new Exception('fplp of configuration non-zero?'); 452 } 453 if ($cfp->bplp !=0) { 454 throw new Exception('bplp of configuration non-zero?'); 455 } 456 if ($cfp->fws) { 457 $cfp->fws = array(); 458 } 459 } 460 } 461 462 /** 463 * Compare two configurations for sorting purposes. 464 * 465 * Configurations based on higher precedence rules 466 * (those earlier in the file) are chosen first. Two 467 * configurations that are the same rule are sorted by 468 * dot (see {@link $dot}), and those configurations 469 * with a dot closer to the left-hand side are chosen first. 470 * @param unknown_type $a 471 * @param unknown_type $b 472 * @return unknown 473 */ 474 static function Configcmp($a, $b) 475 { 476 $x = $a->rp->index - $b->rp->index; 477 if (!$x) { 478 $x = $a->dot - $b->dot; 479 } 480 return $x; 481 } 482 483 /** 484 * Print out information on this configuration. 485 * 486 * @param resource $fp 487 * @see PHP_ParserGenerator_Data::ReportOutput() 488 */ 489 function ConfigPrint($fp) 490 { 491 $rp = $this->rp; 492 fprintf($fp, "%s ::=", $rp->lhs->name); 493 for ($i = 0; $i <= $rp->nrhs; $i++) { 494 if ($i === $this->dot) { 495 fwrite($fp,' *'); 496 } 497 if ($i === $rp->nrhs) { 498 break; 499 } 500 $sp = $rp->rhs[$i]; 501 fprintf($fp,' %s', $sp->name); 502 if ($sp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) { 503 for ($j = 1; $j < $sp->nsubsym; $j++) { 504 fprintf($fp, '|%s', $sp->subsym[$j]->name); 505 } 506 } 507 } 508 } 509 510 /** 511 * Hash a configuration for the associative array {@link $x4a} 512 */ 513 private static function confighash(PHP_ParserGenerator_Config $a) 514 { 515 $h = 0; 516 $h = $h * 571 + $a->rp->index * 37 + $a->dot; 517 return $h; 518 } 519 520 /** 521 * Insert a new record into the array. Return TRUE if successful. 522 * Prior data with the same key is NOT overwritten 523 */ 524 static function Configtable_insert(PHP_ParserGenerator_Config $data) 525 { 526 $h = self::confighash($data); 527 if (isset(self::$x4a[$h])) { 528 $np = self::$x4a[$h]; 529 } else { 530 $np = 0; 531 } 532 while ($np) { 533 if (self::Configcmp($np->data, $data) == 0) { 534 /* An existing entry with the same key is found. */ 535 /* Fail because overwrite is not allows. */ 536 return 0; 537 } 538 $np = $np->next; 539 } 540 /* Insert the new data */ 541 $np = array('data' => $data, 'next' => 0, 'from' => 0); 542 $np = new PHP_ParserGenerator_StateNode; 543 $np->data = $data; 544 if (isset(self::$x4a[$h])) { 545 self::$x4a[$h]->from = $np->next; 546 $np->next = self::$x4a[$h]; 547 } 548 $np->from = $np; 549 self::$x4a[$h] = $np; 550 return 1; 551 } 552 553 /** 554 * Return a pointer to data assigned to the given key. Return NULL 555 * if no such key. 556 * @return PHP_ParserGenerator_Config|0 557 */ 558 static function Configtable_find(PHP_ParserGenerator_Config $key) 559 { 560 $h = self::confighash($key); 561 if (!isset(self::$x4a[$h])) { 562 return 0; 563 } 564 $np = self::$x4a[$h]; 565 while ($np) { 566 if (self::Configcmp($np->data, $key) == 0) { 567 break; 568 } 569 $np = $np->next; 570 } 571 return $np ? $np->data : 0; 572 } 573} 574?> 575