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

Sample parser file format (PHP_LexerGenerator's parser)

  1. %name PHP_LexerGenerator_Parser
  2. %declare_class {class PHP_LexerGenerator_Parser}
  3. %include {
  4. /* ?><?php {//*/
  5. /**
  6.  * PHP_LexerGenerator, a php 5 lexer generator.
  7.  * 
  8.  * This lexer generator translates a file in a format similar to
  9.  * re2c ({@link http://re2c.org}) and translates it into a PHP 5-based lexer
  10.  *
  11.  * PHP version 5
  12.  *
  13.  * LICENSE: This source file is subject to version 3.01 of the PHP license
  14.  * that is available through the world-wide-web at the following URI:
  15.  * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
  16.  * the PHP License and are unable to obtain it through the web, please
  17.  * send a note to license@php.net so we can mail you a copy immediately.
  18.  *
  19.  * @category   php
  20.  * @package    PHP_LexerGenerator
  21.  * @author     Gregory Beaver <cellog@php.net>
  22.  * @copyright  2006 Gregory Beaver
  23.  * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
  24.  * @version    CVS: $Id$
  25.  * @since      File available since Release 0.1.0
  26.  */
  27. /**
  28.  * Token parser for plex files.
  29.  * 
  30.  * This parser converts tokens pulled from {@link PHP_LexerGenerator_Lexer}
  31.  * into abstract patterns and rules, then creates the output file
  32.  * @package    PHP_LexerGenerator
  33.  * @author     Gregory Beaver <cellog@php.net>
  34.  * @copyright  2006 Gregory Beaver
  35.  * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
  36.  * @version    @package_version@
  37.  * @since      Class available since Release 0.1.0
  38.  */
  39. }
  40. %syntax_error {
  41.     echo "Syntax Error on line " $this->lex->line ": token '" 
  42.         $this->lex->value "' while parsing rule:";
  43.     foreach ($this->yystack as $entry{
  44.         echo $this->tokenName($entry->major' ';
  45.     }
  46.     foreach ($this->yy_get_expected_tokens($yymajoras $token{
  47.         $expect[self::$yyTokenName[$token];
  48.     }
  49.     throw new Exception('Unexpected ' $this->tokenName($yymajor'(' $TOKEN
  50.         . '), expected one of: ' implode(','$expect));
  51. }
  52. %include_class {
  53.     private $patterns;
  54.     private $out;
  55.     private $lex;
  56.     private $input;
  57.     private $counter;
  58.     private $token;
  59.     private $value;
  60.     private $line;
  61.  
  62.     public $transTable array(
  63.         => self::PHPCODE,
  64.         => self::COMMENTSTART,
  65.         => self::COMMENTEND,
  66.         => self::QUOTE,
  67.         => self::PATTERN,
  68.         => self::CODE,
  69.         => self::SUBPATTERN,
  70.         => self::PI,
  71.     );
  72.  
  73.     function __construct($outfile$lex)
  74.     {
  75.         $this->out fopen($outfile'wb');
  76.         if (!$this->out{
  77.             throw new Exception('unable to open lexer output file "' $outfile '"');
  78.         }
  79.         $this->lex $lex;
  80.     }
  81.  
  82.     function outputRules($rules$statename)
  83.     {
  84.         static $ruleindex 1;
  85.         $patterns array();
  86.         $pattern '/';
  87.         foreach ($rules as $rule{
  88.             $patterns['^(' $rule['pattern'')';
  89.         }
  90.         $pattern .= implode('|'$patterns);
  91.         $pattern .= '/';
  92.         if ($statename{
  93.             fwrite($this->out'
  94.     const ' $statename ' = ' $ruleindex ';
  95. ');
  96.         }
  97.         fwrite($this->out'
  98.     function yylex' $ruleindex '()
  99.     {
  100.         if (' $this->counter ' >= strlen(' $this->input ')) {
  101.             return false; // end of input
  102.         }
  103.         ');
  104.         fwrite($this->out'    $yy_global_pattern = "' .
  105.             $pattern '";' "\n");
  106.         fwrite($this->out'
  107.         do {
  108.             if (preg_match($yy_global_pattern, substr(' $this->input ', ' .
  109.              $this->counter .
  110.                     '), $yymatches)) {
  111.                 $yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
  112.                 if (!count($yymatches)) {
  113.                     throw new Exception(\'Error: lexing failed because a rule matched\' .
  114.                         \'an empty string\');
  115.                 }
  116.                 next($yymatches); // skip global match
  117.                 ' $this->token ' = key($yymatches); // token number
  118.                 ' $this->value ' = current($yymatches); // token value
  119.                 $r = $this->{\'yy_r' $ruleindex '_\' . ' $this->token '}();
  120.                 if ($r === null) {
  121.                     ' $this->counter ' += strlen($this->value);
  122.                     ' $this->line ' += substr_count("\n", ' $this->value ');
  123.                     // accept this token
  124.                     return true;
  125.                 } elseif ($r === true) {
  126.                     // we have changed state
  127.                     // process this token in the new state
  128.                     return $this->yylex();
  129.                 } elseif ($r === false) {
  130.                     ' $this->counter ' += strlen($this->value);
  131.                     ' $this->line ' += substr_count("\n", ' $this->value ');
  132.                     if (' $this->counter ' >= strlen(' $this->input ')) {
  133.                         return false; // end of input
  134.                     }
  135.                     // skip this token
  136.                     continue;
  137.                 } else {');
  138.         fwrite($this->out'                    $yy_yymore_patterns = array(' "\n");
  139.         for($i 0count($patterns)$i++{
  140.             unset($patterns[$i]);
  141.             fwrite($this->out'        ' ($i 1' => "' .
  142.                 implode('|'$patterns"\",\n");
  143.         }
  144.         fwrite($this->out'    );' "\n");
  145.         fwrite($this->out'
  146.                     // yymore is needed
  147.                     do {
  148.                         if (!strlen($yy_yymore_patterns[' $this->token '])) {
  149.                             throw new Exception(\'cannot do yymore for the last token\');
  150.                         }
  151.                         if (preg_match($yy_yymore_patterns[' $this->token '],
  152.                               substr(' $this->input ', ' $this->counter '), $yymatches)) {
  153.                             $yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
  154.                             next($yymatches); // skip global match
  155.                             ' $this->token ' = key($yymatches); // token number
  156.                             ' $this->value ' = current($yymatches); // token value
  157.                             ' $this->line ' = substr_count("\n", ' $this->value ');
  158.                         }
  159.                     } while ($this->{\'yy_r' $ruleindex '_\' . ' $this->token '}() !== null);
  160.                     // accept
  161.                     ' $this->counter ' += strlen($this->value);
  162.                     ' $this->line ' += substr_count("\n", ' $this->value ');
  163.                     return true;
  164.                 }
  165.             } else {
  166.                 throw new Exception(\'Unexpected input at line\' . ' $this->line ' .
  167.                     \': \' . ' $this->input '[' $this->counter ']);
  168.             }
  169.             break;
  170.         } while (true);
  171.     } // end function
  172.  
  173. ');
  174.         foreach ($rules as $i => $rule{
  175.             fwrite($this->out'    function yy_r' $ruleindex '_' ($i 1'()
  176.     {
  177. $rule['code'.
  178. '    }
  179. ');
  180.         }
  181.         $ruleindex++// for next set of rules
  182.     }
  183.  
  184.     function error($msg)
  185.     {
  186.         echo 'Error on line ' $this->lex->line ': ' $msg;
  187.     }
  188.  
  189.     function _validatePattern($pattern)
  190.     {
  191.         if ($pattern[0== '^'{
  192.             $this->error('Pattern "' $pattern .
  193.                 '" should not begin with ^, lexer may fail');
  194.         }
  195.         if ($pattern[strlen($pattern1== '$'{
  196.             $this->error('Pattern "' $pattern .
  197.                 '" should not end with $, lexer may fail');
  198.         }
  199.         // match ( but not \( or (?:
  200.         $savepattern $pattern;
  201.         $pattern str_replace('\\\\'''$pattern);
  202.         $pattern str_replace('\\('''$pattern);
  203.         if (preg_match('/\([^?][^:]|\(\?[^:]|\(\?$|\($/'$pattern)) {
  204.             $this->error('Pattern "' $savepattern .
  205.                 '" must not contain sub-patterns (like this), generated lexer will fail');
  206.         }
  207.     }
  208. }
  209.  
  210. start ::lexfile.
  211.  
  212. lexfile ::declare rules(B){
  213.     fwrite($this->out'
  214.     private $_yy_state = 1;
  215.     private $_yy_stack = array();
  216.  
  217.     function yylex()
  218.     {
  219.         return $this->{\'yylex\' . $this->_yy_state}();
  220.     }
  221.  
  222.     function yypushstate($state)
  223.     {
  224.         array_push($this->_yy_stack, $this->_yy_state);
  225.         $this->_yy_state = $state;
  226.     }
  227.  
  228.     function yypopstate()
  229.     {
  230.         $this->_yy_state = array_pop($this->_yy_stack);
  231.     }
  232.  
  233.     function yybegin($state)
  234.     {
  235.         $this->_yy_state = $state;
  236.     }
  237.  
  238. ');
  239.     foreach (as $rule{
  240.         $this->outputRules($rule['rules']$rule['statename']);
  241.         if ($rule['code']{
  242.             fwrite($this->out$rule['code']);
  243.         }
  244.     }
  245. }
  246. lexfile ::declare(DPHPCODE(Brules(C){
  247.     fwrite($this->out'
  248.     private $_yy_state = 1;
  249.     private $_yy_stack = array();
  250.  
  251.     function yylex()
  252.     {
  253.         return $this->{\'yylex\' . $this->_yy_state}();
  254.     }
  255.  
  256.     function yypushstate($state)
  257.     {
  258.         array_push($this->_yy_stack, $this->_yy_state);
  259.         $this->_yy_state = $state;
  260.     }
  261.  
  262.     function yypopstate()
  263.     {
  264.         $this->_yy_state = array_pop($this->_yy_stack);
  265.     }
  266.  
  267.     function yybegin($state)
  268.     {
  269.         $this->_yy_state = $state;
  270.     }
  271.  
  272. ');
  273.     if (strlen(B)) {
  274.         fwrite($this->outB);
  275.     }
  276.     foreach (as $rule{
  277.         $this->outputRules($rule['rules']$rule['statename']);
  278.         if ($rule['code']{
  279.             fwrite($this->out$rule['code']);
  280.         }
  281.     }
  282. }
  283. lexfile ::PHPCODE(Bdeclare(Drules(C){
  284.     if (strlen(B)) {
  285.         fwrite($this->outB);
  286.     }
  287.     fwrite($this->out'
  288.     private $_yy_state = 1;
  289.     private $_yy_stack = array();
  290.  
  291.     function yylex()
  292.     {
  293.         return $this->{\'yylex\' . $this->_yy_state}();
  294.     }
  295.  
  296.     function yypushstate($state)
  297.     {
  298.         array_push($this->_yy_stack, $this->_yy_state);
  299.         $this->_yy_state = $state;
  300.     }
  301.  
  302.     function yypopstate()
  303.     {
  304.         $this->_yy_state = array_pop($this->_yy_stack);
  305.     }
  306.  
  307.     function yybegin($state)
  308.     {
  309.         $this->_yy_state = $state;
  310.     }
  311.  
  312. ');
  313.     foreach (as $rule{
  314.         $this->outputRules($rule['rules']$rule['statename']);
  315.         if ($rule['code']{
  316.             fwrite($this->out$rule['code']);
  317.         }
  318.     }
  319. }
  320. lexfile ::PHPCODE(Adeclare(DPHPCODE(Brules(C){
  321.     if (strlen(A)) {
  322.         fwrite($this->outA);
  323.     }
  324.     fwrite($this->out'
  325.     private $_yy_state = 1;
  326.     private $_yy_stack = array();
  327.  
  328.     function yylex()
  329.     {
  330.         return $this->{\'yylex\' . $this->_yy_state}();
  331.     }
  332.  
  333.     function yypushstate($state)
  334.     {
  335.         array_push($this->_yy_stack, $this->_yy_state);
  336.         $this->_yy_state = $state;
  337.     }
  338.  
  339.     function yypopstate()
  340.     {
  341.         $this->_yy_state = array_pop($this->_yy_stack);
  342.     }
  343.  
  344.     function yybegin($state)
  345.     {
  346.         $this->_yy_state = $state;
  347.     }
  348.  
  349. ');
  350.     if (strlen(B)) {
  351.         fwrite($this->outB);
  352.     }
  353.     foreach (as $rule{
  354.         $this->outputRules($rule['rules']$rule['statename']);
  355.         if ($rule['code']{
  356.             fwrite($this->out$rule['code']);
  357.         }
  358.     }
  359. }
  360.  
  361. declare(A::COMMENTSTART declarations(BCOMMENTEND{
  362.     B;
  363.     $this->patterns B['patterns'];
  364. }
  365.  
  366. declarations(A::processing_instructions(Bpattern_declarations(C){
  367.     $expected array(
  368.         'counter' => true,
  369.         'input' => true,
  370.         'token' => true,
  371.         'value' => true,
  372.         'line' => true,
  373.     );
  374.     foreach (as $pi{
  375.         if (isset($expected[$pi['pi']])) {
  376.             unset($expected[$pi['pi']]);
  377.             continue;
  378.         }
  379.         if (count($expected)) {
  380.             throw new Exception('Processing Instructions "' .
  381.                 implode(', 'array_keys($expected)) '" must be defined');
  382.         }
  383.     }
  384.     $expected array(
  385.         'counter' => true,
  386.         'input' => true,
  387.         'token' => true,
  388.         'value' => true,
  389.         'line' => true,
  390.     );
  391.     foreach (as $pi{
  392.         if (isset($expected[$pi['pi']])) {
  393.             $this->{$pi['pi']$pi['definition'];
  394.             continue;
  395.         }
  396.         $this->error('Unknown processing instruction %' $pi['pi'.
  397.             ', should be one of "' implode(', 'array_keys($expected)) '"');
  398.     }
  399.     array('patterns' => C'pis' => B);
  400. }
  401.  
  402. processing_instructions(A::PI(BSUBPATTERN(C){
  403.     array(array('pi' => B'definition' => C));
  404. }
  405. processing_instructions(A::PI(BCODE(C){
  406.     array(array('pi' => B'definition' => C));
  407. }
  408. processing_instructions(A::processing_instructions(PPI(BSUBPATTERN(C){
  409.     P;
  410.     A[array('pi' => B'definition' => C);
  411. }
  412. processing_instructions(A::processing_instructions(PPI(BCODE(C){
  413.     P;
  414.     A[array('pi' => B'definition' => C);
  415. }
  416.  
  417. pattern_declarations(A::PATTERN(Bsubpattern(C){
  418.     array(=> C);
  419. }
  420. pattern_declarations(A::pattern_declarations(BPATTERN(Csubpattern(D){
  421.     B;
  422.     if (isset(A[C])) {
  423.         throw new Exception('Pattern "' '" is already defined as "' .
  424.             A[C'", cannot redefine as "' '"');
  425.     }
  426.     A[CD;
  427. }
  428.  
  429. rules(A::COMMENTSTART rule(BCOMMENTEND{
  430.     array(array('rules' => B'code' => '''statename' => ''));
  431. }
  432. rules(A::COMMENTSTART PI(PSUBPATTERN(Srule(BCOMMENTEND{
  433.     if (!= 'statename'{
  434.         throw new Exception('Error: only %statename processing instruction ' .
  435.             'is allowed in rule sections');
  436.     }
  437.     array(array('rules' => B'code' => '''statename' => S));
  438. }
  439. rules(A::COMMENTSTART rule(BCOMMENTEND PHPCODE(C){
  440.     array(array('rules' => B'code' => C'statename' => ''));
  441. }
  442. rules(A::COMMENTSTART PI(PSUBPATTERN(Srule(BCOMMENTEND PHPCODE(C){
  443.     if (!= 'statename'{
  444.         throw new Exception('Error: only %statename processing instruction ' .
  445.             'is allowed in rule sections');
  446.     }
  447.     array(array('rules' => B'code' => C'statename' => S));
  448. }
  449. rules(A::rules(RCOMMENTSTART rule(BCOMMENTEND{
  450.     R;
  451.     A[array('rules' => B'code' => '''statename' => '');
  452. }
  453. rules(A::rules(RPI(PSUBPATTERN(SCOMMENTSTART rule(BCOMMENTEND{
  454.     if (!= 'statename'{
  455.         throw new Exception('Error: only %statename processing instruction ' .
  456.             'is allowed in rule sections');
  457.     }
  458.     R;
  459.     A[array('rules' => B'code' => '''statename' => S);
  460. }
  461. rules(A::rules(RCOMMENTSTART rule(BCOMMENTEND PHPCODE(C){
  462.     R;
  463.     A[array('rules' => B'code' => C'statename' => '');
  464. }
  465. rules(A::rules(RCOMMENTSTART PI(PSUBPATTERN(Srule(BCOMMENTEND PHPCODE(C){
  466.     if (!= 'statename'{
  467.         throw new Exception('Error: only %statename processing instruction ' .
  468.             'is allowed in rule sections');
  469.     }
  470.     R;
  471.     A[array('rules' => B'code' => C'statename' => S);
  472. }
  473.  
  474. rule(A::rule_subpattern(BCODE(C){
  475.     array(array('pattern' => B'code' => C));
  476. }
  477. rule(A::rule(Rrule_subpattern(BCODE(C).{
  478.     R;
  479.     A[array('pattern' => B'code' => C);
  480. }
  481.  
  482. rule_subpattern(A::QUOTE(B){
  483.     str_replace(array('\\''"')array('\\\\''\\"')preg_quote(B'/'));
  484. }
  485. rule_subpattern(A::SUBPATTERN(B){
  486.     if (!isset($this->patterns[B])) {
  487.         $this->error('Undefined pattern "' '" used in rules');
  488.         throw new Exception('Undefined pattern "' '" used in rules');
  489.     }
  490.     $this->patterns[B];
  491. }
  492. rule_subpattern(A::rule_subpattern(BQUOTE(C){
  493.     str_replace(array('\\''"')array('\\\\''\\"')preg_quote(C'/'));
  494. }
  495. rule_subpattern(A::rule_subpattern(BSUBPATTERN(C){
  496.     if (!isset($this->patterns[C])) {
  497.         $this->error('Undefined pattern "' '" used in rules');
  498.         throw new Exception('Undefined pattern "' '" used in rules');
  499.     }
  500.     $this->patterns[C];
  501. }
  502.  
  503. subpattern(A::QUOTE(B){
  504.     str_replace(array('\\''"')array('\\\\''\\"')preg_quote(B'/'));
  505. }
  506. subpattern(A::SUBPATTERN(B){
  507.     str_replace(array('/''\\''"')array('\\/''\\\\''\"')B);
  508.     preg_replace('/\\\\([0-7]{1,3})/''\\\1'A);
  509.     preg_replace('/\\\\(x[0-9A-Fa-f]{1,2})/''\\x\1'A);
  510.     str_replace(array('\\\\t''\\\\n''\\\\r')array('\\t''\\n''\\r')A);
  511.     $this->_validatePattern(A);
  512. }
  513. subpattern(A::subpattern(BQUOTE(C){
  514.     str_replace(array('\\''"')array('\\\\''\\"')preg_quote(C'/'));
  515. }
  516. subpattern(A::subpattern(BSUBPATTERN(C){
  517.     str_replace(array('/''\\''"')array('\\/''\\\\''\\"')C);
  518.     preg_replace('/\\\\([0-7]{1,3})/''\\\1'A);
  519.     preg_replace('/\\\\(x[0-9A-Fa-f]{1,2})/''\\x\1'A);
  520.     str_replace(array('\\\\t''\\\\n''\\\\r')array('\\t''\\n''\\r')A);
  521.     A;
  522.     $this->_validatePattern(A);
  523. }

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