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;