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.                          &nb