Data.php 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857
  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: Data.php 302209 2010-08-14 14:32:56Z jespino $
  46. * @since File available since Release 0.1.0
  47. */
  48. /**
  49. /**
  50. * The state vector for the entire parser generator is recorded in
  51. * this class.
  52. *
  53. * @package PHP_ParserGenerator
  54. * @author Gregory Beaver <cellog@php.net>
  55. * @copyright 2006 Gregory Beaver
  56. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  57. * @version @package_version@
  58. * @since Class available since Release 0.1.0
  59. */
  60. class PHP_ParserGenerator_Data
  61. {
  62. /**
  63. * Used for terminal and non-terminal offsets into the action table
  64. * when their default should be used instead
  65. */
  66. const NO_OFFSET = -2147483647;
  67. /**
  68. * Table of states sorted by state number
  69. * @var array array of {@link PHP_ParserGenerator_State} objects
  70. */
  71. public $sorted;
  72. /**
  73. * List of all rules
  74. * @var PHP_ParserGenerator_Rule
  75. */
  76. public $rule;
  77. /**
  78. * Number of states
  79. * @var int
  80. */
  81. public $nstate;
  82. /**
  83. * Number of rules
  84. * @var int
  85. */
  86. public $nrule;
  87. /**
  88. * Number of terminal and nonterminal symbols
  89. * @var int
  90. */
  91. public $nsymbol;
  92. /**
  93. * Number of terminal symbols (tokens)
  94. * @var int
  95. */
  96. public $nterminal;
  97. /**
  98. * Sorted array of pointers to symbols
  99. * @var array array of {@link PHP_ParserGenerator_Symbol} objects
  100. */
  101. public $symbols = array();
  102. /**
  103. * Number of errors
  104. * @var int
  105. */
  106. public $errorcnt;
  107. /**
  108. * The error symbol
  109. * @var PHP_ParserGenerator_Symbol
  110. */
  111. public $errsym;
  112. /**
  113. * Name of the generated parser
  114. * @var string
  115. */
  116. public $name;
  117. /**
  118. * Unused relic from the C version
  119. *
  120. * Type of terminal symbols in the parser stack
  121. * @var string
  122. */
  123. public $tokentype;
  124. /**
  125. * Unused relic from the C version
  126. *
  127. * The default type of non-terminal symbols
  128. * @var string
  129. */
  130. public $vartype;
  131. /**
  132. * Name of the start symbol for the grammar
  133. * @var string
  134. */
  135. public $start;
  136. /**
  137. * Size of the parser stack
  138. *
  139. * This is 100 by default, but is set with the %stack_size directive
  140. * @var int
  141. */
  142. public $stacksize;
  143. /**
  144. * Code to put at the start of the parser file
  145. *
  146. * This is set by the %include directive
  147. * @var string
  148. */
  149. public $include_code;
  150. /**
  151. * Line number for start of include code
  152. * @var int
  153. */
  154. public $includeln;
  155. /**
  156. * Code to put in the parser class
  157. *
  158. * This is set by the %include_class directive
  159. * @var string
  160. */
  161. public $include_classcode;
  162. /**
  163. * Line number for start of include code
  164. * @var int
  165. */
  166. public $include_classln;
  167. /**
  168. * any extends/implements code
  169. *
  170. * This is set by the %declare_class directive
  171. * @var string
  172. */
  173. /**
  174. * Line number for class declaration code
  175. * @var int
  176. */
  177. public $declare_classcode;
  178. /**
  179. * Line number for start of class declaration code
  180. * @var int
  181. */
  182. public $declare_classln;
  183. /**
  184. * Code to execute when a syntax error is seen
  185. *
  186. * This is set by the %syntax_error directive
  187. * @var string
  188. */
  189. public $error;
  190. /**
  191. * Line number for start of error code
  192. * @var int
  193. */
  194. public $errorln;
  195. /**
  196. * Code to execute on a stack overflow
  197. *
  198. * This is set by the %stack_overflow directive
  199. */
  200. public $overflow;
  201. /**
  202. * Line number for start of overflow code
  203. * @var int
  204. */
  205. public $overflowln;
  206. /**
  207. * Code to execute on parser failure
  208. *
  209. * This is set by the %parse_failure directive
  210. * @var string
  211. */
  212. public $failure;
  213. /**
  214. * Line number for start of failure code
  215. * @var int
  216. */
  217. public $failureln;
  218. /**
  219. * Code to execute when the parser acccepts (completes parsing)
  220. *
  221. * This is set by the %parse_accept directive
  222. * @var string
  223. */
  224. public $accept;
  225. /**
  226. * Line number for the start of accept code
  227. * @var int
  228. */
  229. public $acceptln;
  230. /**
  231. * Code appended to the generated file
  232. *
  233. * This is set by the %code directive
  234. * @var string
  235. */
  236. public $extracode;
  237. /**
  238. * Line number for the start of the extra code
  239. * @var int
  240. */
  241. public $extracodeln;
  242. /**
  243. * Code to execute to destroy token data
  244. *
  245. * This is set by the %token_destructor directive
  246. * @var string
  247. */
  248. public $tokendest;
  249. /**
  250. * Line number for token destroyer code
  251. * @var int
  252. */
  253. public $tokendestln;
  254. /**
  255. * Code for the default non-terminal destructor
  256. *
  257. * This is set by the %default_destructor directive
  258. * @var string
  259. */
  260. public $vardest;
  261. /**
  262. * Line number for default non-terminal destructor code
  263. * @var int
  264. */
  265. public $vardestln;
  266. /**
  267. * Name of the input file
  268. * @var string
  269. */
  270. public $filename;
  271. /**
  272. * Name of the input file without its extension
  273. * @var string
  274. */
  275. public $filenosuffix;
  276. /**
  277. * Name of the current output file
  278. * @var string
  279. */
  280. public $outname;
  281. /**
  282. * A prefix added to token names
  283. * @var string
  284. */
  285. public $tokenprefix;
  286. /**
  287. * Number of parsing conflicts
  288. * @var int
  289. */
  290. public $nconflict;
  291. /**
  292. * Size of the parse tables
  293. * @var int
  294. */
  295. public $tablesize;
  296. /**
  297. * Public only basis configurations
  298. */
  299. public $basisflag;
  300. /**
  301. * True if any %fallback is seen in the grammer
  302. * @var boolean
  303. */
  304. public $has_fallback;
  305. /**
  306. * Name of the program
  307. * @var string
  308. */
  309. public $argv0;
  310. /**
  311. * Alternate parser template file
  312. * @var string
  313. */
  314. public $parser_template = "";
  315. /* Find a precedence symbol of every rule in the grammar.
  316. *
  317. * Those rules which have a precedence symbol coded in the input
  318. * grammar using the "[symbol]" construct will already have the
  319. * $rp->precsym field filled. Other rules take as their precedence
  320. * symbol the first RHS symbol with a defined precedence. If there
  321. * are not RHS symbols with a defined precedence, the precedence
  322. * symbol field is left blank.
  323. */
  324. function FindRulePrecedences()
  325. {
  326. for ($rp = $this->rule; $rp; $rp = $rp->next) {
  327. if ($rp->precsym === 0) {
  328. for ($i = 0; $i < $rp->nrhs && $rp->precsym === 0; $i++) {
  329. $sp = $rp->rhs[$i];
  330. if ($sp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) {
  331. for ($j = 0; $j < $sp->nsubsym; $j++) {
  332. if ($sp->subsym[$j]->prec >= 0) {
  333. $rp->precsym = $sp->subsym[$j];
  334. break;
  335. }
  336. }
  337. } elseif ($sp->prec >= 0) {
  338. $rp->precsym = $rp->rhs[$i];
  339. }
  340. }
  341. }
  342. }
  343. }
  344. /**
  345. * Find all nonterminals which will generate the empty string.
  346. * Then go back and compute the first sets of every nonterminal.
  347. * The first set is the set of all terminal symbols which can begin
  348. * a string generated by that nonterminal.
  349. */
  350. function FindFirstSets()
  351. {
  352. for ($i = 0; $i < $this->nsymbol; $i++) {
  353. $this->symbols[$i]->lambda = false;
  354. }
  355. for($i = $this->nterminal; $i < $this->nsymbol; $i++) {
  356. $this->symbols[$i]->firstset = array();
  357. }
  358. /* First compute all lambdas */
  359. do{
  360. $progress = 0;
  361. for ($rp = $this->rule; $rp; $rp = $rp->next) {
  362. if ($rp->lhs->lambda) {
  363. continue;
  364. }
  365. for ($i = 0; $i < $rp->nrhs; $i++) {
  366. $sp = $rp->rhs[$i];
  367. if ($sp->type != PHP_ParserGenerator_Symbol::TERMINAL || $sp->lambda === false) {
  368. break;
  369. }
  370. }
  371. if ($i === $rp->nrhs) {
  372. $rp->lhs->lambda = true;
  373. $progress = 1;
  374. }
  375. }
  376. } while ($progress);
  377. /* Now compute all first sets */
  378. do {
  379. $progress = 0;
  380. for ($rp = $this->rule; $rp; $rp = $rp->next) {
  381. $s1 = $rp->lhs;
  382. for ($i = 0; $i < $rp->nrhs; $i++) {
  383. $s2 = $rp->rhs[$i];
  384. if ($s2->type == PHP_ParserGenerator_Symbol::TERMINAL) {
  385. //progress += SetAdd(s1->firstset,s2->index);
  386. $progress += isset($s1->firstset[$s2->index]) ? 0 : 1;
  387. $s1->firstset[$s2->index] = 1;
  388. break;
  389. } elseif ($s2->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) {
  390. for ($j = 0; $j < $s2->nsubsym; $j++) {
  391. //progress += SetAdd(s1->firstset,s2->subsym[j]->index);
  392. $progress += isset($s1->firstset[$s2->subsym[$j]->index]) ? 0 : 1;
  393. $s1->firstset[$s2->subsym[$j]->index] = 1;
  394. }
  395. break;
  396. } elseif ($s1 === $s2) {
  397. if ($s1->lambda === false) {
  398. break;
  399. }
  400. } else {
  401. //progress += SetUnion(s1->firstset,s2->firstset);
  402. $test = array_diff_key($s2->firstset, $s1->firstset);
  403. if (count($test)) {
  404. $progress++;
  405. $s1->firstset += $test;
  406. }
  407. if ($s2->lambda === false) {
  408. break;
  409. }
  410. }
  411. }
  412. }
  413. } while ($progress);
  414. }
  415. /**
  416. * Compute all LR(0) states for the grammar. Links
  417. * are added to between some states so that the LR(1) follow sets
  418. * can be computed later.
  419. */
  420. function FindStates()
  421. {
  422. PHP_ParserGenerator_Config::Configlist_init();
  423. /* Find the start symbol */
  424. if ($this->start) {
  425. $sp = PHP_ParserGenerator_Symbol::Symbol_find($this->start);
  426. if ($sp == 0) {
  427. PHP_ParserGenerator::ErrorMsg($this->filename, 0,
  428. "The specified start symbol \"%s\" is not " .
  429. "in a nonterminal of the grammar. \"%s\" will be used as the start " .
  430. "symbol instead.", $this->start, $this->rule->lhs->name);
  431. $this->errorcnt++;
  432. $sp = $this->rule->lhs;
  433. }
  434. } else {
  435. $sp = $this->rule->lhs;
  436. }
  437. /* Make sure the start symbol doesn't occur on the right-hand side of
  438. ** any rule. Report an error if it does. (YACC would generate a new
  439. ** start symbol in this case.) */
  440. for ($rp = $this->rule; $rp; $rp = $rp->next) {
  441. for ($i = 0; $i < $rp->nrhs; $i++) {
  442. if ($rp->rhs[$i]->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) {
  443. foreach ($rp->rhs[$i]->subsym as $subsp) {
  444. if ($subsp === $sp) {
  445. PHP_ParserGenerator::ErrorMsg($this->filename, 0,
  446. "The start symbol \"%s\" occurs on the " .
  447. "right-hand side of a rule. This will result in a parser which " .
  448. "does not work properly.", $sp->name);
  449. $this->errorcnt++;
  450. }
  451. }
  452. } elseif ($rp->rhs[$i] === $sp) {
  453. PHP_ParserGenerator::ErrorMsg($this->filename, 0,
  454. "The start symbol \"%s\" occurs on the " .
  455. "right-hand side of a rule. This will result in a parser which " .
  456. "does not work properly.", $sp->name);
  457. $this->errorcnt++;
  458. }
  459. }
  460. }
  461. /* The basis configuration set for the first state
  462. ** is all rules which have the start symbol as their
  463. ** left-hand side */
  464. for ($rp = $sp->rule; $rp; $rp = $rp->nextlhs) {
  465. $newcfp = PHP_ParserGenerator_Config::Configlist_addbasis($rp, 0);
  466. $newcfp->fws[0] = 1;
  467. }
  468. /* Compute the first state. All other states will be
  469. ** computed automatically during the computation of the first one.
  470. ** The returned pointer to the first state is not used. */
  471. $newstp = array();
  472. $newstp = $this->getstate();
  473. if (is_array($newstp)) {
  474. $this->buildshifts($newstp[0]); /* Recursively compute successor states */
  475. }
  476. }
  477. /**
  478. * @return PHP_ParserGenerator_State
  479. */
  480. private function getstate()
  481. {
  482. /* Extract the sorted basis of the new state. The basis was constructed
  483. ** by prior calls to "Configlist_addbasis()". */
  484. PHP_ParserGenerator_Config::Configlist_sortbasis();
  485. $bp = PHP_ParserGenerator_Config::Configlist_basis();
  486. /* Get a state with the same basis */
  487. $stp = PHP_ParserGenerator_State::State_find($bp);
  488. if ($stp) {
  489. /* A state with the same basis already exists! Copy all the follow-set
  490. ** propagation links from the state under construction into the
  491. ** preexisting state, then return a pointer to the preexisting state */
  492. for($x = $bp, $y = $stp->bp; $x && $y; $x = $x->bp, $y = $y->bp) {
  493. PHP_ParserGenerator_PropagationLink::Plink_copy($y->bplp, $x->bplp);
  494. PHP_ParserGenerator_PropagationLink::Plink_delete($x->fplp);
  495. $x->fplp = $x->bplp = 0;
  496. }
  497. $cfp = PHP_ParserGenerator_Config::Configlist_return();
  498. PHP_ParserGenerator_Config::Configlist_eat($cfp);
  499. } else {
  500. /* This really is a new state. Construct all the details */
  501. PHP_ParserGenerator_Config::Configlist_closure($this); /* Compute the configuration closure */
  502. PHP_ParserGenerator_Config::Configlist_sort(); /* Sort the configuration closure */
  503. $cfp = PHP_ParserGenerator_Config::Configlist_return(); /* Get a pointer to the config list */
  504. $stp = new PHP_ParserGenerator_State; /* A new state structure */
  505. $stp->bp = $bp; /* Remember the configuration basis */
  506. $stp->cfp = $cfp; /* Remember the configuration closure */
  507. $stp->statenum = $this->nstate++; /* Every state gets a sequence number */
  508. $stp->ap = 0; /* No actions, yet. */
  509. PHP_ParserGenerator_State::State_insert($stp, $stp->bp); /* Add to the state table */
  510. // this can't work, recursion is too deep, move it into FindStates()
  511. //$this->buildshifts($stp); /* Recursively compute successor states */
  512. return array($stp);
  513. }
  514. return $stp;
  515. }
  516. /**
  517. * Construct all successor states to the given state. A "successor"
  518. * state is any state which can be reached by a shift action.
  519. * @param PHP_ParserGenerator_Data
  520. * @param PHP_ParserGenerator_State The state from which successors are computed
  521. */
  522. private function buildshifts(PHP_ParserGenerator_State $stp)
  523. {
  524. // struct config *cfp; /* For looping thru the config closure of "stp" */
  525. // struct config *bcfp; /* For the inner loop on config closure of "stp" */
  526. // struct config *new; /* */
  527. // struct symbol *sp; /* Symbol following the dot in configuration "cfp" */
  528. // struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */
  529. // struct state *newstp; /* A pointer to a successor state */
  530. /* Each configuration becomes complete after it contibutes to a successor
  531. ** state. Initially, all configurations are incomplete */
  532. $cfp = $stp->cfp;
  533. for ($cfp = $stp->cfp; $cfp; $cfp = $cfp->next) {
  534. $cfp->status = PHP_ParserGenerator_Config::INCOMPLETE;
  535. }
  536. /* Loop through all configurations of the state "stp" */
  537. for ($cfp = $stp->cfp; $cfp; $cfp = $cfp->next) {
  538. if ($cfp->status == PHP_ParserGenerator_Config::COMPLETE) {
  539. continue; /* Already used by inner loop */
  540. }
  541. if ($cfp->dot >= $cfp->rp->nrhs) {
  542. continue; /* Can't shift this config */
  543. }
  544. PHP_ParserGenerator_Config::Configlist_reset(); /* Reset the new config set */
  545. $sp = $cfp->rp->rhs[$cfp->dot]; /* Symbol after the dot */
  546. /* For every configuration in the state "stp" which has the symbol "sp"
  547. ** following its dot, add the same configuration to the basis set under
  548. ** construction but with the dot shifted one symbol to the right. */
  549. $bcfp = $cfp;
  550. for ($bcfp = $cfp; $bcfp; $bcfp = $bcfp->next) {
  551. if ($bcfp->status == PHP_ParserGenerator_Config::COMPLETE) {
  552. continue; /* Already used */
  553. }
  554. if ($bcfp->dot >= $bcfp->rp->nrhs) {
  555. continue; /* Can't shift this one */
  556. }
  557. $bsp = $bcfp->rp->rhs[$bcfp->dot]; /* Get symbol after dot */
  558. if (!PHP_ParserGenerator_Symbol::same_symbol($bsp, $sp)) {
  559. continue; /* Must be same as for "cfp" */
  560. }
  561. $bcfp->status = PHP_ParserGenerator_Config::COMPLETE; /* Mark this config as used */
  562. $new = PHP_ParserGenerator_Config::Configlist_addbasis($bcfp->rp, $bcfp->dot + 1);
  563. PHP_ParserGenerator_PropagationLink::Plink_add($new->bplp, $bcfp);
  564. }
  565. /* Get a pointer to the state described by the basis configuration set
  566. ** constructed in the preceding loop */
  567. $newstp = $this->getstate();
  568. if (is_array($newstp)) {
  569. $this->buildshifts($newstp[0]); /* Recursively compute successor states */
  570. $newstp = $newstp[0];
  571. }
  572. /* The state "newstp" is reached from the state "stp" by a shift action
  573. ** on the symbol "sp" */
  574. if ($sp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) {
  575. for($i = 0; $i < $sp->nsubsym; $i++) {
  576. PHP_ParserGenerator_Action::Action_add($stp->ap, PHP_ParserGenerator_Action::SHIFT, $sp->subsym[$i],
  577. $newstp);
  578. }
  579. } else {
  580. PHP_ParserGenerator_Action::Action_add($stp->ap, PHP_ParserGenerator_Action::SHIFT, $sp, $newstp);
  581. }
  582. }
  583. }
  584. /**
  585. * Construct the propagation links
  586. */
  587. function FindLinks()
  588. {
  589. /* Housekeeping detail:
  590. ** Add to every propagate link a pointer back to the state to
  591. ** which the link is attached. */
  592. foreach ($this->sorted as $info) {
  593. $info->key->stp = $info->data;
  594. }
  595. /* Convert all backlinks into forward links. Only the forward
  596. ** links are used in the follow-set computation. */
  597. for ($i = 0; $i < $this->nstate; $i++) {
  598. $stp = $this->sorted[$i];
  599. for ($cfp = $stp->data->cfp; $cfp; $cfp = $cfp->next) {
  600. for ($plp = $cfp->bplp; $plp; $plp = $plp->next) {
  601. $other = $plp->cfp;
  602. PHP_ParserGenerator_PropagationLink::Plink_add($other->fplp, $cfp);
  603. }
  604. }
  605. }
  606. }
  607. /**
  608. * Compute the reduce actions, and resolve conflicts.
  609. */
  610. function FindActions()
  611. {
  612. /* Add all of the reduce actions
  613. ** A reduce action is added for each element of the followset of
  614. ** a configuration which has its dot at the extreme right.
  615. */
  616. for ($i = 0; $i < $this->nstate; $i++) { /* Loop over all states */
  617. $stp = $this->sorted[$i]->data;
  618. for ($cfp = $stp->cfp; $cfp; $cfp = $cfp->next) {
  619. /* Loop over all configurations */
  620. if ($cfp->rp->nrhs == $cfp->dot) { /* Is dot at extreme right? */
  621. for ($j = 0; $j < $this->nterminal; $j++) {
  622. if (isset($cfp->fws[$j])) {
  623. /* Add a reduce action to the state "stp" which will reduce by the
  624. ** rule "cfp->rp" if the lookahead symbol is "$this->symbols[j]" */
  625. PHP_ParserGenerator_Action::Action_add($stp->ap, PHP_ParserGenerator_Action::REDUCE,
  626. $this->symbols[$j], $cfp->rp);
  627. }
  628. }
  629. }
  630. }
  631. }
  632. /* Add the accepting token */
  633. if ($this->start instanceof PHP_ParserGenerator_Symbol) {
  634. $sp = PHP_ParserGenerator_Symbol::Symbol_find($this->start);
  635. if ($sp === 0) {
  636. $sp = $this->rule->lhs;
  637. }
  638. } else {
  639. $sp = $this->rule->lhs;
  640. }
  641. /* Add to the first state (which is always the starting state of the
  642. ** finite state machine) an action to ACCEPT if the lookahead is the
  643. ** start nonterminal. */
  644. PHP_ParserGenerator_Action::Action_add($this->sorted[0]->data->ap, PHP_ParserGenerator_Action::ACCEPT, $sp, 0);
  645. /* Resolve conflicts */
  646. for ($i = 0; $i < $this->nstate; $i++) {
  647. // struct action *ap, *nap;
  648. // struct state *stp;
  649. $stp = $this->sorted[$i]->data;
  650. if (!$stp->ap) {
  651. throw new Exception('state has no actions associated');
  652. }
  653. echo 'processing state ' . $stp->statenum . " actions:\n";
  654. for ($ap = $stp->ap; $ap !== 0 && $ap->next !== 0; $ap = $ap->next) {
  655. echo ' Action ';
  656. $ap->display(true);
  657. }
  658. $stp->ap = PHP_ParserGenerator_Action::Action_sort($stp->ap);
  659. for ($ap = $stp->ap; $ap !== 0 && $ap->next !== 0; $ap = $ap->next) {
  660. for ($nap = $ap->next; $nap !== 0 && $nap->sp === $ap->sp ; $nap = $nap->next) {
  661. /* The two actions "ap" and "nap" have the same lookahead.
  662. ** Figure out which one should be used */
  663. $this->nconflict += $this->resolve_conflict($ap, $nap, $this->errsym);
  664. }
  665. }
  666. }
  667. /* Report an error for each rule that can never be reduced. */
  668. for ($rp = $this->rule; $rp; $rp = $rp->next) {
  669. $rp->canReduce = false;
  670. }
  671. for ($i = 0; $i < $this->nstate; $i++) {
  672. for ($ap = $this->sorted[$i]->data->ap; $ap !== 0; $ap = $ap->next) {
  673. if ($ap->type == PHP_ParserGenerator_Action::REDUCE) {
  674. $ap->x->canReduce = true;
  675. }
  676. }
  677. }
  678. for ($rp = $this->rule; $rp !== 0; $rp = $rp->next) {
  679. if ($rp->canReduce) {
  680. continue;
  681. }
  682. PHP_ParserGenerator::ErrorMsg($this->filename, $rp->ruleline, "This rule can not be reduced (is not connected to the start symbol).\n");
  683. $this->errorcnt++;
  684. }
  685. }
  686. /** Resolve a conflict between the two given actions. If the
  687. * conflict can't be resolve, return non-zero.
  688. *
  689. * NO LONGER TRUE:
  690. * To resolve a conflict, first look to see if either action
  691. * is on an error rule. In that case, take the action which
  692. * is not associated with the error rule. If neither or both
  693. * actions are associated with an error rule, then try to
  694. * use precedence to resolve the conflict.
  695. *
  696. * If either action is a SHIFT, then it must be apx. This
  697. * function won't work if apx->type==REDUCE and apy->type==SHIFT.
  698. * @param PHP_ParserGenerator_Action
  699. * @param PHP_ParserGenerator_Action
  700. * @param PHP_ParserGenerator_Symbol|null The error symbol (if defined. NULL otherwise)
  701. */
  702. function resolve_conflict($apx, $apy, $errsym)
  703. {
  704. $errcnt = 0;
  705. if ($apx->sp !== $apy->sp) {
  706. throw new Exception('no conflict but resolve_conflict called');
  707. }
  708. if ($apx->type == PHP_ParserGenerator_Action::SHIFT && $apy->type == PHP_ParserGenerator_Action::REDUCE) {
  709. $spx = $apx->sp;
  710. $spy = $apy->x->precsym;
  711. if ($spy === 0 || $spx->prec < 0 || $spy->prec < 0) {
  712. /* Not enough precedence information. */
  713. $apy->type = PHP_ParserGenerator_Action::CONFLICT;
  714. $errcnt++;
  715. } elseif ($spx->prec > $spy->prec) { /* Lower precedence wins */
  716. $apy->type = PHP_ParserGenerator_Action::RD_RESOLVED;
  717. } elseif ($spx->prec < $spy->prec) {
  718. $apx->type = PHP_ParserGenerator_Action::SH_RESOLVED;
  719. } elseif ($spx->prec === $spy->prec && $spx->assoc == PHP_ParserGenerator_Symbol::RIGHT) {
  720. /* Use operator */
  721. $apy->type = PHP_ParserGenerator_Action::RD_RESOLVED; /* associativity */
  722. } elseif ($spx->prec === $spy->prec && $spx->assoc == PHP_ParserGenerator_Symbol::LEFT) {
  723. /* to break tie */
  724. $apx->type = PHP_ParserGenerator_Action::SH_RESOLVED;
  725. } else {
  726. if ($spx->prec !== $spy->prec || $spx->assoc !== PHP_ParserGenerator_Symbol::NONE) {
  727. throw new Exception('$spx->prec !== $spy->prec || $spx->assoc !== PHP_ParserGenerator_Symbol::NONE');
  728. }
  729. $apy->type = PHP_ParserGenerator_Action::CONFLICT;
  730. $errcnt++;
  731. }
  732. } elseif ($apx->type == PHP_ParserGenerator_Action::REDUCE && $apy->type == PHP_ParserGenerator_Action::REDUCE) {
  733. $spx = $apx->x->precsym;
  734. $spy = $apy->x->precsym;
  735. if ($spx === 0 || $spy === 0 || $spx->prec < 0 ||
  736. $spy->prec < 0 || $spx->prec === $spy->prec) {
  737. $apy->type = PHP_ParserGenerator_Action::CONFLICT;
  738. $errcnt++;
  739. } elseif ($spx->prec > $spy->prec) {
  740. $apy->type = PHP_ParserGenerator_Action::RD_RESOLVED;
  741. } elseif ($spx->prec < $spy->prec) {
  742. $apx->type = PHP_ParserGenerator_Action::RD_RESOLVED;
  743. }
  744. } else {
  745. if ($apx->type!== PHP_ParserGenerator_Action::SH_RESOLVED &&
  746. $apx->type!== PHP_ParserGenerator_Action::RD_RESOLVED &&
  747. $apx->type!== PHP_ParserGenerator_Action::CONFLICT &&
  748. $apy->type!== PHP_ParserGenerator_Action::SH_RESOLVED &&
  749. $apy->type!== PHP_ParserGenerator_Action::RD_RESOLVED &&
  750. $apy->type!== PHP_ParserGenerator_Action::CONFLICT) {
  751. throw new Exception('$apx->type!== PHP_ParserGenerator_Action::SH_RESOLVED &&
  752. $apx->type!== PHP_ParserGenerator_Action::RD_RESOLVED &&
  753. $apx->type!== PHP_ParserGenerator_Action::CONFLICT &&
  754. $apy->type!== PHP_ParserGenerator_Action::SH_RESOLVED &&
  755. $apy->type!== PHP_ParserGenerator_Action::RD_RESOLVED &&
  756. $apy->type!== PHP_ParserGenerator_Action::CONFLICT');
  757. }
  758. /* The REDUCE/SHIFT case cannot happen because SHIFTs come before
  759. ** REDUCEs on the list. If we reach this point it must be because
  760. ** the parser conflict had already been resolved. */
  761. }
  762. return $errcnt;
  763. }
  764. /**
  765. * Reduce the size of the action tables, if possible, by making use
  766. * of defaults.
  767. *
  768. * In this version, we take the most frequent REDUCE action and make
  769. * it the default.
  770. */
  771. function CompressTables()
  772. {
  773. for ($i = 0; $i < $this->nstate; $i++) {
  774. $stp = $this->sorted[$i]->data;
  775. $nbest = 0;
  776. $rbest = 0;
  777. for ($ap = $stp->ap; $ap; $ap = $ap->next) {
  778. if ($ap->type != PHP_ParserGenerator_Action::REDUCE) {
  779. continue;
  780. }
  781. $rp = $ap->x;
  782. if ($rp === $rbest) {
  783. continue;
  784. }
  785. $n = 1;
  786. for ($ap2 = $ap->next; $ap2; $ap2 = $ap2->next) {
  787. if ($ap2->type != PHP_ParserGenerator_Action::REDUCE) {
  788. continue;
  789. }
  790. $rp2 = $ap2->x;
  791. if ($rp2 === $rbest) {
  792. continue;
  793. }
  794. if ($rp2 === $rp) {
  795. $n++;
  796. }
  797. }
  798. if ($n > $nbest) {
  799. $nbest = $n;
  800. $rbest = $rp;
  801. }
  802. }
  803. /* Do not make a default if the number of rules to default
  804. ** is not at least 1 */
  805. if ($nbest < 1) {
  806. continue;
  807. }
  808. /* Combine matching REDUCE actions into a single default */
  809. for ($ap = $stp->ap; $ap; $ap = $ap->next) {
  810. if ($ap->type == PHP_ParserGenerator_Action::REDUCE && $ap->x === $rbest) {
  811. break;
  812. }
  813. }
  814. if ($ap === 0) {
  815. throw new Exception('$ap is not an object');
  816. }
  817. $ap->sp = PHP_ParserGenerator_Symbol::Symbol_new("{default}");
  818. for ($ap = $ap->next; $ap; $ap = $ap->next) {
  819. if ($ap->type == PHP_ParserGenerator_Action::REDUCE && $ap->x === $rbest) {
  820. $ap->type = PHP_ParserGenerator_Action::NOT_USED;
  821. }
  822. }
  823. $stp->ap = PHP_ParserGenerator_Action::Action_sort($stp->ap);
  824. }
  825. }
  826. /**
  827. * Renumber and resort states so that states with fewer choices
  828. * occur at the end. Except, keep state 0 as the first state.
  829. */
  830. function ResortStates()
  831. {
  832. for ($i = 0; $i < $this->nstate; $i++) {
  833. $stp = $this->sorted[$i]->data;
  834. $stp->nTknAct = $stp->nNtAct = 0;
  835. $stp->iDflt = $this->nstate + $this->nrule;
  836. $stp->iTknOfst = PHP_ParserGenerator_Data::NO_OFFSET;
  837. $stp->iNtOfst = PHP_ParserGenerator_Data::NO_OFFSET;
  838. for ($ap = $stp->ap; $ap; $ap = $ap->next) {
  839. if ($this->compute_action($ap) >= 0) {
  840. if ($ap->sp->index < $this->nterminal) {
  841. $stp->nTknAct++;
  842. } elseif ($ap->sp->index < $this->nsymbol) {
  843. $stp->nNtAct++;
  844. } else {
  845. $stp->iDflt = $this->compute_action($ap);
  846. }
  847. }
  848. }
  849. $this->sorted[$i] = $stp;
  850. }
  851. $save = $this->sorted[0];
  852. unset($this->sorted[0]);
  853. usort($this->sorted, array('PHP_ParserGenerator_State', 'stateResortCompare'));
  854. array_unshift($this->sorted, $save);
  855. for($i = 0; $i < $this->nstate; $i++) {
  856. $this->sorted[$i]->statenum = $i;
  857. }
  858. }
  859. /**
  860. * Given an action, compute the integer value for that action
  861. * which is to be put in the action table of the generated machine.
  862. * Return negative if no action should be generated.
  863. * @param PHP_ParserGenerator_Action
  864. */
  865. function compute_action($ap)
  866. {
  867. switch ($ap->type) {
  868. case PHP_ParserGenerator_Action::SHIFT:
  869. $act = $ap->x->statenum;
  870. break;
  871. case PHP_ParserGenerator_Action::REDUCE:
  872. $act = $ap->x->index + $this->nstate;
  873. break;
  874. case PHP_ParserGenerator_Action::ERROR:
  875. $act = $this->nstate + $this->nrule;
  876. break;
  877. case PHP_ParserGenerator_Action::ACCEPT:
  878. $act = $this->nstate + $this->nrule + 1;
  879. break;
  880. default:
  881. $act = -1;
  882. break;
  883. }
  884. return $act;
  885. }
  886. /**
  887. * Generate the "Parse.out" log file
  888. */
  889. function ReportOutput()
  890. {
  891. $fp = fopen($this->filenosuffix . ".out", "wb");
  892. if (!$fp) {
  893. return;
  894. }
  895. for ($i = 0; $i < $this->nstate; $i++) {
  896. $stp = $this->sorted[$i];
  897. fprintf($fp, "State %d:\n", $stp->statenum);
  898. if ($this->basisflag) {
  899. $cfp = $stp->bp;
  900. } else {
  901. $cfp = $stp->cfp;
  902. }
  903. while ($cfp) {
  904. if ($cfp->dot == $cfp->rp->nrhs) {
  905. $buf = sprintf('(%d)', $cfp->rp->index);
  906. fprintf($fp, ' %5s ', $buf);
  907. } else {
  908. fwrite($fp,' ');
  909. }
  910. $cfp->ConfigPrint($fp);
  911. fwrite($fp, "\n");
  912. if (0) {
  913. //SetPrint(fp,cfp->fws,$this);
  914. //PlinkPrint(fp,cfp->fplp,"To ");
  915. //PlinkPrint(fp,cfp->bplp,"From");
  916. }
  917. if ($this->basisflag) {
  918. $cfp = $cfp->bp;
  919. } else {
  920. $cfp = $cfp->next;
  921. }
  922. }
  923. fwrite($fp, "\n");
  924. for ($ap = $stp->ap; $ap; $ap = $ap->next) {
  925. if ($ap->PrintAction($fp, 30)) {
  926. fprintf($fp,"\n");
  927. }
  928. }
  929. fwrite($fp,"\n");
  930. }
  931. fclose($fp);
  932. }
  933. /**
  934. * The next function finds the template file and opens it, returning
  935. * a pointer to the opened file.
  936. * @return resource
  937. */
  938. private function tplt_open()
  939. {
  940. $templatename = $this->parser_template ? $this->parser_template : dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . "Lempar.php";
  941. $buf = $this->filenosuffix . '.lt';
  942. if (file_exists($buf) && is_readable($buf)) {
  943. $tpltname = $buf;
  944. } elseif (file_exists($templatename) && is_readable($templatename)) {
  945. $tpltname = $templatename;
  946. } elseif ($fp = @fopen($templatename, 'rb', true)) {
  947. return $fp;
  948. }
  949. if (!isset($tpltname)) {
  950. echo "Can't find the parser driver template file \"%s\".\n",
  951. $templatename;
  952. $this->errorcnt++;
  953. return 0;
  954. }
  955. $in = @fopen($tpltname,"rb");
  956. if (!$in) {
  957. printf("Can't open the template file \"%s\".\n", $tpltname);
  958. $this->errorcnt++;
  959. return 0;
  960. }
  961. return $in;
  962. }
  963. #define LINESIZE 1000
  964. /**#@+
  965. * The next cluster of routines are for reading the template file
  966. * and writing the results to the generated parser
  967. */
  968. /**
  969. * The first function transfers data from "in" to "out" until
  970. * a line is seen which begins with "%%". The line number is
  971. * tracked.
  972. *
  973. * if name!=0, then any word that begin with "Parse" is changed to
  974. * begin with *name instead.
  975. */
  976. private function tplt_xfer($name, $in, $out, &$lineno)
  977. {
  978. while (($line = fgets($in, 1024)) && ($line[0] != '%' || $line[1] != '%')) {
  979. $lineno++;
  980. $iStart = 0;
  981. if ($name) {
  982. for ($i = 0; $i < strlen($line); $i++) {
  983. if ($line[$i] == 'P' && substr($line, $i, 5) == "Parse"
  984. && ($i === 0 || preg_match('/[^a-zA-Z]/', $line[$i - 1]))) {
  985. if ($i > $iStart) {
  986. fwrite($out, substr($line, $iStart, $i - $iStart));
  987. }
  988. fwrite($out, $name);
  989. $i += 4;
  990. $iStart = $i + 1;
  991. }
  992. }
  993. }
  994. fwrite($out, substr($line, $iStart));
  995. }
  996. }
  997. /**
  998. * Print a #line directive line to the output file.
  999. */
  1000. private function tplt_linedir($out, $lineno, $filename)
  1001. {
  1002. fwrite($out, '#line ' . $lineno . ' "' . $filename . "\"\n");
  1003. }
  1004. /**
  1005. * Print a string to the file and keep the linenumber up to date
  1006. */
  1007. private function tplt_print($out, $str, $strln, &$lineno)
  1008. {
  1009. if ($str == '') {
  1010. return;
  1011. }
  1012. $this->tplt_linedir($out, $strln, $this->filename);
  1013. $lineno++;
  1014. fwrite($out, $str);
  1015. $lineno += count(explode("\n", $str)) - 1;
  1016. $this->tplt_linedir($out, $lineno + 2, $this->outname);
  1017. $lineno += 2;
  1018. }
  1019. /**#@-*/
  1020. /**
  1021. * Compute all followsets.
  1022. *
  1023. * A followset is the set of all symbols which can come immediately
  1024. * after a configuration.
  1025. */
  1026. function FindFollowSets()
  1027. {
  1028. for ($i = 0; $i < $this->nstate; $i++) {
  1029. for ($cfp = $this->sorted[$i]->data->cfp; $cfp; $cfp = $cfp->next) {
  1030. $cfp->status = PHP_ParserGenerator_Config::INCOMPLETE;
  1031. }
  1032. }
  1033. do {
  1034. $progress = 0;
  1035. for ($i = 0; $i < $this->nstate; $i++) {
  1036. for ($cfp = $this->sorted[$i]->data->cfp; $cfp; $cfp = $cfp->next) {
  1037. if ($cfp->status == PHP_ParserGenerator_Config::COMPLETE) {
  1038. continue;
  1039. }
  1040. for ($plp = $cfp->fplp; $plp; $plp = $plp->next) {
  1041. $a = array_diff_key($cfp->fws, $plp->cfp->fws);
  1042. if (count($a)) {
  1043. $plp->cfp->fws += $a;
  1044. $plp->cfp->status = PHP_ParserGenerator_Config::INCOMPLETE;
  1045. $progress = 1;
  1046. }
  1047. }
  1048. $cfp->status = PHP_ParserGenerator_Config::COMPLETE;
  1049. }
  1050. }
  1051. } while ($progress);
  1052. }
  1053. /**
  1054. * Generate C source code for the parser
  1055. * @param int Output in makeheaders format if true
  1056. */
  1057. function ReportTable($mhflag)
  1058. {
  1059. // FILE *out, *in;
  1060. // char line[LINESIZE];
  1061. // int lineno;
  1062. // struct state *stp;
  1063. // struct action *ap;
  1064. // struct rule *rp;
  1065. // struct acttab *pActtab;
  1066. // int i, j, n;
  1067. // char *name;
  1068. // int mnTknOfst, mxTknOfst;
  1069. // int mnNtOfst, mxNtOfst;
  1070. // struct axset *ax;
  1071. $in = $this->tplt_open();
  1072. if (!$in) {
  1073. return;
  1074. }
  1075. $out = fopen($this->filenosuffix . ".php", "wb");
  1076. if (!$out) {
  1077. fclose($in);
  1078. return;
  1079. }
  1080. $this->outname = $this->filenosuffix . ".php";
  1081. $lineno = 1;
  1082. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1083. /* Generate the include code, if any */
  1084. $this->tplt_print($out, $this->include_code, $this->includeln, $lineno);
  1085. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1086. /* Generate the class declaration code */
  1087. $this->tplt_print($out, $this->declare_classcode, $this->declare_classln,
  1088. $lineno);
  1089. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1090. /* Generate the internal parser class include code, if any */
  1091. $this->tplt_print($out, $this->include_classcode, $this->include_classln,
  1092. $lineno);
  1093. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1094. /* Generate #defines for all tokens */
  1095. //if ($mhflag) {
  1096. //fprintf($out, "#if INTERFACE\n");
  1097. $lineno++;
  1098. if ($this->tokenprefix) {
  1099. $prefix = $this->tokenprefix;
  1100. } else {
  1101. $prefix = '';
  1102. }
  1103. for ($i = 1; $i < $this->nterminal; $i++) {
  1104. fprintf($out, " const %s%-30s = %2d;\n", $prefix, $this->symbols[$i]->name, $i);
  1105. $lineno++;
  1106. }
  1107. //fwrite($out, "#endif\n");
  1108. $lineno++;
  1109. //}
  1110. fwrite($out, " const YY_NO_ACTION = " .
  1111. ($this->nstate + $this->nrule + 2) . ";\n");
  1112. $lineno++;
  1113. fwrite($out, " const YY_ACCEPT_ACTION = " .
  1114. ($this->nstate + $this->nrule + 1) . ";\n");
  1115. $lineno++;
  1116. fwrite($out, " const YY_ERROR_ACTION = " .
  1117. ($this->nstate + $this->nrule) . ";\n");
  1118. $lineno++;
  1119. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1120. /* Generate the action table and its associates:
  1121. **
  1122. ** yy_action[] A single table containing all actions.
  1123. ** yy_lookahead[] A table containing the lookahead for each entry in
  1124. ** yy_action. Used to detect hash collisions.
  1125. ** yy_shift_ofst[] For each state, the offset into yy_action for
  1126. ** shifting terminals.
  1127. ** yy_reduce_ofst[] For each state, the offset into yy_action for
  1128. ** shifting non-terminals after a reduce.
  1129. ** yy_default[] Default action for each state.
  1130. */
  1131. /* Compute the actions on all states and count them up */
  1132. $ax = array();
  1133. for ($i = 0; $i < $this->nstate; $i++) {
  1134. $stp = $this->sorted[$i];
  1135. $ax[$i * 2] = array();
  1136. $ax[$i * 2]['stp'] = $stp;
  1137. $ax[$i * 2]['isTkn'] = 1;
  1138. $ax[$i * 2]['nAction'] = $stp->nTknAct;
  1139. $ax[$i * 2 + 1] = array();
  1140. $ax[$i * 2 + 1]['stp'] = $stp;
  1141. $ax[$i * 2 + 1]['isTkn'] = 0;
  1142. $ax[$i * 2 + 1]['nAction'] = $stp->nNtAct;
  1143. }
  1144. $mxTknOfst = $mnTknOfst = 0;
  1145. $mxNtOfst = $mnNtOfst = 0;
  1146. /* Compute the action table. In order to try to keep the size of the
  1147. ** action table to a minimum, the heuristic of placing the largest action
  1148. ** sets first is used.
  1149. */
  1150. usort($ax, array('PHP_ParserGenerator_Data', 'axset_compare'));
  1151. $pActtab = new PHP_ParserGenerator_ActionTable;
  1152. for ($i = 0; $i < $this->nstate * 2 && $ax[$i]['nAction'] > 0; $i++) {
  1153. $stp = $ax[$i]['stp'];
  1154. if ($ax[$i]['isTkn']) {
  1155. for ($ap = $stp->ap; $ap; $ap = $ap->next) {
  1156. if ($ap->sp->index >= $this->nterminal) {
  1157. continue;
  1158. }
  1159. $action = $this->compute_action($ap);
  1160. if ($action < 0) {
  1161. continue;
  1162. }
  1163. $pActtab->acttab_action($ap->sp->index, $action);
  1164. }
  1165. $stp->iTknOfst = $pActtab->acttab_insert();
  1166. if ($stp->iTknOfst < $mnTknOfst) {
  1167. $mnTknOfst = $stp->iTknOfst;
  1168. }
  1169. if ($stp->iTknOfst > $mxTknOfst) {
  1170. $mxTknOfst = $stp->iTknOfst;
  1171. }
  1172. } else {
  1173. for ($ap = $stp->ap; $ap; $ap = $ap->next) {
  1174. if ($ap->sp->index < $this->nterminal) {
  1175. continue;
  1176. }
  1177. if ($ap->sp->index == $this->nsymbol) {
  1178. continue;
  1179. }
  1180. $action = $this->compute_action($ap);
  1181. if ($action < 0) {
  1182. continue;
  1183. }
  1184. $pActtab->acttab_action($ap->sp->index, $action);
  1185. }
  1186. $stp->iNtOfst = $pActtab->acttab_insert();
  1187. if ($stp->iNtOfst < $mnNtOfst) {
  1188. $mnNtOfst = $stp->iNtOfst;
  1189. }
  1190. if ($stp->iNtOfst > $mxNtOfst) {
  1191. $mxNtOfst = $stp->iNtOfst;
  1192. }
  1193. }
  1194. }
  1195. /* Output the yy_action table */
  1196. fprintf($out, " const YY_SZ_ACTTAB = %d;\n", $pActtab->nAction);
  1197. $lineno++;
  1198. fwrite($out, "static public \$yy_action = array(\n");
  1199. $lineno++;
  1200. $n = $pActtab->nAction;
  1201. for($i = $j = 0; $i < $n; $i++) {
  1202. $action = $pActtab->aAction[$i]['action'];
  1203. if ($action < 0) {
  1204. $action = $this->nsymbol + $this->nrule + 2;
  1205. }
  1206. // change next line
  1207. if ($j === 0) {
  1208. fprintf($out, " /* %5d */ ", $i);
  1209. }
  1210. fprintf($out, " %4d,", $action);
  1211. if ($j == 9 || $i == $n - 1) {
  1212. fwrite($out, "\n");
  1213. $lineno++;
  1214. $j = 0;
  1215. } else {
  1216. $j++;
  1217. }
  1218. }
  1219. fwrite($out, " );\n"); $lineno++;
  1220. /* Output the yy_lookahead table */
  1221. fwrite($out, " static public \$yy_lookahead = array(\n");
  1222. $lineno++;
  1223. for ($i = $j = 0; $i < $n; $i++) {
  1224. $la = $pActtab->aAction[$i]['lookahead'];
  1225. if ($la < 0) {
  1226. $la = $this->nsymbol;
  1227. }
  1228. // change next line
  1229. if ($j === 0) {
  1230. fprintf($out, " /* %5d */ ", $i);
  1231. }
  1232. fprintf($out, " %4d,", $la);
  1233. if ($j == 9 || $i == $n - 1) {
  1234. fwrite($out, "\n");
  1235. $lineno++;
  1236. $j = 0;
  1237. } else {
  1238. $j++;
  1239. }
  1240. }
  1241. fwrite($out, ");\n");
  1242. $lineno++;
  1243. /* Output the yy_shift_ofst[] table */
  1244. fprintf($out, " const YY_SHIFT_USE_DFLT = %d;\n", $mnTknOfst - 1);
  1245. $lineno++;
  1246. $n = $this->nstate;
  1247. while ($n > 0 && $this->sorted[$n - 1]->iTknOfst == PHP_ParserGenerator_Data::NO_OFFSET) {
  1248. $n--;
  1249. }
  1250. fprintf($out, " const YY_SHIFT_MAX = %d;\n", $n - 1);
  1251. $lineno++;
  1252. fwrite($out, " static public \$yy_shift_ofst = array(\n");
  1253. $lineno++;
  1254. for ($i = $j = 0; $i < $n; $i++) {
  1255. $stp = $this->sorted[$i];
  1256. $ofst = $stp->iTknOfst;
  1257. if ($ofst === PHP_ParserGenerator_Data::NO_OFFSET) {
  1258. $ofst = $mnTknOfst - 1;
  1259. }
  1260. // change next line
  1261. if ($j === 0) {
  1262. fprintf($out, " /* %5d */ ", $i);
  1263. }
  1264. fprintf($out, " %4d,", $ofst);
  1265. if ($j == 9 || $i == $n - 1) {
  1266. fwrite($out, "\n");
  1267. $lineno++;
  1268. $j = 0;
  1269. } else {
  1270. $j++;
  1271. }
  1272. }
  1273. fwrite($out, ");\n");
  1274. $lineno++;
  1275. /* Output the yy_reduce_ofst[] table */
  1276. fprintf($out, " const YY_REDUCE_USE_DFLT = %d;\n", $mnNtOfst - 1);
  1277. $lineno++;
  1278. $n = $this->nstate;
  1279. while ($n > 0 && $this->sorted[$n - 1]->iNtOfst == PHP_ParserGenerator_Data::NO_OFFSET) {
  1280. $n--;
  1281. }
  1282. fprintf($out, " const YY_REDUCE_MAX = %d;\n", $n - 1);
  1283. $lineno++;
  1284. fwrite($out, " static public \$yy_reduce_ofst = array(\n");
  1285. $lineno++;
  1286. for ($i = $j = 0; $i < $n; $i++) {
  1287. $stp = $this->sorted[$i];
  1288. $ofst = $stp->iNtOfst;
  1289. if ($ofst == PHP_ParserGenerator_Data::NO_OFFSET) {
  1290. $ofst = $mnNtOfst - 1;
  1291. }
  1292. // change next line
  1293. if ($j == 0) {
  1294. fprintf($out, " /* %5d */ ", $i);
  1295. }
  1296. fprintf($out, " %4d,", $ofst);
  1297. if ($j == 9 || $i == $n - 1) {
  1298. fwrite($out, "\n");
  1299. $lineno++;
  1300. $j = 0;
  1301. } else {
  1302. $j++;
  1303. }
  1304. }
  1305. fwrite($out, ");\n");
  1306. $lineno++;
  1307. /* Output the expected tokens table */
  1308. fwrite($out, " static public \$yyExpectedTokens = array(\n");
  1309. $lineno++;
  1310. for ($i = 0; $i < $this->nstate; $i++) {
  1311. $stp = $this->sorted[$i];
  1312. fwrite($out, " /* $i */ array(");
  1313. for ($ap = $stp->ap; $ap; $ap = $ap->next) {
  1314. if ($ap->sp->index < $this->nterminal) {
  1315. if ($ap->type == PHP_ParserGenerator_Action::SHIFT ||
  1316. $ap->type == PHP_ParserGenerator_Action::REDUCE) {
  1317. fwrite($out, $ap->sp->index . ', ');
  1318. }
  1319. }
  1320. }
  1321. fwrite($out, "),\n");
  1322. $lineno++;
  1323. }
  1324. fwrite($out, ");\n");
  1325. $lineno++;
  1326. /* Output the default action table */
  1327. fwrite($out, " static public \$yy_default = array(\n");
  1328. $lineno++;
  1329. $n = $this->nstate;
  1330. for ($i = $j = 0; $i < $n; $i++) {
  1331. $stp = $this->sorted[$i];
  1332. // change next line
  1333. if ($j == 0) {
  1334. fprintf($out, " /* %5d */ ", $i);
  1335. }
  1336. fprintf($out, " %4d,", $stp->iDflt);
  1337. if ($j == 9 || $i == $n - 1) {
  1338. fprintf($out, "\n"); $lineno++;
  1339. $j = 0;
  1340. } else {
  1341. $j++;
  1342. }
  1343. }
  1344. fwrite($out, ");\n");
  1345. $lineno++;
  1346. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1347. /* Generate the defines */
  1348. fprintf($out, " const YYNOCODE = %d;\n", $this->nsymbol + 1);
  1349. $lineno++;
  1350. if ($this->stacksize) {
  1351. if($this->stacksize <= 0) {
  1352. PHP_ParserGenerator::ErrorMsg($this->filename, 0,
  1353. "Illegal stack size: [%s]. The stack size should be an integer constant.",
  1354. $this->stacksize);
  1355. $this->errorcnt++;
  1356. $this->stacksize = "100";
  1357. }
  1358. fprintf($out, " const YYSTACKDEPTH = %s;\n", $this->stacksize);
  1359. $lineno++;
  1360. } else {
  1361. fwrite($out," const YYSTACKDEPTH = 100;\n");
  1362. $lineno++;
  1363. }
  1364. fprintf($out, " const YYNSTATE = %d;\n", $this->nstate);
  1365. $lineno++;
  1366. fprintf($out, " const YYNRULE = %d;\n", $this->nrule);
  1367. $lineno++;
  1368. fprintf($out, " const YYERRORSYMBOL = %d;\n", $this->errsym->index);
  1369. $lineno++;
  1370. fprintf($out, " const YYERRSYMDT = 'yy%d';\n", $this->errsym->dtnum);
  1371. $lineno++;
  1372. if ($this->has_fallback) {
  1373. fwrite($out, " const YYFALLBACK = 1;\n");
  1374. } else {
  1375. fwrite($out, " const YYFALLBACK = 0;\n");
  1376. }
  1377. $lineno++;
  1378. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1379. /* Generate the table of fallback tokens.
  1380. */
  1381. if ($this->has_fallback) {
  1382. for ($i = 0; $i < $this->nterminal; $i++) {
  1383. $p = $this->symbols[$i];
  1384. if ($p->fallback === 0) {
  1385. // change next line
  1386. fprintf($out, " 0, /* %10s => nothing */\n", $p->name);
  1387. } else {
  1388. // change next line
  1389. fprintf($out, " %3d, /* %10s => %s */\n",
  1390. $p->fallback->index, $p->name, $p->fallback->name);
  1391. }
  1392. $lineno++;
  1393. }
  1394. }
  1395. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1396. /* Generate a table containing the symbolic name of every symbol
  1397. ($yyTokenName)
  1398. */
  1399. for ($i = 0; $i < $this->nsymbol; $i++) {
  1400. fprintf($out," %-15s", "'" . $this->symbols[$i]->name . "',");
  1401. if (($i & 3) == 3) {
  1402. fwrite($out,"\n");
  1403. $lineno++;
  1404. }
  1405. }
  1406. if (($i & 3) != 0) {
  1407. fwrite($out, "\n");
  1408. $lineno++;
  1409. }
  1410. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1411. /* Generate a table containing a text string that describes every
  1412. ** rule in the rule set of the grammer. This information is used
  1413. ** when tracing REDUCE actions.
  1414. */
  1415. for ($i = 0, $rp = $this->rule; $rp; $rp = $rp->next, $i++) {
  1416. if ($rp->index !== $i) {
  1417. throw new Exception('rp->index != i and should be');
  1418. }
  1419. // change next line
  1420. fprintf($out, " /* %3d */ \"%s ::=", $i, $rp->lhs->name);
  1421. for ($j = 0; $j < $rp->nrhs; $j++) {
  1422. $sp = $rp->rhs[$j];
  1423. fwrite($out,' ' . $sp->name);
  1424. if ($sp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) {
  1425. for($k = 1; $k < $sp->nsubsym; $k++) {
  1426. fwrite($out, '|' . $sp->subsym[$k]->name);
  1427. }
  1428. }
  1429. }
  1430. fwrite($out, "\",\n");
  1431. $lineno++;
  1432. }
  1433. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1434. /* Generate code which executes every time a symbol is popped from
  1435. ** the stack while processing errors or while destroying the parser.
  1436. ** (In other words, generate the %destructor actions)
  1437. */
  1438. if ($this->tokendest) {
  1439. for ($i = 0; $i < $this->nsymbol; $i++) {
  1440. $sp = $this->symbols[$i];
  1441. if ($sp === 0 || $sp->type != PHP_ParserGenerator_Symbol::TERMINAL) {
  1442. continue;
  1443. }
  1444. fprintf($out, " case %d:\n", $sp->index);
  1445. $lineno++;
  1446. }
  1447. for ($i = 0; $i < $this->nsymbol &&
  1448. $this->symbols[$i]->type != PHP_ParserGenerator_Symbol::TERMINAL; $i++);
  1449. if ($i < $this->nsymbol) {
  1450. $this->emit_destructor_code($out, $this->symbols[$i], $lineno);
  1451. fprintf($out, " break;\n");
  1452. $lineno++;
  1453. }
  1454. }
  1455. if ($this->vardest) {
  1456. $dflt_sp = 0;
  1457. for ($i = 0; $i < $this->nsymbol; $i++) {
  1458. $sp = $this->symbols[$i];
  1459. if ($sp === 0 || $sp->type == PHP_ParserGenerator_Symbol::TERMINAL ||
  1460. $sp->index <= 0 || $sp->destructor != 0) {
  1461. continue;
  1462. }
  1463. fprintf($out, " case %d:\n", $sp->index);
  1464. $lineno++;
  1465. $dflt_sp = $sp;
  1466. }
  1467. if ($dflt_sp != 0) {
  1468. $this->emit_destructor_code($out, $dflt_sp, $lineno);
  1469. fwrite($out, " break;\n");
  1470. $lineno++;
  1471. }
  1472. }
  1473. for ($i = 0; $i < $this->nsymbol; $i++) {
  1474. $sp = $this->symbols[$i];
  1475. if ($sp === 0 || $sp->type == PHP_ParserGenerator_Symbol::TERMINAL ||
  1476. $sp->destructor === 0) {
  1477. continue;
  1478. }
  1479. fprintf($out, " case %d:\n", $sp->index);
  1480. $lineno++;
  1481. /* Combine duplicate destructors into a single case */
  1482. for ($j = $i + 1; $j < $this->nsymbol; $j++) {
  1483. $sp2 = $this->symbols[$j];
  1484. if ($sp2 && $sp2->type != PHP_ParserGenerator_Symbol::TERMINAL && $sp2->destructor
  1485. && $sp2->dtnum == $sp->dtnum
  1486. && $sp->destructor == $sp2->destructor) {
  1487. fprintf($out, " case %d:\n", $sp2->index);
  1488. $lineno++;
  1489. $sp2->destructor = 0;
  1490. }
  1491. }
  1492. $this->emit_destructor_code($out, $this->symbols[$i], $lineno);
  1493. fprintf($out, " break;\n");
  1494. $lineno++;
  1495. }
  1496. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1497. /* Generate code which executes whenever the parser stack overflows */
  1498. $this->tplt_print($out, $this->overflow, $this->overflowln, $lineno);
  1499. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1500. /* Generate the table of rule information
  1501. **
  1502. ** Note: This code depends on the fact that rules are number
  1503. ** sequentually beginning with 0.
  1504. */
  1505. for ($rp = $this->rule; $rp; $rp = $rp->next) {
  1506. fprintf($out, " array( 'lhs' => %d, 'rhs' => %d ),\n",
  1507. $rp->lhs->index, $rp->nrhs);
  1508. $lineno++;
  1509. }
  1510. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1511. /* Generate code which executes during each REDUCE action */
  1512. for ($rp = $this->rule; $rp; $rp = $rp->next) {
  1513. if ($rp->code) {
  1514. $this->translate_code($rp);
  1515. }
  1516. }
  1517. /* Generate the method map for each REDUCE action */
  1518. for ($rp = $this->rule; $rp; $rp = $rp->next) {
  1519. if ($rp->code === 0) {
  1520. continue;
  1521. }
  1522. fwrite($out, ' ' . $rp->index . ' => ' . $rp->index . ",\n");
  1523. $lineno++;
  1524. for ($rp2 = $rp->next; $rp2; $rp2 = $rp2->next) {
  1525. if ($rp2->code === $rp->code) {
  1526. fwrite($out, ' ' . $rp2->index . ' => ' .
  1527. $rp->index . ",\n");
  1528. $lineno++;
  1529. $rp2->code = 0;
  1530. }
  1531. }
  1532. }
  1533. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1534. for ($rp = $this->rule; $rp; $rp = $rp->next) {
  1535. if ($rp->code === 0) {
  1536. continue;
  1537. }
  1538. $this->emit_code($out, $rp, $lineno);
  1539. }
  1540. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1541. /* Generate code which executes if a parse fails */
  1542. $this->tplt_print($out, $this->failure, $this->failureln, $lineno);
  1543. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1544. /* Generate code which executes when a syntax error occurs */
  1545. $this->tplt_print($out, $this->error, $this->errorln, $lineno);
  1546. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1547. /* Generate code which executes when the parser accepts its input */
  1548. $this->tplt_print($out, $this->accept, $this->acceptln, $lineno);
  1549. $this->tplt_xfer($this->name, $in, $out, $lineno);
  1550. /* Append any addition code the user desires */
  1551. $this->tplt_print($out, $this->extracode, $this->extracodeln, $lineno);
  1552. fclose($in);
  1553. fclose($out);
  1554. }
  1555. /**
  1556. * Generate code which executes when the rule "rp" is reduced. Write
  1557. * the code to "out". Make sure lineno stays up-to-date.
  1558. */
  1559. function emit_code($out, PHP_ParserGenerator_Rule $rp, &$lineno)
  1560. {
  1561. $linecnt = 0;
  1562. /* Generate code to do the reduce action */
  1563. if ($rp->code) {
  1564. $this->tplt_linedir($out, $rp->line, $this->filename);
  1565. fwrite($out, " function yy_r$rp->index(){" . $rp->code);
  1566. $linecnt += count(explode("\n", $rp->code)) - 1;
  1567. $lineno += 3 + $linecnt;
  1568. fwrite($out, " }\n");
  1569. $this->tplt_linedir($out, $lineno, $this->outname);
  1570. } /* End if( rp->code ) */
  1571. }
  1572. /**
  1573. * Append text to a dynamically allocated string. If zText is 0 then
  1574. * reset the string to be empty again. Always return the complete text
  1575. * of the string (which is overwritten with each call).
  1576. *
  1577. * n bytes of zText are stored. If n==0 then all of zText is stored.
  1578. *
  1579. * If n==-1, then the previous character is overwritten.
  1580. * @param string
  1581. * @param int
  1582. */
  1583. function append_str($zText, $n)
  1584. {
  1585. static $z = '';
  1586. $zInt = '';
  1587. if ($zText === '') {
  1588. $ret = $z;
  1589. $z = '';
  1590. return $ret;
  1591. }
  1592. if ($n <= 0) {
  1593. if ($n < 0) {
  1594. if (!strlen($z)) {
  1595. throw new Exception('z is zero-length');
  1596. }
  1597. $z = substr($z, 0, strlen($z) - 1);
  1598. if (!$z) {
  1599. $z = '';
  1600. }
  1601. }
  1602. $n = strlen($zText);
  1603. }
  1604. $i = 0;
  1605. $z .= substr($zText, 0, $n);
  1606. return $z;
  1607. }
  1608. /**
  1609. * zCode is a string that is the action associated with a rule. Expand
  1610. * the symbols in this string so that the refer to elements of the parser
  1611. * stack.
  1612. */
  1613. function translate_code(PHP_ParserGenerator_Rule $rp)
  1614. {
  1615. $lhsused = 0; /* True if the LHS element has been used */
  1616. $used = array(); /* True for each RHS element which is used */
  1617. $this->append_str('', 0);
  1618. for ($i = 0; $i < strlen($rp->code); $i++) {
  1619. $cp = $rp->code[$i];
  1620. if (preg_match('/[A-Za-z]/', $cp) &&
  1621. ($i === 0 || (!preg_match('/[A-Za-z0-9_]/', $rp->code[$i - 1])))) {
  1622. //*xp = 0;
  1623. // previous line is in essence a temporary substr, so
  1624. // we will simulate it
  1625. $test = substr($rp->code, $i);
  1626. preg_match('/[A-Za-z0-9_]+/', $test, $matches);
  1627. $tempcp = $matches[0];
  1628. $j = strlen($tempcp) + $i;
  1629. if ($rp->lhsalias && $tempcp == $rp->lhsalias) {
  1630. $this->append_str("\$this->_retvalue", 0);
  1631. $cp = $rp->code[$j];
  1632. $i = $j;
  1633. $lhsused = 1;
  1634. } else {
  1635. for ($ii = 0; $ii < $rp->nrhs; $ii++) {
  1636. if ($rp->rhsalias[$ii] && $tempcp == $rp->rhsalias[$ii]) {
  1637. if ($rp->code[0] == '@') {
  1638. /* If the argument is of the form @X then substitute
  1639. ** the token number of X, not the value of X */
  1640. $this->append_str("\$this->yystack[\$this->yyidx + " .
  1641. ($ii - $rp->nrhs + 1) . "]->major", -1);
  1642. } else {
  1643. $sp = $rp->rhs[$ii];
  1644. if ($sp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL) {
  1645. $dtnum = $sp->subsym[0]->dtnum;
  1646. } else {
  1647. $dtnum = $sp->dtnum;
  1648. }
  1649. $this->append_str("\$this->yystack[\$this->yyidx + " .
  1650. ($ii - $rp->nrhs + 1) . "]->minor", 0);
  1651. }
  1652. $cp = $rp->code[$j];
  1653. $i = $j;
  1654. $used[$ii] = 1;
  1655. break;
  1656. }
  1657. }
  1658. }
  1659. }
  1660. $this->append_str($cp, 1);
  1661. } /* End loop */
  1662. /* Check to make sure the LHS has been used */
  1663. if ($rp->lhsalias && !$lhsused) {
  1664. PHP_ParserGenerator::ErrorMsg($this->filename, $rp->ruleline,
  1665. "Label \"%s\" for \"%s(%s)\" is never used.",
  1666. $rp->lhsalias, $rp->lhs->name, $rp->lhsalias);
  1667. $this->errorcnt++;
  1668. }
  1669. /* Generate destructor code for RHS symbols which are not used in the
  1670. ** reduce code */
  1671. for($i = 0; $i < $rp->nrhs; $i++) {
  1672. if ($rp->rhsalias[$i] && !isset($used[$i])) {
  1673. PHP_ParserGenerator::ErrorMsg($this->filename, $rp->ruleline,
  1674. "Label %s for \"%s(%s)\" is never used.",
  1675. $rp->rhsalias[$i], $rp->rhs[$i]->name, $rp->rhsalias[$i]);
  1676. $this->errorcnt++;
  1677. } elseif ($rp->rhsalias[$i] == 0) {
  1678. if ($rp->rhs[$i]->type == PHP_ParserGenerator_Symbol::TERMINAL) {
  1679. $hasdestructor = $this->tokendest != 0;
  1680. }else{
  1681. $hasdestructor = $this->vardest !== 0 || $rp->rhs[$i]->destructor !== 0;
  1682. }
  1683. if ($hasdestructor) {
  1684. $this->append_str(" \$this->yy_destructor(" .
  1685. ($rp->rhs[$i]->index) . ", \$this->yystack[\$this->yyidx + " .
  1686. ($i - $rp->nrhs + 1) . "]->minor);\n", 0);
  1687. } else {
  1688. /* No destructor defined for this term */
  1689. }
  1690. }
  1691. }
  1692. $cp = $this->append_str('', 0);
  1693. $rp->code = $cp;
  1694. }
  1695. /**
  1696. * The following routine emits code for the destructor for the
  1697. * symbol sp
  1698. */
  1699. function emit_destructor_code($out, PHP_ParserGenerator_Symbol $sp, &$lineno)
  1700. // FILE *out;
  1701. // struct symbol *sp;
  1702. // struct lemon *lemp;
  1703. // int *lineno;
  1704. {
  1705. $cp = 0;
  1706. $linecnt = 0;
  1707. if ($sp->type == PHP_ParserGenerator_Symbol::TERMINAL) {
  1708. $cp = $this->tokendest;
  1709. if ($cp === 0) {
  1710. return;
  1711. }
  1712. $this->tplt_linedir($out, $this->tokendestln, $this->filename);
  1713. fwrite($out, "{");
  1714. } elseif ($sp->destructor) {
  1715. $cp = $sp->destructor;
  1716. $this->tplt_linedir($out, $sp->destructorln, $this->filename);
  1717. fwrite($out, "{");
  1718. } elseif ($this->vardest) {
  1719. $cp = $this->vardest;
  1720. if ($cp === 0) {
  1721. return;
  1722. }
  1723. $this->tplt_linedir($out, $this->vardestln, $this->filename);
  1724. fwrite($out, "{");
  1725. } else {
  1726. throw new Exception('emit_destructor'); /* Cannot happen */
  1727. }
  1728. for ($i = 0; $i < strlen($cp); $i++) {
  1729. if ($cp[$i]=='$' && $cp[$i + 1]=='$' ) {
  1730. fprintf($out, "(yypminor->yy%d)", $sp->dtnum);
  1731. $i++;
  1732. continue;
  1733. }
  1734. if ($cp[$i] == "\n") {
  1735. $linecnt++;
  1736. }
  1737. fwrite($out, $cp[$i]);
  1738. }
  1739. $lineno += 3 + $linecnt;
  1740. fwrite($out, "}\n");
  1741. $this->tplt_linedir($out, $lineno, $this->outname);
  1742. }
  1743. /**
  1744. * Compare to axset structures for sorting purposes
  1745. */
  1746. static function axset_compare($a, $b)
  1747. {
  1748. return $b['nAction'] - $a['nAction'];
  1749. }
  1750. }