phpDocumentor PHP_ParserGenerator
[ class tree: PHP_ParserGenerator ] [ index: PHP_ParserGenerator ] [ all elements ]

Source for file Parser.php

Documentation is available at Parser.php

  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: This source file is subject to version 3.01 of the PHP license
  11.  * that is available through the world-wide-web at the following URI:
  12.  * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
  13.  * the PHP License and are unable to obtain it through the web, please
  14.  * send a note to license@php.net so we can mail you a copy immediately.
  15.  *
  16.  * @category   php
  17.  * @package    PHP_ParserGenerator
  18.  * @author     Gregory Beaver <cellog@php.net>
  19.  * @copyright  2006 Gregory Beaver
  20.  * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
  21.  * @version    CVS: $Id$
  22.  * @since      File available since Release 0.1.0
  23.  */
  24. /**
  25.  * The grammar parser for lemon grammar files.
  26.  * 
  27.  * @package    PHP_ParserGenerator
  28.  * @author     Gregory Beaver <cellog@php.net>
  29.  * @copyright  2006 Gregory Beaver
  30.  * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
  31.  * @since      Class available since Release 0.1.0
  32.  */
  33. {
  34.     const INITIALIZE 1;
  35.     const WAITING_FOR_DECL_OR_RULE 2;
  36.     const WAITING_FOR_DECL_KEYWORD 3;
  37.     const WAITING_FOR_DECL_ARG 4;
  38.     const WAITING_FOR_PRECEDENCE_SYMBOL 5;
  39.     const WAITING_FOR_ARROW 6;
  40.     const IN_RHS 7;
  41.     const LHS_ALIAS_1 8;
  42.     const LHS_ALIAS_2 9;
  43.     const LHS_ALIAS_3 10;
  44.     const RHS_ALIAS_1 11;
  45.     const RHS_ALIAS_2 12;
  46.     const PRECEDENCE_MARK_1 13;
  47.     const PRECEDENCE_MARK_2 14;
  48.     const RESYNC_AFTER_RULE_ERROR 15;
  49.     const RESYNC_AFTER_DECL_ERROR 16;
  50.     const WAITING_FOR_DESTRUCTOR_SYMBOL 17;
  51.     const WAITING_FOR_DATATYPE_SYMBOL 18;
  52.     const WAITING_FOR_FALLBACK_ID 19;
  53.  
  54.     /**
  55.      * Name of the input file
  56.      *
  57.      * @var string 
  58.      */
  59.     public $filename;
  60.     /**
  61.      * Linenumber at which current token starts
  62.      * @var int 
  63.      */
  64.     public $tokenlineno;
  65.     /**
  66.      * Number of parsing errors so far
  67.      * @var int 
  68.      */
  69.     public $errorcnt;
  70.     /**
  71.      * Index of current token within the input string
  72.      * @var int 
  73.      */
  74.     public $tokenstart;
  75.     /**
  76.      * Global state vector
  77.      * @var PHP_ParserGenerator_Data 
  78.      */
  79.     public $gp;
  80.     /**
  81.      * Parser state (one of the class constants for this class)
  82.      *
  83.      * - PHP_ParserGenerator_Parser::INITIALIZE,
  84.      * - PHP_ParserGenerator_Parser::WAITING_FOR_DECL_OR_RULE,
  85.      * - PHP_ParserGenerator_Parser::WAITING_FOR_DECL_KEYWORD,
  86.      * - PHP_ParserGenerator_Parser::WAITING_FOR_DECL_ARG,
  87.      * - PHP_ParserGenerator_Parser::WAITING_FOR_PRECEDENCE_SYMBOL,
  88.      * - PHP_ParserGenerator_Parser::WAITING_FOR_ARROW,
  89.      * - PHP_ParserGenerator_Parser::IN_RHS,
  90.      * - PHP_ParserGenerator_Parser::LHS_ALIAS_1,
  91.      * - PHP_ParserGenerator_Parser::LHS_ALIAS_2,
  92.      * - PHP_ParserGenerator_Parser::LHS_ALIAS_3,
  93.      * - PHP_ParserGenerator_Parser::RHS_ALIAS_1,
  94.      * - PHP_ParserGenerator_Parser::RHS_ALIAS_2,
  95.      * - PHP_ParserGenerator_Parser::PRECEDENCE_MARK_1,
  96.      * - PHP_ParserGenerator_Parser::PRECEDENCE_MARK_2,
  97.      * - PHP_ParserGenerator_Parser::RESYNC_AFTER_RULE_ERROR,
  98.      * - PHP_ParserGenerator_Parser::RESYNC_AFTER_DECL_ERROR,
  99.      * - PHP_ParserGenerator_Parser::WAITING_FOR_DESTRUCTOR_SYMBOL,
  100.      * - PHP_ParserGenerator_Parser::WAITING_FOR_DATATYPE_SYMBOL,
  101.      * - PHP_ParserGenerator_Parser::WAITING_FOR_FALLBACK_ID
  102.      * @var int 
  103.      */
  104.     public $state;
  105.     /**
  106.      * The fallback token
  107.      * @var PHP_ParserGenerator_Symbol 
  108.      */
  109.     public $fallback;
  110.     /**
  111.      * Left-hand side of the current rule
  112.      * @var PHP_ParserGenerator_Symbol 
  113.      */
  114.     public $lhs;
  115.     /**
  116.      * Alias for the LHS
  117.      * @var string 
  118.      */
  119.     public $lhsalias;
  120.     /**
  121.      * Number of right-hand side symbols seen
  122.      * @var int 
  123.      */
  124.     public $nrhs;
  125.     /**
  126.      * Right-hand side symbols
  127.      * @var array array of {@link PHP_ParserGenerator_Symbol} objects
  128.      */
  129.     public $rhs = array();
  130.     /**
  131.      * Aliases for each RHS symbol name (or NULL)
  132.      * @var array array of strings
  133.      */
  134.     public $alias = array();
  135.     /**
  136.      * Previous rule parsed
  137.      * @var PHP_ParserGenerator_Rule 
  138.      */
  139.     public $prevrule;
  140.     /**
  141.      * Keyword of a declaration
  142.      * 
  143.      * This is one of the %keyword keywords in the grammar file
  144.      * @var string 
  145.      */
  146.     public $declkeyword;
  147.     /**
  148.      * Where the declaration argument should be put
  149.      *
  150.      * This is assigned as a reference to an internal variable
  151.      * @var mixed 
  152.      */
  153.     public $declargslot = array();
  154.     /**
  155.      * Where the declaration linenumber is put
  156.      * 
  157.      * This is assigned as a reference to an internal variable
  158.      * @var mixed 
  159.      */
  160.     public $decllnslot;
  161.     /*enum e_assoc*/
  162.     public $declassoc;    /* Assign this association to decl arguments */
  163.     public $preccounter;           /* Assign this precedence to decl arguments */
  164.     /**
  165.      * @var PHP_ParserGenerator_Rule 
  166.      */
  167.     public $firstrule;    /* Pointer to first rule in the grammar */
  168.     /**
  169.      * @var PHP_ParserGenerator_Rule 
  170.      */
  171.     public $lastrule;     /* Pointer to the most recently parsed rule */
  172.  
  173.     /**
  174.      * @var PHP_ParserGenerator 
  175.      */
  176.     private $lemon;
  177.  
  178.     function __construct(PHP_ParserGenerator $lem)
  179.     {
  180.         $this->lemon $lem;
  181.     }
  182.  
  183.     /**
  184.      * Run the preprocessor over the input file text.  The Lemon variable
  185.      * $azDefine contains the names of all defined
  186.      * macros.  This routine looks for "%ifdef" and "%ifndef" and "%endif" and
  187.      * comments them out.  Text in between is also commented out as appropriate.
  188.      * @param string 
  189.      */
  190.     private function preprocess_input(&$z)
  191.     {
  192.         $lineno $exclude 0;
  193.         for ($i=0$i strlen($z)$i++{
  194.             if ($z[$i== "\n"{
  195.                 $lineno++;
  196.             }
  197.             if ($z[$i!= '%' || ($i && $z[$i-1!= "\n")) {
  198.                 continue;
  199.             }
  200.             if (substr($z$i6=== "%endif" && trim($z[$i+6]=== ''{
  201.                 if ($exclude{
  202.                     $exclude--;
  203.                     if ($exclude === 0{
  204.                         for ($j $start$j $i$j++{
  205.                             if ($z[$j!= "\n"$z[$j' ';
  206.                         }
  207.                     }
  208.                 }
  209.                 for ($j $i$j strlen($z&& $z[$j!= "\n"$j++{
  210.                     $z[$j' ';
  211.                 }
  212.             elseif (substr($z$i6=== "%ifdef" && trim($z[$i+6]=== '' ||
  213.                       substr($z$i7=== "%ifndef" && trim($z[$i+7]=== ''{
  214.                 if ($exclude{
  215.                     $exclude++;
  216.                 else {
  217.                     $j $i;
  218.                     $n strtok(substr($z$j)" \t");
  219.                     $exclude 1;
  220.                     if (isset($this->lemon->azDefine[$n])) {
  221.                         $exclude 0;
  222.                     }
  223.                     if ($z[$i 3]=='n'{
  224.                         // this is a rather obtuse way of checking whether this is %ifndef
  225.                         $exclude !$exclude;
  226.                     }
  227.                     if ($exclude{
  228.                         $start $i;
  229.                         $start_lineno $lineno;
  230.                     }
  231.                 }
  232.                 //for ($j = $i; $j < strlen($z) && $z[$j] != "\n"; $j++) $z[$j] = ' ';
  233.                 $j strpos(substr($z$i)"\n");
  234.                 if ($j === false{
  235.                     $z substr($z0$i)// remove instead of adding ' '
  236.                 else {
  237.                     $z substr($z0$isubstr($z$i $j)// remove instead of adding ' '
  238.                 }
  239.             }
  240.         }
  241.         if ($exclude{
  242.             throw new Exception("unterminated %ifdef starting on line $start_lineno\n");
  243.         }
  244.     }
  245.  
  246.     /**
  247.      * In spite of its name, this function is really a scanner.
  248.      * 
  249.      * It reads in the entire input file (all at once) then tokenizes it.
  250.      * Each token is passed to the function "parseonetoken" which builds all
  251.      * the appropriate data structures in the global state vector "gp".
  252.      * @param PHP_ParserGenerator_Data 
  253.      */
  254.     function Parse(PHP_ParserGenerator_Data $gp)
  255.     {
  256.         $startline 0;
  257.     
  258.         $this->gp = $gp;
  259.         $this->filename = $gp->filename;
  260.         $this->errorcnt = 0;
  261.         $this->state = self::INITIALIZE;
  262.     
  263.         /* Begin by reading the input file */
  264.         $filebuf file_get_contents($this->filename);
  265.         if (!$filebuf{
  266.             PHP_ParserGenerator::ErrorMsg($this->filename0"Can't open this file for reading.");
  267.             $gp->errorcnt++;
  268.             return;
  269.         }
  270.         if (filesize($this->filename!= strlen($filebuf)) {
  271.             ErrorMsg($this->filename0"Can't read in all %d bytes of this file.",
  272.                 filesize($this->filename));
  273.             $gp->errorcnt++;
  274.             return;
  275.         }
  276.  
  277.         /* Make an initial pass through the file to handle %ifdef and %ifndef */
  278.         $this->preprocess_input($filebuf);
  279.     
  280.         /* Now scan the text of the input file */
  281.         $lineno 1;
  282.         for ($cp 0$c $filebuf[0]$cp strlen($filebuf)$cp++{
  283.             $c $filebuf[$cp];
  284.             if ($c == "\n"$lineno++;              /* Keep track of the line number */
  285.             if (trim($c=== ''{
  286.                 continue;
  287.             }  /* Skip all white space */
  288.             if ($filebuf[$cp== '/' && ($cp strlen($filebuf)) && $filebuf[$cp 1== '/'{
  289.                 /* Skip C++ style comments */
  290.                 $cp += 2;
  291.                 $z strpos(substr($filebuf$cp)"\n");
  292.                 if ($z === false{
  293.                     $cp strlen($filebuf);
  294.                     break;
  295.                 }
  296.                 $lineno++;
  297.                 $cp += $z;
  298.                 continue;
  299.             }
  300.             if ($filebuf[$cp== '/' && ($cp strlen($filebuf)) && $filebuf[$cp 1== '*'{
  301.                 /* Skip C style comments */
  302.                 $cp += 2;
  303.                 $z strpos(substr($filebuf$cp)'*/');
  304.                 if ($z !== false{
  305.                     $lineno += count(explode("\n"substr($filebuf$cp$z))) 1;
  306.                 }
  307.                 $cp += $z 1;
  308.                 continue;
  309.             }
  310.             $this->tokenstart = $cp;                /* Mark the beginning of the token */
  311.             $this->tokenlineno = $lineno;           /* Linenumber on which token begins */
  312.             if ($filebuf[$cp== '"'{                     /* String literals */
  313.                 $cp++;
  314.                 $oldcp $cp;
  315.                 $test strpos(substr($filebuf$cp)'"');
  316.                 if ($test === false{
  317.                     PHP_ParserGenerator::ErrorMsg($this->filename$startline,
  318.                     "String starting on this line is not terminated before the end of the file.");
  319.                     $this->errorcnt++;
  320.                     $nextcp $cp strlen($filebuf);
  321.                 else {
  322.                     $cp += $test;
  323.                     $nextcp $cp 1;
  324.                 }
  325.                 $lineno += count(explode("\n"substr($filebuf$oldcp$cp $oldcp))) 1;
  326.             elseif ($filebuf[$cp== '{'{               /* A block of C code */
  327.                 $cp++;
  328.                 for ($level 1$cp strlen($filebuf&& ($level || $filebuf[$cp!= '}')$cp++{
  329.                     if ($filebuf[$cp== "\n"{
  330.                         $lineno++;
  331.                     elseif ($filebuf[$cp== '{'{
  332.                         $level++;
  333.                     elseif ($filebuf[$cp== '}'{
  334.                         $level--;
  335.                     elseif ($filebuf[$cp== '/' && $filebuf[$cp 1== '*'{
  336.                         /* Skip comments */
  337.                         $cp += 2;
  338.                         $z strpos(substr($filebuf$cp)'*/');
  339.                         if ($z !== false{
  340.                             $lineno += count(explode("\n"substr($filebuf$cp$z))) 1;
  341.                         }
  342.                         $cp += $z 2;
  343.                     elseif ($filebuf[$cp== '/' && $filebuf[$cp 1== '/'{
  344.                         /* Skip C++ style comments too */
  345.                         $cp += 2;
  346.                         $z strpos(substr($filebuf$cp)"\n");
  347.                         if ($z === false{
  348.                             $cp strlen($filebuf);
  349.                             break;
  350.                         else {
  351.                             $lineno++;
  352.                         }
  353.                         $cp += $z;
  354.                     elseif ($filebuf[$cp== "'" || $filebuf[$cp== '"'{
  355.                         /* String a character literals */
  356.                         $startchar $filebuf[$cp];
  357.                         $prevc 0;
  358.                         for ($cp++$cp strlen($filebuf&& ($filebuf[$cp!= $startchar || $prevc === '\\')$cp++{
  359.                             if ($filebuf[$cp== "\n"{
  360.                                 $lineno++;
  361.                             }
  362.                             if ($prevc === '\\'{
  363.                                 $prevc 0;
  364.                             else {
  365.                                 $prevc $filebuf[$cp];
  366.                             }
  367.                         }
  368.                     }
  369.                 }
  370.                 if ($cp >= strlen($filebuf)) {
  371.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  372.                         "PHP code starting on this line is not terminated before the end of the file.");
  373.                     $this->errorcnt++;
  374.                     $nextcp $cp;
  375.                 else {
  376.                     $nextcp $cp 1;
  377.                 }
  378.             elseif (preg_match('/[a-zA-Z0-9]/'$filebuf[$cp])) {
  379.                 /* Identifiers */
  380.                 preg_match('/[a-zA-Z0-9_]+/'substr($filebuf$cp)$preg_results);
  381.                 $cp += strlen($preg_results[0]);
  382.                 $nextcp $cp;
  383.             elseif ($filebuf[$cp== ':' && $filebuf[$cp 1== ':' &&
  384.                       $filebuf[$cp 2== '='{
  385.                 /* The operator "::=" */
  386.                 $cp += 3;
  387.                 $nextcp $cp;
  388.             elseif (($filebuf[$cp== '/' || $filebuf[$cp== '|'&&
  389.                       preg_match('/[a-zA-Z]/'$filebuf[$cp 1])) {
  390.                 $cp += 2;
  391.                 preg_match('/[a-zA-Z0-9_]+/'substr($filebuf$cp)$preg_results);
  392.                 $cp += strlen($preg_results[0]);
  393.                 $nextcp $cp;
  394.             else {
  395.                 /* All other (one character) operators */
  396.                 $cp ++;
  397.                 $nextcp $cp;
  398.             }
  399.             $this->parseonetoken(substr($filebuf$this->tokenstart,
  400.                 $cp $this->tokenstart))/* Parse the token */
  401.             $cp $nextcp 1;
  402.         }
  403.         $gp->rule $this->firstrule;
  404.         $gp->errorcnt $this->errorcnt;
  405.     }
  406.  
  407.     /**
  408.      * Parse a single token
  409.      * @param string token
  410.      */
  411.     function parseonetoken($token)
  412.     {
  413.         $x $token;
  414.         $this->0// for referencing in WAITING_FOR_DECL_KEYWORD
  415.         if (PHP_ParserGenerator::DEBUG{
  416.             printf("%s:%d: Token=[%s] state=%d\n",
  417.                 $this->filename$this->tokenlineno$token$this->state);
  418.         }
  419.         switch ($this->state{
  420.             case self::INITIALIZE:
  421.                 $this->prevrule = 0;
  422.                 $this->preccounter = 0;
  423.                 $this->firstrule = $this->lastrule = 0;
  424.                 $this->gp->nrule = 0;
  425.                 /* Fall thru to next case */
  426.             case self::WAITING_FOR_DECL_OR_RULE:
  427.                 if ($x[0== '%'{
  428.                     $this->state = self::WAITING_FOR_DECL_KEYWORD;
  429.                 elseif (preg_match('/[a-z]/'$x[0])) {
  430.                     $this->lhs = PHP_ParserGenerator_Symbol::Symbol_new($x);
  431.                     $this->nrhs = 0;
  432.                     $this->lhsalias = 0;
  433.                     $this->state = self::WAITING_FOR_ARROW;
  434.                 elseif ($x[0== '{'{
  435.                     if ($this->prevrule === 0{
  436.                         PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  437.                             "There is no prior rule opon which to attach the code
  438.                              fragment which begins on this line.");
  439.                         $this->errorcnt++;
  440.                     elseif ($this->prevrule->code != 0{
  441.                         PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  442.                             "Code fragment beginning on this line is not the first \
  443.                              to follow the previous rule.");
  444.                         $this->errorcnt++;
  445.                     else {
  446.                         $this->prevrule->line = $this->tokenlineno;
  447.                         $this->prevrule->code = substr($x1);
  448.                     }
  449.                 elseif ($x[0== '['{
  450.                     $this->state = self::PRECEDENCE_MARK_1;
  451.                 else {
  452.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  453.                     "Token \"%s\" should be either \"%%\" or a nonterminal name.",
  454.                     $x);
  455.                     $this->errorcnt++;
  456.                 }
  457.                 break;
  458.             case self::PRECEDENCE_MARK_1:
  459.                 if (!preg_match('/[A-Z]/'$x[0])) {
  460.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  461.                         "The precedence symbol must be a terminal.");
  462.                     $this->errorcnt++;
  463.                 elseif ($this->prevrule === 0{
  464.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  465.                         "There is no prior rule to assign precedence \"[%s]\"."$x);
  466.                     $this->errorcnt++;
  467.                 elseif ($this->prevrule->precsym != 0{
  468.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  469.                         "Precedence mark on this line is not the first to follow the previous rule.");
  470.                     $this->errorcnt++;
  471.                 else {
  472.                     $this->prevrule->precsym = PHP_ParserGenerator_Symbol::Symbol_new($x);
  473.                 }
  474.                 $this->state = self::PRECEDENCE_MARK_2;
  475.                 break;
  476.             case self::PRECEDENCE_MARK_2:
  477.                 if ($x[0!= ']'{
  478.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  479.                         "Missing \"]\" on precedence mark.");
  480.                     $this->errorcnt++;
  481.                 }
  482.                 $this->state = self::WAITING_FOR_DECL_OR_RULE;
  483.                 break;
  484.             case self::WAITING_FOR_ARROW:
  485.                 if ($x[0== ':' && $x[1== ':' && $x[2== '='{
  486.                     $this->state = self::IN_RHS;
  487.                 elseif ($x[0== '('{
  488.                     $this->state = self::LHS_ALIAS_1;
  489.                 else {
  490.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  491.                         "Expected to see a \":\" following the LHS symbol \"%s\".",
  492.                     $this->lhs->name);
  493.                     $this->errorcnt++;
  494.                     $this->state = self::RESYNC_AFTER_RULE_ERROR;
  495.                 }
  496.                 break;
  497.             case self::LHS_ALIAS_1:
  498.                 if (preg_match('/[A-Za-z]/'$x[0])) {
  499.                     $this->lhsalias = $x;
  500.                     $this->state = self::LHS_ALIAS_2;
  501.                 else {
  502.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  503.                         "\"%s\" is not a valid alias for the LHS \"%s\"\n",
  504.                         $x$this->lhs->name);
  505.                     $this->errorcnt++;
  506.                     $this->state = self::RESYNC_AFTER_RULE_ERROR;
  507.                 }
  508.                 break;
  509.             case self::LHS_ALIAS_2:
  510.                 if ($x[0== ')'{
  511.                     $this->state = self::LHS_ALIAS_3;
  512.                 else {
  513.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  514.                         "Missing \")\" following LHS alias name \"%s\".",$this->lhsalias);
  515.                     $this->errorcnt++;
  516.                     $this->state = self::RESYNC_AFTER_RULE_ERROR;
  517.                 }
  518.                 break;
  519.             case self::LHS_ALIAS_3:
  520.                 if ($x == '::='{
  521.                     $this->state = self::IN_RHS;
  522.                 else {
  523.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  524.                         "Missing \"->\" following: \"%s(%s)\".",
  525.                     $this->lhs->name$this->lhsalias);
  526.                     $this->errorcnt++;
  527.                     $this->state = self::RESYNC_AFTER_RULE_ERROR;
  528.                 }
  529.                 break;
  530.             case self::IN_RHS:
  531.                 if ($x[0== '.'{
  532.                     $rp new PHP_ParserGenerator_Rule;
  533.                     $rp->ruleline $this->tokenlineno;
  534.                     for ($i 0$i $this->nrhs$i++{
  535.                         $rp->rhs[$i$this->rhs[$i];
  536.                         $rp->rhsalias[$i$this->alias[$i];
  537.                     }
  538.                     $rp->lhs $this->lhs;
  539.                     $rp->lhsalias $this->lhsalias;
  540.                     $rp->nrhs $this->nrhs;
  541.                     $rp->code 0;
  542.                     $rp->precsym 0;
  543.                     $rp->index $this->gp->nrule++;
  544.                     $rp->nextlhs $rp->lhs->rule;
  545.                     $rp->lhs->rule $rp;
  546.                     $rp->next 0;
  547.                     if ($this->firstrule === 0{
  548.                         $this->firstrule = $this->lastrule = $rp;
  549.                     else {
  550.                         $this->lastrule->next = $rp;
  551.                         $this->lastrule = $rp;
  552.                     }
  553.                     $this->prevrule = $rp;
  554.                     $this->state = self::WAITING_FOR_DECL_OR_RULE;
  555.                 elseif (preg_match('/[a-zA-Z]/'$x[0])) {
  556.                     if ($this->nrhs >= PHP_ParserGenerator::MAXRHS{
  557.                         PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  558.                             "Too many symbols on RHS or rule beginning at \"%s\".",
  559.                             $x);
  560.                         $this->errorcnt++;
  561.                         $this->state = self::RESYNC_AFTER_RULE_ERROR;
  562.                     else {
  563.                         if (isset($this->rhs[$this->nrhs - 1])) {
  564.                             $msp $this->rhs[$this->nrhs - 1];
  565.                             if ($msp->type == PHP_ParserGenerator_Symbol::MULTITERMINAL{
  566.                                 $inf array_reduce($msp->subsym,
  567.                                     array($this'_printmulti')'');
  568.                                 PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  569.                                     'WARNING: symbol ' $x ' will not' .
  570.                                     ' be part of previous multiterminal %s',
  571.                                     substr($inf0strlen($inf1)
  572.                                     );
  573.                             }
  574.                         }
  575.                         $this->rhs[$this->nrhsPHP_ParserGenerator_Symbol::Symbol_new($x);
  576.                         $this->alias[$this->nrhs0;
  577.                         $this->nrhs++;
  578.                     }
  579.                 elseif (($x[0== '|' || $x[0== '/'&& $this->nrhs > 0{
  580.                     $msp $this->rhs[$this->nrhs - 1];
  581.                     if ($msp->type != PHP_ParserGenerator_Symbol::MULTITERMINAL{
  582.                         $origsp $msp;
  583.                         $msp new PHP_ParserGenerator_Symbol;
  584.                         $msp->type PHP_ParserGenerator_Symbol::MULTITERMINAL;
  585.                         $msp->nsubsym 1;
  586.                         $msp->subsym array($origsp);
  587.                         $msp->name $origsp->name;
  588.                         $this->rhs[$this->nrhs - 1$msp;
  589.                     }
  590.                     $msp->nsubsym++;
  591.                     $msp->subsym[$msp->nsubsym 1PHP_ParserGenerator_Symbol::Symbol_new(substr($x1));
  592.                     if (preg_match('/[a-z]/'$x[1]||
  593.                           preg_match('/[a-z]/'$msp->subsym[0]->name[0])) {
  594.                         PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  595.                         "Cannot form a compound containing a non-terminal");
  596.                         $this->errorcnt++;
  597.                     }
  598.                 elseif ($x[0== '(' && $this->nrhs > 0{
  599.                     $this->state = self::RHS_ALIAS_1;
  600.                 else {
  601.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  602.                         "Illegal character on RHS of rule: \"%s\"."$x);
  603.                     $this->errorcnt++;
  604.                     $this->state = self::RESYNC_AFTER_RULE_ERROR;
  605.                 }
  606.                 break;
  607.             case self::RHS_ALIAS_1:
  608.                 if (preg_match('/[A-Za-z]/'$x[0])) {
  609.                     $this->alias[$this->nrhs - 1$x;
  610.                     $this->state = self::RHS_ALIAS_2;
  611.                 else {
  612.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  613.                         "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n",
  614.                         $x$this->rhs[$this->nrhs - 1]->name);
  615.                     $this->errorcnt++;
  616.                     $this->state = self::RESYNC_AFTER_RULE_ERROR;
  617.                 }
  618.                 break;
  619.             case self::RHS_ALIAS_2:
  620.                 if ($x[0== ')'{
  621.                     $this->state = self::IN_RHS;
  622.                 else {
  623.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  624.                         "Missing \")\" following LHS alias name \"%s\"."$this->lhsalias);
  625.                     $this->errorcnt++;
  626.                     $this->state = self::RESYNC_AFTER_RULE_ERROR;
  627.                 }
  628.                 break;
  629.             case self::WAITING_FOR_DECL_KEYWORD:
  630.                 if(preg_match('/[A-Za-z]/'$x[0])) {
  631.                     $this->declkeyword = $x;
  632.                     $this->declargslot = &$this->a;
  633.                     $this->decllnslot = &$this->a;
  634.                     $this->state = self::WAITING_FOR_DECL_ARG;
  635.                     if ('name' == $x{
  636.                         $this->declargslot = &$this->gp->name;
  637.                     elseif ('include' == $x{
  638.                         $this->declargslot = &$this->gp->include_code;
  639.                         $this->decllnslot = &$this->gp->includeln;
  640.                     elseif ('include_class' == $x{
  641.                         $this->declargslot = &$this->gp->include_classcode;
  642.                         $this->decllnslot = &$this->gp->include_classln;
  643.                     elseif ('declare_class' == $x{
  644.                         $this->declargslot = &$this->gp->declare_classcode;
  645.                         $this->decllnslot = &$this->gp->declare_classln;
  646.                     elseif ('code' == $x{
  647.                         $this->declargslot = &$this->gp->extracode;
  648.                         $this->decllnslot = &$this->gp->extracodeln;
  649.                     elseif ('token_destructor' == $x{
  650.                         $this->declargslot = &$this->gp->tokendest;
  651.                         $this->decllnslot = &$this->gp->tokendestln;
  652.                     elseif ('default_destructor' == $x{
  653.                         $this->declargslot = &$this->gp->vardest;
  654.                         $this->decllnslot = &$this->gp->vardestln;
  655.                     elseif ('token_prefix' == $x{
  656.                         $this->declargslot = &$this->gp->tokenprefix;
  657.                     elseif ('syntax_error' == $x{
  658.                         $this->declargslot = &$this->gp->error;
  659.                         $this->decllnslot = &$this->gp->errorln;
  660.                     elseif ('parse_accept' == $x{
  661.                         $this->declargslot = &$this->gp->accept;
  662.                         $this->decllnslot = &$this->gp->acceptln;
  663.                     elseif ('parse_failure' == $x{
  664.                         $this->declargslot = &$this->gp->failure;
  665.                         $this->decllnslot = &$this->gp->failureln;
  666.                     elseif ('stack_overflow' == $x{
  667.                         $this->declargslot = &$this->gp->overflow;
  668.                         $this->decllnslot = &$this->gp->overflowln;
  669.                     elseif ('token_type' == $x{
  670.                         $this->declargslot = &$this->gp->tokentype;
  671.                     elseif ('default_type' == $x{
  672.                         $this->declargslot = &$this->gp->vartype;
  673.                     elseif ('stack_size' == $x{
  674.                         $this->declargslot = &$this->gp->stacksize;
  675.                     elseif ('start_symbol' == $x{
  676.                         $this->declargslot = &$this->gp->start;
  677.                     elseif ('left' == $x{
  678.                         $this->preccounter++;
  679.                         $this->declassoc = PHP_ParserGenerator_Symbol::LEFT;
  680.                         $this->state = self::WAITING_FOR_PRECEDENCE_SYMBOL;
  681.                     elseif ('right' == $x{
  682.                         $this->preccounter++;
  683.                         $this->declassoc = PHP_ParserGenerator_Symbol::RIGHT;
  684.                         $this->state = self::WAITING_FOR_PRECEDENCE_SYMBOL;
  685.                     elseif ('nonassoc' == $x{
  686.                         $this->preccounter++;
  687.                         $this->declassoc = PHP_ParserGenerator_Symbol::NONE;
  688.                         $this->state = self::WAITING_FOR_PRECEDENCE_SYMBOL;
  689.                     elseif ('destructor' == $x{
  690.                         $this->state = self::WAITING_FOR_DESTRUCTOR_SYMBOL;
  691.                     elseif ('type' == $x{
  692.                         $this->state = self::WAITING_FOR_DATATYPE_SYMBOL;
  693.                     elseif ('fallback' == $x{
  694.                         $this->fallback = 0;
  695.                         $this->state = self::WAITING_FOR_FALLBACK_ID;
  696.                     else {
  697.                         PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  698.                         "Unknown declaration keyword: \"%%%s\"."$x);
  699.                         $this->errorcnt++;
  700.                         $this->state = self::RESYNC_AFTER_DECL_ERROR;
  701.                     }
  702.                 else {
  703.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  704.                         "Illegal declaration keyword: \"%s\"."$x);
  705.                     $this->errorcnt++;
  706.                     $this->state = self::RESYNC_AFTER_DECL_ERROR;
  707.                 }
  708.                 break;
  709.             case self::WAITING_FOR_DESTRUCTOR_SYMBOL:
  710.                 if (!preg_match('/[A-Za-z]/'$x[0])) {
  711.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  712.                         "Symbol name missing after %destructor keyword");
  713.                     $this->errorcnt++;
  714.                     $this->state = self::RESYNC_AFTER_DECL_ERROR;
  715.                 else {
  716.                     $sp PHP_ParserGenerator_Symbol::Symbol_new($x);
  717.                     $this->declargslot = &$sp->destructor;
  718.                     $this->decllnslot = &$sp->destructorln;
  719.                     $this->state = self::WAITING_FOR_DECL_ARG;
  720.                 }
  721.                 break;
  722.             case self::WAITING_FOR_DATATYPE_SYMBOL:
  723.                 if (!preg_match('/[A-Za-z]/'$x[0])) {
  724.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  725.                         "Symbol name missing after %destructor keyword");
  726.                     $this->errorcnt++;
  727.                     $this->state = self::RESYNC_AFTER_DECL_ERROR;
  728.                 else {
  729.                     $sp PHP_ParserGenerator_Symbol::Symbol_new($x);
  730.                     $this->declargslot = &$sp->datatype;
  731.                     $this->state = self::WAITING_FOR_DECL_ARG;
  732.                 }
  733.                 break;
  734.             case self::WAITING_FOR_PRECEDENCE_SYMBOL:
  735.                 if ($x[0== '.'{
  736.                     $this->state = self::WAITING_FOR_DECL_OR_RULE;
  737.                 elseif (preg_match('/[A-Z]/'$x[0])) {
  738.                     $sp PHP_ParserGenerator_Symbol::Symbol_new($x);
  739.                     if ($sp->prec >= 0{
  740.                         PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  741.                             "Symbol \"%s\" has already been given a precedence."$x);
  742.                         $this->errorcnt++;
  743.                     else {
  744.                         $sp->prec $this->preccounter;
  745.                         $sp->assoc $this->declassoc;
  746.                     }
  747.                 else {
  748.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  749.                         "Can't assign a precedence to \"%s\"."$x);
  750.                     $this->errorcnt++;
  751.                 }
  752.                 break;
  753.             case self::WAITING_FOR_DECL_ARG:
  754.                 if (preg_match('/[A-Za-z0-9{"]/'$x[0])) {
  755.                     if ($this->declargslot != 0{
  756.                         PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  757.                             "The argument \"%s\" to declaration \"%%%s\" is not the first.",
  758.                             $x[0== '"' substr($x1$x$this->declkeyword);
  759.                         $this->errorcnt++;
  760.                         $this->state = self::RESYNC_AFTER_DECL_ERROR;
  761.                     else {
  762.                         $this->declargslot = ($x[0== '"' || $x[0== '{'substr($x1$x;
  763.                         $this->1;
  764.                         if (!$this->decllnslot{
  765.                             $this->decllnslot = $this->tokenlineno;
  766.                         }
  767.                         $this->state = self::WAITING_FOR_DECL_OR_RULE;
  768.                     }
  769.                 else {
  770.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  771.                         "Illegal argument to %%%s: %s",$this->declkeyword$x);
  772.                     $this->errorcnt++;
  773.                     $this->state = self::RESYNC_AFTER_DECL_ERROR;
  774.                 }
  775.                 break;
  776.             case self::WAITING_FOR_FALLBACK_ID:
  777.                 if ($x[0== '.'{
  778.                     $this->state = self::WAITING_FOR_DECL_OR_RULE;
  779.                 elseif (!preg_match('/[A-Z]/'$x[0])) {
  780.                     PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  781.                     "%%fallback argument \"%s\" should be a token"$x);
  782.                     $this->errorcnt++;
  783.                 else {
  784.                     $sp PHP_ParserGenerator_Symbol::Symbol_new($x);
  785.                     if ($this->fallback === 0{
  786.                         $this->fallback = $sp;
  787.                     elseif (is_object($sp->fallback)) {
  788.                         PHP_ParserGenerator::ErrorMsg($this->filename$this->tokenlineno,
  789.                         "More than one fallback assigned to token %s"$x);
  790.                         $this->errorcnt++;
  791.                     else {
  792.                         $sp->fallback $this->fallback;
  793.                         $this->gp->has_fallback = 1;
  794.                     }
  795.                 }
  796.                 break;
  797.             case self::RESYNC_AFTER_RULE_ERROR:
  798.             /*      if ($x[0] == '.') $this->state = self::WAITING_FOR_DECL_OR_RULE;
  799.             **      break; */
  800.             case self::RESYNC_AFTER_DECL_ERROR:
  801.                 if ($x[0== '.'{
  802.                     $this->state = self::WAITING_FOR_DECL_OR_RULE;
  803.                 }
  804.                 if ($x[0== '%'{
  805.                     $this->state = self::WAITING_FOR_DECL_KEYWORD;
  806.                 }
  807.                 break;
  808.         }
  809.     }
  810.  
  811.     /**
  812.      * return a descriptive string for a multi-terminal token.
  813.      *
  814.      * @param string $a 
  815.      * @param string $b 
  816.      * @return string 
  817.      */
  818.     private function _printmulti($a$b)
  819.     {
  820.         if (!$a{
  821.             $a '';
  822.         }
  823.         $a .= $b->name '|';
  824.         return $a;
  825.     }
  826. }

Documentation generated on Sun, 02 Jul 2006 09:11:02 -0400 by phpDocumentor 1.3.0