1 
2 package java_cup;
3 
4 import java.util.Hashtable;
5 import java.util.Enumeration;
6 
7 /** This class represents a production in the grammar.  It contains
8  *  a LHS non terminal, and an array of RHS symbols.  As various
9  *  transformations are done on the RHS of the production, it may shrink.
10  *  As a result a separate length is always maintained to indicate how much
11  *  of the RHS array is still valid.<p>
12  *
13  *  I addition to construction and manipulation operations, productions provide
14  *  methods for factoring out actions (see  remove_embedded_actions()), for
15  *  computing the nullability of the production (i.e., can it derive the empty
16  *  string, see check_nullable()), and operations for computing its first
17  *  set (i.e., the set of terminals that could appear at the beginning of some
18  *  string derived from the production, see check_first_set()).
19  *
20  * @see     java_cup.production_part
21  * @see     java_cup.symbol_part
22  * @see     java_cup.action_part
23  * @version last updated: 11/25/95
24  * @author  Scott Hudson
25  */
26 
27 public class production {
28 
29   /*-----------------------------------------------------------*/
30   /*--- Constructor(s) ----------------------------------------*/
31   /*-----------------------------------------------------------*/
32 
33   /** Full constructor.  This constructor accepts a LHS non terminal,
34    *  an array of RHS parts (including terminals, non terminals, and
35    *  actions), and a string for a final reduce action.   It does several
36    *  manipulations in the process of  creating a production object.
37    *  After some validity checking it translates labels that appear in
38    *  actions into code for accessing objects on the runtime parse stack.
39    *  It them merges adjacent actions if they appear and moves any trailing
40    *  action into the final reduce actions string.  Next it removes any
41    *  embedded actions by factoring them out with new action productions.
42    *  Finally it assigns a unique index to the production.<p>
43    *
44    *  Factoring out of actions is accomplished by creating new "hidden"
45    *  non terminals.  For example if the production was originally: <pre>
46    *    A ::= B {action} C D
47    *  </pre>
48    *  then it is factored into two productions:<pre>
49    *    A ::= B X C D
50    *    X ::= {action}
51    *  </pre>
52    *  (where X is a unique new non terminal).  This has the effect of placing
53    *  all actions at the end where they can be handled as part of a reduce by
54    *  the parser.
55    */
production( non_terminal lhs_sym, production_part rhs_parts[], int rhs_l, String action_str)56   public production(
57     non_terminal    lhs_sym,
58     production_part rhs_parts[],
59     int             rhs_l,
60     String          action_str)
61     throws internal_error
62     {
63       int         i;
64       action_part tail_action;
65 
66       /* remember the length */
67       if (rhs_l >= 0)
68 	_rhs_length = rhs_l;
69       else if (rhs_parts != null)
70 	_rhs_length = rhs_parts.length;
71       else
72 	_rhs_length = 0;
73 
74       /* make sure we have a valid left-hand-side */
75       if (lhs_sym == null)
76 	throw new internal_error(
77 	  "Attempt to construct a production with a null LHS");
78 
79       /* translate labels appearing in action strings */
80       action_str = translate_labels(
81 			 rhs_parts, rhs_l, action_str, lhs_sym.stack_type());
82 
83       /* count use of lhs */
84       lhs_sym.note_use();
85 
86       /* create the part for left-hand-side */
87       _lhs = new symbol_part(lhs_sym);
88 
89       /* merge adjacent actions (if any) */
90       _rhs_length = merge_adjacent_actions(rhs_parts, _rhs_length);
91 
92       /* strip off any trailing action */
93       tail_action = strip_trailing_action(rhs_parts, _rhs_length);
94       if (tail_action != null) _rhs_length--;
95 
96       /* allocate and copy over the right-hand-side */
97       _rhs = new production_part[_rhs_length];
98       for (i=0; i<_rhs_length; i++)
99 	_rhs[i] = rhs_parts[i];
100 
101       /* count use of each rhs symbol */
102       for (i=0; i<_rhs_length; i++)
103 	if (!_rhs[i].is_action())
104 	  ((symbol_part)_rhs[i]).the_symbol().note_use();
105 
106       /* merge any trailing action with action string parameter */
107       if (action_str == null) action_str = "";
108       if (tail_action != null && tail_action.code_string() != null)
109 	action_str = tail_action.code_string() + action_str;
110 
111       /* stash the action */
112       _action = new action_part(action_str);
113 
114       /* rewrite production to remove any embedded actions */
115       remove_embedded_actions();
116 
117       /* assign an index */
118       _index = next_index++;
119 
120       /* put us in the global collection of productions */
121       _all.put(new Integer(_index),this);
122 
123       /* put us in the production list of the lhs non terminal */
124       lhs_sym.add_production(this);
125     }
126 
127   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
128 
129   /** Constructor with no action string. */
production( non_terminal lhs_sym, production_part rhs_parts[], int rhs_l)130   public production(
131     non_terminal    lhs_sym,
132     production_part rhs_parts[],
133     int             rhs_l)
134     throws internal_error
135     {
136       this(lhs_sym,rhs_parts,rhs_l,null);
137     }
138 
139   /*-----------------------------------------------------------*/
140   /*--- (Access to) Static (Class) Variables ------------------*/
141   /*-----------------------------------------------------------*/
142 
143   /** Table of all productions.  Elements are stored using their index as
144    *  the key.
145    */
146   protected static Hashtable _all = new Hashtable();
147 
148   /** Access to all productions. */
all()149   public static Enumeration all() {return _all.elements();};
150 
151   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
152 
153   /** Total number of productions. */
number()154   public static int number() {return _all.size();};
155 
156   /** Static counter for assigning unique index numbers. */
157   protected static int next_index;
158 
159   /*-----------------------------------------------------------*/
160   /*--- (Access to) Instance Variables ------------------------*/
161   /*-----------------------------------------------------------*/
162 
163   /** The left hand side non-terminal. */
164   protected symbol_part _lhs;
165 
166   /** The left hand side non-terminal. */
lhs()167   public symbol_part lhs() {return _lhs;}
168 
169   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
170 
171   /** A collection of parts for the right hand side. */
172   protected production_part _rhs[];
173 
174   /** Access to the collection of parts for the right hand side. */
rhs(int indx)175   public production_part rhs(int indx) throws internal_error
176     {
177       if (indx >= 0 && indx < _rhs_length)
178 	return _rhs[indx];
179       else
180 	throw new internal_error(
181 	  "Index out of range for right hand side of production");
182     }
183 
184   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
185 
186   /** How much of the right hand side array we are presently using. */
187   protected int _rhs_length;
188 
189   /** How much of the right hand side array we are presently using. */
rhs_length()190   public int rhs_length() {return _rhs_length;}
191 
192   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
193 
194   /** An action_part containing code for the action to be performed when we
195    *  reduce with this production.
196    */
197   protected action_part _action;
198 
199   /** An action_part containing code for the action to be performed when we
200    *  reduce with this production.
201    */
action()202   public action_part action() {return _action;}
203 
204   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
205 
206   /** Index number of the production. */
207   protected int _index;
208 
209   /** Index number of the production. */
index()210   public int index() {return _index;}
211 
212   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
213 
214   /** Count of number of reductions using this production. */
215   protected int _num_reductions = 0;
216 
217   /** Count of number of reductions using this production. */
num_reductions()218   public int num_reductions() {return _num_reductions;}
219 
220   /** Increment the count of reductions with this non-terminal */
note_reduction_use()221   public void note_reduction_use() {_num_reductions++;}
222 
223   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
224 
225   /** Is the nullability of the production known or unknown? */
226   protected boolean _nullable_known = false;
227 
228   /** Is the nullability of the production known or unknown? */
nullable_known()229   public boolean nullable_known() {return _nullable_known;}
230 
231   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
232 
233   /** Nullability of the production (can it derive the empty string). */
234   protected boolean _nullable = false;
235 
236   /** Nullability of the production (can it derive the empty string). */
nullable()237   public boolean nullable() {return _nullable;}
238 
239   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
240 
241   /** First set of the production.  This is the set of terminals that
242    *  could appear at the front of some string derived from this production.
243    */
244   protected terminal_set _first_set = new terminal_set();
245 
246   /** First set of the production.  This is the set of terminals that
247    *  could appear at the front of some string derived from this production.
248    */
first_set()249   public terminal_set first_set() {return _first_set;}
250 
251   /*-----------------------------------------------------------*/
252   /*--- Static Methods ----------------------------------------*/
253   /*-----------------------------------------------------------*/
254 
255   /** Determine if a given character can be a label id starter.
256    * @param c the character in question.
257    */
is_id_start(char c)258   protected static boolean is_id_start(char c)
259     {
260       return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_');
261 
262       //later need to handle non-8-bit chars here
263     }
264 
265   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
266 
267   /** Determine if a character can be in a label id.
268    * @param c the character in question.
269    */
is_id_char(char c)270   protected static boolean is_id_char(char c)
271     {
272       return is_id_start(c) || (c >= '0' && c <= '9');
273     }
274 
275   /*-----------------------------------------------------------*/
276   /*--- General Methods ---------------------------------------*/
277   /*-----------------------------------------------------------*/
278 
279   /** Determine the translation for one label id found within a code_string.
280    *  Symbols appearing in the RHS correspond to objects found on the parse
281    *  stack at runtime.  The code to access them, becomes code to access an
282    *  object at the appropriate offset from the top of the stack, and then
283    *  cast that to the proper type.
284    *
285    * @param id_str    the name of the id to be translated.
286    * @param act_pos   the original position of the action it appears in.
287    * @param label_map a hash table mapping labels to positions in the RHS.
288    * @param type_map  a hash table mapping labels to declared symbol types.
289    */
label_translate( String id_str, int act_pos, Hashtable label_map, Hashtable label_types)290   protected String label_translate(
291     String    id_str,     /* the id string we are (possibly) translating */
292     int       act_pos,    /* position of the action                      */
293     Hashtable label_map,  /* map from labels to positions in the RHS     */
294     Hashtable label_types)/* map from labels to stack types              */
295     {
296       Integer label_pos;
297       String  label_type;
298       int     offset;
299 
300       /* look up the id */
301       label_pos  = (Integer)label_map.get(id_str);
302 
303       /* if we don't find it, just return the id */
304       if (label_pos == null) return id_str;
305 
306       /* extract the type of the labeled symbol */
307       label_type = (String)label_types.get(id_str);
308 
309       /* is this for the LHS? */
310       if (label_pos.intValue() == -1)
311         {
312 	  /* return the result object cast properly */
313 	  return "((" + label_type + ")" + emit.pre("result") + ")";
314          }
315 
316        /* its a RHS label */
317 
318        /* if the label appears after the action, we have an error */
319        if (label_pos.intValue() > act_pos)
320          {
321 	   /* emit an error message */
322 	   System.err.println("*** Label \"" + id_str +
323 	     "\" appears in action before it appears in production");
324 	    lexer.error_count++;
325 
326 	    // later need to print the production this is in
327 
328 	    /* just return the id unchanged */
329 	      return id_str;
330 	  }
331 
332       /* calculate the stack offset as the difference in position from
333 	 label to action minus one */
334       offset = (act_pos - label_pos.intValue())-1;
335 
336       /* translation is properly cast element at that offset from TOS */
337       return "(/*"+id_str+"*/("+label_type+")" +
338        emit.pre("stack") + ".elementAt(" + emit.pre("top") +"-"+ offset + "))";
339 
340     }
341 
342   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
343 
344   /** Translate all the label names within an action string to appropriate code.
345    * @param act_string  the string to be translated
346    * @param act_pos     the position that the action originally held in the
347    *                    production.
348    * @param label_map   a hash table mapping labels to positions in the RHS.
349    * @param type_map    a hash table mapping labels to declared symbol types.
350    */
action_translate( String act_string, int act_pos, Hashtable label_map, Hashtable label_types)351   protected String action_translate(
352     String    act_string,  /* the action string                     */
353     int       act_pos,     /* the position of the action on the RHS */
354     Hashtable label_map,   /* map from labels to RHS positions      */
355     Hashtable label_types) /* map from labels to symbol stack types */
356     {
357       int          id_start;
358       int          pos;
359       int          len;
360       String       id_str;
361       boolean      in_id;
362       StringBuffer result;
363       char         buffer[];
364 
365       /* if we have no string we are done */
366       if (act_string == null || act_string.length()== 0) return act_string;
367 
368       len = act_string.length();
369 
370       /* set up a place to put the result */
371       result = new StringBuffer(len + 50);
372 
373       /* extract string into array */
374       buffer = new char[len + 1];
375       act_string.getChars(0, len, buffer, 0);
376 
377       /* put terminator in buffer so we can look one past the end */
378       buffer[len] = '\0';
379 
380       /* walk down the input buffer looking for identifiers */
381       in_id = false;
382       for (pos = id_start = 0; pos <= len; pos++)
383 	{
384 	  /* are we currently working on an id? */
385 	  if (in_id)
386 	    {
387 	      /* does this end the id? */
388 	      if (!is_id_char(buffer[pos]))
389 		{
390 		  /* extract the id string and translate it */
391 		  id_str = new String(buffer, id_start, pos - id_start);
392 		  result.append(
393 		      label_translate(id_str, act_pos, label_map,label_types));
394 
395 		  /* copy over the ending character */
396 		  if (buffer[pos] != '\0')
397 	            result.append(buffer, pos, 1);
398 
399 		  /* and we are done with this id */
400 		  in_id = false;
401 		}
402 	      else
403 		{
404 		  /* we are still in the id, so just keep going */
405 		}
406 	    }
407 	  else /* we are not inside an id */
408 	    {
409 	      /* start a new id? */
410 	      if (is_id_start(buffer[pos]))
411 	        {
412 		  /* start keeping these chars as an id */
413 	          in_id = true;
414 	          id_start = pos;
415 	        }
416 	     else
417 	       {
418 	         /* just copy over the char */
419 		 if (buffer[pos] != '\0')
420 	           result.append(buffer, pos, 1);
421 	       }
422 	    }
423 	}
424 
425       /* return the accumulated result */
426       return result.toString();
427     }
428 
429   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
430 
431   /** Translate label names to appropriate code within all action strings.
432    * @param rhs          array of RHS parts.
433    * @param rhs_len      how much of rhs to consider valid.
434    * @param final_action the final action string of the production.
435    * @param lhs_type     the object type associated with the LHS symbol.
436    */
translate_labels( production_part rhs[], int rhs_len, String final_action, String lhs_type)437   protected String translate_labels(
438     production_part  rhs[],
439     int              rhs_len,
440     String           final_action,
441     String           lhs_type)
442     {
443       Hashtable   label_map   = new Hashtable(11);
444       Hashtable   label_types = new Hashtable(11);
445       symbol_part part;
446       action_part act_part;
447       int         pos;
448 
449       /* walk down the parts and extract the labels */
450       for (pos = 0; pos < rhs_len; pos++)
451 	{
452 	  if (!rhs[pos].is_action())
453 	    {
454 	      part = (symbol_part)rhs[pos];
455 
456 	      /* if it has a label enter it in the tables */
457 	      if (part.label() != null)
458 		{
459 		  label_map.put(part.label(), new Integer(pos));
460 		  label_types.put(part.label(), part.the_symbol().stack_type());
461 		}
462 	    }
463 	}
464 
465       /* add a label for the LHS */
466       label_map.put("RESULT", new Integer(-1));
467       label_types.put("RESULT", lhs_type);
468 
469       /* now walk across and do each action string */
470       for (pos = 0; pos < rhs_len; pos++)
471 	{
472 	  if (rhs[pos].is_action())
473 	    {
474 	       act_part = (action_part)rhs[pos];
475 	       act_part.set_code_string(
476 		 action_translate(
477 		   act_part.code_string(), pos, label_map, label_types));
478 	    }
479 	}
480 
481       /* now do the final action string at the position after the last */
482       return action_translate(final_action, rhs_len, label_map, label_types);
483 
484     }
485 
486   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
487 
488   /** Helper routine to merge adjacent actions in a set of RHS parts
489    * @param rhs_parts array of RHS parts.
490    * @param len       amount of that array that is valid.
491    * @return          remaining valid length.
492    */
merge_adjacent_actions(production_part rhs_parts[], int len)493   protected int merge_adjacent_actions(production_part rhs_parts[], int len)
494     {
495       int from_loc, to_loc, merge_cnt;
496 
497       /* bail out early if we have no work to do */
498       if (rhs_parts == null || len == 0) return 0;
499 
500       merge_cnt = 0;
501       to_loc = -1;
502       for (from_loc=0; from_loc<len; from_loc++)
503 	{
504 	  /* do we go in the current position or one further */
505 	  if (to_loc < 0 || !rhs_parts[to_loc].is_action()
506 			 || !rhs_parts[from_loc].is_action())
507 	    {
508 	      /* next one */
509 	      to_loc++;
510 
511 	      /* clear the way for it */
512 	      if (to_loc != from_loc) rhs_parts[to_loc] = null;
513 	    }
514 
515 	  /* if this is not trivial? */
516 	  if (to_loc != from_loc)
517 	    {
518 	      /* do we merge or copy? */
519 	      if (rhs_parts[to_loc] != null && rhs_parts[to_loc].is_action() &&
520 		  rhs_parts[from_loc].is_action())
521 	      {
522 	        /* merge */
523 	        rhs_parts[to_loc] = new action_part(
524 		  ((action_part)rhs_parts[to_loc]).code_string() +
525 		  ((action_part)rhs_parts[from_loc]).code_string());
526 	        merge_cnt++;
527 	      }
528 	    else
529 	      {
530 	        /* copy */
531 	        rhs_parts[to_loc] = rhs_parts[from_loc];
532 	      }
533 	    }
534 	}
535 
536       /* return the used length */
537       return len - merge_cnt;
538     }
539 
540   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
541 
542   /** Helper routine to strip a trailing action off rhs and return it
543    * @param rhs_parts array of RHS parts.
544    * @param len       how many of those are valid.
545    * @return          the removed action part.
546    */
strip_trailing_action( production_part rhs_parts[], int len)547   protected action_part strip_trailing_action(
548     production_part rhs_parts[],
549     int             len)
550     {
551       action_part result;
552 
553       /* bail out early if we have nothing to do */
554       if (rhs_parts == null || len == 0) return null;
555 
556       /* see if we have a trailing action */
557       if (rhs_parts[len-1].is_action())
558 	{
559 	  /* snip it out and return it */
560 	  result = (action_part)rhs_parts[len-1];
561 	  rhs_parts[len-1] = null;
562 	  return result;
563 	}
564       else
565 	return null;
566     }
567 
568   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
569 
570   /** Remove all embedded actions from a production by factoring them
571    *  out into individual action production using new non terminals.
572    *  if the original production was:  <pre>
573    *    A ::= B {action1} C {action2} D
574    *  </pre>
575    *  then it will be factored into: <pre>
576    *    A ::= B NT$1 C NT$2 D
577    *    NT$1 ::= {action1}
578    *    NT$2 ::= {action2}
579    *  </pre>
580    *  where NT$1 and NT$2 are new system created non terminals.
581    */
remove_embedded_actions()582   protected void remove_embedded_actions() throws internal_error
583     {
584       non_terminal new_nt;
585       production   new_prod;
586 
587       /* walk over the production and process each action */
588       for (int act_loc = 0; act_loc < rhs_length(); act_loc++)
589 	if (rhs(act_loc).is_action())
590 	  {
591 	    /* create a new non terminal for the action production */
592 	    new_nt = non_terminal.create_new();
593 
594 	    /* create a new production with just the action */
595 	    new_prod = new action_production(this, new_nt, null, 0,
596 			((action_part)rhs(act_loc)).code_string());
597 
598 	    /* replace the action with the generated non terminal */
599 	    _rhs[act_loc] = new symbol_part(new_nt);
600 	  }
601     }
602 
603   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
604 
605   /** Check to see if the production (now) appears to be nullable.
606    *  A production is nullable if its RHS could derive the empty string.
607    *  This results when the RHS is empty or contains only non terminals
608    *  which themselves are nullable.
609    */
check_nullable()610   public boolean check_nullable() throws internal_error
611     {
612       production_part part;
613       symbol          sym;
614       int             pos;
615 
616       /* if we already know bail out early */
617       if (nullable_known()) return nullable();
618 
619       /* if we have a zero size RHS we are directly nullable */
620       if (rhs_length() == 0)
621 	{
622 	  /* stash and return the result */
623 	  return set_nullable(true);
624 	}
625 
626       /* otherwise we need to test all of our parts */
627       for (pos=0; pos<rhs_length(); pos++)
628 	{
629 	  part = rhs(pos);
630 
631 	  /* only look at non-actions */
632 	  if (!part.is_action())
633 	    {
634 	      sym = ((symbol_part)part).the_symbol();
635 
636 	      /* if its a terminal we are definitely not nullable */
637 	      if (!sym.is_non_term())
638 		return set_nullable(false);
639 	      /* its a non-term, is it marked nullable */
640 	      else if (!((non_terminal)sym).nullable())
641 		/* this one not (yet) nullable, so we aren't */
642 	        return false;
643 	    }
644 	}
645 
646       /* if we make it here all parts are nullable */
647       return set_nullable(true);
648     }
649 
650   /** set (and return) nullability */
set_nullable(boolean v)651   boolean set_nullable(boolean v)
652     {
653       _nullable_known = true;
654       _nullable = v;
655       return v;
656     }
657 
658   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
659 
660   /** Update (and return) the first set based on current NT firsts.
661    *  This assumes that nullability has already been computed for all non
662    *  terminals and productions.
663    */
check_first_set()664   public terminal_set check_first_set() throws internal_error
665     {
666       int    part;
667       symbol sym;
668 
669       /* walk down the right hand side till we get past all nullables */
670       for (part=0; part<rhs_length(); part++)
671 	{
672 	  /* only look at non-actions */
673 	  if (!rhs(part).is_action())
674 	    {
675 	      sym = ((symbol_part)rhs(part)).the_symbol();
676 
677 	      /* is it a non-terminal?*/
678 	      if (sym.is_non_term())
679 		{
680 		  /* add in current firsts from that NT */
681 		  _first_set.add(((non_terminal)sym).first_set());
682 
683 		  /* if its not nullable, we are done */
684 		  if (!((non_terminal)sym).nullable())
685 		    break;
686 		}
687 	      else
688 		{
689 	          /* its a terminal -- add that to the set */
690 		  _first_set.add((terminal)sym);
691 
692 		  /* we are done */
693 		  break;
694 		}
695 	    }
696 	}
697 
698       /* return our updated first set */
699       return first_set();
700     }
701 
702   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
703 
704   /** Equality comparison. */
equals(production other)705   public boolean equals(production other)
706     {
707       if (other == null) return false;
708       return other._index == _index;
709     }
710 
711   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
712 
713   /** Generic equality comparison. */
equals(Object other)714   public boolean equals(Object other)
715     {
716       if (!(other instanceof production))
717 	return false;
718       else
719 	return equals((production)other);
720     }
721 
722   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
723 
724   /** Produce a hash code. */
hashCode()725   public int hashCode()
726     {
727       /* just use a simple function of the index */
728       return _index*13;
729     }
730 
731   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
732 
733   /** Convert to a string. */
toString()734   public String toString()
735     {
736       String result;
737 
738       /* catch any internal errors */
739       try {
740         result = "production [" + index() + "]: ";
741         result += ((lhs() != null) ? lhs().toString() : "$$NULL-LHS$$");
742         result += " :: = ";
743         for (int i = 0; i<rhs_length(); i++)
744 	  result += rhs(i) + " ";
745         result += ";";
746         if (action()  != null && action().code_string() != null)
747 	  result += " {" + action().code_string() + "}";
748 
749         if (nullable_known())
750 	  if (nullable())
751 	    result += "[NULLABLE]";
752 	  else
753 	    result += "[NOT NULLABLE]";
754       } catch (internal_error e) {
755 	/* crash on internal error since we can't throw it from here (because
756 	   superclass does not throw anything. */
757 	e.crash();
758 	result = null;
759       }
760 
761       return result;
762     }
763 
764   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
765 
766   /** Convert to a simpler string. */
to_simple_string()767   public String to_simple_string() throws internal_error
768     {
769       String result;
770 
771       result = ((lhs() != null) ? lhs().the_symbol().name() : "NULL_LHS");
772       result += " ::= ";
773       for (int i = 0; i < rhs_length(); i++)
774 	if (!rhs(i).is_action())
775 	  result += ((symbol_part)rhs(i)).the_symbol().name() + " ";
776 
777       return result;
778     }
779 
780   /*-----------------------------------------------------------*/
781 
782 };
783