ParserGenerator.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  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. * There are a few PHP-specific changes to the lemon parser generator.
  9. *
  10. * - %extra_argument is removed, as class constructor can be used to
  11. * pass in extra information
  12. * - %token_type and company are irrelevant in PHP, and so are removed
  13. * - %declare_class is added to define the parser class name and any
  14. * implements/extends information
  15. * - %include_class is added to allow insertion of extra class information
  16. * such as constants, a class constructor, etc.
  17. *
  18. * Other changes make the parser more robust, and also make reporting
  19. * syntax errors simpler. Detection of expected tokens eliminates some
  20. * problematic edge cases where an unexpected token could cause the parser
  21. * to simply accept input.
  22. *
  23. * Otherwise, the file format is identical to the Lemon parser generator
  24. *
  25. * PHP version 5
  26. *
  27. * LICENSE:
  28. *
  29. * Copyright (c) 2006, Gregory Beaver <cellog@php.net>
  30. * All rights reserved.
  31. *
  32. * Redistribution and use in source and binary forms, with or without
  33. * modification, are permitted provided that the following conditions
  34. * are met:
  35. *
  36. * * Redistributions of source code must retain the above copyright
  37. * notice, this list of conditions and the following disclaimer.
  38. * * Redistributions in binary form must reproduce the above copyright
  39. * notice, this list of conditions and the following disclaimer in
  40. * the documentation and/or other materials provided with the distribution.
  41. * * Neither the name of the PHP_ParserGenerator nor the names of its
  42. * contributors may be used to endorse or promote products derived
  43. * from this software without specific prior written permission.
  44. *
  45. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  46. * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  47. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  48. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  49. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  50. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  51. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  52. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  53. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  54. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  55. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  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 CVS: $Id: ParserGenerator.php 302382 2010-08-17 06:08:09Z jespino $
  63. * @link http://pear.php.net/package/PHP_ParserGenerator
  64. * @since File available since Release 0.1.0
  65. */
  66. /**#@+
  67. * Basic components of the parser generator
  68. */
  69. require_once 'PHP/ParserGenerator/Action.php';
  70. require_once 'PHP/ParserGenerator/ActionTable.php';
  71. require_once 'PHP/ParserGenerator/Config.php';
  72. require_once 'PHP/ParserGenerator/Data.php';
  73. require_once 'PHP/ParserGenerator/Symbol.php';
  74. require_once 'PHP/ParserGenerator/Rule.php';
  75. require_once 'PHP/ParserGenerator/Parser.php';
  76. require_once 'PHP/ParserGenerator/PropagationLink.php';
  77. require_once 'PHP/ParserGenerator/State.php';
  78. /**#@-*/
  79. /**
  80. * The basic home class for the parser generator
  81. *
  82. * @category PHP
  83. * @package PHP_ParserGenerator
  84. * @author Gregory Beaver <cellog@php.net>
  85. * @copyright 2006 Gregory Beaver
  86. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  87. * @version Release: @package_version@
  88. * @link http://pear.php.net/package/PHP_ParserGenerator
  89. * @since Class available since Release 0.1.0
  90. * @example Lempar.php
  91. * @example examples/Parser.y Sample parser file format (PHP_LexerGenerator's parser)
  92. * @example examples/Parser.php Sample parser file format PHP code (PHP_LexerGenerator's parser)
  93. */
  94. class PHP_ParserGenerator
  95. {
  96. /**
  97. * Set this to 1 to turn on debugging of Lemon's parsing of
  98. * grammar files.
  99. */
  100. const DEBUG = 0;
  101. const MAXRHS = 1000;
  102. const OPT_FLAG = 1, OPT_INT = 2, OPT_DBL = 3, OPT_STR = 4,
  103. OPT_FFLAG = 5, OPT_FINT = 6, OPT_FDBL = 7, OPT_FSTR = 8;
  104. public $azDefine = array();
  105. private static $_options = array(
  106. 'b' => array(
  107. 'type' => self::OPT_FLAG,
  108. 'arg' => 'basisflag',
  109. 'message' => 'Print only the basis in report.'
  110. ),
  111. 'c' => array(
  112. 'type' => self::OPT_FLAG,
  113. 'arg' => 'compress',
  114. 'message' => 'Don\'t compress the action table.'
  115. ),
  116. 'D' => array(
  117. 'type' => self::OPT_FSTR,
  118. 'arg' => 'handleDOption',
  119. 'message' => 'Define an %ifdef macro.'
  120. ),
  121. 'g' => array(
  122. 'type' => self::OPT_FLAG,
  123. 'arg' => 'rpflag',
  124. 'message' => 'Print grammar without actions.'
  125. ),
  126. 'm' => array(
  127. 'type' => self::OPT_FLAG,
  128. 'arg' => 'mhflag',
  129. 'message' => 'Output a makeheaders compatible file'
  130. ),
  131. 'q' => array(
  132. 'type' => self::OPT_FLAG,
  133. 'arg' => 'quiet',
  134. 'message' => '(Quiet) Don\'t print the report file.'
  135. ),
  136. 's' => array(
  137. 'type' => self::OPT_FLAG,
  138. 'arg' => 'statistics',
  139. 'message' => 'Print parser stats to standard output.'
  140. ),
  141. 'x' => array(
  142. 'type' => self::OPT_FLAG,
  143. 'arg' => 'version',
  144. 'message' => 'Print the version number.'
  145. ),
  146. 'T' => array(
  147. 'type' => self::OPT_STR,
  148. 'arg' => 'parser_template',
  149. 'message' => 'Use different parser template file.'
  150. )
  151. );
  152. private $_basisflag = 0;
  153. private $_compress = 0;
  154. private $_rpflag = 0;
  155. private $_mhflag = 0;
  156. private $_quiet = 0;
  157. private $_statistics = 0;
  158. private $_version = 0;
  159. private $_size;
  160. private $_parser_template = "";
  161. /**
  162. * Process a flag command line argument.
  163. *
  164. * @param int $i
  165. * @param array $argv
  166. *
  167. * @return int
  168. */
  169. function handleflags($i, $argv)
  170. {
  171. if (!isset($argv[1]) || !isset(self::$_options[$argv[$i][1]])) {
  172. throw new Exception('Command line syntax error: undefined option "' . $argv[$i] . '"');
  173. }
  174. $v = self::$_options[$argv[$i][1]] == '-';
  175. if (self::$_options[$argv[$i][1]]['type'] == self::OPT_FLAG) {
  176. $this->{self::$_options[$argv[$i][1]]['arg']} = 1;
  177. } elseif (self::$_options[$argv[$i][1]]['type'] == self::OPT_FFLAG) {
  178. $this->{self::$_options[$argv[$i][1]]['arg']}($v);
  179. } elseif (self::$_options[$argv[$i][1]]['type'] == self::OPT_FSTR) {
  180. $this->{self::$_options[$argv[$i][1]]['arg']}(substr($v, 2));
  181. } else {
  182. throw new Exception('Command line syntax error: missing argument on switch: "' . $argv[$i] . '"');
  183. }
  184. return 0;
  185. }
  186. /**
  187. * Process a command line switch which has an argument.
  188. *
  189. * @param int $i
  190. * @param array $argv
  191. *
  192. * @return int
  193. */
  194. function handleswitch($i, $argv)
  195. {
  196. $lv = 0;
  197. $dv = 0.0;
  198. $sv = $end = $cp = '';
  199. $j; // int
  200. $errcnt = 0;
  201. $cp = strstr($argv[$i], '=');
  202. if (!$cp) {
  203. throw new Exception('INTERNAL ERROR: handleswitch passed bad argument, no "=" in arg');
  204. }
  205. $argv[$i] = substr($argv[$i], 0, strlen($argv[$i]) - strlen($cp));
  206. if (!isset(self::$_options[$argv[$i]])) {
  207. throw new Exception('Command line syntax error: undefined option "' . $argv[$i] .
  208. $cp . '"');
  209. }
  210. $cp = substr($cp, 1);
  211. switch (self::$_options[$argv[$i]]['type']) {
  212. case self::OPT_FLAG:
  213. case self::OPT_FFLAG:
  214. throw new Exception('Command line syntax error: option requires an argument "' .
  215. $argv[$i] . '=' . $cp . '"');
  216. case self::OPT_DBL:
  217. case self::OPT_FDBL:
  218. $dv = (double) $cp;
  219. break;
  220. case self::OPT_INT:
  221. case self::OPT_FINT:
  222. $lv = (int) $cp;
  223. break;
  224. case self::OPT_STR:
  225. case self::OPT_FSTR:
  226. $sv = $cp;
  227. break;
  228. }
  229. switch(self::$_options[$argv[$i]]['type']) {
  230. case self::OPT_FLAG:
  231. case self::OPT_FFLAG:
  232. break;
  233. case self::OPT_DBL:
  234. $this->{self::$_options[$argv[$i]]['arg']} = $dv;
  235. break;
  236. case self::OPT_FDBL:
  237. $this->{self::$_options[$argv[$i]]['arg']}($dv);
  238. break;
  239. case self::OPT_INT:
  240. $this->{self::$_options[$argv[$i]]['arg']} = $lv;
  241. break;
  242. case self::OPT_FINT:
  243. $this->{self::$_options[$argv[$i]]['arg']}($lv);
  244. break;
  245. case self::OPT_STR:
  246. $this->{self::$_options[$argv[$i]]['arg']} = $sv;
  247. break;
  248. case self::OPT_FSTR:
  249. $this->{self::$_options[$argv[$i]]['arg']}($sv);
  250. break;
  251. }
  252. return 0;
  253. }
  254. /**
  255. * OptInit
  256. *
  257. * @param array $a Arguments
  258. *
  259. * @return int
  260. */
  261. function OptInit($a)
  262. {
  263. $errcnt = 0;
  264. $argv = $a;
  265. try {
  266. if (is_array($argv) && count($argv) && self::$_options) {
  267. for ($i = 1; $i < count($argv); $i++) {
  268. if ($argv[$i][0] == '+' || $argv[$i][0] == '-') {
  269. $errcnt += $this->handleflags($i, $argv);
  270. } elseif (strstr($argv[$i], '=')) {
  271. $errcnt += $this->handleswitch($i, $argv);
  272. }
  273. }
  274. }
  275. } catch (Exception $e) {
  276. $this->OptPrint();
  277. echo $e->getMessage()."\n";
  278. exit(1);
  279. }
  280. return 0;
  281. }
  282. /**
  283. * Return the index of the N-th non-switch argument. Return -1
  284. * if N is out of range.
  285. *
  286. * @param int $n
  287. * @param int $a
  288. *
  289. * @return int
  290. */
  291. private function _argindex($n, $a)
  292. {
  293. $dashdash = 0;
  294. if (!is_array($a) || !count($a)) {
  295. return -1;
  296. }
  297. for ($i=1; $i < count($a); $i++) {
  298. if ($dashdash || !($a[$i][0] == '-' || $a[$i][0] == '+' || strchr($a[$i], '='))) {
  299. if ($n == 0) {
  300. return $i;
  301. }
  302. $n--;
  303. }
  304. if ($_SERVER['argv'][$i] == '--') {
  305. $dashdash = 1;
  306. }
  307. }
  308. return -1;
  309. }
  310. /**
  311. * Return the value of the non-option argument as indexed by $i
  312. *
  313. * @param int $i
  314. * @param array $a the value of $argv
  315. *
  316. * @return 0|string
  317. */
  318. private function _optArg($i, $a)
  319. {
  320. if (-1 == ($ind = $this->_argindex($i, $a))) {
  321. return 0;
  322. }
  323. return $a[$ind];
  324. }
  325. /**
  326. * @param array $a
  327. *
  328. * @return int number of arguments
  329. */
  330. function OptNArgs($a)
  331. {
  332. $cnt = $dashdash = 0;
  333. if (is_array($a) && count($a)) {
  334. for ($i = 1; $i < count($a); $i++) {
  335. if ($dashdash
  336. || !($a[$i][0] == '-' || $a[$i][0] == '+' || strchr($a[$i], '='))
  337. ) {
  338. $cnt++;
  339. }
  340. if ($a[$i] == "--") {
  341. $dashdash = 1;
  342. }
  343. }
  344. }
  345. return $cnt;
  346. }
  347. /**
  348. * Print out command-line options
  349. *
  350. * @return void
  351. */
  352. function OptPrint()
  353. {
  354. $max = 0;
  355. foreach (self::$_options as $label => $info) {
  356. $len = strlen($label) + 1;
  357. switch ($info['type']) {
  358. case self::OPT_FLAG:
  359. case self::OPT_FFLAG:
  360. break;
  361. case self::OPT_INT:
  362. case self::OPT_FINT:
  363. $len += 9; /* length of "<integer>" */
  364. break;
  365. case self::OPT_DBL:
  366. case self::OPT_FDBL:
  367. $len += 6; /* length of "<real>" */
  368. break;
  369. case self::OPT_STR:
  370. case self::OPT_FSTR:
  371. $len += 8; /* length of "<string>" */
  372. break;
  373. }
  374. if ($len > $max) {
  375. $max = $len;
  376. }
  377. }
  378. foreach (self::$_options as $label => $info) {
  379. switch ($info['type']) {
  380. case self::OPT_FLAG:
  381. case self::OPT_FFLAG:
  382. echo " -$label";
  383. echo str_repeat(' ', $max - strlen($label));
  384. echo " $info[message]\n";
  385. break;
  386. case self::OPT_INT:
  387. case self::OPT_FINT:
  388. echo " $label=<integer>" . str_repeat(' ', $max - strlen($label) - 9);
  389. echo " $info[message]\n";
  390. break;
  391. case self::OPT_DBL:
  392. case self::OPT_FDBL:
  393. echo " $label=<real>" . str_repeat(' ', $max - strlen($label) - 6);
  394. echo " $info[message]\n";
  395. break;
  396. case self::OPT_STR:
  397. case self::OPT_FSTR:
  398. echo " $label=<string>" . str_repeat(' ', $max - strlen($label) - 8);
  399. echo " $info[message]\n";
  400. break;
  401. }
  402. }
  403. }
  404. /**
  405. * This routine is called with the argument to each -D command-line option.
  406. * Add the macro defined to the azDefine array.
  407. *
  408. * @param string $z
  409. *
  410. * @return void
  411. */
  412. private function _handleDOption($z)
  413. {
  414. if ($a = strstr($z, '=')) {
  415. $z = substr($a, 1); // strip first =
  416. }
  417. $this->azDefine[] = $z;
  418. }
  419. /**************** From the file "main.c" ************************************/
  420. /*
  421. ** Main program file for the LEMON parser generator.
  422. */
  423. /**
  424. * The main program. Parse the command line and do it...
  425. *
  426. * @return int Number of error and conflicts
  427. */
  428. function main()
  429. {
  430. $lem = new PHP_ParserGenerator_Data;
  431. $this->OptInit($_SERVER['argv']);
  432. if ($this->_version) {
  433. echo "Lemon version 1.0/PHP_ParserGenerator port version @package_version@\n";
  434. exit(0);
  435. }
  436. if ($this->OptNArgs($_SERVER['argv']) != 1) {
  437. echo "Exactly one filename argument is required.\n";
  438. exit(1);
  439. }
  440. $lem->errorcnt = 0;
  441. $lem->parser_template = $this->_parser_template;
  442. /* Initialize the machine */
  443. $lem->argv0 = $_SERVER['argv'][0];
  444. $lem->filename = $this->_optArg(0, $_SERVER['argv']);
  445. $a = pathinfo($lem->filename);
  446. if (isset($a['extension'])) {
  447. $ext = '.' . $a['extension'];
  448. $lem->filenosuffix = substr($lem->filename, 0, strlen($lem->filename) - strlen($ext));
  449. } else {
  450. $lem->filenosuffix = $lem->filename;
  451. }
  452. $lem->basisflag = $this->_basisflag;
  453. $lem->has_fallback = 0;
  454. $lem->nconflict = 0;
  455. $lem->name = $lem->include_code = $lem->include_classcode = $lem->arg = $lem->tokentype = $lem->start = 0;
  456. $lem->vartype = 0;
  457. $lem->stacksize = 0;
  458. $lem->error = $lem->overflow = $lem->failure = $lem->accept = $lem->tokendest = $lem->tokenprefix = $lem->outname = $lem->extracode = 0;
  459. $lem->vardest = 0;
  460. $lem->tablesize = 0;
  461. PHP_ParserGenerator_Symbol::Symbol_new("$");
  462. $lem->errsym = PHP_ParserGenerator_Symbol::Symbol_new("error");
  463. /* Parse the input file */
  464. $parser = new PHP_ParserGenerator_Parser($this);
  465. $parser->Parse($lem);
  466. if ($lem->errorcnt) {
  467. exit($lem->errorcnt);
  468. }
  469. if ($lem->rule === 0) {
  470. printf("Empty grammar.\n");
  471. exit(1);
  472. }
  473. /* Count and index the symbols of the grammar */
  474. $lem->nsymbol = PHP_ParserGenerator_Symbol::Symbol_count();
  475. PHP_ParserGenerator_Symbol::Symbol_new("{default}");
  476. $lem->symbols = PHP_ParserGenerator_Symbol::Symbol_arrayof();
  477. for ($i = 0; $i <= $lem->nsymbol; $i++) {
  478. $lem->symbols[$i]->index = $i;
  479. }
  480. usort($lem->symbols, array('PHP_ParserGenerator_Symbol', 'sortSymbols'));
  481. for ($i = 0; $i <= $lem->nsymbol; $i++) {
  482. $lem->symbols[$i]->index = $i;
  483. }
  484. // find the first lower-case symbol
  485. for ($i = 1; ord($lem->symbols[$i]->name[0]) <= ord('Z'); $i++);
  486. $lem->nterminal = $i;
  487. /* Generate a reprint of the grammar, if requested on the command line */
  488. if ($this->_rpflag) {
  489. $this->Reprint();
  490. } else {
  491. /* Initialize the size for all follow and first sets */
  492. $this->SetSize($lem->nterminal);
  493. /* Find the precedence for every production rule (that has one) */
  494. $lem->FindRulePrecedences();
  495. /* Compute the lambda-nonterminals and the first-sets for every
  496. ** nonterminal */
  497. $lem->FindFirstSets();
  498. /* Compute all LR(0) states. Also record follow-set propagation
  499. ** links so that the follow-set can be computed later */
  500. $lem->nstate = 0;
  501. $lem->FindStates();
  502. $lem->sorted = PHP_ParserGenerator_State::State_arrayof();
  503. /* Tie up loose ends on the propagation links */
  504. $lem->FindLinks();
  505. /* Compute the follow set of every reducible configuration */
  506. $lem->FindFollowSets();
  507. /* Compute the action tables */
  508. $lem->FindActions();
  509. /* Compress the action tables */
  510. if ($this->_compress===0) {
  511. $lem->CompressTables();
  512. }
  513. /* Reorder and renumber the states so that states with fewer choices
  514. ** occur at the end. */
  515. $lem->ResortStates();
  516. /* Generate a report of the parser generated. (the "y.output" file) */
  517. if (!$this->_quiet) {
  518. $lem->ReportOutput();
  519. }
  520. /* Generate the source code for the parser */
  521. $lem->ReportTable($this->_mhflag);
  522. /* Produce a header file for use by the scanner. (This step is
  523. ** omitted if the "-m" option is used because makeheaders will
  524. ** generate the file for us.) */
  525. //if (!$this->_mhflag) {
  526. // $this->ReportHeader();
  527. //}
  528. }
  529. if ($this->_statistics) {
  530. printf(
  531. "Parser statistics: %d terminals, %d nonterminals, %d rules\n",
  532. $lem->nterminal,
  533. $lem->nsymbol - $lem->nterminal,
  534. $lem->nrule
  535. );
  536. printf(
  537. " %d states, %d parser table entries, %d conflicts\n",
  538. $lem->nstate,
  539. $lem->tablesize,
  540. $lem->nconflict
  541. );
  542. }
  543. if ($lem->nconflict) {
  544. printf("%d parsing conflicts.\n", $lem->nconflict);
  545. }
  546. exit($lem->errorcnt + $lem->nconflict);
  547. return ($lem->errorcnt + $lem->nconflict);
  548. }
  549. /**
  550. * SetSize
  551. *
  552. * @param int $n
  553. *
  554. * @access public
  555. * @return void
  556. */
  557. function SetSize($n)
  558. {
  559. $this->_size = $n + 1;
  560. }
  561. /**
  562. * Merge in a merge sort for a linked list
  563. *
  564. * Side effects:
  565. * The "next" pointers for elements in the lists a and b are
  566. * changed.
  567. *
  568. * @param mixed $a A sorted, null-terminated linked list. (May be null).
  569. * @param mixed $b A sorted, null-terminated linked list. (May be null).
  570. * @param function $cmp A pointer to the comparison function.
  571. * @param integer $offset Offset in the structure to the "next" field.
  572. *
  573. * @return mixed A pointer to the head of a sorted list containing the
  574. * elements of both a and b.
  575. */
  576. static function merge($a, $b, $cmp, $offset)
  577. {
  578. if ($a === 0) {
  579. $head = $b;
  580. } elseif ($b === 0) {
  581. $head = $a;
  582. } else {
  583. if (call_user_func($cmp, $a, $b) < 0) {
  584. $ptr = $a;
  585. $a = $a->$offset;
  586. } else {
  587. $ptr = $b;
  588. $b = $b->$offset;
  589. }
  590. $head = $ptr;
  591. while ($a && $b) {
  592. if (call_user_func($cmp, $a, $b) < 0) {
  593. $ptr->$offset = $a;
  594. $ptr = $a;
  595. $a = $a->$offset;
  596. } else {
  597. $ptr->$offset = $b;
  598. $ptr = $b;
  599. $b = $b->$offset;
  600. }
  601. }
  602. if ($a !== 0) {
  603. $ptr->$offset = $a;
  604. } else {
  605. $ptr->$offset = $b;
  606. }
  607. }
  608. return $head;
  609. }
  610. #define LISTSIZE 30
  611. /**
  612. * Side effects:
  613. * The "next" pointers for elements in list are changed.
  614. *
  615. * @param mixed $list Pointer to a singly-linked list of structures.
  616. * @param mixed $next Pointer to pointer to the second element of the list.
  617. * @param function $cmp A comparison function.
  618. *
  619. * @return mixed A pointer to the head of a sorted list containing the
  620. * elements orginally in list.
  621. */
  622. static function msort($list, $next, $cmp)
  623. {
  624. if ($list === 0) {
  625. return $list;
  626. }
  627. if ($list->$next === 0) {
  628. return $list;
  629. }
  630. $set = array_fill(0, 30, 0);
  631. while ($list) {
  632. $ep = $list;
  633. $list = $list->$next;
  634. $ep->$next = 0;
  635. for ($i = 0; $i < 29 && $set[$i] !== 0; $i++) {
  636. $ep = self::merge($ep, $set[$i], $cmp, $next);
  637. $set[$i] = 0;
  638. }
  639. $set[$i] = $ep;
  640. }
  641. $ep = 0;
  642. for ($i = 0; $i < 30; $i++) {
  643. if ($set[$i] !== 0) {
  644. $ep = self::merge($ep, $set[$i], $cmp, $next);
  645. }
  646. }
  647. return $ep;
  648. }
  649. /* Find a good place to break "msg" so that its length is at least "min"
  650. ** but no more than "max". Make the point as close to max as possible.
  651. */
  652. static function findbreak($msg, $min, $max)
  653. {
  654. if ($min >= strlen($msg)) {
  655. return strlen($msg);
  656. }
  657. for ($i = $spot = $min; $i <= $max && $i < strlen($msg); $i++) {
  658. $c = $msg[$i];
  659. if ($c == '-' && $i < $max - 1) {
  660. $spot = $i + 1;
  661. }
  662. if ($c == ' ') {
  663. $spot = $i;
  664. }
  665. }
  666. return $spot;
  667. }
  668. static function ErrorMsg($filename, $lineno, $format)
  669. {
  670. /* Prepare a prefix to be prepended to every output line */
  671. if ($lineno > 0) {
  672. $prefix = sprintf("%20s:%d: ", $filename, $lineno);
  673. } else {
  674. $prefix = sprintf("%20s: ", $filename);
  675. }
  676. $prefixsize = strlen($prefix);
  677. $availablewidth = 79 - $prefixsize;
  678. /* Generate the error message */
  679. $ap = func_get_args();
  680. array_shift($ap); // $filename
  681. array_shift($ap); // $lineno
  682. array_shift($ap); // $format
  683. $errmsg = vsprintf($format, $ap);
  684. $linewidth = strlen($errmsg);
  685. /* Remove trailing "\n"s from the error message. */
  686. while ($linewidth > 0
  687. && in_array($errmsg[$linewidth-1], array("\n", "\r"), true)
  688. ) {
  689. --$linewidth;
  690. $errmsg = substr($errmsg, 0, strlen($errmsg) - 1);
  691. }
  692. /* Print the error message */
  693. $base = 0;
  694. $errmsg = str_replace(
  695. array("\r", "\n", "\t"),
  696. array(' ', ' ', ' '),
  697. $errmsg
  698. );
  699. while (strlen($errmsg)) {
  700. $end = $restart = self::findbreak($errmsg, 0, $availablewidth);
  701. if (strlen($errmsg) <= 79 && $end < strlen($errmsg) && $end <= 79) {
  702. $end = $restart = strlen($errmsg);
  703. }
  704. while (isset($errmsg[$restart]) && $errmsg[$restart] == ' ') {
  705. $restart++;
  706. }
  707. printf("%s%.${end}s\n", $prefix, $errmsg);
  708. $errmsg = substr($errmsg, $restart);
  709. }
  710. }
  711. /**
  712. * Duplicate the input file without comments and without actions
  713. * on rules
  714. *
  715. * @return void
  716. */
  717. function Reprint()
  718. {
  719. printf("// Reprint of input file \"%s\".\n// Symbols:\n", $this->filename);
  720. $maxlen = 10;
  721. for ($i = 0; $i < $this->nsymbol; $i++) {
  722. $sp = $this->symbols[$i];
  723. $len = strlen($sp->name);
  724. if ($len > $maxlen ) {
  725. $maxlen = $len;
  726. }
  727. }
  728. $ncolumns = 76 / ($maxlen + 5);
  729. if ($ncolumns < 1) {
  730. $ncolumns = 1;
  731. }
  732. $skip = ($this->nsymbol + $ncolumns - 1) / $ncolumns;
  733. for ($i = 0; $i < $skip; $i++) {
  734. print "//";
  735. for ($j = $i; $j < $this->nsymbol; $j += $skip) {
  736. $sp = $this->symbols[$j];
  737. //assert( sp->index==j );
  738. printf(" %3d %-${maxlen}.${maxlen}s", $j, $sp->name);
  739. }
  740. print "\n";
  741. }
  742. for ($rp = $this->rule; $rp; $rp = $rp->next) {
  743. printf("%s", $rp->lhs->name);
  744. /*if ($rp->lhsalias) {
  745. printf("(%s)", $rp->lhsalias);
  746. }*/
  747. print " ::=";
  748. for ($i = 0; $i < $rp->nrhs; $i++) {
  749. $sp = $rp->rhs[$i];
  750. printf(" %s", $sp->name);
  751. if ($sp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) {
  752. for ($j = 1; $j < $sp->nsubsym; $j++) {
  753. printf("|%s", $sp->subsym[$j]->name);
  754. }
  755. }
  756. /*if ($rp->rhsalias[$i]) {
  757. printf("(%s)", $rp->rhsalias[$i]);
  758. }*/
  759. }
  760. print ".";
  761. if ($rp->precsym) {
  762. printf(" [%s]", $rp->precsym->name);
  763. }
  764. /*if ($rp->code) {
  765. print "\n " . $rp->code);
  766. }*/
  767. print "\n";
  768. }
  769. }
  770. }