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

Source for file Parser.php

Documentation is available at Parser.php

  1. <?php
  2. /* Driver template for the PHP_PHP_LexerGenerator_ParserrGenerator parser generator. (PHP port of LEMON)
  3. */
  4.  
  5. /**
  6.  * This can be used to store both the string representation of
  7.  * a token, and any useful meta-data associated with the token.
  8.  *
  9.  * meta-data should be stored as an array
  10.  */
  11. class PHP_LexerGenerator_ParseryyToken implements ArrayAccess
  12. {
  13.     public $string = '';
  14.     public $metadata = array();
  15.  
  16.     function __construct($s$m array())
  17.     {
  18.         if ($s instanceof PHP_LexerGenerator_ParseryyToken{
  19.             $this->string = $s->string;
  20.             $this->metadata = $s->metadata;
  21.         else {
  22.             $this->string = (string) $s;
  23.             if ($m instanceof PHP_LexerGenerator_ParseryyToken{
  24.                 $this->metadata = $m->metadata;
  25.             elseif (is_array($m)) {
  26.                 $this->metadata = $m;
  27.             }
  28.         }
  29.     }
  30.  
  31.     function __toString()
  32.     {
  33.         return $this->_string;
  34.     }
  35.  
  36.     function offsetExists($offset)
  37.     {
  38.         return isset($this->metadata[$offset]);
  39.     }
  40.  
  41.     function offsetGet($offset)
  42.     {
  43.         return $this->metadata[$offset];
  44.     }
  45.  
  46.     function offsetSet($offset$value)
  47.     {
  48.         if ($offset === null{
  49.             if (isset($value[0])) {
  50.                 $x ($value instanceof PHP_LexerGenerator_ParseryyToken?
  51.                     $value->metadata $value;
  52.                 $this->metadata = array_merge($this->metadata$x);
  53.                 return;
  54.             }
  55.             $offset count($this->metadata);
  56.         }
  57.         if ($value === null{
  58.             return;
  59.         }
  60.         if ($value instanceof PHP_LexerGenerator_ParseryyToken{
  61.             if ($value->metadata{
  62.                 $this->metadata[$offset$value->metadata;
  63.             }
  64.         elseif ($value{
  65.             $this->metadata[$offset$value;
  66.         }
  67.     }
  68.  
  69.     function offsetUnset($offset)
  70.     {
  71.         unset($this->metadata[$offset]);
  72.     }
  73. }
  74.  
  75. /** The following structure represents a single element of the
  76.  * parser's stack.  Information stored includes:
  77.  *
  78.  *   +  The state number for the parser at this level of the stack.
  79.  *
  80.  *   +  The value of the token stored at this level of the stack.
  81.  *      (In other words, the "major" token.)
  82.  *
  83.  *   +  The semantic value stored at this level of the stack.  This is
  84.  *      the information used by the action routines in the grammar.
  85.  *      It is sometimes called the "minor" token.
  86.  */
  87. {
  88.     public $stateno;       /* The state-number */
  89.     public $major;         /* The major token value.  This is the code
  90.                      ** number for the token at this stack level */
  91.     public $minor/* The user-supplied minor token value.  This
  92.                      ** is the value of the token  */
  93. };
  94.  
  95. // code external to the class is included here
  96. #line 3 "Parser.y"
  97.  
  98. /* ?><?php {//*/
  99. /**
  100.  * PHP_LexerGenerator, a php 5 lexer generator.
  101.  * 
  102.  * This lexer generator translates a file in a format similar to
  103.  * re2c ({@link http://re2c.org}) and translates it into a PHP 5-based lexer
  104.  *
  105.  * PHP version 5
  106.  *
  107.  * LICENSE:
  108.  * 
  109.  * Copyright (c) 2006, Gregory Beaver <cellog@php.net>
  110.  * All rights reserved.
  111.  *
  112.  * Redistribution and use in source and binary forms, with or without
  113.  * modification, are permitted provided that the following conditions
  114.  * are met:
  115.  *
  116.  *     * Redistributions of source code must retain the above copyright
  117.  *       notice, this list of conditions and the following disclaimer.
  118.  *     * Redistributions in binary form must reproduce the above copyright
  119.  *       notice, this list of conditions and the following disclaimer in
  120.  *       the documentation and/or other materials provided with the distribution.
  121.  *     * Neither the name of the PHP_LexerGenerator nor the names of its
  122.  *       contributors may be used to endorse or promote products derived
  123.  *       from this software without specific prior written permission.
  124.  *
  125.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  126.  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  127.  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  128.  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  129.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  130.  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  131.  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  132.  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  133.  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  134.  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  135.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  136.  *
  137.  * @category   php
  138.  * @package    PHP_LexerGenerator
  139.  * @author     Gregory Beaver <cellog@php.net>
  140.  * @copyright  2006 Gregory Beaver
  141.  * @license    http://www.opensource.org/licenses/bsd-license.php New BSD License
  142.  * @version    CVS: $Id$
  143.  * @since      File available since Release 0.1.0
  144.  */
  145. /**
  146.  * For regular expression validation
  147.  */
  148. require_once 'PHP/LexerGenerator/Regex/Lexer.php';
  149. require_once 'PHP/LexerGenerator/Regex/Parser.php';
  150. require_once 'PHP/LexerGenerator/Exception.php';
  151. /**
  152.  * Token parser for plex files.
  153.  * 
  154.  * This parser converts tokens pulled from {@link PHP_LexerGenerator_Lexer}
  155.  * into abstract patterns and rules, then creates the output file
  156.  * @package    PHP_LexerGenerator
  157.  * @author     Gregory Beaver <cellog@php.net>
  158.  * @copyright  2006 Gregory Beaver
  159.  * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
  160.  * @version    @package_version@
  161.  * @since      Class available since Release 0.1.0
  162.  */
  163. #line 166 "Parser.php"
  164.  
  165. // declare_class is output here
  166. #line 2 "Parser.y"
  167. class PHP_LexerGenerator_Parser#line 171 "Parser.php"
  168. {
  169. /* First off, code is included which follows the "include_class" declaration
  170. ** in the input file. */
  171. #line 82 "Parser.y"
  172.  
  173.     
  174.     private $patterns;
  175.     private $out;
  176.     private $lex;
  177.     private $input;
  178.     private $counter;
  179.     private $token;
  180.     private $value;
  181.     private $line;
  182.     private $_regexLexer;
  183.     private $_regexParser;
  184.     private $_patternIndex 0;
  185.  
  186.     public $transTable = array(
  187.         => self::PHPCODE,
  188.         => self::COMMENTSTART,
  189.         => self::COMMENTEND,
  190.         => self::QUOTE,
  191.         => self::PATTERN,
  192.         => self::CODE,
  193.         => self::SUBPATTERN,
  194.         => self::PI,
  195.     );
  196.  
  197.     function __construct($outfile$lex)
  198.     {
  199.         $this->out fopen($outfile'wb');
  200.         if (!$this->out{
  201.             throw new Exception('unable to open lexer output file "' $outfile '"');
  202.         }
  203.         $this->lex $lex;
  204.         $this->_regexLexer new PHP_LexerGenerator_Regex_Lexer('');
  205.         $this->_regexParser new PHP_LexerGenerator_Regex_Parser($this->_regexLexer);
  206.     }
  207.  
  208.     function outputRules($rules$statename)
  209.     {
  210.         static $ruleindex 1;
  211.         $patterns array();
  212.         $pattern '/';
  213.         $ruleMap array();
  214.         $tokenindex array();
  215.         $i 0;
  216.         $actualindex 1;
  217.         foreach ($rules as $rule{
  218.             $ruleMap[$i++$actualindex;
  219.             $tokenindex[$actualindex$rule['subpatterns'];
  220.             $actualindex += $rule['subpatterns'1;
  221.             $patterns['^(' $rule['pattern'')';
  222.         }
  223.         $tokenindex var_export($tokenindextrue);
  224.         $tokenindex explode("\n"$tokenindex);
  225.         // indent for prettiness
  226.         
  227.         $tokenindex implode("\n            "$tokenindex);
  228.         $pattern .= implode('|'$patterns);
  229.         $pattern .= '/';
  230.         if (!$statename{
  231.             $statename $ruleindex;
  232.         }
  233.         fwrite($this->out'
  234.     function yylex' $ruleindex '()
  235.     {
  236.         $tokenMap = ' $tokenindex ';
  237.         if (' $this->counter ' >= strlen(' $this->input ')) {
  238.             return false; // end of input
  239.         }
  240.         ');
  241.         fwrite($this->out'$yy_global_pattern = "' .
  242.             $pattern '";' "\n");
  243.         fwrite($this->out'
  244.         do {
  245.             if (preg_match($yy_global_pattern, substr(' $this->input ', ' .
  246.              $this->counter .
  247.                     '), $yymatches)) {
  248.                 $yysubmatches = $yymatches;
  249.                 $yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
  250.                 if (!count($yymatches)) {
  251.                     throw new Exception(\'Error: lexing failed because a rule matched\' .
  252.                         \'an empty string.  Input "\' . substr(' $this->input ',
  253.                         ' $this->counter ', 5) . \'... state ' $statename '\');
  254.                 }
  255.                 next($yymatches); // skip global match
  256.                 ' $this->token ' = key($yymatches); // token number
  257.                 if ($tokenMap[' $this->token ']) {
  258.                     // extract sub-patterns for passing to lex function
  259.                     $yysubmatches = array_slice($yysubmatches, ' $this->token ' + 1,
  260.                         $tokenMap[' $this->token ']);
  261.                 } else {
  262.                     $yysubmatches = array();
  263.                 }
  264.                 ' $this->value ' = current($yymatches); // token value
  265.                 $r = $this->{\'yy_r' $ruleindex '_\' . ' $this->token '}($yysubmatches);
  266.                 if ($r === null) {
  267.                     ' $this->counter ' += strlen($this->value);
  268.                     ' $this->line ' += substr_count("\n", ' $this->value ');
  269.                     // accept this token
  270.                     return true;
  271.                 } elseif ($r === true) {
  272.                     // we have changed state
  273.                     // process this token in the new state
  274.                     return $this->yylex();
  275.                 } elseif ($r === false) {
  276.                     ' $this->counter ' += strlen($this->value);
  277.                     ' $this->line ' += substr_count("\n", ' $this->value ');
  278.                     if (' $this->counter ' >= strlen(' $this->input ')) {
  279.                         return false; // end of input
  280.                     }
  281.                     // skip this token
  282.                     continue;
  283.                 } else {');
  284.         fwrite($this->out'                    $yy_yymore_patterns = array(' "\n");
  285.         for($i 0count($patterns)$i++{
  286.             unset($patterns[$i]);
  287.             fwrite($this->out'        ' $ruleMap[$i' => "' .
  288.                 implode('|'$patterns"\",\n");
  289.         }
  290.         fwrite($this->out'    );' "\n");
  291.         fwrite($this->out'
  292.                     // yymore is needed
  293.                     do {
  294.                         if (!strlen($yy_yymore_patterns[' $this->token '])) {
  295.                             throw new Exception(\'cannot do yymore for the last token\');
  296.                         }
  297.                         if (preg_match($yy_yymore_patterns[' $this->token '],
  298.                               substr(' $this->input ', ' $this->counter '), $yymatches)) {
  299.                             $yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
  300.                             next($yymatches); // skip global match
  301.                             ' $this->token ' = key($yymatches); // token number
  302.                             ' $this->value ' = current($yymatches); // token value
  303.                             ' $this->line ' = substr_count("\n", ' $this->value ');
  304.                         }
  305.                     } while ($this->{\'yy_r' $ruleindex '_\' . ' $this->token '}() !== null);
  306.                     // accept
  307.                     ' $this->counter ' += strlen($this->value);
  308.                     ' $this->line ' += substr_count("\n", ' $this->value ');
  309.                     return true;
  310.                 }
  311.             } else {
  312.                 throw new Exception(\'Unexpected input at line\' . ' $this->line ' .
  313.                     \': \' . ' $this->input '[' $this->counter ']);
  314.             }
  315.             break;
  316.         } while (true);
  317.     } // end function
  318.  
  319. ');
  320.         if ($statename{
  321.             fwrite($this->out'
  322.     const ' $statename ' = ' $ruleindex ';
  323. ');
  324.         }
  325.         foreach ($rules as $i => $rule{
  326.             fwrite($this->out'    function yy_r' $ruleindex '_' $ruleMap[$i'($yy_subpatterns)
  327.     {
  328. $rule['code'.
  329. '    }
  330. ');
  331.         }
  332.         $ruleindex++// for next set of rules
  333.     }
  334.  
  335.     function error($msg)
  336.     {
  337.         echo 'Error on line ' $this->lex->line ': ' $msg;
  338.     }
  339.  
  340.     function _validatePattern($pattern$update false)
  341.     {
  342.         $this->_regexLexer->reset($pattern);
  343.         $this->_regexParser->reset($this->_patternIndex$update);
  344.         try {
  345.             while ($this->_regexLexer->yylex()) {
  346.                 $this->_regexParser->doParse(
  347.                     $this->_regexLexer->token$this->_regexLexer->value);
  348.             }
  349.             $this->_regexParser->doParse(00);
  350.         catch (PHP_LexerGenerator_Exception $e{
  351.             $this->error($e->getMessage());
  352.             throw new PHP_LexerGenerator_Exception('Invalid pattern "' $pattern '"');
  353.         }
  354.         return $this->_regexParser->result;
  355.     }
  356. #line 359 "Parser.php"
  357.  
  358. /* Next is all token values, as class constants
  359. */
  360. /* 
  361. ** These constants (all generated automatically by the parser generator)
  362. ** specify the various kinds of tokens (terminals) that the parser
  363. ** understands. 
  364. **
  365. ** Each symbol here is a terminal symbol in the grammar.
  366. */
  367.     const PHPCODE                        =  1;
  368.     const COMMENTSTART                   =  2;
  369.     const COMMENTEND                     =  3;
  370.     const PI                             =  4;
  371.     const SUBPATTERN                     =  5;
  372.     const CODE                           =  6;
  373.     const PATTERN                        =  7;
  374.     const QUOTE                          =  8;
  375.     const YY_NO_ACTION 91;
  376.     const YY_ACCEPT_ACTION 90;
  377.     const YY_ERROR_ACTION 89;
  378.  
  379. /* Next are that tables used to determine what action to take based on the
  380. ** current state and lookahead token.  These tables are used to implement
  381. ** functions that take a state number and lookahead value and return an
  382. ** action integer.  
  383. **
  384. ** Suppose the action integer is N.  Then the action is determined as
  385. ** follows
  386. **
  387. **   0 <= N < self::YYNSTATE                              Shift N.  That is,
  388. **                                                        push the lookahead
  389. **                                                        token onto the stack
  390. **                                                        and goto state N.
  391. **
  392. **   self::YYNSTATE <= N < self::YYNSTATE+self::YYNRULE   Reduce by rule N-YYNSTATE.
  393. **
  394. **   N == self::YYNSTATE+self::YYNRULE                    A syntax error has occurred.
  395. **
  396. **   N == self::YYNSTATE+self::YYNRULE+1                  The parser accepts its
  397. **                                                        input. (and concludes parsing)
  398. **
  399. **   N == self::YYNSTATE+self::YYNRULE+2                  No such action.  Denotes unused
  400. **                                                        slots in the yy_action[] table.
  401. **
  402. ** The action table is constructed as a single large static array $yy_action.
  403. ** Given state S and lookahead X, the action is computed as
  404. **
  405. **      self::$yy_action[self::$yy_shift_ofst[S] + X ]
  406. **
  407. ** If the index value self::$yy_shift_ofst[S]+X is out of range or if the value
  408. ** self::$yy_lookahead[self::$yy_shift_ofst[S]+X] is not equal to X or if
  409. ** self::$yy_shift_ofst[S] is equal to self::YY_SHIFT_USE_DFLT, it means that
  410. ** the action is not in the table and that self::$yy_default[S] should be used instead.  
  411. **
  412. ** The formula above is for computing the action when the lookahead is
  413. ** a terminal symbol.  If the lookahead is a non-terminal (as occurs after
  414. ** a reduce action) then the static $yy_reduce_ofst array is used in place of
  415. ** the static $yy_shift_ofst array and self::YY_REDUCE_USE_DFLT is used in place of
  416. ** self::YY_SHIFT_USE_DFLT.
  417. **
  418. ** The following are the tables generated in this section:
  419. **
  420. **  self::$yy_action        A single table containing all actions.
  421. **  self::$yy_lookahead     A table containing the lookahead for each entry in
  422. **                          yy_action.  Used to detect hash collisions.
  423. **  self::$yy_shift_ofst    For each state, the offset into self::$yy_action for
  424. **                          shifting terminals.
  425. **  self::$yy_reduce_ofst   For each state, the offset into self::$yy_action for
  426. **                          shifting non-terminals after a reduce.
  427. **  self::$yy_default       Default action for each state.
  428. */
  429.     const YY_SZ_ACTTAB 80;
  430. static public $yy_action array(
  431.  /*     0 */    35,   24,   50,   50,   48,   51,   51,   54,   47,   43,
  432.  /*    10 */    53,   54,   45,   31,   53,   32,   30,   50,   50,    1,
  433.  /*    20 */    51,   51,   34,   50,   17,    8,   51,   90,   52,    6,
  434.  /*    30 */     3,   29,   50,   50,   25,   51,   51,   11,   38,   18,
  435.  /*    40 */     1,   41,   42,   39,   10,   36,   18,   12,   37,   18,
  436.  /*    50 */    20,    7,    2,   16,   13,   15,   18,   27,    9,    2,
  437.  /*    60 */     5,   28,   14,    1,   44,   40,   33,   49,   56,   46,
  438.  /*    70 */    26,   19,    1,   55,    2,   21,    4,   23,   22,    8,
  439.     );
  440.     static public $yy_lookahead array(
  441.  /*     0 */     3,    3,    5,    5,    1,    8,    8,    5,    6,    2,
  442.  /*    10 */     8,    5,    6,   13,    8,    3,    3,    5,    5,   19,
  443.  /*    20 */     8,    8,    4,    5,    1,    2,    8,   10,   11,   12,
  444.  /*    30 */     5,    4,    5,    5,   13,    8,    8,   18,    5,   20,
  445.  /*    40 */    19,    8,    5,    6,   18,    5,   20,   18,    8,   20,
  446.  /*    50 */     4,    1,    2,    7,   18,    7,   20,   13,    1,    2,
  447.  /*    60 */     5,   14,   15,   19,    5,    6,   13,    1,    1,    1,
  448.  /*    70 */    16,   20,   19,    3,    2,   17,   12,    4,   17,    2,
  449. );
  450.     const YY_SHIFT_USE_DFLT = -4;
  451.     const YY_SHIFT_MAX 35;
  452.     static public $yy_shift_ofst array(
  453.  /*     0 */    23,   27,   18,   28,   50,   28,   57,   72,   73,   72,
  454.  /*    10 */    13,   12,   -3,   -2,   46,   40,   40,   77,    2,    6,
  455.  /*    20 */    59,   33,   33,   37,    3,    7,   48,    7,   70,   55,
  456.  /*    30 */    68,    7,   67,    7,   25,   66,
  457. );
  458.     const