IR
[repair.git] / Repair / RepairCompiler / MCC / IR / SemanticChecker.java
1 package MCC.IR;
2
3 import java.util.*;
4 import java.math.BigInteger;
5 import MCC.State;
6
7 public class SemanticChecker {
8
9     private static final boolean CREATE_MISSING = true;
10
11     public State state;
12
13     Vector vConstraints;
14     Vector vRules;
15
16     SymbolTableStack sts;
17
18     SymbolTable stSets;
19     SymbolTable stRelations;
20     SymbolTable stTypes;
21     SymbolTable stGlobals;
22
23     StructureTypeDescriptor dCurrentType;
24
25     IRErrorReporter er;
26     
27     public SemanticChecker () {
28         dCurrentType = null;
29         stTypes = null;
30         er = null;
31     }
32
33     public boolean check(State state, IRErrorReporter er) {
34
35         this.state = state;
36         State.currentState = state;
37
38         // Don't clear the state!!  Do clear the IR-related state
39         this.state.stTypes = null;
40
41         if (er == null) {
42             throw new IRException("IRBuilder.build: Received null ErrorReporter");
43         } else {
44             this.er = er;
45         }
46
47         if (state.ptStructures == null) {
48             throw new IRException("IRBuilder.build: Received null ParseNode");
49         }
50
51         state.vConstraints = new Vector();
52         vConstraints = state.vConstraints;
53
54         state.vRules = new Vector();
55         vRules = state.vRules;
56
57         state.stTypes = new SymbolTable();
58         stTypes = state.stTypes;
59
60         state.stSets = new SymbolTable();
61         stSets = state.stSets;
62
63         state.stRelations = new SymbolTable();
64         stRelations = state.stRelations;
65
66         state.stGlobals = new SymbolTable();
67         stGlobals = state.stGlobals;
68
69         sts = new SymbolTableStack();
70         
71         // add int and bool to the types list
72         stTypes.add(ReservedTypeDescriptor.BIT);
73         stTypes.add(ReservedTypeDescriptor.BYTE);
74         stTypes.add(ReservedTypeDescriptor.SHORT);
75         stTypes.add(ReservedTypeDescriptor.INT);
76
77         stSets.add(new ReservedSetDescriptor("int", ReservedTypeDescriptor.INT));
78         stSets.add(new ReservedSetDescriptor("token", ReservedTypeDescriptor.INT));
79
80         boolean ok = true; 
81
82         er.setFilename(state.infile + ".struct");
83         if (!parse_structures(state.ptStructures)) {
84             ok = false;
85         }
86
87         er.setFilename(state.infile + ".space");
88         if (!parse_space(state.ptSpace)) {
89             ok = false;
90         }
91         
92         er.setFilename(state.infile + ".constraints");
93         if (!parse_constraints(state.ptConstraints)) {
94             ok = false;
95         }
96
97         er.setFilename(state.infile + ".model");
98         if (!parse_rules(state.ptModel)) {
99             ok = false;
100         }
101
102         return ok;
103     }
104
105     /********************** HELPER FUNCTIONS ************************/ 
106
107     /**
108      * special case lookup that returns null if no such type exists 
109      */
110     private TypeDescriptor lookupType(String typename) {
111         return lookupType(typename, false);
112     }
113
114     /**
115      * does a look up in the types symbol table. if the type is
116      * not found than a missing type descriptor is returned
117      */
118     private TypeDescriptor lookupType(String typename, boolean createmissing) {
119         if (stTypes.get(typename) != null) {
120             // the type exists, so plug in the descriptor directly 
121             return (TypeDescriptor) stTypes.get(typename);              
122         } else if (createmissing) {
123             return new MissingTypeDescriptor(typename);
124         } else {
125             return null;
126         }       
127     }
128
129     /**
130      * reserve a name 
131      */
132     private VarDescriptor reserveName(ParseNode pn) {
133         assert pn != null;
134         String varname = pn.getTerminal();
135         assert varname != null;
136                 
137         /* do semantic check and if valid, add it to symbol table
138            and add it to the quantifier as well */
139         if (sts.peek().contains(varname)) {
140             /* Semantic Error: redefinition */
141             er.report(pn, "Redefinition of '" + varname + "'");
142             return null;
143         } else {
144             VarDescriptor vd = new VarDescriptor(varname);
145             sts.peek().add(vd);
146             return vd;
147         }
148     }
149
150     /**
151      * special case lookup that returns null if no such set exists 
152      */
153     private SetDescriptor lookupSet(String setname) {
154         return lookupSet(setname, false);
155     }
156
157     /**
158      * does a look up in the set's symbol table. if the set is
159      * not found than a missing set descriptor is returned
160      */
161     private SetDescriptor lookupSet(String setname, boolean createmissing) {
162         if (stSets.get(setname) != null) {
163             // the set exists, so plug in the descriptor directly 
164             return (SetDescriptor) stSets.get(setname);              
165         } else if (createmissing) {
166             return new MissingSetDescriptor(setname);
167         } else {
168             return null;
169         }       
170     }
171     
172     /**
173      * does a look up in the set's symbol table. if the set is
174      * not found than a missing set descriptor is returned
175      */
176     private RelationDescriptor lookupRelation(String relname) {
177         if (stRelations.get(relname) != null) {
178             // the relation exists, so plug in the descriptor directly 
179             return (RelationDescriptor) stRelations.get(relname);              
180         } else {
181             return null;
182         }       
183     }
184     
185     
186     private static int count = 0;
187     private boolean precheck(ParseNode pn, String label) {              
188         if (pn == null) {
189             er.report(pn, "IE: Expected '" + label + "', got null");
190             assert false;
191             return false;
192         }
193
194         if (! pn.getLabel().equals(label)) {
195             er.report(pn, "IE: Expected '" + label + "', got '" + pn.getLabel() + "'");
196             assert false;
197             return false;
198         }
199
200         if (state.verbose >= 2) {
201             System.err.println("visiting*" + (count++) + ": " + label);
202         }
203
204         return true;
205     }
206
207     /********************* PARSING FUNCTIONS ************************/ 
208
209     private boolean parse_rules(ParseNode pn) {
210         if (!precheck(pn, "rules")) {
211             return false;
212         }
213
214         boolean ok = true;
215         ParseNodeVector rules = pn.getChildren();
216         
217         for (int i = 0; i < rules.size(); i++) {
218             ParseNode rule = rules.elementAt(i);
219             if (!parse_rule(rule)) {
220                 ok = false;
221             }
222         }
223                
224         /* type check */       
225         Iterator ruleiterator = state.vRules.iterator();
226         
227         while (ruleiterator.hasNext()) {
228             Rule rule = (Rule) ruleiterator.next();            
229             Expr guard = rule.getGuardExpr();
230             final SymbolTable rulest = rule.getSymbolTable();
231             SemanticAnalyzer sa = new SemanticAnalyzer() {
232                     public IRErrorReporter getErrorReporter() { return er; }
233                     public SymbolTable getSymbolTable() { return rulest; }
234                 };
235             TypeDescriptor guardtype = guard.typecheck(sa);
236             
237             if (guardtype == null) {
238                 ok = false;
239             } else if (guardtype != ReservedTypeDescriptor.INT) {
240                 er.report(null, "Type of guard must be 'int' not '" + guardtype.getSymbol() + "'");
241                 ok = false;
242             }
243             
244             if (!rule.getInclusion().typecheck(sa)) {
245                 ok = false;
246             }           
247             
248             Iterator quantifiers = rule.quantifiers();
249             
250             while (quantifiers.hasNext()) {
251                 Quantifier q = (Quantifier) quantifiers.next();
252                 
253                 if (q instanceof ForQuantifier && !((ForQuantifier)q).typecheck(sa)) {
254                     ok = false;
255                 }
256             }              
257         }        
258
259         /* do any post checks ?? */
260
261         return ok;
262     }
263
264     private boolean parse_rule(ParseNode pn) {
265         if (!precheck(pn, "rule")) {
266             return false;
267         }
268
269         boolean ok = true;
270         Rule rule = new Rule();
271         
272         /* get rule type */
273         boolean isstatic = pn.getChild("static") != null;
274         boolean isdelay = pn.getChild("delay") != null;
275         rule.setStatic(isstatic);
276         rule.setDelay(isdelay);
277
278         /* set up symbol table for constraint */
279         assert sts.empty();
280         sts.push(stGlobals);
281         sts.push(rule.getSymbolTable());
282
283         /* optional quantifiers */
284         if (pn.getChild("quantifiers") != null) {
285             ParseNodeVector quantifiers = pn.getChild("quantifiers").getChildren();
286             
287             for (int i = 0; i < quantifiers.size(); i++) {
288                 ParseNode qn = quantifiers.elementAt(i);
289                 Quantifier quantifier = parse_quantifier(qn);
290
291                 if (quantifier == null) {
292                     ok = false;
293                 } else {
294                     rule.addQuantifier(quantifier);
295                 }
296             }             
297         }
298         
299         /* get guard expr */
300         Expr guard = parse_expr(pn.getChild("expr"));
301
302         if (guard == null) {
303             ok = false;
304         } else {
305             rule.setGuardExpr(guard);
306         }
307
308         /* inclusion constraint */
309         Inclusion inclusion = parse_inclusion(pn.getChild("inclusion"));
310         
311         if (inclusion == null) {
312             ok = false;
313         } else {
314             rule.setInclusion(inclusion);
315         }
316         
317         /* pop symbol table stack */
318         SymbolTable st = sts.pop();
319         sts.pop(); /* pop off globals */
320
321         /* make sure the stack we pop is our rule s.t. */
322         assert st == rule.getSymbolTable(); 
323         assert sts.empty();
324
325         /* add rule to global set */
326         vRules.addElement(rule);
327
328         return ok;
329     }
330
331     private Inclusion parse_inclusion(ParseNode pn) {
332         if (!precheck(pn, "inclusion")) {
333             return null;
334         }
335
336         if (pn.getChild("set") != null) {
337             ParseNode set = pn.getChild("set");
338             Expr expr = parse_expr(set.getChild("expr"));
339             
340             if (expr == null) {
341                 return null;
342             }
343
344             String setname = set.getChild("name").getTerminal();
345             assert setname != null;
346             SetDescriptor sd = lookupSet(setname);
347             
348             if (sd == null) {
349                 er.report(set.getChild("name"), "Undefined set '" + setname + "'");
350                 return null;
351             }
352
353             return new SetInclusion(expr, sd);
354         } else if (pn.getChild("relation") != null) {
355             ParseNode relation = pn.getChild("relation");
356             Expr leftexpr = parse_expr(relation.getChild("left").getChild("expr"));
357             Expr rightexpr = parse_expr(relation.getChild("right").getChild("expr"));
358             
359             if ((leftexpr == null) || (rightexpr == null)) {
360                 return null;
361             }
362
363             String relname = relation.getChild("name").getTerminal();
364             assert relname != null;
365             RelationDescriptor rd = lookupRelation(relname);
366
367             if (rd == null) {
368                 er.report(relation.getChild("name"), "Undefined relation '" + relname + "'");
369                 return null;
370             }
371
372             return new RelationInclusion(leftexpr, rightexpr, rd);
373         } else {
374             throw new IRException();
375         }
376     }
377
378     private boolean parse_constraints(ParseNode pn) {
379         if (!precheck(pn, "constraints")) {
380             return false;
381         }
382
383         boolean ok = true;
384         ParseNodeVector constraints = pn.getChildren();
385         
386         for (int i = 0; i < constraints.size(); i++) {
387             ParseNode constraint = constraints.elementAt(i);
388             assert constraint.getLabel().equals("constraint");
389             if (!parse_constraint(constraint)) {
390                 ok = false;
391             }
392         }
393
394         /* do any post checks... (type constraints, etc?) */
395          
396         return ok;
397     }
398
399     private boolean parse_constraint(ParseNode pn) {
400         if (!precheck(pn, "constraint")) {
401             return false;
402         }
403
404         boolean ok = true;
405         Constraint constraint = new Constraint();
406
407         /* test crash */
408         boolean crash = pn.getChild("crash") != null;
409         constraint.setCrash(crash);
410
411         /* set up symbol table for constraint */
412         assert sts.empty();
413         sts.push(constraint.getSymbolTable());
414         
415         /* get quantifiers */
416         if (pn.getChild("quantifiers") != null) {
417             ParseNodeVector quantifiers = pn.getChild("quantifiers").getChildren();
418             
419             for (int i = 0; i < quantifiers.size(); i++) {
420                 ParseNode qn = quantifiers.elementAt(i);
421                 assert qn.getLabel().equals("quantifier");
422                 Quantifier quantifier = parse_quantifier(qn);
423                 if (quantifier == null) {
424                     ok = false;
425                 } else {
426                     constraint.addQuantifier(quantifier);
427                 }
428             }
429         }
430                                                       
431         /* get body */
432         LogicStatement logicexpr = parse_body(pn.getChild("body"));
433
434         if (logicexpr == null) {
435             ok = false;
436         } else {
437             constraint.setLogicStatement(logicexpr);
438         }
439         
440         /* pop symbol table stack */
441         SymbolTable st = sts.pop();
442
443         /* make sure the stack we pop is our constraint s.t. */
444         assert st == constraint.getSymbolTable(); 
445         assert sts.empty();
446
447         /* add to vConstraints */
448         vConstraints.addElement(constraint);           
449
450         return ok;
451     }
452
453     private Quantifier parse_quantifier(ParseNode pn) {
454         if (!precheck(pn, "quantifier")) {
455             return null;           
456         }
457
458         if (pn.getChild("forall") != null) { /* forall element in Set */
459             SetQuantifier sq = new SetQuantifier();
460
461             /* get var */
462             VarDescriptor vd = reserveName(pn.getChild("var"));
463             
464             if (vd == null) {
465                 return null;
466             } 
467
468             sq.setVar(vd);
469
470             /* parse the set */
471             SetDescriptor set = parse_set(pn.getChild("set"));
472             assert set != null;
473             sq.setSet(set);
474
475             vd.setType(set.getType());
476
477             /* return to caller */
478             return sq;
479
480         } else if (pn.getChild("relatiion") != null) { /* for < v1, v2 > in Relation */
481             RelationQuantifier rq = new RelationQuantifier();
482
483             /* get vars */
484             VarDescriptor vd1 = reserveName(pn.getChild("left"));
485             VarDescriptor vd2 = reserveName(pn.getChild("right"));
486             
487             if ((vd1 == null) || (vd2 == null)) {
488                 return null;
489             }
490             
491             rq.setTuple(vd1, vd2);
492             
493             /* get relation */
494             String relationname = pn.getChild("relation").getTerminal();
495             assert relationname != null;
496             RelationDescriptor rd = lookupRelation(relationname);
497
498             if (rd == null) {
499                 return null;
500             }
501             
502             rq.setRelation(rd);
503             vd1.setType(rd.getDomain().getType());
504             vd2.setType(rd.getRange().getType());
505             return rq;
506         } else if (pn.getChild("for") != null) { /* for j = x to y */
507             ForQuantifier fq = new ForQuantifier();
508             
509             /* grab var */
510             VarDescriptor vd = reserveName(pn.getChild("var"));
511
512             if (vd == null) {
513                 return null;
514             }
515
516             vd.setType(ReservedTypeDescriptor.INT);
517             fq.setVar(vd);
518
519             /* grab lower/upper bounds */
520             Expr lower = parse_expr(pn.getChild("lower").getChild("expr"));
521             Expr upper = parse_expr(pn.getChild("upper").getChild("expr"));
522
523             if ((lower == null) || (upper == null)) {
524                 return null;
525             }
526
527             fq.setBounds(lower, upper);
528
529             return fq;
530         } else {
531             throw new IRException("not supported yet");
532         }
533
534     }
535
536     private LogicStatement parse_body(ParseNode pn) {
537         if (!precheck(pn, "body")) {
538             return null;           
539         }
540         
541         if (pn.getChild("and") != null) {
542             /* body AND body */
543             LogicStatement left, right;
544             left = parse_body(pn.getChild("and").getChild("left").getChild("body"));
545             right = parse_body(pn.getChild("and").getChild("right").getChild("body"));
546             
547             if ((left == null) || (right == null)) {
548                 return null;
549             }
550             
551             // what do we want to call the and/or/not body classes?
552             return new LogicStatement(LogicStatement.AND, left, right);
553         } else if (pn.getChild("or") != null) {
554             /* body OR body */
555             LogicStatement left, right;
556             left = parse_body(pn.getChild("or").getChild("left").getChild("body"));
557             right = parse_body(pn.getChild("or").getChild("right").getChild("body"));
558             
559             if ((left == null) || (right == null)) {
560                 return null;
561             }
562
563             return new LogicStatement(LogicStatement.OR, left, right);
564         } else if (pn.getChild("not") != null) {
565             /* NOT body */
566             LogicStatement left = parse_body(pn.getChild("not").getChild("left").getChild("body"));
567             
568             if (left == null) {
569                 return null;
570             }
571             
572             return new LogicStatement(LogicStatement.NOT, left);
573         } else if (pn.getChild("predicate") != null) {
574             return parse_predicate(pn.getChild("predicate"));
575         } else {
576             throw new IRException();
577         }                        
578     }
579
580     private Predicate parse_predicate(ParseNode pn) {
581         if (!precheck(pn, "predicate")) {
582             return null;
583         }
584
585         if (pn.getChild("comparison") != null) {
586             ParseNode cn = pn.getChild("comparison");
587             
588             /* get the expr's */
589             Expr left = parse_expr(cn.getChild("left").getChild("expr"));
590             Expr right = parse_expr(cn.getChild("right").getChild("expr"));
591
592             if ((left == null) || (right == null)) {
593                 return null;
594             }
595
596             /* get comparison operator */
597             String comparison = cn.getChild("compare").getTerminal();
598             assert comparison != null;
599                          
600             return new ComparisonPredicate(comparison, left, right);
601         } else if (pn.getChild("inclusion") != null) {
602             ParseNode in = pn.getChild("inclusion");
603             
604             /* get quantiifer var */
605             VarDescriptor vd = parse_quantifiervar(in.getChild("quantifiervar"));
606            
607             if (vd == null) { 
608                 return null;
609             }
610
611             /* get set expr */
612             SetExpr setexpr = parse_setexpr(in.getChild("setexpr"));
613
614             if (setexpr == null) {
615                 return null;
616             }
617
618             return new InclusionPredicate(vd, setexpr);
619         } else {
620             throw new IRException();
621         }       
622     }
623
624     private SetDescriptor parse_set(ParseNode pn) {
625         if (!precheck(pn, "set")) {
626             return null;
627         }
628     
629         if (pn.getChild("name") != null) {
630             String setname = pn.getChild("name").getTerminal();
631             assert setname != null;
632                 
633             if (!stSets.contains(setname)) {
634                 /* Semantic Error: unknown set */
635                 er.report(pn, "Unknown set '" + setname + "' referenced in quantifier");
636                 return null;
637             } else {
638                 /* all good, get setdescriptor */
639                 SetDescriptor sd = (SetDescriptor) stSets.get(setname);
640                 assert sd != null;
641                 return sd;
642             }            
643         } else if (pn.getChild("listofliterals") != null) {            
644             TokenSetDescriptor tokenset = new TokenSetDescriptor();
645             ParseNodeVector token_literals = pn.getChild("listofliterals").getChildren();
646             assert token_literals.size() > 0;
647             
648             for (int i = 0; i < token_literals.size(); i++) {
649                 ParseNode literal = token_literals.elementAt(i);
650                 assert literal.getLabel().equals("literal");
651                 LiteralExpr litexpr = parse_literal(literal);
652
653                 if (litexpr == null) {
654                     return null;
655                 }
656                 
657                 if (litexpr instanceof TokenLiteralExpr || litexpr instanceof IntegerLiteralExpr) {
658                     tokenset.addLiteral(litexpr);
659                 } else {
660                     er.report(literal, "Elements of a user-defined set must be of type token or integer");
661                     // return null; /* don't think we need to return null */
662                 }
663             }               
664
665             return tokenset;
666         } else {
667             throw new IRException(pn.getTerminal());
668         }
669     }
670     
671     private boolean parse_space(ParseNode pn) {
672         if (!precheck(pn, "space")) {
673             return false;
674         }
675         
676         boolean ok = true;
677         ParseNodeVector sets = pn.getChildren("setdefinition");
678         ParseNodeVector relations = pn.getChildren("relationdefinition");
679
680         assert pn.getChildren().size() == (sets.size() + relations.size());
681         
682         /* parse sets */
683         for (int i = 0; i < sets.size(); i++) {
684             if (!parse_setdefinition(sets.elementAt(i))) {
685                 ok = false;
686             }
687         }
688
689         /* parse relations */
690         for (int i = 0; i < relations.size(); i++) {
691             if (!parse_relationdefinition(relations.elementAt(i))) {
692                 ok = false;
693             }
694         }
695
696         // ok, all the spaces have been parsed, now we should typecheck and check
697         // for cycles etc.
698
699         // #TBD#: typecheck and check for cycles
700       
701         /* replace missing with actual */
702         Iterator allsets = state.stSets.descriptors();
703         
704         while (allsets.hasNext()) {
705             SetDescriptor sd = (SetDescriptor) allsets.next();
706             Vector subsets = sd.getSubsets();
707
708             for (int i = 0; i < subsets.size(); i++) {
709                 SetDescriptor subset = (SetDescriptor) subsets.elementAt(i);
710                 
711                 if (subset instanceof MissingSetDescriptor) {
712                     SetDescriptor newsubset = lookupSet(subset.getSymbol());
713
714                     if (newsubset == null) {
715                         er.report(null, "Unknown subset '" + subset.getSymbol() + "'");
716                     } else {
717                         subsets.setElementAt(newsubset, i);
718                     }
719                 }
720             }
721         }
722         
723         return ok;
724     }
725
726     private boolean parse_setdefinition(ParseNode pn) {
727         if (!precheck(pn, "setdefinition")) {
728             return false;
729         }
730         
731         boolean ok = true;
732         
733         /* get set name */
734         String setname = pn.getChild("name").getTerminal();
735         assert (setname != null);
736
737         SetDescriptor sd = new SetDescriptor(setname);
738         
739         /* get set type */
740         String settype = pn.getChild("type").getTerminal();
741         TypeDescriptor type = lookupType(settype);
742         if (type == null) {
743             er.report(pn, "Undefined type '" + settype + "'");
744             ok = false; 
745         } else {
746             sd.setType(type);
747         }
748
749         /* is this a partition? */
750         boolean partition = pn.getChild("partition") != null;
751         sd.isPartition(partition); 
752
753         /* if set has subsets, add them to set descriptor */
754         if (pn.getChild("setlist") != null) {
755             ParseNodeVector setlist = pn.getChild("setlist").getChildren();
756             
757             for (int i = 0; i < setlist.size(); i++) {
758                 String subsetname = setlist.elementAt(i).getLabel();
759                 sd.addSubset(lookupSet(subsetname, CREATE_MISSING));
760             }            
761         }
762
763         /* add set to symbol table */
764         if (stSets.contains(setname)) {
765             // Semantic Check: redefinition
766             er.report(pn, "Redefinition of set: " + setname);
767             ok = false;
768         } else {
769             stSets.add(sd);
770         }
771
772         return ok;
773     }
774
775     private boolean parse_relationdefinition(ParseNode pn) {
776         if (!precheck(pn, "relationdefinition")) {
777             return false;
778         }
779
780         boolean ok = true;
781
782         /* get relation name */
783         String relname = pn.getChild("name").getTerminal();
784         assert relname != null;
785
786         RelationDescriptor rd = new RelationDescriptor(relname);
787
788         /* check if static */
789         boolean bStatic = pn.getChild("static") != null;
790         rd.isStatic(bStatic);
791
792         /* get domain */
793         String domainsetname = pn.getChild("domain").getChild("type").getTerminal();
794         assert domainsetname != null;
795
796         /* get range */
797         String rangesetname = pn.getChild("range").getChild("type").getTerminal();
798         assert rangesetname != null;
799
800         /* get domain multiplicity */
801         String domainmult = pn.getChild("domain").getChild("mult").getTerminal();
802         assert domainmult != null;
803
804         /* get range multiplicity */
805         String rangemult = pn.getChild("range").getChild("mult").getTerminal();
806         assert rangemult != null;
807
808         /* NOTE: it is assumed that the sets have been parsed already so that the 
809            set namespace is fully populated. any missing setdescriptors for the set
810            symbol table will be assumed to be errors and reported. */
811
812         SetDescriptor domainset = lookupSet(domainsetname);
813         if (domainset == null) {
814             er.report(pn, "Undefined set '" + domainsetname + "' referenced in relation '" + relname + "'");
815             ok = false;
816         } else {
817             rd.setDomain(domainset);
818         }
819
820         SetDescriptor rangeset = lookupSet(rangesetname);
821         if (rangeset == null) {
822             er.report(pn, "Undefined set '" + rangesetname + "' referenced in relation '" + relname + "'");
823             ok = false;
824         } else {
825             rd.setRange(rangeset);
826         }
827
828         // #TBD#: eventually we'll use the multiplicities but now we don't... oh well
829
830         /* lets add the relation to the global symbol table */
831         if (!stRelations.contains(relname)) {
832             stRelations.add(rd);
833         } else {
834             er.report(pn, "Redefinition of relation '" + relname + "'");
835             ok = false;
836         }
837
838         return ok;
839     }
840
841     private boolean parse_structures(ParseNode pn) {
842         if (!precheck(pn, "structures")) {
843             return false;
844         }
845         
846         boolean ok = true;
847         ParseNodeVector structures = pn.getChildren("structure");
848
849         for (int i = 0; i < structures.size(); i++) {
850             if (!parse_structure(structures.elementAt(i))) {
851                 ok = false;
852             }
853         }
854
855         ParseNodeVector globals = pn.getChildren("global");
856
857         for (int i = 0; i < globals.size(); i++) {
858             if (!parse_global(globals.elementAt(i))) {
859                 ok = false;
860             }
861         }
862
863         // ok, all the structures have been parsed, now we gotta type check       
864
865         Enumeration types = stTypes.getDescriptors();
866         while (types.hasMoreElements()) {
867             TypeDescriptor t = (TypeDescriptor) types.nextElement();
868
869             if (t instanceof ReservedTypeDescriptor) {
870                 // we don't need to check reserved types
871             } else if (t instanceof StructureTypeDescriptor) {
872                 
873                 StructureTypeDescriptor type = (StructureTypeDescriptor) t;
874                 TypeDescriptor subtype = type.getSubType();
875
876                 // check that the subtype is valid
877                 if (subtype instanceof MissingTypeDescriptor) {
878                     TypeDescriptor newtype = lookupType(subtype.getSymbol());
879                     if (newtype == null) {
880                         // subtype not defined anywheere
881                         // #TBD#: somehow determine how we can get the original parsenode (global function?)
882                         er.report(null, "Undefined subtype '" + subtype.getSymbol() + "'");
883                         ok = false;
884                     } else {
885                         type.setSubType(newtype);
886                     }
887                 }
888
889                 Iterator fields = type.getFields();
890
891                 while (fields.hasNext()) {
892                     FieldDescriptor field = (FieldDescriptor) fields.next();                        
893                     TypeDescriptor fieldtype = field.getType();
894
895                     assert fieldtype != null;
896
897                     // check that the type is valid
898                     if (fieldtype instanceof MissingTypeDescriptor) {
899                         TypeDescriptor newtype = lookupType(fieldtype.getSymbol());
900                         if (newtype == null) {
901                             // type never defined
902                             // #TBD#: replace new ParseNode with original parsenode
903                             er.report(null, "Undefined type '" + fieldtype.getSymbol() + "'");
904                             ok = false;
905                         } else {
906                             assert newtype != null;
907                             field.setType(newtype);
908                         }
909                     }                        
910                 }
911
912                 Iterator labels = type.getLabels();
913
914                 while (labels.hasNext()) {
915                     LabelDescriptor label = (LabelDescriptor) labels.next();
916                     TypeDescriptor labeltype = label.getType();
917
918                     assert labeltype != null;
919
920                     // check that the type is valid
921                     if (labeltype instanceof MissingTypeDescriptor) {
922                         TypeDescriptor newtype = lookupType(labeltype.getSymbol());
923                         if (newtype == null) {
924                             // type never defined
925                             // #TBD#: replace new ParseNode with original parsenode
926                             er.report(null, "Undefined type '" + labeltype.getSymbol() + "'");
927                             ok = false;
928                         } else {
929                             assert newtype != null;
930                             label.setType(newtype);
931                         }
932                     }
933                 }
934                 
935             } else {
936                 throw new IRException("shouldn't be any other typedescriptor classes");
937             }
938         }
939
940         if (!ok) {
941             return false;
942         }
943
944         types = stTypes.getDescriptors();
945
946         while (types.hasMoreElements()) {
947             TypeDescriptor t = (TypeDescriptor) types.nextElement();
948
949             if (t instanceof ReservedTypeDescriptor) {
950                 // we don't need to check reserved types
951             } else if (t instanceof StructureTypeDescriptor) {
952                 
953                 StructureTypeDescriptor type = (StructureTypeDescriptor)t;
954                 TypeDescriptor subtype = type.getSubType();
955                 Iterator fields = type.getFields();
956
957                 while (fields.hasNext()) {
958                     FieldDescriptor field = (FieldDescriptor) fields.next();                        
959
960                     if (field instanceof ArrayDescriptor) {
961                         ArrayDescriptor ad = (ArrayDescriptor) field;
962                         Expr indexbound = ad.getIndexBound();
963                         TypeDescriptor indextype = indexbound.typecheck(new SemanticAnalyzer() {
964                                 public IRErrorReporter getErrorReporter() { return er; }
965                                 public SymbolTable getSymbolTable() { return stGlobals; }
966                             });
967
968                         if (indextype == null) {
969                             ok = false;
970                         } else if (indextype != ReservedTypeDescriptor.INT) {
971                             er.report(null, "'" + type.getSymbol() + "." + field.getSymbol() + "' index bounds must be type 'int' not '" + indextype.getSymbol() + "'");
972                             ok = false;
973                         }
974                     }
975                 }
976
977                 Iterator labels = type.getLabels();
978
979                 while (labels.hasNext()) {
980                     LabelDescriptor label = (LabelDescriptor) labels.next();
981                     Expr index = label.getIndex();
982
983                     if (index != null) {
984                         TypeDescriptor indextype = index.typecheck(new SemanticAnalyzer() {
985                                 public IRErrorReporter getErrorReporter() { return er; }
986                                 public SymbolTable getSymbolTable() { return stGlobals; }
987                             });
988                         
989                         if (indextype != ReservedTypeDescriptor.INT) {
990                             er.report(null, "Label '" + type.getSymbol() + "." + label.getSymbol() + "' index must be type 'int' not '" + indextype.getSymbol() + "'");
991                             ok = false;
992                         }
993                     }
994                 }
995                 
996             } else {
997                 throw new IRException("shouldn't be any other typedescriptor classes");
998             }
999
1000         }
1001
1002         // #TBD#: need to make sure that no cycles exist in any of the declarations or subtypes
1003         // subtypes, of course, are not real subtypes, they are merely a way to validate a 
1004         // cast, i believe
1005
1006         return ok;
1007     }
1008
1009     private boolean parse_global(ParseNode pn) {
1010         if (!precheck(pn, "global")) {
1011             return false;
1012         }
1013
1014         String name = pn.getChild("name").getTerminal();
1015         assert name != null;
1016
1017         String type = pn.getChild("type").getTerminal();
1018         assert type != null;
1019         TypeDescriptor td = lookupType(type);
1020         assert td != null;
1021         assert !(td instanceof ReservedTypeDescriptor);
1022         
1023         if (stGlobals.contains(name)) {
1024             /* redefinition of global */
1025             er.report(pn, "Redefinition of global '" + name + "'");
1026             return false;
1027         }
1028
1029         stGlobals.add(new VarDescriptor(name, name, td));
1030         return true;
1031     }
1032
1033     private boolean parse_structure(ParseNode pn) {
1034         if (!precheck(pn, "structure")) {
1035             return false;
1036         }
1037
1038         boolean ok = true;
1039         String typename = pn.getChild("name").getTerminal();
1040         StructureTypeDescriptor type = new StructureTypeDescriptor(typename);
1041         
1042         if (pn.getChild("subtype") != null) {
1043             // has a subtype, lets try to resolve it
1044             String subtype = pn.getChild("subtype").getTerminal();
1045
1046             if (subtype.equals(typename)) {
1047                 // Semantic Error: cyclic inheritance
1048                 er.report(pn, "Cyclic inheritance prohibited");
1049                 ok = false;
1050             }
1051
1052             /* lookup the type to get the type descriptor */
1053             type.setSubType(lookupType(subtype, CREATE_MISSING));
1054         }
1055
1056         // set the current type so that the recursive parses on the labels
1057         // and fields can add themselves automatically to the current type         
1058         dCurrentType = type;
1059         
1060         // parse the labels and fields
1061         if (!parse_labelsandfields(pn.getChild("lf"))) {
1062             ok = false;
1063         }
1064
1065         if (stTypes.contains(typename)) {
1066             er.report(pn, "Redefinition of type '" + typename + "'");
1067             ok = false;
1068         } else {
1069             stTypes.add(type);
1070         }
1071         
1072         return ok;
1073     }
1074
1075     private boolean parse_labelsandfields(ParseNode pn) {
1076         if (!precheck(pn, "lf")) {
1077             return false;
1078         }
1079
1080         boolean ok = true;
1081      
1082         // check the fields first (need the field names 
1083         // to type check the labels)
1084         if (!parse_fields(pn.getChild("fields"))) {
1085             ok = false;
1086         }
1087
1088         // check the labels now that all the fields are sorted
1089         if (!parse_labels(pn.getChild("labels"))) {
1090             ok = false;
1091         }
1092
1093         return ok;
1094     }
1095
1096     private boolean parse_fields(ParseNode pn) {
1097         if (!precheck(pn, "fields")) {
1098             return false;
1099         }
1100         
1101         boolean ok = true;
1102         
1103         /* NOTE: because the order of the fields is important when defining a data structure,
1104            and because the order is defined by the order of the fields defined in the field
1105            vector, its important that the parser returns the fields in the correct order */
1106         
1107         ParseNodeVector fields = pn.getChildren();
1108
1109         for (int i = 0; i < fields.size(); i++) {
1110             ParseNode field = fields.elementAt(i);            
1111             FieldDescriptor fd;
1112             boolean reserved;
1113             String name = null;
1114
1115             if (field.getChild("reserved") != null) {
1116                 // reserved field
1117                 // #TBD#: it will be necessary for reserved field descriptors to generate
1118                 // a unique symbol for the type descriptor requires it for its hashtable
1119                 fd = new ReservedFieldDescriptor();
1120                 reserved = true;
1121             } else {
1122                 name = field.getChild("name").getTerminal();                
1123                 fd = new FieldDescriptor(name);
1124                 reserved = false;
1125             }
1126
1127             String type = field.getChild("type").getTerminal();
1128             boolean ptr = field.getChild("*") != null;
1129             fd.setPtr(ptr);
1130
1131             fd.setType(lookupType(type, CREATE_MISSING));
1132
1133             if (field.getChild("index") != null) {
1134                 // field is an array, so create an array descriptor to wrap the 
1135                 // field descriptor and then replace the top level field descriptor with
1136                 // this array descriptor
1137                 Expr expr = parse_expr(field.getChild("index").getChild("expr"));
1138                 if (expr == null) {
1139                     // #ATTN#: do we really want to return an exception here?
1140                     throw new IRException("invalid index expression");
1141                 }
1142                 ArrayDescriptor ad = new ArrayDescriptor(fd, expr);               
1143                 fd = ad;
1144             }
1145
1146             // add the current field to the current type
1147             if (reserved == false) {
1148                 // lets double check that we are redefining a field
1149                 if (dCurrentType.getField(name) != null) {
1150                     // Semantic Error: field redefinition 
1151                     er.report(pn, "Redefinition of field '" + name + "'");
1152                     ok = false;
1153                 } else {
1154                     dCurrentType.addField(fd);
1155                 }
1156             } else {
1157                 dCurrentType.addField(fd);
1158             }
1159         }
1160         
1161         return ok;
1162     }
1163
1164     private boolean parse_labels(ParseNode pn) {
1165         if (!precheck(pn, "labels")) {
1166             return false;
1167         }
1168
1169         boolean ok = true;
1170
1171         /* NOTE: parse_labels should be called after the fields have been parsed because any
1172            labels not found in the field set of the current type will be flagged as errors */
1173
1174         ParseNodeVector labels = pn.getChildren();
1175
1176         for (int i = 0; i < labels.size(); i++) {           
1177             ParseNode label = labels.elementAt(i);
1178             String name = label.getChild("name").getTerminal();
1179             LabelDescriptor ld = new LabelDescriptor(name); 
1180
1181             if (label.getChild("index") != null) {
1182                 Expr expr = parse_expr(label.getChild("index").getChild("expr"));
1183                 if (expr == null) {
1184                     /* #ATTN#: do we really want to return an exception here? */
1185                     throw new IRException("Invalid expression");
1186                 }
1187                 ld.setIndex(expr);                
1188             } 
1189             
1190             String type = label.getChild("type").getTerminal();
1191
1192             ld.setType(lookupType(type, CREATE_MISSING));
1193                         
1194             String field = label.getChild("field").getTerminal();           
1195             FieldDescriptor fd = dCurrentType.getField(field);
1196
1197             if (fd == null) {
1198                 /* Semantic Error: Undefined field in label */
1199                 er.report(label, "Undefined field '" + field + "' in label");
1200                 ok = false;
1201             } else {
1202                 ld.setField(fd);
1203             }
1204
1205             /* add label to current type */
1206             if (dCurrentType.getLabel(name) != null) {
1207                 /* semantic error: label redefinition */
1208                 er.report(pn, "Redefinition of label '" + name + "'");
1209                 ok = false;
1210             } else {
1211                 dCurrentType.addLabel(ld);            
1212             }
1213         }
1214
1215         return ok;
1216     }
1217
1218     private Expr parse_expr(ParseNode pn) {
1219         if (!precheck(pn, "expr")) {
1220             return null;
1221         }
1222
1223         if (pn.getChild("var") != null) {
1224             // we've got a variable reference... we'll have to scope check it later
1225             // when we are completely done... there are also some issues of cyclic definitions
1226             return new VarExpr(pn.getChild("var").getTerminal());
1227         } else if (pn.getChild("literal") != null) {
1228             return parse_literal(pn.getChild("literal"));
1229         } else if (pn.getChild("operator") != null) {
1230             return parse_operator(pn.getChild("operator"));
1231         } else if (pn.getChild("relation") != null) {
1232             return parse_relation(pn.getChild("relation"));
1233         } else if (pn.getChild("sizeof") != null) {
1234             return parse_sizeof(pn.getChild("sizeof"));
1235         } else if (pn.getChild("simple_expr") != null) {
1236             return parse_simple_expr(pn.getChild("simple_expr"));        
1237         } else if (pn.getChild("elementof") != null) {
1238             return parse_elementof(pn.getChild("elementof"));        
1239         } else if (pn.getChild("tupleof") != null) {
1240             return parse_tupleof(pn.getChild("tupleof"));        
1241         } else if (pn.getChild("isvalid") != null) {
1242             er.report(pn, "Operation 'isvalid' is currently unsupported.");
1243             return null;
1244         } else {
1245             er.report(pn, "Unknown or invalid expression type '" + pn.getTerminal() + "'");
1246             return null;
1247         }            
1248     }
1249
1250     private Expr parse_elementof(ParseNode pn) {
1251         if (!precheck(pn, "elementof")) {
1252             return null;
1253         }
1254
1255         /* get setname */
1256         String setname = pn.getChild("name").getTerminal();
1257         assert setname != null;
1258         SetDescriptor sd = lookupSet(setname);
1259
1260         if (sd == null) {
1261             er.report(pn, "Undefined set '" + setname + "'");
1262             return null;
1263         }
1264
1265         /* get left side expr */
1266         Expr expr = parse_expr(pn.getChild("expr"));
1267         
1268         if (expr == null) {
1269             return null;
1270         }
1271
1272         return new ElementOfExpr(expr, sd);
1273     }
1274
1275     private Expr parse_tupleof(ParseNode pn) {
1276         if (!precheck(pn, "tupleof")) {
1277             return null;
1278         }
1279         
1280         /* get relation */
1281         String relname = pn.getChild("name").getTerminal();
1282         assert relname != null;
1283         RelationDescriptor rd = lookupRelation(relname);
1284
1285         if (rd == null) {
1286             er.report(pn, "Undefined relation '" + relname + "'");
1287             return null;
1288         }
1289
1290         Expr left = parse_expr(pn.getChild("left").getChild("expr"));
1291         Expr right = parse_expr(pn.getChild("right").getChild("expr"));
1292
1293         if ((left == null) || (right == null)) {
1294             return null;
1295         }
1296
1297         return new TupleOfExpr(left, right, rd);
1298     }
1299
1300     private Expr parse_simple_expr(ParseNode pn) {
1301         if (!precheck(pn, "simple_expr")) {
1302             return null;
1303         }
1304
1305         // only locations so far
1306         return parse_location(pn.getChild("location"));
1307     }
1308
1309     private Expr parse_location(ParseNode pn) {
1310         if (!precheck(pn, "location")) {
1311             return null;
1312         }
1313
1314         if (pn.getChild("var") != null) {
1315             // should be changed into a namespace check */
1316             return new VarExpr(pn.getChild("var").getTerminal());
1317         } else if (pn.getChild("cast") != null) {
1318             return parse_cast(pn.getChild("cast"));
1319         } else if (pn.getChild("dot") != null) {
1320             return parse_dot(pn.getChild("dot"));
1321         } else {
1322             throw new IRException();
1323         }
1324     }
1325
1326     private RelationExpr parse_relation(ParseNode pn) {
1327         if (!precheck(pn, "relation")) {
1328             return null;
1329         }
1330
1331         RelationExpr re = new RelationExpr();
1332
1333         /* get quantitifer var */
1334         VarDescriptor vd = parse_quantifiervar(pn.getChild("quantifiervar"));
1335         
1336         if (vd == null) {
1337             return null;
1338         }
1339
1340         // #TBD#: bad name
1341         re.setDomain(vd);
1342
1343         /* grab list of relations */
1344         ParseNodeVector relations = pn.getChild("relations").getChildren();
1345         assert relations.size() >= 1;
1346         
1347         // #TBD#: verify that the relations are in the correct order and that the 
1348         // first relation is getting the correct "inv" assigned from "expr"
1349
1350         /* lets verify that these relations are defined */
1351         for (int i = 0; i < relations.size(); i++) {           
1352             ParseNode rn = relations.elementAt(i);
1353             String relname = rn.getLabel();
1354             boolean inverse = rn.getChild("inv") != null;
1355             
1356             RelationDescriptor relation = lookupRelation(relname);
1357             
1358             if (relation == null) {
1359                 /* Semantic Error: relation not definied" */
1360                 er.report(rn, "Undefined relation '" + relname + "'");
1361                 return null;
1362             }                       
1363
1364             re.setRelation(relation, inverse);
1365             
1366             /* if we are not at end of list then create new relation and 
1367                replace it to chain the relations */
1368             if (i + 1 < relations.size()) {
1369                 re = new RelationExpr(re);  // should create relation with domain as older 're'
1370             } 
1371         }
1372
1373         return re;                  
1374     }
1375
1376     private SizeofExpr parse_sizeof(ParseNode pn) {
1377         if (!precheck(pn, "sizeof")) {
1378             return null;
1379         }
1380
1381         /* get setexpr */
1382         SetExpr setexpr = parse_setexpr(pn.getChild("setexpr"));
1383         
1384         if (setexpr == null) {
1385             return null;
1386         }
1387
1388         return new SizeofExpr(setexpr);
1389     }
1390
1391     private CastExpr parse_cast(ParseNode pn) {
1392         if (!precheck(pn, "cast")) {
1393             return null;
1394         }
1395
1396         /* get type */
1397         String typename = pn.getChild("type").getTerminal();
1398         assert typename != null;
1399         TypeDescriptor type = lookupType(typename);
1400
1401         if (type == null) {
1402             /* semantic error: undefined type in cast */
1403             er.report(pn, "Undefined type '" + typename + "' in cast operator");
1404             return null;
1405         } 
1406
1407         /* get expr */
1408         Expr expr = parse_simple_expr(pn.getChild("simple_expr"));
1409         
1410         if (expr == null) {
1411             return null;
1412         } 
1413
1414         return new CastExpr(type, expr);
1415     }
1416
1417     private SetExpr parse_setexpr(ParseNode pn) {
1418         if (!precheck(pn, "setexpr")) {
1419             return null;
1420         }
1421
1422         // #TBD#: setexpr and parse_relation seem to be cousins... is there a reduction/refactor possible?
1423
1424         if (pn.getChild("set") != null) {
1425             String setname = pn.getChild("set").getTerminal();
1426             assert setname != null;
1427             SetDescriptor sd = lookupSet(setname);
1428
1429             if (sd == null) {
1430                 er.report(pn, "Unknown or undefined set '" + setname + "'");             
1431                 return null;
1432             } else {                         
1433                 return new SetExpr(sd);
1434             }
1435         } else if (pn.getChild("dot") != null) {
1436             VarDescriptor vd = parse_quantifiervar(pn.getChild("dot").getChild("quantifiervar"));
1437             RelationDescriptor relation = lookupRelation(pn.getChild("dot").getChild("relation").getTerminal());
1438             return new ImageSetExpr(vd, relation);
1439         } else if (pn.getChild("dotinv") != null) {
1440             VarDescriptor vd = parse_quantifiervar(pn.getChild("dotinv").getChild("quantifiervar"));
1441             RelationDescriptor relation = lookupRelation(pn.getChild("dotinv").getChild("relation").getTerminal());
1442             return new ImageSetExpr(ImageSetExpr.INVERSE, vd, relation);
1443         } else {
1444             throw new IRException();
1445         }
1446     }
1447
1448     private VarDescriptor parse_quantifiervar(ParseNode pn) {
1449         if (!precheck(pn, "quantifiervar")) {
1450             return null;
1451         }
1452
1453         /* get var */
1454         String varname = pn.getTerminal();
1455         assert varname != null;
1456         
1457         /* NOTE: quantifier var's are only found in the constraints and
1458            model definitions... therefore we can do a semantic check here 
1459            to make sure that the variables exist in the symbol table */
1460
1461         /* NOTE: its assumed that the symbol table stack is appropriately
1462            set up with the parent quantifier symbol table */
1463         assert !sts.empty();          
1464
1465         /* do semantic check and if valid, add it to symbol table
1466            and add it to the quantifier as well */
1467         if (sts.peek().contains(varname)) {
1468             return new VarDescriptor(varname);
1469         } else {
1470             /* Semantic Error: undefined variable */
1471             er.report(pn, "Undefined variable '" + varname + "'");
1472             return null;
1473         }
1474     }
1475     
1476     private LiteralExpr parse_literal(ParseNode pn) {
1477         if (!precheck(pn, "literal")) {
1478             return null;
1479         }
1480
1481         if (pn.getChild("boolean") != null) {
1482             if (pn.getChild("boolean").getChild("true") != null) {
1483                 return new BooleanLiteralExpr(true);
1484             } else {
1485                 return new BooleanLiteralExpr(false);
1486             } 
1487         } else if (pn.getChild("decimal") != null) {            
1488             String integer = pn.getChild("decimal").getTerminal();
1489
1490             /* Check for integer literal overflow */
1491             BigInteger intLitBI = new BigInteger(integer);
1492             BigInteger intMax = new BigInteger("" + Integer.MAX_VALUE);
1493             BigInteger intMin = new BigInteger("" + Integer.MIN_VALUE);
1494             int value;
1495
1496             if (intLitBI.compareTo(intMin) < 0) {
1497                 value = Integer.MIN_VALUE;
1498                 er.warn(pn, "Integer literal overflow");
1499             } else if (intLitBI.compareTo(intMax) > 0) {
1500                 value = Integer.MAX_VALUE;
1501                 er.warn(pn, "Integer literal overflow");
1502             } else {
1503                 /* no truncation needed */
1504                 value = Integer.parseInt(integer);
1505             }
1506
1507             return new IntegerLiteralExpr(value);
1508         } else if (pn.getChild("token") != null) {
1509             return new TokenLiteralExpr(pn.getChild("token").getTerminal());
1510         } else if (pn.getChild("string") != null) {
1511             throw new IRException("string unsupported");
1512         } else if (pn.getChild("char") != null) {
1513             throw new IRException("char unsupported");
1514         } else {
1515             throw new IRException("unknown literal expression type.");
1516         }
1517     }
1518
1519     private OpExpr parse_operator(ParseNode pn) {
1520         if (!precheck(pn, "operator")) {
1521             return null; 
1522         }
1523
1524         String opname = pn.getChild("op").getTerminal();
1525
1526         Opcode opcode;
1527
1528         if (opname.equals("add")) {
1529             opcode = Opcode.ADD;
1530         } else if (opname.equals("sub")) {
1531             opcode = Opcode.SUB;
1532         } else if (opname.equals("mult")) {
1533             opcode = Opcode.MULT;
1534         } else if (opname.equals("div")) {
1535             opcode = Opcode.DIV;
1536         } else if (opname.equals("and")) {
1537             opcode = Opcode.AND;
1538         } else if (opname.equals("or")) {
1539             opcode = Opcode.OR;
1540         } else if (opname.equals("not")) {
1541             opcode = Opcode.NOT;
1542         } else if (opname.equals("gt")) {
1543             opcode = Opcode.GT;
1544         } else if (opname.equals("ge")) {
1545             opcode = Opcode.GE;
1546         } else if (opname.equals("lt")) {
1547             opcode = Opcode.LT;
1548         } else if (opname.equals("le")) {
1549             opcode = Opcode.LE;
1550         } else if (opname.equals("eq")) {
1551             opcode = Opcode.EQ;
1552         } else if (opname.equals("ne")) {
1553             opcode = Opcode.NE;
1554         } else {
1555             er.report(pn, "Unsupported operation: " + opname);
1556             return null;
1557         }
1558         
1559         Expr left = parse_expr(pn.getChild("left").getChild("expr"));
1560         Expr right = null;
1561         
1562         if (pn.getChild("right") != null) {
1563             right = parse_expr(pn.getChild("right").getChild("expr"));
1564         }
1565
1566         if (left == null) {           
1567             return null;
1568         }
1569
1570         if (right == null && opcode != Opcode.NOT) {
1571             er.report(pn, "Two arguments required.");
1572             return null;
1573         }
1574
1575         return new OpExpr(opcode, left, right);
1576     }
1577
1578     private DotExpr parse_dot(ParseNode pn) {
1579         if (!precheck(pn, "dot")) {
1580             return null;
1581         }
1582
1583         Expr left = parse_simple_expr(pn.getChild("simple_expr"));
1584
1585         if (left == null) {
1586             return null;
1587         }
1588
1589         String field = pn.getChild("field").getTerminal();
1590
1591         Expr index = null;
1592
1593         if (pn.getChild("index") != null) {
1594             index = parse_expr(pn.getChild("index").getChild("expr"));
1595             
1596             if (index == null) {
1597                 return null;
1598             }
1599         }
1600
1601         return new DotExpr(left, field, index);        
1602     }
1603
1604 }
1605