Source for file Data.php
Documentation is available at Data.php
* PHP_ParserGenerator, a php 5 parser generator.
* This is a direct port of the Lemon parser generator, found at
* {@link http://www.hwaci.com/sw/lemon/}
* Copyright (c) 2006, Gregory Beaver <cellog@php.net>
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution.
* * Neither the name of the PHP_ParserGenerator nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @package PHP_ParserGenerator
* @author Gregory Beaver <cellog@php.net>
* @copyright 2006 Gregory Beaver
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
* @since File available since Release 0.1.0
* The state vector for the entire parser generator is recorded in
* @package PHP_ParserGenerator
* @author Gregory Beaver <cellog@php.net>
* @copyright 2006 Gregory Beaver
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
* @version @package_version@
* @since Class available since Release 0.1.0
* Used for terminal and non-terminal offsets into the action table
* when their default should be used instead
const NO_OFFSET = - 2147483647;
* Table of states sorted by state number
* @var array array of {@link PHP_ParserGenerator_State} objects
* @var PHP_ParserGenerator_Rule
* Number of terminal and nonterminal symbols
* Number of terminal symbols (tokens)
* Sorted array of pointers to symbols
* @var array array of {@link PHP_ParserGenerator_Symbol} objects
* @var PHP_ParserGenerator_Symbol
* Name of the generated parser
* Unused relic from the C version
* Type of terminal symbols in the parser stack
* Unused relic from the C version
* The default type of non-terminal symbols
* Name of the start symbol for the grammar
* Size of the parser stack
* This is 100 by default, but is set with the %stack_size directive
* Code to put at the start of the parser file
* This is set by the %include directive
* Line number for start of include code
* Code to put in the parser class
* This is set by the %include_class directive
* Line number for start of include code
* any extends/implements code
* This is set by the %declare_class directive
* Line number for class declaration code
* Line number for start of class declaration code
* Code to execute when a syntax error is seen
* This is set by the %syntax_error directive
* Line number for start of error code
* Code to execute on a stack overflow
* This is set by the %stack_overflow directive
* Line number for start of overflow code
* Code to execute on parser failure
* This is set by the %parse_failure directive
* Line number for start of failure code
* Code to execute when the parser acccepts (completes parsing)
* This is set by the %parse_accept directive
* Line number for the start of accept code
* Code appended to the generated file
* This is set by the %code directive
* Line number for the start of the extra code
* Code to execute to destroy token data
* This is set by the %token_destructor directive
* Line number for token destroyer code
* Code for the default non-terminal destructor
* This is set by the %default_destructor directive
* Line number for default non-terminal destructor code
* Name of the input file without its extension
* Name of the current output file
* A prefix added to token names
* Number of parsing conflicts
* Size of the parse tables
* Public only basis configurations
* True if any %fallback is seen in the grammer
/* Find a precedence symbol of every rule in the grammar.
* Those rules which have a precedence symbol coded in the input
* grammar using the "[symbol]" construct will already have the
* $rp->precsym field filled. Other rules take as their precedence
* symbol the first RHS symbol with a defined precedence. If there
* are not RHS symbols with a defined precedence, the precedence
* symbol field is left blank.
for ($rp = $this->rule; $rp; $rp = $rp->next) {
if ($rp->precsym === 0) {
for ($i = 0; $i < $rp->nrhs && $rp->precsym === 0; $i++ ) {
for ($j = 0; $j < $sp->nsubsym; $j++ ) {
if ($sp->subsym[$j]->prec >= 0) {
$rp->precsym = $sp->subsym[$j];
} elseif ($sp->prec >= 0) {
$rp->precsym = $rp->rhs[$i];
* Find all nonterminals which will generate the empty string.
* Then go back and compute the first sets of every nonterminal.
* The first set is the set of all terminal symbols which can begin
* a string generated by that nonterminal.
for ($i = 0; $i < $this->nsymbol; $i++ ) {
$this->symbols[$i]->lambda = false;
$this->symbols[$i]->firstset = array();
/* First compute all lambdas */
for ($rp = $this->rule; $rp; $rp = $rp->next) {
for ($i = 0; $i < $rp->nrhs; $i++ ) {
/* Now compute all first sets */
for ($rp = $this->rule; $rp; $rp = $rp->next) {
for ($i = 0; $i < $rp->nrhs; $i++ ) {
//progress += SetAdd(s1->firstset,s2->index);
$progress += isset ($s1->firstset[$s2->index]) ? 0 : 1;
$s1->firstset[$s2->index] = 1;
for ($j = 0; $j < $s2->nsubsym; $j++ ) {
//progress += SetAdd(s1->firstset,s2->subsym[j]->index);
$progress += isset ($s1->firstset[$s2->subsym[$j]->index]) ? 0 : 1;
$s1->firstset[$s2->subsym[$j]->index] = 1;
if ($s1->lambda === false) {
//progress += SetUnion(s1->firstset,s2->firstset);
if ($s2->lambda === false) {
* Compute all LR(0) states for the grammar. Links
* are added to between some states so that the LR(1) follow sets
/* Find the start symbol */
"The specified start symbol \"%s\" is not " .
"in a nonterminal of the grammar. \"%s\" will be used as the start " .
/* Make sure the start symbol doesn't occur on the right-hand side of
** any rule. Report an error if it does. (YACC would generate a new
** start symbol in this case.) */
for ($rp = $this->rule; $rp; $rp = $rp->next) {
for ($i = 0; $i < $rp->nrhs; $i++ ) {
foreach ($rp->rhs[$i]->subsym as $subsp) {
"The start symbol \"%s\" occurs on the " .
"right-hand side of a rule. This will result in a parser which " .
"does not work properly.", $sp->name);
} elseif ($rp->rhs[$i] === $sp) {
"The start symbol \"%s\" occurs on the " .
"right-hand side of a rule. This will result in a parser which " .
"does not work properly.", $sp->name);
/* The basis configuration set for the first state
** is all rules which have the start symbol as their
for ($rp = $sp->rule; $rp; $rp = $rp->nextlhs) {
/* Compute the first state. All other states will be
** computed automatically during the computation of the first one.
** The returned pointer to the first state is not used. */
$newstp = $this->getstate();
$this->buildshifts($newstp[0]); /* Recursively compute successor states */
* @return PHP_ParserGenerator_State
private function getstate()
/* Extract the sorted basis of the new state. The basis was constructed
** by prior calls to "Configlist_addbasis()". */
/* Get a state with the same basis */
/* A state with the same basis already exists! Copy all the follow-set
** propagation links from the state under construction into the
** preexisting state, then return a pointer to the preexisting state */
for($x = $bp, $y = $stp->bp; $x && $y; $x = $x->bp, $y = $y->bp) {
/* This really is a new state. Construct all the details */
$stp->bp = $bp; /* Remember the configuration basis */
$stp->cfp = $cfp; /* Remember the configuration closure */
$stp->statenum = $this->nstate++ ; /* Every state gets a sequence number */
$stp->ap = 0; /* No actions, yet. */
// this can't work, recursion is too deep, move it into FindStates()
//$this->buildshifts($stp); /* Recursively compute successor states */
* Construct all successor states to the given state. A "successor"
* state is any state which can be reached by a shift action.
* @param PHP_ParserGenerator_Data
* @param PHP_ParserGenerator_State The state from which successors are computed
private function buildshifts(PHP_ParserGenerator_State $stp)
// struct config *cfp; /* For looping thru the config closure of "stp" */
// struct config *bcfp; /* For the inner loop on config closure of "stp" */
// struct config *new; /* */
// struct symbol *sp; /* Symbol following the dot in configuration "cfp" */
// struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */
// struct state *newstp; /* A pointer to a successor state */
/* Each configuration becomes complete after it contibutes to a successor
** state. Initially, all configurations are incomplete */
for ($cfp = $stp->cfp; $cfp; $cfp = $cfp->next) {
/* Loop through all configurations of the state "stp" */
for ($cfp = $stp->cfp; $cfp; $cfp = $cfp->next) {
continue; /* Already used by inner loop */
if ($cfp->dot >= $cfp->rp->nrhs) {
continue; /* Can't shift this config */
$sp = $cfp->rp->rhs[$cfp->dot]; /* Symbol after the dot */
/* For every configuration in the state "stp" which has the symbol "sp"
** following its dot, add the same configuration to the basis set under
** construction but with the dot shifted one symbol to the right. */
for ($bcfp = $cfp; $bcfp; $bcfp = $bcfp->next) {
continue; /* Already used */
if ($bcfp->dot >= $bcfp->rp->nrhs) {
continue; /* Can't shift this one */
$bsp = $bcfp->rp->rhs[$bcfp->dot]; /* Get symbol after dot */
continue; /* Must be same as for "cfp" */
/* Get a pointer to the state described by the basis configuration set
** constructed in the preceding loop */
$newstp = $this->getstate();
$this->buildshifts($newstp[0]); /* Recursively compute successor states */
/* The state "newstp" is reached from the state "stp" by a shift action
for($i = 0; $i < $sp->nsubsym; $i++ ) {
* Construct the propagation links
** Add to every propagate link a pointer back to the state to
** which the link is attached. */
foreach ($this->sorted as $info) {
$info->key->stp = $info->data;
/* Convert all backlinks into forward links. Only the forward
** links are used in the follow-set computation. */
for ($i = 0; $i < $this->nstate; $i++ ) {
for ($cfp = $stp->data->cfp; $cfp; $cfp = $cfp->next) {
for ($plp = $cfp->bplp; $plp; $plp = $plp->next) {
* Compute the reduce actions, and resolve conflicts.
/* Add all of the reduce actions
** A reduce action is added for each element of the followset of
** a configuration which has its dot at the extreme right.
for ($i = 0; $i < $this->nstate; $i++ ) { /* Loop over all states */
$stp = $this->sorted[$i]->data;
for ($cfp = $stp->cfp; $cfp; $cfp = $cfp->next) {
/* Loop over all configurations */
if ($cfp->rp->nrhs == $cfp->dot) { /* Is dot at extreme right? */
if (isset ($cfp->fws[$j])) {
/* Add a reduce action to the state "stp" which will reduce by the
** rule "cfp->rp" if the lookahead symbol is "$this->symbols[j]" */
/* Add the accepting token */
/* Add to the first state (which is always the starting state of the
** finite state machine) an action to ACCEPT if the lookahead is the
for ($i = 0; $i < $this->nstate; $i++ ) {
// struct action *ap, *nap;
$stp = $this->sorted[$i]->data;
throw new Exception('state has no actions associated');
for ($ap = $stp->ap; $ap !== 0 && $ap->next !== 0; $ap = $ap->next) {
for ($nap = $ap->next; $nap !== 0 && $nap->sp === $ap->sp ; $nap = $nap->next) {
/* The two actions "ap" and "nap" have the same lookahead.
** Figure out which one should be used */
/* Report an error for each rule that can never be reduced. */
for ($rp = $this->rule; $rp; $rp = $rp->next) {
for ($i = 0; $i < $this->nstate; $i++ ) {
for ($ap = $this->sorted[$i]->data->ap; $ap !== 0; $ap = $ap->next) {
$ap->x->canReduce = true;
for ($rp = $this->rule; $rp !== 0; $rp = $rp->next) {
/** Resolve a conflict between the two given actions. If the
* conflict can't be resolve, return non-zero.
* To resolve a conflict, first look to see if either action
* is on an error rule. In that case, take the action which
* is not associated with the error rule. If neither or both
* actions are associated with an error rule, then try to
* use precedence to resolve the conflict.
* If either action is a SHIFT, then it must be apx. This
* function won't work if apx->type==REDUCE and apy->type==SHIFT.
* @param PHP_ParserGenerator_Action
* @param PHP_ParserGenerator_Action
* @param PHP_ParserGenerator_Symbol|nullThe error symbol (if defined. NULL otherwise)
if ($apx->sp !== $apy->sp) {
throw new Exception('no conflict but resolve_conflict called');
if ($spy === 0 || $spx->prec < 0 || $spy->prec < 0) {
/* Not enough precedence information. */
} elseif ($spx->prec > $spy->prec) { /* Lower precedence wins */
} elseif ($spx->prec < $spy->prec) {
throw new Exception('$spx->prec !== $spy->prec || $spx->assoc !== PHP_ParserGenerator_Symbol::NONE');
if ($spx === 0 || $spy === 0 || $spx->prec < 0 ||
$spy->prec < 0 || $spx->prec === $spy->prec) {
} elseif ($spx->prec > $spy->prec) {
} elseif ($spx->prec < $spy->prec) {
throw new Exception('$apx->type!== PHP_ParserGenerator_Action::SH_RESOLVED &&
$apx->type!== PHP_ParserGenerator_Action::RD_RESOLVED &&
$apx->type!== PHP_ParserGenerator_Action::CONFLICT &&
$apy->type!== PHP_ParserGenerator_Action::SH_RESOLVED &&
$apy->type!== PHP_ParserGenerator_Action::RD_RESOLVED &&
$apy->type!== PHP_ParserGenerator_Action::CONFLICT');
/* The REDUCE/SHIFT case cannot happen because SHIFTs come before
** REDUCEs on the list. If we reach this point it must be because
** the parser conflict had already been resolved. */
* Reduce the size of the action tables, if possible, by making use
* In this version, we take the most frequent REDUCE action and make
for ($i = 0; $i < $this->nstate; $i++ ) {
$stp = $this->sorted[$i]->data;
for ($ap = $stp->ap; $ap; $ap = $ap->next) {
for ($ap2 = $ap->next; $ap2; $ap2 = $ap2->next) {
/* Do not make a default if the number of rules to default
/* Combine matching REDUCE actions into a single default */
for ($ap = $stp->ap; $ap; $ap = $ap->next) {
throw new Exception('$ap is not an object');
for ($ap = $ap->next; $ap; $ap = $ap->next) {
* Renumber and resort states so that states with fewer choices
* occur at the end. Except, keep state 0 as the first state.
for ($i = 0; $i < $this->nstate; $i++ ) {
$stp = $this->sorted[$i]->data;
$stp->nTknAct = $stp->nNtAct = 0;
for ($ap = $stp->ap; $ap; $ap = $ap->next) {
} elseif ($ap->sp->index < $this->nsymbol) {
usort($this->sorted, array('PHP_ParserGenerator_State', 'stateResortCompare'));
for($i = 0; $i < $this->nstate; $i++ ) {
$this->sorted[$i]->statenum = $i;
* Given an action, compute the integer value for that action
* which is to be put in the action table of the generated machine.
* Return negative if no action should be generated.
* @param PHP_ParserGenerator_Action
$act = $ap->x->index + $this->nstate;
* Generate the "Parse.out" log file
for ($i = 0; $i < $this->nstate; $i++ ) {
fprintf($fp, "State %d:\n", $stp->statenum);
if ($cfp->dot == $cfp->rp->nrhs) {
$buf = sprintf('(%d)', $cfp->rp->index);
//SetPrint(fp,cfp->fws,$this);
//PlinkPrint(fp,cfp->fplp,"To ");
//PlinkPrint(fp,cfp->bplp,"From");
for ($ap = $stp->ap; $ap; $ap = $ap->next) {
if ($ap->PrintAction($fp, 30)) {
* The next function finds the template file and opens it, returning
* a pointer to the opened file.
private function tplt_open()
$templatename = dirname(dirname(__FILE__ )) . DIRECTORY_SEPARATOR . "Lempar.php";
$tpltname = $templatename;
} elseif ($fp = @fopen($templatename, 'rb', true)) {
echo "Can't find the parser driver template file \"%s\".\n",
$in = @fopen($tpltname,"rb");
printf("Can't open the template file \"%s\".\n", $tpltname);
* The next cluster of routines are for reading the template file
* and writing the results to the generated parser
* The first function transfers data from "in" to "out" until
* a line is seen which begins with "%%". The line number is
* if name!=0, then any word that begin with "Parse" is changed to
* begin with *name instead.
private function tplt_xfer($name, $in, $out, &$lineno)
while (($line = fgets($in, 1024)) && ($line[0] != '%' || $line[1] != '%')) {
for ($i = 0; $i < strlen($line); $i++ ) {
if ($line[$i] == 'P' && substr($line, $i, 5) == "Parse"
&& ($i === 0 || preg_match('/[^a-zA-Z]/', $line[$i - 1]))) {
* Print a #line directive line to the output file.
private function tplt_linedir($out, $lineno, $filename)
fwrite($out, '#line ' . $lineno . ' "' . $filename . "\"\n");
* Print a string to the file and keep the linenumber up to date
private function tplt_print($out, $str, $strln, &$lineno)
$this->tplt_linedir($out, $strln, $this->filename);
$this->tplt_linedir($out, $lineno + 2, $this->outname);
* Compute all followsets.
* A followset is the set of all symbols which can come immediately
for ($i = 0; $i < $this->nstate; $i++ ) {
for ($cfp = $this->sorted[$i]->data->cfp; $cfp; $cfp = $cfp->next) {
for ($i = 0; $i < $this->nstate; $i++ ) {
for ($cfp = $this->sorted[$i]->data->cfp; $cfp; $cfp = $cfp->next) {
for ($plp = $cfp->fplp; $plp; $plp = $plp->next) {
* Generate C source code for the parser
* @param int Output in makeheaders format if true
// struct acttab *pActtab;
// int mnTknOfst, mxTknOfst;
// int mnNtOfst, mxNtOfst;
$in = $this->tplt_open();
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate the include code, if any */
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate the class declaration code */
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate the internal parser class include code, if any */
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate #defines for all tokens */
//fprintf($out, "#if INTERFACE\n");
fprintf($out, " const %s%-30s = %2d;\n", $prefix, $this->symbols[$i]->name, $i);
//fwrite($out, "#endif\n");
fwrite($out, " const YY_NO_ACTION = " .
fwrite($out, " const YY_ACCEPT_ACTION = " .
fwrite($out, " const YY_ERROR_ACTION = " .
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate the action table and its associates:
** yy_action[] A single table containing all actions.
** yy_lookahead[] A table containing the lookahead for each entry in
** yy_action. Used to detect hash collisions.
** yy_shift_ofst[] For each state, the offset into yy_action for
** yy_reduce_ofst[] For each state, the offset into yy_action for
** shifting non-terminals after a reduce.
** yy_default[] Default action for each state.
/* Compute the actions on all states and count them up */
for ($i = 0; $i < $this->nstate; $i++ ) {
$ax[$i * 2]['stp'] = $stp;
$ax[$i * 2]['isTkn'] = 1;
$ax[$i * 2]['nAction'] = $stp->nTknAct;
$ax[$i * 2 + 1] = array();
$ax[$i * 2 + 1]['stp'] = $stp;
$ax[$i * 2 + 1]['isTkn'] = 0;
$ax[$i * 2 + 1]['nAction'] = $stp->nNtAct;
$mxTknOfst = $mnTknOfst = 0;
$mxNtOfst = $mnNtOfst = 0;
/* Compute the action table. In order to try to keep the size of the
** action table to a minimum, the heuristic of placing the largest action
usort($ax, array('PHP_ParserGenerator_Data', 'axset_compare'));
for ($i = 0; $i < $this->nstate * 2 && $ax[$i]['nAction'] > 0; $i++ ) {
for ($ap = $stp->ap; $ap; $ap = $ap->next) {
$pActtab->acttab_action($ap->sp->index, $action);
$stp->iTknOfst = $pActtab->acttab_insert();
if ($stp->iTknOfst < $mnTknOfst) {
$mnTknOfst = $stp->iTknOfst;
if ($stp->iTknOfst > $mxTknOfst) {
$mxTknOfst = $stp->iTknOfst;
for ($ap = $stp->ap; $ap; $ap = $ap->next) {
if ($ap->sp->index == $this->nsymbol) {
$pActtab->acttab_action($ap->sp->index, $action);
$stp->iNtOfst = $pActtab->acttab_insert();
if ($stp->iNtOfst < $mnNtOfst) {
$mnNtOfst = $stp->iNtOfst;
if ($stp->iNtOfst > $mxNtOfst) {
$mxNtOfst = $stp->iNtOfst;
/* Output the yy_action table */
fprintf($out, " const YY_SZ_ACTTAB = %d;\n", $pActtab->nAction);
fwrite($out, "static public \$yy_action = array(\n");
for($i = $j = 0; $i < $n; $i++ ) {
$action = $pActtab->aAction[$i]['action'];
if ($j == 9 || $i == $n - 1) {
fwrite($out, " );\n"); $lineno++ ;
/* Output the yy_lookahead table */
fwrite($out, " static public \$yy_lookahead = array(\n");
for ($i = $j = 0; $i < $n; $i++ ) {
$la = $pActtab->aAction[$i]['lookahead'];
if ($j == 9 || $i == $n - 1) {
/* Output the yy_shift_ofst[] table */
fprintf($out, " const YY_SHIFT_USE_DFLT = %d;\n", $mnTknOfst - 1);
fprintf($out, " const YY_SHIFT_MAX = %d;\n", $n - 1);
fwrite($out, " static public \$yy_shift_ofst = array(\n");
for ($i = $j = 0; $i < $n; $i++ ) {
if ($j == 9 || $i == $n - 1) {
/* Output the yy_reduce_ofst[] table */
fprintf($out, " const YY_REDUCE_USE_DFLT = %d;\n", $mnNtOfst - 1);
fprintf($out, " const YY_REDUCE_MAX = %d;\n", $n - 1);
fwrite($out, " static public \$yy_reduce_ofst = array(\n");
for ($i = $j = 0; $i < $n; $i++ ) {
if ($j == 9 || $i == $n - 1) {
/* Output the expected tokens table */
fwrite($out, " static public \$yyExpectedTokens = array(\n");
for ($i = 0; $i < $this->nstate; $i++ ) {
fwrite($out, " /* $i */ array(");
for ($ap = $stp->ap; $ap; $ap = $ap->next) {
fwrite($out, $ap->sp->index . ', ');
/* Output the default action table */
fwrite($out, " static public \$yy_default = array(\n");
for ($i = $j = 0; $i < $n; $i++ ) {
fprintf($out, " %4d,", $stp->iDflt);
if ($j == 9 || $i == $n - 1) {
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate the defines */
"Illegal stack size: [%s]. The stack size should be an integer constant.",
fwrite($out," const YYSTACKDEPTH = 100;\n");
fwrite($out, " const YYFALLBACK = 1;\n");
fwrite($out, " const YYFALLBACK = 0;\n");
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate the table of fallback tokens.
if ($p->fallback === 0) {
fprintf($out, " 0, /* %10s => nothing */\n", $p->name);
fprintf($out, " %3d, /* %10s => %s */\n",
$p->fallback->index, $p->name, $p->fallback->name);
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate a table containing the symbolic name of every symbol
for ($i = 0; $i < $this->nsymbol; $i++ ) {
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate a table containing a text string that describes every
** rule in the rule set of the grammer. This information is used
** when tracing REDUCE actions.
for ($i = 0, $rp = $this->rule; $rp; $rp = $rp->next, $i++ ) {
throw new Exception('rp->index != i and should be');
fprintf($out, " /* %3d */ \"%s ::=", $i, $rp->lhs->name);
for ($j = 0; $j < $rp->nrhs; $j++ ) {
for($k = 1; $k < $sp->nsubsym; $k++ ) {
fwrite($out, '|' . $sp->subsym[$k]->name);
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate code which executes every time a symbol is popped from
** the stack while processing errors or while destroying the parser.
** (In other words, generate the %destructor actions)
for ($i = 0; $i < $this->nsymbol; $i++ ) {
fprintf($out, " case %d:\n", $sp->index);
for ($i = 0; $i < $this->nsymbol &&
for ($i = 0; $i < $this->nsymbol; $i++ ) {
$sp->index <= 0 || $sp->destructor != 0) {
fprintf($out, " case %d:\n", $sp->index);
for ($i = 0; $i < $this->nsymbol; $i++ ) {
fprintf($out, " case %d:\n", $sp->index);
/* Combine duplicate destructors into a single case */
for ($j = $i + 1; $j < $this->nsymbol; $j++ ) {
&& $sp2->dtnum == $sp->dtnum
&& $sp->destructor == $sp2->destructor) {
fprintf($out, " case %d:\n", $sp2->index);
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate code which executes whenever the parser stack overflows */
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate the table of rule information
** Note: This code depends on the fact that rules are number
** sequentually beginning with 0.
for ($rp = $this->rule; $rp; $rp = $rp->next) {
fprintf($out, " array( 'lhs' => %d, 'rhs' => %d ),\n",
$rp->lhs->index, $rp->nrhs);
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate code which executes during each REDUCE action */
for ($rp = $this->rule; $rp; $rp = $rp->next) {
/* Generate the method map for each REDUCE action */
for ($rp = $this->rule; $rp; $rp = $rp->next) {
fwrite($out, ' ' . $rp->index . ' => ' . $rp->index . ",\n");
for ($rp2 = $rp->next; $rp2; $rp2 = $rp2->next) {
if ($rp2->code === $rp->code) {
fwrite($out, ' ' . $rp2->index . ' => ' .
$this->tplt_xfer($this->name, $in, $out, $lineno);
for ($rp = $this->rule; $rp; $rp = $rp->next) {
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate code which executes if a parse fails */
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate code which executes when a syntax error occurs */
$this->tplt_print($out, $this->error, $this->errorln, $lineno);
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Generate code which executes when the parser accepts its input */
$this->tplt_xfer($this->name, $in, $out, $lineno);
/* Append any addition code the user desires */
* Generate code which executes when the rule "rp" is reduced. Write
* the code to "out". Make sure lineno stays up-to-date.
function emit_code($out, PHP_ParserGenerator_Rule $rp, &$lineno)
/* Generate code to do the reduce action */
$this->tplt_linedir($out, $rp->line, $this->filename);
fwrite($out, " function yy_r$rp->index(){" . $rp->code);
$this->tplt_linedir($out, $lineno, $this->outname);
} /* End if( rp->code ) */
* Append text to a dynamically allocated string. If zText is 0 then
* reset the string to be empty again. Always return the complete text
* of the string (which is overwritten with each call).
* n bytes of zText are stored. If n==0 then all of zText is stored.
* If n==-1, then the previous character is overwritten.
throw new Exception('z is zero-length');
* zCode is a string that is the action associated with a rule. Expand
* the symbols in this string so that the refer to elements of the parser
$lhsused = 0; /* True if the LHS element has been used */
$used = array(); /* True for each RHS element which is used */
for($i = 0; $i < $rp->nrhs; $i++ ) {
for ($i = 0; $i < strlen($rp->code); $i++ ) {
($i === 0 || (!preg_match('/[A-Za-z0-9_]/', $rp->code[$i - 1])))) {
// previous line is in essence a temporary substr, so
$test = substr($rp->code, $i);
if ($rp->lhsalias && $tempcp == $rp->lhsalias) {
for ($ii = 0; $ii < $rp->nrhs; $ii++ ) {
if ($rp->rhsalias[$ii] && $tempcp == $rp->rhsalias[$ii]) {
if ($ii !== 0 && $rp->code[$ii - 1] == '@') {
/* If the argument is of the form @X then substitute
|