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

Source for file ParserGenerator.php

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

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