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