util/qlalr/lalr.g
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 -----------------------------------------------------------------------------
       
     2 --
       
     3 -- Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 -- All rights reserved.
       
     5 -- Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 --
       
     7 -- This file is part of the QLALR project on Qt Labs.
       
     8 --
       
     9 -- $QT_BEGIN_LICENSE:LGPL$
       
    10 -- No Commercial Usage
       
    11 -- This file contains pre-release code and may not be distributed.
       
    12 -- You may use this file in accordance with the terms and conditions
       
    13 -- contained in the Technology Preview License Agreement accompanying
       
    14 -- this package.
       
    15 --
       
    16 -- GNU Lesser General Public License Usage
       
    17 -- Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 -- General Public License version 2.1 as published by the Free Software
       
    19 -- Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 -- packaging of this file.  Please review the following information to
       
    21 -- ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 -- will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 --
       
    24 -- In addition, as a special exception, Nokia gives you certain additional
       
    25 -- rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 -- version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 --
       
    28 -- If you have questions regarding the use of this file, please contact
       
    29 -- Nokia at qt-info@nokia.com.
       
    30 --
       
    31 --
       
    32 --
       
    33 --
       
    34 --
       
    35 --
       
    36 --
       
    37 --
       
    38 -- $QT_END_LICENSE$
       
    39 --
       
    40 -----------------------------------------------------------------------------
       
    41 
       
    42 
       
    43 %parser grammar
       
    44 
       
    45 %decl recognizer.h
       
    46 %impl recognizer.cpp
       
    47 
       
    48 %token ID "identifier"
       
    49 %token STRING_LITERAL "string literal"
       
    50 
       
    51 %token DECL_FILE "%decl"
       
    52 %token EXPECT "%expect"
       
    53 %token EXPECT_RR "%expect-lr"
       
    54 %token IMPL_FILE "%impl"
       
    55 %token LEFT "%left"
       
    56 %token MERGED_OUTPUT "%merged_output"
       
    57 %token NONASSOC "%nonassoc"
       
    58 %token PARSER "%parser"
       
    59 %token PREC "%prec"
       
    60 %token RIGHT "%right"
       
    61 %token START "%start"
       
    62 %token TOKEN "%token"
       
    63 %token TOKEN_PREFIX "%token_prefix"
       
    64 
       
    65 %token COLON ":"
       
    66 %token OR "|"
       
    67 %token SEMICOLON ";"
       
    68 
       
    69 %token DECL
       
    70 %token IMPL
       
    71 
       
    72 %token ERROR
       
    73 
       
    74 %start Specification
       
    75 
       
    76 
       
    77 /:
       
    78 /****************************************************************************
       
    79 **
       
    80 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
    81 ** All rights reserved.
       
    82 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
    83 **
       
    84 ** This file is part of the QLALR project on Qt Labs.
       
    85 **
       
    86 ** $QT_BEGIN_LICENSE:LGPL$
       
    87 ** No Commercial Usage
       
    88 ** This file contains pre-release code and may not be distributed.
       
    89 ** You may use this file in accordance with the terms and conditions
       
    90 ** contained in the Technology Preview License Agreement accompanying
       
    91 ** this package.
       
    92 **
       
    93 ** GNU Lesser General Public License Usage
       
    94 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    95 ** General Public License version 2.1 as published by the Free Software
       
    96 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    97 ** packaging of this file.  Please review the following information to
       
    98 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    99 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
   100 **
       
   101 ** In addition, as a special exception, Nokia gives you certain additional
       
   102 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
   103 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
   104 **
       
   105 ** If you have questions regarding the use of this file, please contact
       
   106 ** Nokia at qt-info@nokia.com.
       
   107 **
       
   108 **
       
   109 **
       
   110 **
       
   111 **
       
   112 **
       
   113 **
       
   114 **
       
   115 ** $QT_END_LICENSE$
       
   116 **
       
   117 ****************************************************************************/
       
   118 
       
   119 #include <QtCore/QtDebug>
       
   120 #include <QtCore/QString>
       
   121 #include <QtCore/QFile>
       
   122 #include <QtCore/QTextStream>
       
   123 
       
   124 #include "$header"
       
   125 #include "lalr.h"
       
   126 
       
   127 #include <cstdlib>
       
   128 
       
   129 class Recognizer: protected $table
       
   130 {
       
   131 public:
       
   132   Recognizer (Grammar *grammar, bool no_lines);
       
   133   ~Recognizer();
       
   134 
       
   135   bool parse (const QString &input_file = QString ());
       
   136 
       
   137   inline QString decls () const { return _M_decls; }
       
   138   inline QString impls () const { return _M_impls; }
       
   139 
       
   140 protected:
       
   141   inline void reallocateStack ();
       
   142 
       
   143   inline QString &sym (int index)
       
   144   { return sym_stack [tos + index - 1]; }
       
   145 
       
   146 protected: // scanner
       
   147   int nextToken();
       
   148 
       
   149   inline void inp ()
       
   150   {
       
   151     if (_M_currentChar != _M_lastChar)
       
   152       {
       
   153 	ch = *_M_currentChar++;
       
   154 	
       
   155 	if (ch == QLatin1Char('\n'))
       
   156 	  ++_M_line;
       
   157       }
       
   158     else
       
   159       ch = QChar();
       
   160   }
       
   161 
       
   162   QString expand (const QString &text) const;
       
   163 
       
   164 protected:
       
   165   // recognizer
       
   166   int tos;
       
   167   int stack_size;
       
   168   QVector<QString> sym_stack;
       
   169   int *state_stack;
       
   170 
       
   171   QString _M_contents;
       
   172   QString::const_iterator _M_firstChar;
       
   173   QString::const_iterator _M_lastChar;
       
   174   QString::const_iterator _M_currentChar;
       
   175 
       
   176   // scanner
       
   177   QChar ch;
       
   178   int _M_line;
       
   179   int _M_action_line;
       
   180   Grammar *_M_grammar;
       
   181   RulePointer _M_current_rule;
       
   182   QString _M_input_file;
       
   183 
       
   184   QString _M_decls;
       
   185   QString _M_impls;
       
   186   QString _M_current_value;
       
   187   bool _M_no_lines;
       
   188 };
       
   189 :/
       
   190 
       
   191 /.
       
   192 /****************************************************************************
       
   193 **
       
   194 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
   195 ** All rights reserved.
       
   196 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
   197 **
       
   198 ** This file is part of the QLALR project on Qt Labs.
       
   199 **
       
   200 ** $QT_BEGIN_LICENSE:LGPL$
       
   201 ** No Commercial Usage
       
   202 ** This file contains pre-release code and may not be distributed.
       
   203 ** You may use this file in accordance with the terms and conditions
       
   204 ** contained in the Technology Preview License Agreement accompanying
       
   205 ** this package.
       
   206 **
       
   207 ** GNU Lesser General Public License Usage
       
   208 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
   209 ** General Public License version 2.1 as published by the Free Software
       
   210 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
   211 ** packaging of this file.  Please review the following information to
       
   212 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
   213 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
   214 **
       
   215 ** In addition, as a special exception, Nokia gives you certain additional
       
   216 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
   217 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
   218 **
       
   219 ** If you have questions regarding the use of this file, please contact
       
   220 ** Nokia at qt-info@nokia.com.
       
   221 **
       
   222 **
       
   223 **
       
   224 **
       
   225 **
       
   226 **
       
   227 **
       
   228 **
       
   229 ** $QT_END_LICENSE$
       
   230 **
       
   231 ****************************************************************************/
       
   232 
       
   233 #include "recognizer.h"
       
   234 #include <cstdlib>
       
   235 #include <cstring>
       
   236 #include <cctype>
       
   237 
       
   238 Recognizer::Recognizer (Grammar *grammar, bool no_lines):
       
   239   tos(0),
       
   240   stack_size(0),
       
   241   state_stack(0),
       
   242   _M_line(1),
       
   243   _M_action_line(0),
       
   244   _M_grammar(grammar),
       
   245   _M_no_lines(no_lines)
       
   246 {
       
   247 }
       
   248 
       
   249 Recognizer::~Recognizer()
       
   250 {
       
   251   if (stack_size)
       
   252     ::qFree(state_stack);
       
   253 }
       
   254 
       
   255 inline void Recognizer::reallocateStack()
       
   256 {
       
   257   if (! stack_size)
       
   258     stack_size = 128;
       
   259   else
       
   260     stack_size <<= 1;
       
   261 
       
   262   sym_stack.resize (stack_size);
       
   263 
       
   264   if (! state_stack)
       
   265     state_stack = reinterpret_cast<int*> (::qMalloc(stack_size * sizeof(int)));
       
   266   else
       
   267     state_stack = reinterpret_cast<int*> (::qRealloc(state_stack, stack_size * sizeof(int)));
       
   268 }
       
   269 
       
   270 int Recognizer::nextToken()
       
   271 {
       
   272   QString text;
       
   273 
       
   274  Lagain:
       
   275   while (ch.isSpace ())
       
   276     inp ();
       
   277   
       
   278   if (ch.isNull ())
       
   279     return EOF_SYMBOL;
       
   280 
       
   281   int token = ch.unicode ();
       
   282 
       
   283   if (token == '"')
       
   284     {
       
   285       inp(); // skip "
       
   286       text.clear ();
       
   287       while (! ch.isNull () && ch != QLatin1Char ('"'))
       
   288         {
       
   289           if (ch == QLatin1Char ('\\'))
       
   290             {
       
   291               text += ch;
       
   292               inp();
       
   293             }
       
   294           text += ch;
       
   295           inp ();
       
   296         }
       
   297 
       
   298       if (ch == QLatin1Char ('"'))
       
   299         inp ();
       
   300       else
       
   301         qerr << _M_input_file << ":" << _M_line << ": Warning. Expected `\"'" << endl;
       
   302 
       
   303       _M_current_value = text;
       
   304       return (token = STRING_LITERAL);
       
   305     }
       
   306 
       
   307   else if (ch.isLetterOrNumber () || ch == QLatin1Char ('_'))
       
   308     {
       
   309       text.clear ();
       
   310       do { text += ch; inp (); }
       
   311       while (ch.isLetterOrNumber () || ch == QLatin1Char ('_') || ch == QLatin1Char ('.'));
       
   312       _M_current_value = text;
       
   313       return (token = ID);
       
   314     }
       
   315 
       
   316   else if (token == '%')
       
   317     {
       
   318       text.clear ();
       
   319 
       
   320       do { inp (); }
       
   321       while (ch.isSpace ());
       
   322 
       
   323       do { text += ch; inp (); }
       
   324       while (ch.isLetterOrNumber () || ch == QLatin1Char ('_') || ch == QLatin1Char ('-'));
       
   325 
       
   326       if (text == QLatin1String("token_prefix"))
       
   327         return (token = TOKEN_PREFIX);
       
   328       else if (text == QLatin1String("merged_output"))
       
   329         return (token = MERGED_OUTPUT);
       
   330       else if (text == QLatin1String("token"))
       
   331         return (token = TOKEN);
       
   332       else if (text == QLatin1String("start"))
       
   333         return (token = START);
       
   334       else if (text == QLatin1String("parser"))
       
   335         return (token = PARSER);
       
   336       else if (text == QLatin1String("decl"))
       
   337         return (token = DECL_FILE);
       
   338       else if (text == QLatin1String("impl"))
       
   339         return (token = IMPL_FILE);
       
   340       else if (text == QLatin1String("expect"))
       
   341         return (token = EXPECT);
       
   342       else if (text == QLatin1String("expect-rr"))
       
   343         return (token = EXPECT_RR);
       
   344       else if (text == QLatin1String("left"))
       
   345         return (token = LEFT);
       
   346       else if (text == QLatin1String("right"))
       
   347         return (token = RIGHT);
       
   348       else if (text == QLatin1String("nonassoc"))
       
   349         return (token = NONASSOC);
       
   350       else if (text == QLatin1String("prec"))
       
   351         return (token = PREC);
       
   352       else
       
   353         {
       
   354           qerr << _M_input_file << ":" << _M_line << ": Unknown keyword `" << text << "'" << endl;
       
   355           exit (EXIT_FAILURE);
       
   356           return (token = ERROR);
       
   357         }
       
   358     }
       
   359 
       
   360   inp ();
       
   361 
       
   362   if (token == '-' && ch == QLatin1Char ('-'))
       
   363     {
       
   364       do { inp (); }
       
   365       while (! ch.isNull () && ch != QLatin1Char ('\n'));
       
   366       goto Lagain;
       
   367     }
       
   368 
       
   369   else if (token == ':' && ch == QLatin1Char (':'))
       
   370     {
       
   371       inp ();
       
   372       if (ch != QLatin1Char ('='))
       
   373         return (token = ERROR);
       
   374       inp ();
       
   375       return (token = COLON);
       
   376     }
       
   377 
       
   378   else if (token == '/' && ch == QLatin1Char (':'))
       
   379     {
       
   380       _M_action_line = _M_line;
       
   381 
       
   382       text.clear ();
       
   383       if (! _M_no_lines)
       
   384         text += QLatin1String ("\n#line ") + QString::number (_M_action_line) + " \"" + _M_input_file + "\"\n";
       
   385       inp (); // skip ':'
       
   386 
       
   387       forever
       
   388         {
       
   389           while (! ch.isNull ())
       
   390             {
       
   391               token = ch.unicode ();
       
   392               inp ();
       
   393 
       
   394               if (token == ':' && ch == QLatin1Char ('/'))
       
   395                 break;
       
   396 
       
   397               text += QLatin1Char (token);
       
   398             }
       
   399 
       
   400           if (ch != QLatin1Char ('/'))
       
   401             return (token = ERROR);
       
   402 
       
   403           inp ();
       
   404 
       
   405           if (ch.isNull () || ch.isSpace ())
       
   406             {
       
   407               _M_current_value = text;
       
   408               return (token = DECL);
       
   409             }
       
   410 	  else
       
   411 	    text += QLatin1String (":/");
       
   412         }
       
   413     }
       
   414 
       
   415   else if (token == '/' && ch == QLatin1Char ('.'))
       
   416     {
       
   417       _M_action_line = _M_line;
       
   418 
       
   419       text.clear ();
       
   420       if (! _M_no_lines)
       
   421         text += QLatin1String ("\n#line ") + QString::number (_M_action_line) + " \"" + _M_input_file + "\"\n";
       
   422 
       
   423       inp (); // skip ':'
       
   424 
       
   425       forever
       
   426         {
       
   427           while (! ch.isNull ())
       
   428             {
       
   429               token = ch.unicode ();
       
   430               inp ();
       
   431 
       
   432               if (token == '.' && ch == QLatin1Char ('/'))
       
   433                 break;
       
   434 
       
   435               text += QLatin1Char (token);
       
   436             }
       
   437 
       
   438           if (ch != QLatin1Char ('/'))
       
   439             return (token = ERROR);
       
   440 
       
   441           inp ();
       
   442 
       
   443           if (ch.isNull () || ch.isSpace ())
       
   444             {
       
   445               _M_current_value = text;
       
   446               return (token = IMPL);
       
   447             }
       
   448 	  else
       
   449 	    text += QLatin1String ("./");
       
   450         }
       
   451     }
       
   452 
       
   453   switch (token) {
       
   454   case ':':
       
   455     return (token = COLON);
       
   456 
       
   457   case ';':
       
   458     return (token = SEMICOLON);
       
   459 
       
   460   case '|':
       
   461     return (token = OR);
       
   462 
       
   463   default:
       
   464     break;
       
   465   }
       
   466 
       
   467   return token;
       
   468 }
       
   469 
       
   470 bool Recognizer::parse (const QString &input_file)
       
   471 {
       
   472   _M_input_file = input_file;
       
   473 
       
   474   QFile file(_M_input_file);
       
   475   if (! file.open(QFile::ReadOnly))
       
   476     {
       
   477       qerr << "qlalr: no input file\n";
       
   478       return false;
       
   479     }
       
   480 
       
   481   QString _M_contents = QTextStream(&file).readAll();
       
   482   _M_firstChar = _M_contents.constBegin();
       
   483   _M_lastChar = _M_contents.constEnd();
       
   484   _M_currentChar = _M_firstChar;
       
   485   _M_line = 1;
       
   486 
       
   487   int yytoken = -1;
       
   488   inp ();
       
   489 
       
   490   reallocateStack();
       
   491 
       
   492   _M_current_rule = _M_grammar->rules.end ();
       
   493   _M_decls.clear ();
       
   494   _M_impls.clear ();
       
   495 
       
   496   tos = 0;
       
   497   state_stack[++tos] = 0;
       
   498 
       
   499   while (true)
       
   500     {
       
   501       if (yytoken == -1 && - TERMINAL_COUNT != action_index [state_stack [tos]])
       
   502         yytoken = nextToken();
       
   503 
       
   504       int act = t_action (state_stack [tos], yytoken);
       
   505 
       
   506       if (act == ACCEPT_STATE)
       
   507         return true;
       
   508 
       
   509       else if (act > 0)
       
   510         {
       
   511           if (++tos == stack_size)
       
   512             reallocateStack();
       
   513 
       
   514           sym_stack [tos] = _M_current_value;
       
   515           state_stack [tos] = act;
       
   516           yytoken = -1;
       
   517         }
       
   518 
       
   519       else if (act < 0)
       
   520         {
       
   521           int r = - act - 1;
       
   522 
       
   523           tos -= rhs [r];
       
   524           act = state_stack [tos++];
       
   525 
       
   526           switch (r) {
       
   527 ./
       
   528 
       
   529 ----------------------------------------------------------- SPECS
       
   530 Specification ::= Options Tokens Start Rules ;
       
   531 
       
   532 Options ::= Empty ;
       
   533 Options ::= Options Option ;
       
   534 
       
   535 StartHeader ::= START ID ;
       
   536 /.
       
   537 case $rule_number: {
       
   538   Name name = _M_grammar->intern (sym(2));
       
   539   _M_grammar->start = name;
       
   540   _M_grammar->non_terminals.insert (name);
       
   541 } break;
       
   542 ./
       
   543 
       
   544 Start ::= StartHeader UserActionOpt ;
       
   545 
       
   546 ----------------------------------------------------------- OPTIONS
       
   547 Option ::= PARSER ID ;
       
   548 /.
       
   549 case $rule_number: {
       
   550   _M_grammar->table_name = sym(2);
       
   551 } break;
       
   552 ./
       
   553 
       
   554 Option ::= MERGED_OUTPUT ID ;
       
   555 /.
       
   556 case $rule_number: {
       
   557   _M_grammar->merged_output = sym(2);
       
   558 } break;
       
   559 ./
       
   560 
       
   561 Option ::= DECL_FILE ID ;
       
   562 /.
       
   563 case $rule_number: {
       
   564    _M_grammar->decl_file_name = sym(2);
       
   565 } break;
       
   566 ./
       
   567 
       
   568 
       
   569 Option ::= IMPL_FILE ID ;
       
   570 /.
       
   571 case $rule_number: {
       
   572    _M_grammar->impl_file_name = sym(2);
       
   573 } break;
       
   574 ./
       
   575 
       
   576 Option ::= EXPECT ID ;
       
   577 /.
       
   578 case $rule_number: {
       
   579    _M_grammar->expected_shift_reduce = sym(2).toInt();
       
   580 } break;
       
   581 ./
       
   582 
       
   583 Option ::= EXPECT_RR ID ;
       
   584 /.
       
   585 case $rule_number: {
       
   586    _M_grammar->expected_reduce_reduce = sym(2).toInt();
       
   587 } break;
       
   588 ./
       
   589 
       
   590 
       
   591 Option ::= TOKEN_PREFIX ID ;
       
   592 /.
       
   593 case $rule_number: {
       
   594   _M_grammar->token_prefix = sym(2);
       
   595 } break;
       
   596 ./
       
   597 
       
   598 
       
   599 ----------------------------------------------------------- TOKENS
       
   600 Tokens ::= Empty ;
       
   601 Tokens ::= Tokens Token ;
       
   602 
       
   603 Token ::= TOKEN TerminalList ;
       
   604 
       
   605 TerminalList ::= Terminal ;
       
   606 
       
   607 TerminalList ::= TerminalList Terminal ;
       
   608 
       
   609 Terminal ::= ID Empty ;
       
   610 /.case $rule_number:./
       
   611 
       
   612 Terminal ::= ID STRING_LITERAL ;
       
   613 /.case $rule_number: {
       
   614   Name name = _M_grammar->intern (sym(1));
       
   615   _M_grammar->terminals.insert (name);
       
   616   _M_grammar->spells.insert (name, sym(2));
       
   617 } break;
       
   618 ./
       
   619 
       
   620 PrecHeader: LEFT ;
       
   621 /.
       
   622 case $rule_number: {
       
   623   _M_grammar->current_assoc = Grammar::Left;
       
   624   ++_M_grammar->current_prec;
       
   625 } break;
       
   626 ./
       
   627 
       
   628 PrecHeader: RIGHT ;
       
   629 /.
       
   630 case $rule_number: {
       
   631   _M_grammar->current_assoc = Grammar::Right;
       
   632   ++_M_grammar->current_prec;
       
   633 } break;
       
   634 ./
       
   635 
       
   636 PrecHeader: NONASSOC ;
       
   637 /.
       
   638 case $rule_number: {
       
   639   _M_grammar->current_assoc = Grammar::NonAssoc;
       
   640   ++_M_grammar->current_prec;
       
   641 } break;
       
   642 ./
       
   643 
       
   644 Token ::= PrecHeader TokenList ;
       
   645 
       
   646 TokenList ::= TokenId ;
       
   647 TokenList ::= TokenList TokenId ;
       
   648 
       
   649 TokenId ::= ID ;
       
   650 /.
       
   651 case $rule_number: {
       
   652   Name name = _M_grammar->intern (sym(1));
       
   653   _M_grammar->terminals.insert (name);
       
   654 
       
   655   Grammar::TokenInfo info;
       
   656   info.prec = _M_grammar->current_prec;
       
   657   info.assoc = _M_grammar->current_assoc;
       
   658   _M_grammar->token_info.insert (name, info);
       
   659 } break;
       
   660 ./
       
   661 
       
   662 ----------------------------------------------------------- Code
       
   663 Code ::= DECL ;
       
   664 /.
       
   665 case $rule_number: {
       
   666   _M_decls += expand (sym(1));
       
   667 } break;
       
   668 ./
       
   669 
       
   670 
       
   671 Code ::= IMPL ;
       
   672 /.
       
   673 case $rule_number: {
       
   674   _M_impls += expand (sym(1));
       
   675 } break;
       
   676 ./
       
   677 
       
   678 UserAction ::= Code ;
       
   679 UserAction ::= UserAction Code ;
       
   680 
       
   681 UserActionOpt ::= ;
       
   682 UserActionOpt ::= UserAction ;
       
   683 
       
   684 ----------------------------------------------------------- RULES
       
   685 Rules ::= Empty ;
       
   686 Rules ::= Rules Rule ;
       
   687 
       
   688 RuleHeader ::= ID COLON ;
       
   689 /.
       
   690 case $rule_number: {
       
   691   _M_current_rule = _M_grammar->rules.insert (_M_grammar->rules.end (), Rule ());
       
   692   _M_current_rule->lhs = _M_grammar->intern (sym(1));
       
   693   _M_grammar->declared_lhs.insert (_M_current_rule->lhs);
       
   694 
       
   695   if (_M_grammar->terminals.find (_M_current_rule->lhs) != _M_grammar->terminals.end ())
       
   696     {
       
   697       qerr << _M_input_file << ":" << _M_line << ": Invalid non terminal `" << *_M_current_rule->lhs << "'" << endl;
       
   698       return false;
       
   699     }
       
   700 
       
   701   _M_grammar->non_terminals.insert (_M_current_rule->lhs);
       
   702 } break;
       
   703 ./
       
   704 
       
   705 
       
   706 Rule ::= RuleHeader RuleDefinition SEMICOLON UserActionOpt ;
       
   707 
       
   708 RuleDefinition ::= Symbols PrecOpt UserActionOpt ;
       
   709 RuleDefinition ::= RuleDefinition NewRule OR Symbols PrecOpt UserActionOpt ;
       
   710 
       
   711 NewRule ::= ;
       
   712 /.
       
   713 case $rule_number: {
       
   714   Name lhs = _M_current_rule->lhs;
       
   715   _M_current_rule = _M_grammar->rules.insert (_M_grammar->rules.end (), Rule ());
       
   716   _M_current_rule->lhs = lhs;
       
   717   _M_grammar->declared_lhs.insert (_M_current_rule->lhs);
       
   718 
       
   719   if (_M_grammar->terminals.find (_M_current_rule->lhs) != _M_grammar->terminals.end ())
       
   720     {
       
   721       qerr << _M_input_file << ":" << _M_line << ": Invalid non terminal `" << *_M_current_rule->lhs << "'" << endl;
       
   722       return false;
       
   723     }
       
   724 
       
   725   _M_grammar->non_terminals.insert (_M_current_rule->lhs);
       
   726 } break;
       
   727 ./
       
   728 
       
   729 PrecOpt ::= ;
       
   730 /.
       
   731 case $rule_number: {
       
   732   _M_current_rule->prec = _M_grammar->names.end ();
       
   733 
       
   734   for (NameList::iterator it = _M_current_rule->rhs.begin (); it != _M_current_rule->rhs.end (); ++it)
       
   735     {
       
   736       if (! _M_grammar->isTerminal (*it))
       
   737         continue;
       
   738 
       
   739       _M_current_rule->prec = *it;
       
   740     }
       
   741 } break;
       
   742 ./
       
   743 
       
   744 PrecOpt ::= PREC ID ;
       
   745 /.
       
   746 case $rule_number: {
       
   747   Name tok = _M_grammar->intern (sym(2));
       
   748   if (! _M_grammar->isTerminal (tok))
       
   749     {
       
   750       qerr << _M_input_file << ":" << _M_line << ": `" << *tok << " is not a terminal symbol" << endl;
       
   751       _M_current_rule->prec = _M_grammar->names.end ();
       
   752     }
       
   753   else
       
   754     _M_current_rule->prec = tok;
       
   755 } break;
       
   756 ./
       
   757 
       
   758 ----------------------------------------------------------- SYMBOLS
       
   759 Symbols ::= Empty ;
       
   760 Symbols ::= Symbols ID ;
       
   761 /.
       
   762 case $rule_number: {
       
   763   Name name = _M_grammar->intern (sym(2));
       
   764 
       
   765   if (_M_grammar->terminals.find (name) == _M_grammar->terminals.end ())
       
   766     _M_grammar->non_terminals.insert (name);
       
   767 
       
   768   _M_current_rule->rhs.push_back (name);
       
   769 } break;
       
   770 ./
       
   771 
       
   772 ----------------------------------------------------------- HELPERS
       
   773 Empty ::= ;
       
   774 /.
       
   775 case $rule_number: {
       
   776   sym(1) = QString();
       
   777 } break;
       
   778 ./
       
   779 
       
   780 
       
   781 
       
   782 
       
   783 ----------------------------------------------------------- END
       
   784 /.
       
   785           } // switch
       
   786 
       
   787           state_stack [tos] = nt_action (act, lhs [r] - TERMINAL_COUNT);
       
   788         }
       
   789       
       
   790       else
       
   791         {
       
   792           break;
       
   793         }
       
   794     }
       
   795 
       
   796   qerr << _M_input_file << ":" << _M_line << ": Syntax error" << endl;
       
   797   return false;
       
   798 }
       
   799 
       
   800 ./