fixed problems with CDL.cup
[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("inclusion") != null) {
586             ParseNode in = pn.getChild("inclusion");
587             
588             /* get quantiifer var */
589             VarDescriptor vd = parse_quantifiervar(in.getChild("quantifiervar"));
590            
591             if (vd == null) { 
592                 return null;
593             }
594
595             /* get set expr */
596             SetExpr setexpr = parse_setexpr(in.getChild("setexpr"));
597
598             if (setexpr == null) {
599                 return null;
600             }
601
602             return new InclusionPredicate(vd, setexpr);
603         } else if (pn.getChild("sizeof") != null) {
604             ParseNode sizeof = pn.getChild("sizeof");
605             
606             /* get set expr */
607             SetExpr setexpr = parse_setexpr(sizeof.getChild("setexpr"));
608
609             if (setexpr == null) {
610                 return null;
611             }
612
613             /* get comparison operator */
614             String compareop = sizeof.getChild("compare").getTerminal();
615             Opcode opcode = Opcode.decodeFromString(opname);
616
617             if (opcode == null) {
618                 er.report(pn, "Unsupported operation: " + opname);
619                 return null;
620             }
621
622
623
624             return new InclusionPredicate(vd, setexpr);
625         } else if (pn.getChild("comparison") != null) {
626             ParseNode cn = pn.getChild("comparison");
627             
628             /* get the expr's */
629             Expr left = parse_expr(cn.getChild("left").getChild("expr"));
630             Expr right = parse_expr(cn.getChild("right").getChild("expr"));
631
632             if ((left == null) || (right == null)) {
633                 return null;
634             }
635
636             /* get comparison operator */
637             String comparison = cn.getChild("compare").getTerminal();
638             assert comparison != null;
639                          
640             return new ComparisonPredicate(comparison, left, right);
641         } else {
642             throw new IRException();
643         }       
644     }
645
646     private SetDescriptor parse_set(ParseNode pn) {
647         if (!precheck(pn, "set")) {
648             return null;
649         }
650     
651         if (pn.getChild("name") != null) {
652             String setname = pn.getChild("name").getTerminal();
653             assert setname != null;
654                 
655             if (!stSets.contains(setname)) {
656                 /* Semantic Error: unknown set */
657                 er.report(pn, "Unknown set '" + setname + "' referenced in quantifier");
658                 return null;
659             } else {
660                 /* all good, get setdescriptor */
661                 SetDescriptor sd = (SetDescriptor) stSets.get(setname);
662                 assert sd != null;
663                 return sd;
664             }            
665         } else if (pn.getChild("listofliterals") != null) {            
666             TokenSetDescriptor tokenset = new TokenSetDescriptor();
667             ParseNodeVector token_literals = pn.getChild("listofliterals").getChildren();
668             assert token_literals.size() > 0;
669             
670             for (int i = 0; i < token_literals.size(); i++) {
671                 ParseNode literal = token_literals.elementAt(i);
672                 assert literal.getLabel().equals("literal");
673                 LiteralExpr litexpr = parse_literal(literal);
674
675                 if (litexpr == null) {
676                     return null;
677                 }
678                 
679                 if (litexpr instanceof TokenLiteralExpr || litexpr instanceof IntegerLiteralExpr) {
680                     tokenset.addLiteral(litexpr);
681                 } else {
682                     er.report(literal, "Elements of a user-defined set must be of type token or integer");
683                     // return null; /* don't think we need to return null */
684                 }
685             }               
686
687             return tokenset;
688         } else {
689             throw new IRException(pn.getTerminal());
690         }
691     }
692     
693     private boolean parse_space(ParseNode pn) {
694         if (!precheck(pn, "space")) {
695             return false;
696         }
697         
698         boolean ok = true;
699         ParseNodeVector sets = pn.getChildren("setdefinition");
700         ParseNodeVector relations = pn.getChildren("relationdefinition");
701
702         assert pn.getChildren().size() == (sets.size() + relations.size());
703         
704         /* parse sets */
705         for (int i = 0; i < sets.size(); i++) {
706             if (!parse_setdefinition(sets.elementAt(i))) {
707                 ok = false;
708             }
709         }
710
711         /* parse relations */
712         for (int i = 0; i < relations.size(); i++) {
713             if (!parse_relationdefinition(relations.elementAt(i))) {
714                 ok = false;
715             }
716         }
717
718         // ok, all the spaces have been parsed, now we should typecheck and check
719         // for cycles etc.
720
721         // #TBD#: typecheck and check for cycles
722       
723         /* replace missing with actual */
724         Iterator allsets = state.stSets.descriptors();
725         
726         while (allsets.hasNext()) {
727             SetDescriptor sd = (SetDescriptor) allsets.next();
728             Vector subsets = sd.getSubsets();
729
730             for (int i = 0; i < subsets.size(); i++) {
731                 SetDescriptor subset = (SetDescriptor) subsets.elementAt(i);
732                 
733                 if (subset instanceof MissingSetDescriptor) {
734                     SetDescriptor newsubset = lookupSet(subset.getSymbol());
735
736                     if (newsubset == null) {
737                         er.report(null, "Unknown subset '" + subset.getSymbol() + "'");
738                     } else {
739                         subsets.setElementAt(newsubset, i);
740                     }
741                 }
742             }
743         }
744         
745         return ok;
746     }
747
748     private boolean parse_setdefinition(ParseNode pn) {
749         if (!precheck(pn, "setdefinition")) {
750             return false;
751         }
752         
753         boolean ok = true;
754         
755         /* get set name */
756         String setname = pn.getChild("name").getTerminal();
757         assert (setname != null);
758
759         SetDescriptor sd = new SetDescriptor(setname);
760         
761         /* get set type */
762         String settype = pn.getChild("type").getTerminal();
763         TypeDescriptor type = lookupType(settype);
764         if (type == null) {
765             er.report(pn, "Undefined type '" + settype + "'");
766             ok = false; 
767         } else {
768             sd.setType(type);
769         }
770
771         /* is this a partition? */
772         boolean partition = pn.getChild("partition") != null;
773         sd.isPartition(partition); 
774
775         /* if set has subsets, add them to set descriptor */
776         if (pn.getChild("setlist") != null) {
777             ParseNodeVector setlist = pn.getChild("setlist").getChildren();
778             
779             for (int i = 0; i < setlist.size(); i++) {
780                 String subsetname = setlist.elementAt(i).getLabel();
781                 sd.addSubset(lookupSet(subsetname, CREATE_MISSING));
782             }            
783         }
784
785         /* add set to symbol table */
786         if (stSets.contains(setname)) {
787             // Semantic Check: redefinition
788             er.report(pn, "Redefinition of set: " + setname);
789             ok = false;
790         } else {
791             stSets.add(sd);
792         }
793
794         return ok;
795     }
796
797     private boolean parse_relationdefinition(ParseNode pn) {
798         if (!precheck(pn, "relationdefinition")) {
799             return false;
800         }
801
802         boolean ok = true;
803
804         /* get relation name */
805         String relname = pn.getChild("name").getTerminal();
806         assert relname != null;
807
808         RelationDescriptor rd = new RelationDescriptor(relname);
809
810         /* check if static */
811         boolean bStatic = pn.getChild("static") != null;
812         rd.isStatic(bStatic);
813
814         /* get domain */
815         String domainsetname = pn.getChild("domain").getChild("type").getTerminal();
816         assert domainsetname != null;
817
818         /* get range */
819         String rangesetname = pn.getChild("range").getChild("type").getTerminal();
820         assert rangesetname != null;
821
822         /* get domain multiplicity */
823         String domainmult = pn.getChild("domain").getChild("mult").getTerminal();
824         assert domainmult != null;
825
826         /* get range multiplicity */
827         String rangemult = pn.getChild("range").getChild("mult").getTerminal();
828         assert rangemult != null;
829
830         /* NOTE: it is assumed that the sets have been parsed already so that the 
831            set namespace is fully populated. any missing setdescriptors for the set
832            symbol table will be assumed to be errors and reported. */
833
834         SetDescriptor domainset = lookupSet(domainsetname);
835         if (domainset == null) {
836             er.report(pn, "Undefined set '" + domainsetname + "' referenced in relation '" + relname + "'");
837             ok = false;
838         } else {
839             rd.setDomain(domainset);
840         }
841
842         SetDescriptor rangeset = lookupSet(rangesetname);
843         if (rangeset == null) {
844             er.report(pn, "Undefined set '" + rangesetname + "' referenced in relation '" + relname + "'");
845             ok = false;
846         } else {
847             rd.setRange(rangeset);
848         }
849
850         // #TBD#: eventually we'll use the multiplicities but now we don't... oh well
851
852         /* lets add the relation to the global symbol table */
853         if (!stRelations.contains(relname)) {
854             stRelations.add(rd);
855         } else {
856             er.report(pn, "Redefinition of relation '" + relname + "'");
857             ok = false;
858         }
859
860         return ok;
861     }
862
863     private boolean parse_structures(ParseNode pn) {
864         if (!precheck(pn, "structures")) {
865             return false;
866         }
867         
868         boolean ok = true;
869         ParseNodeVector structures = pn.getChildren("structure");
870
871         for (int i = 0; i < structures.size(); i++) {
872             if (!parse_structure(structures.elementAt(i))) {
873                 ok = false;
874             }
875         }
876
877         ParseNodeVector globals = pn.getChildren("global");
878
879         for (int i = 0; i < globals.size(); i++) {
880             if (!parse_global(globals.elementAt(i))) {
881                 ok = false;
882             }
883         }
884
885         // ok, all the structures have been parsed, now we gotta type check       
886
887         Enumeration types = stTypes.getDescriptors();
888         while (types.hasMoreElements()) {
889             TypeDescriptor t = (TypeDescriptor) types.nextElement();
890
891             if (t instanceof ReservedTypeDescriptor) {
892                 // we don't need to check reserved types
893             } else if (t instanceof StructureTypeDescriptor) {
894                 
895                 StructureTypeDescriptor type = (StructureTypeDescriptor) t;
896                 TypeDescriptor subtype = type.getSubType();
897
898                 // check that the subtype is valid
899                 if (subtype instanceof MissingTypeDescriptor) {
900                     TypeDescriptor newtype = lookupType(subtype.getSymbol());
901                     if (newtype == null) {
902                         // subtype not defined anywheere
903                         // #TBD#: somehow determine how we can get the original parsenode (global function?)
904                         er.report(null, "Undefined subtype '" + subtype.getSymbol() + "'");
905                         ok = false;
906                     } else {
907                         type.setSubType(newtype);
908                     }
909                 }
910
911                 Iterator fields = type.getFields();
912
913                 while (fields.hasNext()) {
914                     FieldDescriptor field = (FieldDescriptor) fields.next();                        
915                     TypeDescriptor fieldtype = field.getType();
916
917                     assert fieldtype != null;
918
919                     // check that the type is valid
920                     if (fieldtype instanceof MissingTypeDescriptor) {
921                         TypeDescriptor newtype = lookupType(fieldtype.getSymbol());
922                         if (newtype == null) {
923                             // type never defined
924                             // #TBD#: replace new ParseNode with original parsenode
925                             er.report(null, "Undefined type '" + fieldtype.getSymbol() + "'");
926                             ok = false;
927                         } else {
928                             assert newtype != null;
929                             field.setType(newtype);
930                         }
931                     }                        
932                 }
933
934                 Iterator labels = type.getLabels();
935
936                 while (labels.hasNext()) {
937                     LabelDescriptor label = (LabelDescriptor) labels.next();
938                     TypeDescriptor labeltype = label.getType();
939
940                     assert labeltype != null;
941
942                     // check that the type is valid
943                     if (labeltype instanceof MissingTypeDescriptor) {
944                         TypeDescriptor newtype = lookupType(labeltype.getSymbol());
945                         if (newtype == null) {
946                             // type never defined
947                             // #TBD#: replace new ParseNode with original parsenode
948                             er.report(null, "Undefined type '" + labeltype.getSymbol() + "'");
949                             ok = false;
950                         } else {
951                             assert newtype != null;
952                             label.setType(newtype);
953                         }
954                     }
955                 }
956                 
957             } else {
958                 throw new IRException("shouldn't be any other typedescriptor classes");
959             }
960         }
961
962         if (!ok) {
963             return false;
964         }
965
966         types = stTypes.getDescriptors();
967
968         while (types.hasMoreElements()) {
969             TypeDescriptor t = (TypeDescriptor) types.nextElement();
970
971             if (t instanceof ReservedTypeDescriptor) {
972                 // we don't need to check reserved types
973             } else if (t instanceof StructureTypeDescriptor) {
974                 
975                 StructureTypeDescriptor type = (StructureTypeDescriptor)t;
976                 TypeDescriptor subtype = type.getSubType();
977                 Iterator fields = type.getFields();
978
979                 while (fields.hasNext()) {
980                     FieldDescriptor field = (FieldDescriptor) fields.next();                        
981
982                     if (field instanceof ArrayDescriptor) {
983                         ArrayDescriptor ad = (ArrayDescriptor) field;
984                         Expr indexbound = ad.getIndexBound();
985                         TypeDescriptor indextype = indexbound.typecheck(new SemanticAnalyzer() {
986                                 public IRErrorReporter getErrorReporter() { return er; }
987                                 public SymbolTable getSymbolTable() { return stGlobals; }
988                             });
989
990                         if (indextype == null) {
991                             ok = false;
992                         } else if (indextype != ReservedTypeDescriptor.INT) {
993                             er.report(null, "'" + type.getSymbol() + "." + field.getSymbol() + "' index bounds must be type 'int' not '" + indextype.getSymbol() + "'");
994                             ok = false;
995                         }
996                     }
997                 }
998
999                 Iterator labels = type.getLabels();
1000
1001                 while (labels.hasNext()) {
1002                     LabelDescriptor label = (LabelDescriptor) labels.next();
1003                     Expr index = label.getIndex();
1004
1005                     if (index != null) {
1006                         TypeDescriptor indextype = index.typecheck(new SemanticAnalyzer() {
1007                                 public IRErrorReporter getErrorReporter() { return er; }
1008                                 public SymbolTable getSymbolTable() { return stGlobals; }
1009                             });
1010                         
1011                         if (indextype != ReservedTypeDescriptor.INT) {
1012                             er.report(null, "Label '" + type.getSymbol() + "." + label.getSymbol() + "' index must be type 'int' not '" + indextype.getSymbol() + "'");
1013                             ok = false;
1014                         }
1015                     }
1016                 }
1017                 
1018             } else {
1019                 throw new IRException("shouldn't be any other typedescriptor classes");
1020             }
1021
1022         }
1023
1024         // #TBD#: need to make sure that no cycles exist in any of the declarations or subtypes
1025         // subtypes, of course, are not real subtypes, they are merely a way to validate a 
1026         // cast, i believe
1027
1028         return ok;
1029     }
1030
1031     private boolean parse_global(ParseNode pn) {
1032         if (!precheck(pn, "global")) {
1033             return false;
1034         }
1035
1036         String name = pn.getChild("name").getTerminal();
1037         assert name != null;
1038
1039         String type = pn.getChild("type").getTerminal();
1040         assert type != null;
1041         TypeDescriptor td = lookupType(type);
1042         assert td != null;
1043         assert !(td instanceof ReservedTypeDescriptor);
1044         
1045         if (stGlobals.contains(name)) {
1046             /* redefinition of global */
1047             er.report(pn, "Redefinition of global '" + name + "'");
1048             return false;
1049         }
1050
1051         stGlobals.add(new VarDescriptor(name, name, td));
1052         return true;
1053     }
1054
1055     private boolean parse_structure(ParseNode pn) {
1056         if (!precheck(pn, "structure")) {
1057             return false;
1058         }
1059
1060         boolean ok = true;
1061         String typename = pn.getChild("name").getTerminal();
1062         StructureTypeDescriptor type = new StructureTypeDescriptor(typename);
1063         
1064         if (pn.getChild("subtype") != null) {
1065             // has a subtype, lets try to resolve it
1066             String subtype = pn.getChild("subtype").getTerminal();
1067
1068             if (subtype.equals(typename)) {
1069                 // Semantic Error: cyclic inheritance
1070                 er.report(pn, "Cyclic inheritance prohibited");
1071                 ok = false;
1072             }
1073
1074             /* lookup the type to get the type descriptor */
1075             type.setSubType(lookupType(subtype, CREATE_MISSING));
1076         }
1077
1078         // set the current type so that the recursive parses on the labels
1079         // and fields can add themselves automatically to the current type         
1080         dCurrentType = type;
1081         
1082         // parse the labels and fields
1083         if (!parse_labelsandfields(pn.getChild("lf"))) {
1084             ok = false;
1085         }
1086
1087         if (stTypes.contains(typename)) {
1088             er.report(pn, "Redefinition of type '" + typename + "'");
1089             ok = false;
1090         } else {
1091             stTypes.add(type);
1092         }
1093         
1094         return ok;
1095     }
1096
1097     private boolean parse_labelsandfields(ParseNode pn) {
1098         if (!precheck(pn, "lf")) {
1099             return false;
1100         }
1101
1102         boolean ok = true;
1103      
1104         // check the fields first (need the field names 
1105         // to type check the labels)
1106         if (!parse_fields(pn.getChild("fields"))) {
1107             ok = false;
1108         }
1109
1110         // check the labels now that all the fields are sorted
1111         if (!parse_labels(pn.getChild("labels"))) {
1112             ok = false;
1113         }
1114
1115         return ok;
1116     }
1117
1118     private boolean parse_fields(ParseNode pn) {
1119         if (!precheck(pn, "fields")) {
1120             return false;
1121         }
1122         
1123         boolean ok = true;
1124         
1125         /* NOTE: because the order of the fields is important when defining a data structure,
1126            and because the order is defined by the order of the fields defined in the field
1127            vector, its important that the parser returns the fields in the correct order */
1128         
1129         ParseNodeVector fields = pn.getChildren();
1130
1131         for (int i = 0; i < fields.size(); i++) {
1132             ParseNode field = fields.elementAt(i);            
1133             FieldDescriptor fd;
1134             boolean reserved;
1135             String name = null;
1136
1137             if (field.getChild("reserved") != null) {
1138                 // reserved field
1139                 // #TBD#: it will be necessary for reserved field descriptors to generate
1140                 // a unique symbol for the type descriptor requires it for its hashtable
1141                 fd = new ReservedFieldDescriptor();
1142                 reserved = true;
1143             } else {
1144                 name = field.getChild("name").getTerminal();                
1145                 fd = new FieldDescriptor(name);
1146                 reserved = false;
1147             }
1148
1149             String type = field.getChild("type").getTerminal();
1150             boolean ptr = field.getChild("*") != null;
1151             fd.setPtr(ptr);
1152
1153             fd.setType(lookupType(type, CREATE_MISSING));
1154
1155             if (field.getChild("index") != null) {
1156                 // field is an array, so create an array descriptor to wrap the 
1157                 // field descriptor and then replace the top level field descriptor with
1158                 // this array descriptor
1159                 Expr expr = parse_expr(field.getChild("index").getChild("expr"));
1160                 if (expr == null) {
1161                     // #ATTN#: do we really want to return an exception here?
1162                     throw new IRException("invalid index expression");
1163                 }
1164                 ArrayDescriptor ad = new ArrayDescriptor(fd, expr);               
1165                 fd = ad;
1166             }
1167
1168             // add the current field to the current type
1169             if (reserved == false) {
1170                 // lets double check that we are redefining a field
1171                 if (dCurrentType.getField(name) != null) {
1172                     // Semantic Error: field redefinition 
1173                     er.report(pn, "Redefinition of field '" + name + "'");
1174                     ok = false;
1175                 } else {
1176                     dCurrentType.addField(fd);
1177                 }
1178             } else {
1179                 dCurrentType.addField(fd);
1180             }
1181         }
1182         
1183         return ok;
1184     }
1185
1186     private boolean parse_labels(ParseNode pn) {
1187         if (!precheck(pn, "labels")) {
1188             return false;
1189         }
1190
1191         boolean ok = true;
1192
1193         /* NOTE: parse_labels should be called after the fields have been parsed because any
1194            labels not found in the field set of the current type will be flagged as errors */
1195
1196         ParseNodeVector labels = pn.getChildren();
1197
1198         for (int i = 0; i < labels.size(); i++) {           
1199             ParseNode label = labels.elementAt(i);
1200             String name = label.getChild("name").getTerminal();
1201             LabelDescriptor ld = new LabelDescriptor(name); 
1202
1203             if (label.getChild("index") != null) {
1204                 Expr expr = parse_expr(label.getChild("index").getChild("expr"));
1205                 if (expr == null) {
1206                     /* #ATTN#: do we really want to return an exception here? */
1207                     throw new IRException("Invalid expression");
1208                 }
1209                 ld.setIndex(expr);                
1210             } 
1211             
1212             String type = label.getChild("type").getTerminal();
1213
1214             ld.setType(lookupType(type, CREATE_MISSING));
1215                         
1216             String field = label.getChild("field").getTerminal();           
1217             FieldDescriptor fd = dCurrentType.getField(field);
1218
1219             if (fd == null) {
1220                 /* Semantic Error: Undefined field in label */
1221                 er.report(label, "Undefined field '" + field + "' in label");
1222                 ok = false;
1223             } else {
1224                 ld.setField(fd);
1225             }
1226
1227             /* add label to current type */
1228             if (dCurrentType.getLabel(name) != null) {
1229                 /* semantic error: label redefinition */
1230                 er.report(pn, "Redefinition of label '" + name + "'");
1231                 ok = false;
1232             } else {
1233                 dCurrentType.addLabel(ld);            
1234             }
1235         }
1236
1237         return ok;
1238     }
1239
1240     private Expr parse_expr(ParseNode pn) {
1241         if (!precheck(pn, "expr")) {
1242             return null;
1243         }
1244
1245         if (pn.getChild("var") != null) {
1246             // we've got a variable reference... we'll have to scope check it later
1247             // when we are completely done... there are also some issues of cyclic definitions
1248             return new VarExpr(pn.getChild("var").getTerminal());
1249         } else if (pn.getChild("literal") != null) {
1250             return parse_literal(pn.getChild("literal"));
1251         } else if (pn.getChild("operator") != null) {
1252             return parse_operator(pn.getChild("operator"));
1253         } else if (pn.getChild("relation") != null) {
1254             return parse_relation(pn.getChild("relation"));
1255         } else if (pn.getChild("sizeof") != null) {
1256             return parse_sizeof(pn.getChild("sizeof"));
1257         } else if (pn.getChild("simple_expr") != null) {
1258             return parse_simple_expr(pn.getChild("simple_expr"));        
1259         } else if (pn.getChild("elementof") != null) {
1260             return parse_elementof(pn.getChild("elementof"));        
1261         } else if (pn.getChild("tupleof") != null) {
1262             return parse_tupleof(pn.getChild("tupleof"));        
1263         } else if (pn.getChild("isvalid") != null) {
1264             er.report(pn, "Operation 'isvalid' is currently unsupported.");
1265             return null;
1266         } else {
1267             er.report(pn, "Unknown or invalid expression type '" + pn.getTerminal() + "'");
1268             return null;
1269         }            
1270     }
1271
1272     private Expr parse_elementof(ParseNode pn) {
1273         if (!precheck(pn, "elementof")) {
1274             return null;
1275         }
1276
1277         /* get setname */
1278         String setname = pn.getChild("name").getTerminal();
1279         assert setname != null;
1280         SetDescriptor sd = lookupSet(setname);
1281
1282         if (sd == null) {
1283             er.report(pn, "Undefined set '" + setname + "'");
1284             return null;
1285         }
1286
1287         /* get left side expr */
1288         Expr expr = parse_expr(pn.getChild("expr"));
1289         
1290         if (expr == null) {
1291             return null;
1292         }
1293
1294         return new ElementOfExpr(expr, sd);
1295     }
1296
1297     private Expr parse_tupleof(ParseNode pn) {
1298         if (!precheck(pn, "tupleof")) {
1299             return null;
1300         }
1301         
1302         /* get relation */
1303         String relname = pn.getChild("name").getTerminal();
1304         assert relname != null;
1305         RelationDescriptor rd = lookupRelation(relname);
1306
1307         if (rd == null) {
1308             er.report(pn, "Undefined relation '" + relname + "'");
1309             return null;
1310         }
1311
1312         Expr left = parse_expr(pn.getChild("left").getChild("expr"));
1313         Expr right = parse_expr(pn.getChild("right").getChild("expr"));
1314
1315         if ((left == null) || (right == null)) {
1316             return null;
1317         }
1318
1319         return new TupleOfExpr(left, right, rd);
1320     }
1321
1322     private Expr parse_simple_expr(ParseNode pn) {
1323         if (!precheck(pn, "simple_expr")) {
1324             return null;
1325         }
1326
1327         // only locations so far
1328         return parse_location(pn.getChild("location"));
1329     }
1330
1331     private Expr parse_location(ParseNode pn) {
1332         if (!precheck(pn, "location")) {
1333             return null;
1334         }
1335
1336         if (pn.getChild("var") != null) {
1337             // should be changed into a namespace check */
1338             return new VarExpr(pn.getChild("var").getTerminal());
1339         } else if (pn.getChild("cast") != null) {
1340             return parse_cast(pn.getChild("cast"));
1341         } else if (pn.getChild("dot") != null) {
1342             return parse_dot(pn.getChild("dot"));
1343         } else {
1344             throw new IRException();
1345         }
1346     }
1347
1348     private RelationExpr parse_relation(ParseNode pn) {
1349         if (!precheck(pn, "relation")) {
1350             return null;
1351         }
1352
1353         RelationExpr re = new RelationExpr();
1354
1355         /* get quantitifer var */
1356         VarDescriptor vd = parse_quantifiervar(pn.getChild("quantifiervar"));
1357         
1358         if (vd == null) {
1359             return null;
1360         }
1361
1362         // #TBD#: bad name
1363         re.setDomain(vd);
1364
1365         /* grab list of relations */
1366         ParseNodeVector relations = pn.getChild("relations").getChildren();
1367         assert relations.size() >= 1;
1368         
1369         // #TBD#: verify that the relations are in the correct order and that the 
1370         // first relation is getting the correct "inv" assigned from "expr"
1371
1372         /* lets verify that these relations are defined */
1373         for (int i = 0; i < relations.size(); i++) {           
1374             ParseNode rn = relations.elementAt(i);
1375             String relname = rn.getLabel();
1376             boolean inverse = rn.getChild("inv") != null;
1377             
1378             RelationDescriptor relation = lookupRelation(relname);
1379             
1380             if (relation == null) {
1381                 /* Semantic Error: relation not definied" */
1382                 er.report(rn, "Undefined relation '" + relname + "'");
1383                 return null;
1384             }                       
1385
1386             re.setRelation(relation, inverse);
1387             
1388             /* if we are not at end of list then create new relation and 
1389                replace it to chain the relations */
1390             if (i + 1 < relations.size()) {
1391                 re = new RelationExpr(re);  // should create relation with domain as older 're'
1392             } 
1393         }
1394
1395         return re;                  
1396     }
1397
1398     private SizeofExpr parse_sizeof(ParseNode pn) {
1399         if (!precheck(pn, "sizeof")) {
1400             return null;
1401         }
1402
1403         /* get setexpr */
1404         SetExpr setexpr = parse_setexpr(pn.getChild("setexpr"));
1405         
1406         if (setexpr == null) {
1407             return null;
1408         }
1409
1410         return new SizeofExpr(setexpr);
1411     }
1412
1413     private CastExpr parse_cast(ParseNode pn) {
1414         if (!precheck(pn, "cast")) {
1415             return null;
1416         }
1417
1418         /* get type */
1419         String typename = pn.getChild("type").getTerminal();
1420         assert typename != null;
1421         TypeDescriptor type = lookupType(typename);
1422
1423         if (type == null) {
1424             /* semantic error: undefined type in cast */
1425             er.report(pn, "Undefined type '" + typename + "' in cast operator");
1426             return null;
1427         } 
1428
1429         /* get expr */
1430         Expr expr = parse_simple_expr(pn.getChild("simple_expr"));
1431         
1432         if (expr == null) {
1433             return null;
1434         } 
1435
1436         return new CastExpr(type, expr);
1437     }
1438
1439     private SetExpr parse_setexpr(ParseNode pn) {
1440         if (!precheck(pn, "setexpr")) {
1441             return null;
1442         }
1443
1444         // #TBD#: setexpr and parse_relation seem to be cousins... is there a reduction/refactor possible?
1445
1446         if (pn.getChild("set") != null) {
1447             String setname = pn.getChild("set").getTerminal();
1448             assert setname != null;
1449             SetDescriptor sd = lookupSet(setname);
1450
1451             if (sd == null) {
1452                 er.report(pn, "Unknown or undefined set '" + setname + "'");             
1453                 return null;
1454             } else {                         
1455                 return new SetExpr(sd);
1456             }
1457         } else if (pn.getChild("dot") != null) {
1458             VarDescriptor vd = parse_quantifiervar(pn.getChild("dot").getChild("quantifiervar"));
1459             RelationDescriptor relation = lookupRelation(pn.getChild("dot").getChild("relation").getTerminal());
1460             return new ImageSetExpr(vd, relation);
1461         } else if (pn.getChild("dotinv") != null) {
1462             VarDescriptor vd = parse_quantifiervar(pn.getChild("dotinv").getChild("quantifiervar"));
1463             RelationDescriptor relation = lookupRelation(pn.getChild("dotinv").getChild("relation").getTerminal());
1464             return new ImageSetExpr(ImageSetExpr.INVERSE, vd, relation);
1465         } else {
1466             throw new IRException();
1467         }
1468     }
1469
1470     private VarDescriptor parse_quantifiervar(ParseNode pn) {
1471         if (!precheck(pn, "quantifiervar")) {
1472             return null;
1473         }
1474
1475         /* get var */
1476         String varname = pn.getTerminal();
1477         assert varname != null;
1478         
1479         /* NOTE: quantifier var's are only found in the constraints and
1480            model definitions... therefore we can do a semantic check here 
1481            to make sure that the variables exist in the symbol table */
1482
1483         /* NOTE: its assumed that the symbol table stack is appropriately
1484            set up with the parent quantifier symbol table */
1485         assert !sts.empty();          
1486
1487         /* do semantic check and if valid, add it to symbol table
1488            and add it to the quantifier as well */
1489         if (sts.peek().contains(varname)) {
1490             return new VarDescriptor(varname);
1491         } else {
1492             /* Semantic Error: undefined variable */
1493             er.report(pn, "Undefined variable '" + varname + "'");
1494             return null;
1495         }
1496     }
1497     
1498     private LiteralExpr parse_literal(ParseNode pn) {
1499         if (!precheck(pn, "literal")) {
1500             return null;
1501         }
1502
1503         if (pn.getChild("boolean") != null) {
1504             if (pn.getChild("boolean").getChild("true") != null) {
1505                 return new BooleanLiteralExpr(true);
1506             } else {
1507                 return new BooleanLiteralExpr(false);
1508             } 
1509         } else if (pn.getChild("decimal") != null) {            
1510             String integer = pn.getChild("decimal").getTerminal();
1511
1512             /* Check for integer literal overflow */
1513             BigInteger intLitBI = new BigInteger(integer);
1514             BigInteger intMax = new BigInteger("" + Integer.MAX_VALUE);
1515             BigInteger intMin = new BigInteger("" + Integer.MIN_VALUE);
1516             int value;
1517
1518             if (intLitBI.compareTo(intMin) < 0) {
1519                 value = Integer.MIN_VALUE;
1520                 er.warn(pn, "Integer literal overflow");
1521             } else if (intLitBI.compareTo(intMax) > 0) {
1522                 value = Integer.MAX_VALUE;
1523                 er.warn(pn, "Integer literal overflow");
1524             } else {
1525                 /* no truncation needed */
1526                 value = Integer.parseInt(integer);
1527             }
1528
1529             return new IntegerLiteralExpr(value);
1530         } else if (pn.getChild("token") != null) {
1531             return new TokenLiteralExpr(pn.getChild("token").getTerminal());
1532         } else if (pn.getChild("string") != null) {
1533             throw new IRException("string unsupported");
1534         } else if (pn.getChild("char") != null) {
1535             throw new IRException("char unsupported");
1536         } else {
1537             throw new IRException("unknown literal expression type.");
1538         }
1539     }
1540
1541     private OpExpr parse_operator(ParseNode pn) {
1542         if (!precheck(pn, "operator")) {
1543             return null; 
1544         }
1545
1546         String opname = pn.getChild("op").getTerminal();
1547         Opcode opcode = Opcode.decodeFromString(opname);
1548
1549         if (opcode == null) {
1550             er.report(pn, "Unsupported operation: " + opname);
1551             return null;
1552         }
1553         
1554         Expr left = parse_expr(pn.getChild("left").getChild("expr"));
1555         Expr right = null;
1556         
1557         if (pn.getChild("right") != null) {
1558             right = parse_expr(pn.getChild("right").getChild("expr"));
1559         }
1560
1561         if (left == null) {           
1562             return null;
1563         }
1564
1565         if (right == null && opcode != Opcode.NOT) {
1566             er.report(pn, "Two arguments required.");
1567             return null;
1568         }
1569
1570         return new OpExpr(opcode, left, right);
1571     }
1572
1573     private DotExpr parse_dot(ParseNode pn) {
1574         if (!precheck(pn, "dot")) {
1575             return null;
1576         }
1577
1578         Expr left = parse_simple_expr(pn.getChild("simple_expr"));
1579
1580         if (left == null) {
1581             return null;
1582         }
1583
1584         String field = pn.getChild("field").getTerminal();
1585
1586         Expr index = null;
1587
1588         if (pn.getChild("index") != null) {
1589             index = parse_expr(pn.getChild("index").getChild("expr"));
1590             
1591             if (index == null) {
1592                 return null;
1593             }
1594         }
1595
1596         return new DotExpr(left, field, index);        
1597     }
1598
1599 }
1600