parser.cup 35.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90

/*================================================================*/ 
/* 
  JavaCup Specification for the JavaCup Specification Language
  by Scott Hudson, GVU Center, Georgia Tech, August 1995
  and Frank Flannery, Department of Computer Science, Princeton Univ,
  July 1996
  Bug Fixes: C. Scott Ananian, Dept of Electrical Engineering, Princeton
  University, October 1996. [later Massachusetts Institute of Technology]


  This JavaCup specification is used to implement JavaCup itself.
  It specifies the parser for the JavaCup specification language.
  (It also serves as a reasonable example of what a typical JavaCup
  spec looks like).

  The specification has the following parts:
    Package and import declarations
      These serve the same purpose as in a normal Java source file
      (and will appear in the generated code for the parser). In this 
      case we are part of the java_cup package and we import both the
      java_cup runtime system and Hashtable from the standard Java
      utilities package.

    Action code
      This section provides code that is included with the class encapsulating
      the various pieces of user code embedded in the grammar (i.e., the
      semantic actions).  This provides a series of helper routines and
      data structures that the semantic actions use.

    Parser code    
      This section provides code included in the parser class itself.  In
      this case we override the default error reporting routines.

    Init with and scan with 
      These sections provide small bits of code that initialize, then 
      indicate how to invoke the scanner.

    Symbols and grammar
      These sections declare all the terminal and non terminal symbols 
      and the types of objects that they will be represented by at runtime,
      then indicate the start symbol of the grammar (), and finally provide
      the grammar itself (with embedded actions).

    Operation of the parser
      The parser acts primarily by accumulating data structures representing
      various parts of the specification.  Various small parts (e.g., single
      code strings) are stored as static variables of the emit class and
      in a few cases as variables declared in the action code section.  
      Terminals, non terminals, and productions, are maintained as collection
      accessible via static methods of those classes.  In addition, two 
      symbol tables are kept:  
	symbols   maintains the name to object mapping for all symbols
	non_terms maintains a separate mapping containing only the non terms

      Several intermediate working structures are also declared in the action
      code section.  These include: rhs_parts, rhs_pos, and lhs_nt which
      build up parts of the current production while it is being parsed.
	
  Author(s)
    Scott Hudson, GVU Center, Georgia Tech.
    Frank Flannery, Department of Computer Science, Princeton Univ.
    C. Scott Ananian, Department of Electrical Engineering, Princeton Univ.

  Revisions
    v0.9a   First released version                     [SEH] 8/29/95
    v0.9b   Updated for beta language (throws clauses) [SEH] 11/25/95
    v0.10a  Made many improvements/changes. now offers:
              return value
              left/right positions and propagations
              cleaner label references
              precedence and associativity for terminals
              contextual precedence for productions
              [FF] 7/3/96
    v0.10b  Fixed %prec directive so it works like it's supposed to.
              [CSA] 10/10/96
    v0.10g   Added support for array types on symbols.
              [CSA] 03/23/98
    v0.10i  Broaden set of IDs allowed in multipart_id and label_id so
            that only java reserved words (and not CUP reserved words like
            'parser' and 'start') are prohibited.  Allow reordering of
	    action code, parser code, init code, and scan with sections,
	    and made closing semicolon optional for these sections.
	    Added 'nonterminal' as a terminal symbol, finally fixing a
	    spelling mistake that's been around since the beginning.
	    For backwards compatibility, you can still misspell the
	    word if you like.
*/
/*================================================================*/ 

Johannes Roith's avatar
Johannes Roith committed
91
package de.in.tum.www2.cup.internal;
92
import java_cup.runtime.*;
Johannes Roith's avatar
Johannes Roith committed
93
import java_cup.runtime.ComplexSymbolFactory.ComplexSymbol;
94 95
import java.util.Hashtable;
import java.util.Stack;
Johannes Roith's avatar
Johannes Roith committed
96
import java.util.List;
97
import java.util.LinkedList;
Johannes Roith's avatar
Johannes Roith committed
98 99 100 101 102
import java.util.ArrayList;
import de.in.tum.www2.cup.*;
import de.in.tum.www2.cup.ast.*;


103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
/*----------------------------------------------------------------*/ 

action code {:
  /** helper routine to clone a new production part adding a given label */
  protected production_part add_lab(production_part part, String lab)
    throws internal_error
    {
      /* if there is no label, or this is an action, just return the original */
      if (lab == null || part.is_action()) return part;

      /* otherwise build a new one with the given label attached */
      return new symbol_part(((symbol_part)part).the_symbol(),lab);
    }

  /** max size of right hand side we will support */
  protected final int MAX_RHS = 200;

  /** array for accumulating right hand side parts */
  protected production_part[] rhs_parts = new production_part[MAX_RHS];

  /** where we are currently in building a right hand side */
  protected int rhs_pos = 0;

  /** start a new right hand side */
  protected void new_rhs() {rhs_pos = 0; }

  /** add a new right hand side part */
  protected void add_rhs_part(production_part part) throws java.lang.Exception
    {
      if (rhs_pos >= MAX_RHS)
	throw new Exception("Internal Error: Productions limited to " + 
			     MAX_RHS + " symbols and actions"); 

      rhs_parts[rhs_pos] = part;
      rhs_pos++;
    }

  /** string to build up multiple part names */
  protected String multipart_name = new String();
  protected Stack multipart_names = new Stack();

  /** table of declared symbols -- contains production parts indexed by name */
  protected Hashtable symbols = new Hashtable();

  /** table of just non terminals -- contains non_terminals indexed by name */
  protected Hashtable non_terms = new Hashtable();

  /** declared start non_terminal */
  protected non_terminal start_nt = null;

  /** left hand side non terminal of the current production */
  protected non_terminal lhs_nt;

  /** Current precedence number */
  int _cur_prec = 0;

  /** Current precedence side */
  int _cur_side = assoc.no_prec;

  /** update the precedences we are declaring */
  protected void update_precedence(int p) {
    _cur_side = p;
    _cur_prec++;
  }
  /** add relevant data to terminals */ 
  protected void add_precedence(String term) {
    if (term == null) {
      System.err.println("Unable to add precedence to nonexistent terminal");
    } else {
      symbol_part sp = (symbol_part)symbols.get(term);
      if (sp == null) {
	System.err.println("Could find terminal " + term + " while declaring precedence");
      } else {
Johannes Roith's avatar
Johannes Roith committed
176
	symbol sym = sp.the_symbol();
177 178 179 180 181 182
	if (sym instanceof terminal) 
	  ((terminal)sym).set_precedence(_cur_side, _cur_prec);
	else System.err.println("Precedence declaration: Can't find terminal " + term);
      }
    }
  }
Johannes Roith's avatar
Johannes Roith committed
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

  List<IProductionRightPart> rhs_accumulator = new ArrayList<IProductionRightPart>();
  
  protected void ast_add_rhs_part(IProductionRightPart part)
  {
	rhs_accumulator.add(part);
  }

  protected ArrayList<IProductionRightPart> ast_get_rhs_parts_copy ()
  {
  	return new ArrayList<IProductionRightPart>(rhs_accumulator);
  }
  
  protected void ast_new_rhs()
  {
  	rhs_accumulator.clear();
  }

201
  protected void ast_add_precedence(Object lst, Range range)
Johannes Roith's avatar
Johannes Roith committed
202
  {
203
	Precedence p = new Precedence(_cur_side, (List<Terminal>) lst,  range);
Johannes Roith's avatar
Johannes Roith committed
204 205 206
	parserResult.precedences.add(p);
  }
  
207 208 209 210 211 212 213 214 215 216 217 218
   private boolean WITH_DEBUGGING_SYMBOLS = true;
   private int cur_debug_id = 0;

   public int get_new_debug_id() {
   	return cur_debug_id++;
   }
	public String attach_debug_symbol(int id, String code) {
		if (!WITH_DEBUGGING_SYMBOLS)
			return code;
		return "//@@CUPBDG" + id + "\n" + code;
	}
  
219 220 221 222 223 224 225
:};

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

parser code {:

  private java.io.InputStream input_stream;
Johannes Roith's avatar
Johannes Roith committed
226 227 228 229 230 231 232 233 234 235 236
  private ErrorManager errMan;
  private CupContext context;
  private terminal.terminal_shared terminal_shared;
  private non_terminal.non_terminal_shared non_terminal_shared;
  private ParserResult parserResult;

  public void init(ErrorManager errMan, CupContext context, java.io.InputStream is) {
  	this.errMan = errMan;
  	this.context = context;
  	this.terminal_shared = terminal.getShared(context);
  	this.non_terminal_shared = non_terminal.getShared(context);
237 238 239
  	this.input_stream = is;
  }
  
Johannes Roith's avatar
Johannes Roith committed
240 241 242 243
  public ParserResult getResult() {
  	return parserResult;
  }
  
244
 /* override error routines */
245
 
246
  protected Lexer lexer;
247 248

  @Override
249 250 251 252 253
  public void report_fatal_error(
    String   message,
    Object   info)
    {
      done_parsing();
254
      if (info instanceof java_cup.runtime.Symbol)
255
      	errMan.Fatal(ErrorSource.Parser, message + "\nCan't recover from previous error(s), giving up.",(ComplexSymbol)info);
Johannes Roith's avatar
Johannes Roith committed
256
      else
257
      	errMan.Fatal(ErrorSource.Parser, message + "\nCan't recover from previous error(s), giving up.",(ComplexSymbol)cur_token);
Johannes Roith's avatar
Johannes Roith committed
258
      throw new ParserAbortException();
259 260
    }

261
    @Override
262 263
    public void report_error(String message, Object info)
    {
264
      if (info instanceof java_cup.runtime.Symbol)
265
         errMan.Error(ErrorSource.Parser, message, (ComplexSymbol) info);
266
      else
267
         errMan.Error(ErrorSource.Parser, message, (ComplexSymbol) cur_token);
268
    }
269 270 271 272 273 274 275 276 277 278
    
    @Override
	protected void report_expected_token_ids() {
	  List<Integer> ids = expected_token_ids();
	  LinkedList<String> list = new LinkedList<String>();
	  for (Integer expected : ids)
		  list.add(symbl_name_from_id(expected));
	  String msg = "instead expected token classes are " + list;
	  errMan.Error(ErrorSource.Parser, msg);
	}
279 280 281 282
:};

/*---------------------------------------------------------------- */

Johannes Roith's avatar
Johannes Roith committed
283
init with {:
284 285
    ComplexSymbolFactory f = new ComplexSymbolFactory();
    symbolFactory = f;
Johannes Roith's avatar
Johannes Roith committed
286 287
    parserResult = new ParserResult();
    lexer = new Lexer(errMan, f, this.input_stream);
288 289 290 291 292 293 294 295 296 297 298 299 300 301
:}
/*lexer.init(); :};*/
scan with {: 
    return lexer.next_token(); 
:};

/*----------------------------------------------------------------*/ 

terminal
  PACKAGE, IMPORT, CODE, ACTION, PARSER, TERMINAL, NON, INIT, SCAN, WITH, 
  START, SEMI, COMMA, STAR, DOT, COLON, COLON_COLON_EQUALS, BAR, PRECEDENCE,
  LEFT, RIGHT, NONASSOC, PERCENT_PREC, LBRACK, RBRACK, NONTERMINAL, GT, LT,
  QUESTION, SUPER, EXTENDS, CLASS;

Johannes Roith's avatar
Johannes Roith committed
302
terminal String ID, CODE_STRING;
303 304 305 306 307 308 309 310

non terminal
  spec, package_spec, import_list, action_code_part, 
  code_parts, code_part, opt_semi, non_terminal,
  parser_code_part, symbol_list, start_spec, production_list, 
  multipart_id, import_spec, import_id, init_code, scan_code, symbol, 
  type_id, term_name_list, non_term_name_list, production, prod_part_list, 
  prod_part, new_term_id, new_non_term_id, rhs_list, rhs, empty,
Johannes Roith's avatar
Johannes Roith committed
311
  precedence_list, preced, preced_type, terminal_list, precedence_l, declares_term, 
312 313 314 315 316 317 318 319 320 321 322 323 324 325
  declares_non_term, class_name;

non terminal String  nt_id, symbol_id, label_id, opt_label, terminal_id,
                     term_id, robust_id, typearglist, typearguement, wildcard;

/*----------------------------------------------------------------*/ 

start with spec;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

spec ::= 
	{:
          /* declare "error" as a terminal */
Johannes Roith's avatar
Johannes Roith committed
326
          symbols.put("error", new symbol_part(terminal_shared.getError()));
327 328

          /* declare start non terminal */
Johannes Roith's avatar
Johannes Roith committed
329
          non_terms.put("$START", non_terminal_shared.getSTART_nt());
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
	:}
	package_spec
	import_list
	class_name
	code_parts
	symbol_list
	precedence_list
	start_spec
	production_list
	|
	/* error recovery assuming something went wrong before symbols 
	   and we have TERMINAL or NON TERMINAL to sync on.  if we get
	   an error after that, we recover inside symbol_list or 
	   production_list 
	*/
	error 
	symbol_list
	precedence_list
	start_spec
	production_list
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

package_spec ::= 
355
	PACKAGE : p
356
	multipart_id : mi
357 358
	{:
	  /* save the package name */
Johannes Roith's avatar
Johannes Roith committed
359 360 361
	  context.package_name = multipart_name;

	  parserResult.pack = new de.in.tum.www2.cup.ast.Package(
362 363 364
	  					new Name (multipart_name, 
	  						Range.fromLocations(mixleft, mixright)
	  					),
365 366
	  					new Range(Position.fromLocation(pxleft),
	  					Position.fromComplexSymbolRight((ComplexSymbol) cur_token)));
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387

	  /* reset the accumulated multipart name */
	  multipart_name = new String();
	:}
	SEMI
	|
	empty
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

import_list ::=
	import_list
	import_spec
	|
	empty
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

import_spec ::=
388
	IMPORT : import_token
389
	import_id : ii
390
	SEMI : semi {:
391
	  /* save this import on the imports list */
Johannes Roith's avatar
Johannes Roith committed
392 393 394
	  context.import_list.push(multipart_name);

      Import imprt = new Import(
395 396 397
      					new Name (multipart_name, 
							Range.fromLocations(iixleft, iixright)),
	  					Range.fromLocations(import_tokenxleft, semixright));
Johannes Roith's avatar
Johannes Roith committed
398 399
      parserResult.imports.add(imprt);
      
400 401 402 403 404 405 406
	  /* reset the accumulated multipart name */
	  multipart_name = new String();
	:}
	;

class_name ::=
    empty |
407
    CLASS : cls ID:id SEMI : semi
408
    {:
Johannes Roith's avatar
Johannes Roith committed
409 410 411 412
     context.parser_class_name = id;
     context.symbol_const_class_name = id + "Sym";
     
     parserResult.className = new ClassName (
413 414 415 416
     							new Name(id, new Range(Position.fromLocation(idxleft),
										Position.fromLocation(idxright))),
								new Range(Position.fromLocation(clsxleft),
										Position.fromLocation(semixright)));
Johannes Roith's avatar
Johannes Roith committed
417
     
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
    :}
    ;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

// allow any order; all parts are optional. [CSA, 23-Jul-1999]
// (we check in the part action to make sure we don't have 2 of any part)
code_part ::=
	action_code_part | parser_code_part | init_code | scan_code ;
code_parts ::= 
	| code_parts code_part;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

action_code_part ::= 
Johannes Roith's avatar
Johannes Roith committed
433
	ACTION : act CODE CODE_STRING : user_code opt_semi : smi
434
	{:
Johannes Roith's avatar
Johannes Roith committed
435
	  if (context.action_code!=null)
436 437
	    errMan.Warning(ErrorSource.Parser,
	    		"Redundant action code (skipping)",
438
	    		Range.fromLocations(actxleft, smixright));
Johannes Roith's avatar
Johannes Roith committed
439 440
	  else { /* save the user included code string */
	    context.action_code = user_code;
441 442

		int debug_id = get_new_debug_id();
Johannes Roith's avatar
Johannes Roith committed
443
		// TODO: attach inner range! based on user_codexleft, user_codexright
444
	    parserResult.appendActionCode (new SpecialCodeBlock(
445
								    debug_id,
446 447
	    							SpecialCodeBlock.BlockType.Action,
	    							user_code, 
448
	    							Range.fromLocations(actxleft, smixright)));
Johannes Roith's avatar
Johannes Roith committed
449
	  }
450 451 452 453 454 455
	:}
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

parser_code_part ::= 
Johannes Roith's avatar
Johannes Roith committed
456
	PARSER : prs CODE CODE_STRING:user_code opt_semi : smi
457
	{:
Johannes Roith's avatar
Johannes Roith committed
458
	  if (context.parser_code!=null)
459 460
	    errMan.Warning(ErrorSource.Parser,
	    				"Redundant parser code (skipping)",
461
	    		Range.fromLocations(prsxleft, smixright));
Johannes Roith's avatar
Johannes Roith committed
462 463
	  else { /* save the user included code string */
	    context.parser_code = user_code;
464
		int debug_id = get_new_debug_id();
465
	    parserResult.appendParserCode (new SpecialCodeBlock(
466
	    							debug_id,
467 468
	    							SpecialCodeBlock.BlockType.Parser,
	    							user_code,
Johannes Roith's avatar
Johannes Roith committed
469
	    							new Range(Position.fromLocation(prsxleft),
470
	    								Position.fromLocation(smixright))));
Johannes Roith's avatar
Johannes Roith committed
471
		}
472 473 474 475 476 477
	:}
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

init_code ::= 
Johannes Roith's avatar
Johannes Roith committed
478
	INIT : init WITH CODE_STRING:user_code opt_semi : smi
479
	{: 
Johannes Roith's avatar
Johannes Roith committed
480
	  if (context.init_code!=null)
481
	    errMan.Warning(ErrorSource.Parser, "Redundant init code (skipping)", 
482
	    		Range.fromLocations(initxleft, smixright));
Johannes Roith's avatar
Johannes Roith committed
483 484
	  else {/* save the user code */
	    context.init_code = user_code;
485
		int debug_id = get_new_debug_id();
486
	    parserResult.appendInitCode(new SpecialCodeBlock(
487
	    							debug_id,
488 489
	    							SpecialCodeBlock.BlockType.Init,
	    							user_code, 
Johannes Roith's avatar
Johannes Roith committed
490
	    							new Range(Position.fromLocation(initxleft),
491
	    								Position.fromLocation(smixright))));
Johannes Roith's avatar
Johannes Roith committed
492
		}
493 494 495 496 497 498
	:}
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

scan_code ::=
Johannes Roith's avatar
Johannes Roith committed
499
	SCAN : scan WITH CODE_STRING:user_code opt_semi : smi
500
	{: 
Johannes Roith's avatar
Johannes Roith committed
501
	  if (context.scan_code!=null)
502
	    errMan.Warning(ErrorSource.Parser, "Redundant scan code (skipping)", 
503
	    		Range.fromLocations(scanxleft, smixright));
Johannes Roith's avatar
Johannes Roith committed
504 505 506
	  else {
	  	 /* save the user code */
	    context.scan_code = user_code;
507
		int debug_id = get_new_debug_id();
508
	    parserResult.appendScanCode (new SpecialCodeBlock(
509
	    							debug_id,
510 511
	    							SpecialCodeBlock.BlockType.Scan,
	    							user_code, 
Johannes Roith's avatar
Johannes Roith committed
512
	    							new Range(Position.fromLocation(scanxleft),
513
	    								Position.fromLocation(smixright))));
Johannes Roith's avatar
Johannes Roith committed
514 515
	    
	  }
516 517 518 519 520 521 522 523 524 525
	:}
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

symbol_list ::= symbol_list symbol | symbol;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

symbol ::= 
526
	TERMINAL : t
Johannes Roith's avatar
Johannes Roith committed
527 528 529 530 531
	type_id : tid
	declares_term : lst {:
		parserResult.symbols.add(new TypedTerminalDeclaration(
							(String) tid,
							(List<Terminal>) lst,
532
							new Range(Position.fromLocation(txleft),
533
							Position.fromLocation(lstxright))));
Johannes Roith's avatar
Johannes Roith committed
534
	:}
535
	|
536
	TERMINAL : t
Johannes Roith's avatar
Johannes Roith committed
537 538 539
	declares_term : lst {:
		parserResult.symbols.add(new TerminalDeclaration(
							(List<Terminal>) lst,
540
							new Range(Position.fromLocation(txleft),
541
							Position.fromLocation(lstxright))));
Johannes Roith's avatar
Johannes Roith committed
542 543
		;
	:}
544
	|
545
	non_terminal : nt
Johannes Roith's avatar
Johannes Roith committed
546 547 548 549 550
	type_id : tid
	declares_non_term : lst {:
		parserResult.symbols.add(new TypedNonTerminalDeclaration(
							(String) tid,
							(List<NonTerminal>) lst,
551
							new Range(Position.fromLocation(ntxleft),
552
							Position.fromLocation(lstxright))));
Johannes Roith's avatar
Johannes Roith committed
553
	:}
554
	|
555
	non_terminal : nt
Johannes Roith's avatar
Johannes Roith committed
556 557 558
	declares_non_term:lst {:
		parserResult.symbols.add(new NonTerminalDeclaration(
							(List<NonTerminal>) lst,
559
							new Range(Position.fromLocation(ntxleft),
560
							Position.fromLocation(lstxright))));
Johannes Roith's avatar
Johannes Roith committed
561 562
	:}
	
563
	|
Johannes Roith's avatar
Johannes Roith committed
564
	
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
	/* error recovery productions -- sync on semicolon */

	TERMINAL
	error 
	{:
	  /* reset the accumulated multipart name */
	  multipart_name = new String();
	:}
	SEMI
	|
	non_terminal
	error 
	{:
	  /* reset the accumulated multipart name */
	  multipart_name = new String();
	:}
	SEMI
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

declares_term ::=
Johannes Roith's avatar
Johannes Roith committed
587
	term_name_list : lst
588 589 590
	{: 
	  /* reset the accumulated multipart name */
	  multipart_name = new String();
Johannes Roith's avatar
Johannes Roith committed
591
	  RESULT = lst;
592 593 594 595 596 597
	:}
	SEMI
	;
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

declares_non_term ::=
Johannes Roith's avatar
Johannes Roith committed
598
	non_term_name_list : lst
599 600 601
	{: 
	  /* reset the accumulated multipart name */
	  multipart_name = new String();
Johannes Roith's avatar
Johannes Roith committed
602
	  RESULT = lst;
603 604 605 606 607
	:}
	SEMI
	;
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

Johannes Roith's avatar
Johannes Roith committed
608 609 610 611 612 613 614 615 616 617
term_name_list ::= 
	term_name_list : lst COMMA new_term_id : item {:
		((List<Terminal>) lst).add((Terminal) item);
		RESULT = lst;
	:} | new_term_id:item {:
		List<Terminal> lst = new ArrayList<Terminal>();
		lst.add((Terminal) item);
		RESULT = lst;
	:}
	;
618 619 620 621

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

non_term_name_list ::=
Johannes Roith's avatar
Johannes Roith committed
622 623 624 625
	non_term_name_list : lst COMMA new_non_term_id : item {:
		((List<NonTerminal>) lst).add((NonTerminal) item);
		RESULT = lst;
	:}
626
	|
Johannes Roith's avatar
Johannes Roith committed
627 628 629 630 631
	new_non_term_id : item {:
		List<NonTerminal> lst = new ArrayList<NonTerminal>();
		lst.add((NonTerminal) item);
		RESULT = lst;
	:}
632 633 634 635 636 637 638 639 640 641 642 643 644 645
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 


precedence_list ::= precedence_l | empty; 

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

precedence_l ::= precedence_l preced | preced;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

preced ::= 
646
	PRECEDENCE : prec
Johannes Roith's avatar
Johannes Roith committed
647
	preced_type
648
    terminal_list:lst SEMI : semi
Johannes Roith's avatar
Johannes Roith committed
649
    {:
650 651 652
	  ast_add_precedence(lst, new Range(
	  			Position.fromLocation(precxleft), 
	  			Position.fromLocation(semixright)));
Johannes Roith's avatar
Johannes Roith committed
653 654 655 656 657 658 659
    :}
	;

preced_type ::= 
	  LEFT     {: update_precedence(assoc.left);     :}
	| RIGHT    {: update_precedence(assoc.right);    :}
	| NONASSOC {: update_precedence(assoc.nonassoc); :}
660 661 662 663
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

Johannes Roith's avatar
Johannes Roith committed
664 665
terminal_list ::= terminal_list:lst COMMA terminal_id:trm
	{:
666 667
		Range range = new Range(Position.fromLocation(trmxleft),
	      						Position.fromLocation(trmxright));
668
		((List<Terminal>) lst).add(new Terminal(new Name (trm, range), (Range) range.clone()));
Johannes Roith's avatar
Johannes Roith committed
669 670
		RESULT = lst;
	:}
671
	|
Johannes Roith's avatar
Johannes Roith committed
672 673
	terminal_id:trm
	{:
674 675
		Range range = new Range(Position.fromLocation(trmxleft),
	      						Position.fromLocation(trmxright));
Johannes Roith's avatar
Johannes Roith committed
676
		List<Terminal> lst = new ArrayList<Terminal>();
677
		lst.add(new Terminal(new Name (trm, range), (Range) range.clone()));
Johannes Roith's avatar
Johannes Roith committed
678 679
		RESULT = lst;
	:}
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

terminal_id ::= term_id:sym
	{:	
	  add_precedence(sym);
	  RESULT = sym;
	:};
	  
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

term_id ::= symbol_id:sym
        {:
	  /* check that the symbol_id is a terminal */
	  if (symbols.get(sym) == null)
	    {
	      /* issue a message */
698
	      errMan.Error(ErrorSource.Parser, "Terminal \"" + sym + 
699
			   "\" has not been declared", Range.fromLocations(symxleft, symxright));
700 701 702 703 704 705 706
	    }
          RESULT = sym;
         :};

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

start_spec ::= 
707 708
	START : start WITH nt_id : start_name 
	SEMI : semi	{: 
709 710 711 712
	  /* verify that the name has been declared as a non terminal */
	  non_terminal nt = (non_terminal)non_terms.get(start_name);
	  if (nt == null)
	    {
713 714
	    errMan.Error(ErrorSource.Parser, 
	    			"Start non terminal \"" + start_name + "\" has not been declared", 
715
	  		         Range.fromLocations(start_namexleft, start_namexright));
716 717 718 719 720 721 722 723 724
	    }
          else
	    {
	      /* remember the non-terminal for later */
	      start_nt = nt;

	      /* build a special start production */
	      new_rhs();
	      add_rhs_part(add_lab(new symbol_part(start_nt), "start_val"));
Johannes Roith's avatar
Johannes Roith committed
725 726
	      add_rhs_part(new symbol_part(terminal_shared.getEOF()));
	      if (!context.getXmlActions()) add_rhs_part(new action_part("RESULT = start_val;"));
727

728
		  production p = new production(context, null, non_terminal_shared.getSTART_nt(), rhs_parts, rhs_pos);
729 730
		  //p.index();
		  context.start_production = p;
731
	      new_rhs();
Johannes Roith's avatar
Johannes Roith committed
732
	      
733 734
	      Range range = new Range(Position.fromLocation(startxleft), 
	      					Position.fromLocation(semixright));
735 736
	      Range innerRange = new Range(Position.fromLocation(start_namexleft),
	      							Position.fromLocation(start_namexright));
737
	      parserResult.startWith = new StartWith(new NonTerminal (
738
	      							new Name (start_name, innerRange), (Range) innerRange.clone()), range);
739 740 741 742 743 744 745 746 747 748 749 750 751
	    }
	:}
	|
	empty
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

production_list ::= production_list production | production;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

production ::=
Johannes Roith's avatar
Johannes Roith committed
752
	nt_id : lhs_id
753 754 755 756 757
	{:
	  /* lookup the lhs nt */
	  lhs_nt = (non_terminal)non_terms.get(lhs_id);

          /* if it wasn't declared, emit a message */
758
	  if (lhs_nt == null) {
Johannes Roith's avatar
Johannes Roith committed
759
	      if (errMan.getErrorCount() == 0)
760 761
	      errMan.Warning(ErrorSource.Parser, "LHS non terminal \"" + lhs_id + 
			       "\" has not been declared", 
762
	    		Range.fromLocations(lhs_idxleft, lhs_idxright));
763 764 765 766 767 768 769
	    }

	  /* reset the rhs accumulation */
	  new_rhs();
	:}
	COLON_COLON_EQUALS
/*	{: :}*/
Johannes Roith's avatar
Johannes Roith committed
770
	rhs_list : rhs
771 772 773 774 775 776 777 778 779 780 781 782
	SEMI : s
	{:
		parserResult.productions.add(
				new Production(
					new Name(
						(String) lhs_id,
						Range.fromLocations(lhs_idxleft, lhs_idxright)
					),
					(List<ProductionRight>) rhs,
					Range.fromLocations(lhs_idxleft, sxright)
				)
		);
Johannes Roith's avatar
Johannes Roith committed
783
	:}
784
	|
785 786 787
	error  : e {:
		errMan.Error(ErrorSource.Parser, "Production Syntax Error", Position.fromLocation(exleft));
	:}
788 789 790 791 792
	SEMI
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

Johannes Roith's avatar
Johannes Roith committed
793 794 795 796 797 798 799 800 801 802
rhs_list ::= rhs_list : lst BAR rhs : item {:
		if (item != null)
			((List<ProductionRight>) lst).add((ProductionRight) item);
		RESULT = lst;
	:} | rhs : item {:
		List<ProductionRight> lst = new ArrayList<ProductionRight>();
		if (item != null)
			lst.add((ProductionRight) item);
		RESULT = lst;
	:};
803 804 805 806

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

rhs ::= 
807
	prod_part_list : ppl PERCENT_PREC term_id:term_name 
808
	{:
Johannes Roith's avatar
Johannes Roith committed
809
	  symbol sym = null;
810 811 812 813 814 815 816 817 818 819 820
	  
		Range range = Range.fromLocations(term_namexleft, term_namexright);
	    ProductionRight pr = new ProductionRight( 
                            ast_get_rhs_parts_copy(), 
                            new Terminal(
                            	new Name(term_name, range),
                            	(Range) range.clone()
                            ),
					        Range.fromLocations(pplxleft, term_namexright));
	      
	  
821 822 823 824
	  if (lhs_nt != null) 
	    {
	      /* Find the precedence symbol */
	      if (term_name == null) {
825 826
	      
	      // jrTODO: report proper error!
827 828 829
		System.err.println("No terminal for contextual precedence");
		sym = null;
	      } else {
830 831 832 833 834 835 836
	      symbol_part sp = null;
	      try{
		      sp = (symbol_part) symbols.get(term_name);
		  } catch (NullPointerException e) {
		  	// fix crash.
			errMan.Error(ErrorSource.Parser,
					"could not resolve symbol!",
837
					Range.fromLocations(term_namexleft, term_namexright));
838 839 840
		  }
	      if (sp != null)
			  sym = sp.the_symbol();
841 842
	      }
	      /* build the production */
843
	      
844 845
	      production p;
	      if ((sym!=null) && (sym instanceof terminal)) {
846
		p = new production(context, pr, lhs_nt, rhs_parts, rhs_pos,
847 848 849 850 851 852
				   ((terminal)sym).precedence_num(),
				   ((terminal)sym).precedence_side());
		((symbol_part)symbols.get(term_name)).the_symbol().note_use();
	      } else {
		System.err.println("Invalid terminal " + term_name + 
				   " for contextual precedence assignment");
853
		p = new production(context, pr, lhs_nt, rhs_parts, rhs_pos);
854
	      }
855 856
	      
	    pr.connectToInternalProduction(p);
857

858
		RESULT = pr;
859

860 861 862 863 864 865 866 867 868
	      /* if we have no start non-terminal declared and this is 
		 the first production, make its lhs nt the start_nt 
		 and build a special start production for it. */
              if (start_nt == null)
		{
		  start_nt = lhs_nt;

	          /* build a special start production */
	          new_rhs();
Johannes Roith's avatar
Johannes Roith committed
869 870
	          ast_new_rhs();
	          
871
	          add_rhs_part(add_lab(new symbol_part(start_nt),"start_val"));
Johannes Roith's avatar
Johannes Roith committed
872 873
	          add_rhs_part(new symbol_part(terminal_shared.getEOF()));
		  if (!context.getXmlActions()) add_rhs_part(new action_part("RESULT = start_val;"));
874
		  if ((sym!=null) && (sym instanceof terminal)) {
Johannes Roith's avatar
Johannes Roith committed
875
		    context.start_production = 
876
		      new production(context, null, non_terminal_shared.getSTART_nt(), rhs_parts, 
877 878 879
				     rhs_pos, ((terminal)sym).precedence_num(),
				     ((terminal)sym).precedence_side());
		  } else {
Johannes Roith's avatar
Johannes Roith committed
880
		    context.start_production = 
881
		      new production(context, null, non_terminal_shared.getSTART_nt(), rhs_parts, rhs_pos);
882 883
		  }
	          new_rhs();
Johannes Roith's avatar
Johannes Roith committed
884
	          ast_new_rhs();
885 886 887 888 889
		}
	    }

	  /* reset the rhs accumulation in any case */
	  new_rhs();
Johannes Roith's avatar
Johannes Roith committed
890
	  ast_new_rhs();
891 892
	:}
	|
893
	prod_part_list : ppl
Johannes Roith's avatar
Johannes Roith committed
894 895 896
	{:
	
		
897 898
	  if (lhs_nt != null) 
	    {
899 900
	    
		 ProductionRight pr = new ProductionRight(
901
                            ast_get_rhs_parts_copy(), 
902
                            null,
903
					        Range.fromLocations(pplxleft, pplxright));
904 905 906 907 908
					        
	      /* build the production */
	      production p = new production(context, pr, lhs_nt, rhs_parts, rhs_pos);
	      pr.connectToInternalProduction(p);
          RESULT = pr;
909 910 911 912

	      /* if we have no start non-terminal declared and this is 
		 the first production, make its lhs nt the start_nt 
		 and build a special start production for it. */
Johannes Roith's avatar
Johannes Roith committed
913
        if (start_nt == null)
914 915 916 917 918
		{
		  start_nt = lhs_nt;

	          /* build a special start production */
	          new_rhs();
Johannes Roith's avatar
Johannes Roith committed
919 920
	          ast_new_rhs();
	          
921
	          add_rhs_part(add_lab(new symbol_part(start_nt),"start_val"));
Johannes Roith's avatar
Johannes Roith committed
922 923 924
	          add_rhs_part(new symbol_part(terminal_shared.getEOF()));
		  if (!context.getXmlActions()) add_rhs_part(new action_part("RESULT = start_val;"));
	          context.start_production = 
925
		     new production(context, null, non_terminal_shared.getSTART_nt(), rhs_parts, rhs_pos);
926 927

	          new_rhs();
Johannes Roith's avatar
Johannes Roith committed
928
	          ast_new_rhs();
929 930 931
		}
	    }
	  
Johannes Roith's avatar
Johannes Roith committed
932 933
	  
	  
934 935
	  /* reset the rhs accumulation in any case */
	  new_rhs();
Johannes Roith's avatar
Johannes Roith committed
936
	  ast_new_rhs();
937 938 939 940 941
	:} 
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

942
prod_part_list ::= prod_part_list prod_part | empty;
943 944 945 946 947 948 949 950 951 952

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

prod_part ::=
	symbol_id:symid opt_label:labid
	{: 
	  /* try to look up the id */
	  production_part symb = (production_part)symbols.get(symid);

	  /* if that fails, symbol is undeclared */
Johannes Roith's avatar
Johannes Roith committed
953 954
	  if (symb == null) {
	      if (errMan.getErrorCount() == 0)
955
	        errMan.Error(ErrorSource.Parser, "Symbol \"" + symid + 
956
			       "\" has not been declared", Range.fromLocations(symidxleft, symidxright));
Johannes Roith's avatar
Johannes Roith committed
957 958
	  }
	  else {
959 960
	      /* add a labeled production part */
	      add_rhs_part(add_lab(symb, labid));
961

962
		  ProductionSymbolRef ref = null;
963
		  Range range = Range.fromLocations(symidxleft, symidxright);
964
		  if (labid == null)
965
			  ref = new ProductionSymbolRef(new Name(symid, range), (Range) range.clone());
Johannes Roith's avatar
Johannes Roith committed
966
		  else
967
			  ref = new LabeledProductionSymbolRef(new Name(symid, range), labid, (Range) range.clone());
968
	      ast_add_rhs_part(ref);
969 970
	    }
	:}
Johannes Roith's avatar
Johannes Roith committed
971
	| CODE_STRING : code_str
Johannes Roith's avatar
Johannes Roith committed
972
	{:
973
	  /* add a new production part */
974 975 976

	  int debug_id = get_new_debug_id();
	  add_rhs_part(new action_part(attach_debug_symbol(debug_id, code_str)));
977
	  
978
	  ast_add_rhs_part(new ActionCodeBlock(debug_id, code_str, 
979
	  		Range.fromLocations(code_strxleft, code_strxright)
Johannes Roith's avatar
Johannes Roith committed
980
	  ));
981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
	:}
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

opt_label ::=
	COLON label_id:labid
	{: RESULT = labid; :}
	|
	empty
	{: RESULT = null; :}
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

multipart_id ::=
	multipart_id DOT robust_id:another_id
	{: multipart_name = multipart_name.concat("."+another_id); :}
	|multipart_id 
        {: multipart_names.push(multipart_name); multipart_name="";:}
        LT typearglist:types GT
         {: multipart_name =
         ((String)multipart_names.pop()).concat("<"+types+">"); :}
        |
	robust_id:an_id
	{: multipart_name = multipart_name.concat(an_id); :}
	;
/*. . . . . . . . . . . .TUM CHANGES. . . . . . . . . . . . . . . */


typearglist ::= 
            typearguement:arg
            {: RESULT = arg; :}
            | typearglist:list COMMA typearguement:arg
            {: RESULT = list + "," + arg; :}
            ;

typearguement ::=
              type_id
              {: RESULT = multipart_name; multipart_name = new String(); :}
              | wildcard:w
              {: RESULT = w; :}
              ;

wildcard ::=
         QUESTION
         {: RESULT = " ? "; :}
         | QUESTION EXTENDS type_id
         {: RESULT = " ? extends "+multipart_name; multipart_name = new
         String(); :}
         | QUESTION SUPER type_id
         {: RESULT = " ? super "+multipart_name; multipart_name = new
         String(); :}
         ;

/*. . . . . . . . . . .END TUM CHANGES. . . . . . . . . . . . . . */ 


/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

import_id ::= 
	multipart_id DOT STAR
	{: multipart_name = multipart_name.concat(".*"); :}
	|
	multipart_id
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

1050 1051 1052
type_id ::= multipart_id {:
		RESULT = multipart_name;
	:}
1053
	| type_id LBRACK RBRACK
1054 1055 1056 1057
	{:
		multipart_name = multipart_name.concat("[]");
		RESULT = multipart_name;
	:}
1058 1059 1060 1061 1062
       	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

new_term_id ::= 
1063
	ID : term_id 
1064 1065 1066 1067 1068
	{: 
	  /* see if this terminal has been declared before */
	  if (symbols.get(term_id) != null)
	    {
	      /* issue a message */
1069
	      errMan.Error(ErrorSource.Parser, "Symbol \"" + term_id + 
1070
			   "\" has already been declared", Range.fromLocations(term_idxleft, term_idxright));
1071 1072 1073 1074 1075
	    }
	  else
	    {
	      /* if no type declared, declare one */
	      if (multipart_name.equals("")) {
Johannes Roith's avatar
Johannes Roith committed
1076
			multipart_name = "Object";
1077
	      }
Johannes Roith's avatar
Johannes Roith committed
1078
	      
1079 1080 1081
	     Range range = Range.fromLocations(term_idxleft, term_idxright);
	     Terminal t = new Terminal(new Name(term_id, range), (Range) range.clone());
	     
1082 1083
	      /* build a production_part and put it in the table */ 
	      symbols.put(term_id, 
1084
	        new symbol_part(new terminal(context, t, term_id, multipart_name)));
Johannes Roith's avatar
Johannes Roith committed
1085
	        
1086
	      RESULT = t;
1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099
	    }
	:}
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

new_non_term_id ::=
	ID:non_term_id
	{: 
	  /* see if this non terminal has been declared before */
	  if (symbols.get(non_term_id) != null)
	    {
	      /* issue a message */
1100
	      errMan.Error(ErrorSource.Parser, "Symbol \"" + non_term_id + 
1101
			          "\" has already been declared", Range.fromLocations(non_term_idxleft, non_term_idxright));
1102 1103 1104 1105
	    }
	  else
	    {
	      if (multipart_name.equals("")) {
1106
			multipart_name ="Object";
1107
	      }
1108 1109 1110 1111 1112 1113
	      
	      // jrTODO: positions!
	      // jrTODO: non_term_id vs. multipart_name
	      Range range = Range.fromLocations(non_term_idxleft, non_term_idxright);
	      NonTerminal nt = new NonTerminal(new Name(non_term_id, range), (Range) range.clone());
	      
1114 1115
	      /* build the non terminal object */
              non_terminal this_nt = 
1116
			new non_terminal(context, nt, non_term_id, multipart_name);
1117 1118 1119 1120 1121 1122

	      /* put it in the non_terms table */
	      non_terms.put(non_term_id, this_nt);

	      /* build a production_part and put it in the symbols table */ 
	      symbols.put(non_term_id, new symbol_part(this_nt));
Johannes Roith's avatar
Johannes Roith committed
1123

1124 1125
		RESULT = nt;

1126 1127 1128 1129 1130 1131 1132 1133 1134
	    }
	:}
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

nt_id ::=
	ID:the_id
	{: RESULT = the_id; :}
1135
	| error : e
1136
	{:
1137 1138 1139
		errMan.Error(ErrorSource.Parser, 
						"Illegal use of reserved word", 
						Position.fromLocation(exleft));
1140
						
1141 1142 1143 1144 1145 1146 1147 1148 1149
		RESULT="ILLEGAL";
	:}
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

symbol_id ::=
	ID:the_id
	{: RESULT = the_id; :}
1150
	| error : e
1151
	{:
1152 1153 1154
		errMan.Error(ErrorSource.Parser, 
					"Illegal use of reserved word",
					Position.fromLocation(exleft));
1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185
		RESULT="ILLEGAL";
	:}
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

label_id ::= 
	robust_id:the_id
	{: RESULT = the_id; :}
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

robust_id ::= /* all ids that aren't reserved words in Java */
	ID:the_id	{: RESULT = the_id; :}
	/* package is reserved. */
	/* import is reserved. */
	| CODE		{: RESULT = "code"; :}
	| ACTION	{: RESULT = "action"; :}
	| PARSER	{: RESULT = "parser"; :}
	| TERMINAL	{: RESULT = "terminal"; :}
	| NON		{: RESULT = "non"; :}
	| NONTERMINAL   {: RESULT = "nonterminal"; :}
	| INIT		{: RESULT = "init"; :}
	| SCAN		{: RESULT = "scan"; :}
	| WITH		{: RESULT = "with"; :}
	| START		{: RESULT = "start"; :}
	| PRECEDENCE	{: RESULT = "precedence"; :}
	| LEFT		{: RESULT = "left"; :}
	| RIGHT		{: RESULT = "right"; :}
	| NONASSOC	{: RESULT = "nonassoc"; :}
1186
	| error : e
1187
	{:
1188 1189 1190
		errMan.Error(ErrorSource.Parser, 
					"Illegal use of reserved word",
					Position.fromLocation(exleft));
1191

1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218
		RESULT="ILLEGAL";
	:}
	;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

non_terminal ::= NON TERMINAL | NONTERMINAL;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

opt_semi ::= /* nothing */
	| SEMI;

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ 

empty ::= /* nothing */;

/*----------------------------------------------------------------*/