001    /**
002     * @author paulk, Jul 19, 2002
003     */
004    
005    package toolbus.environment;
006    
007    import java.util.List;
008    
009    import toolbus.Functions;
010    import toolbus.TBTermFactory;
011    import toolbus.TBTermVar;
012    import toolbus.exceptions.ToolBusError;
013    import toolbus.exceptions.ToolBusException;
014    import toolbus.exceptions.ToolBusInternalError;
015    import aterm.ATerm;
016    import aterm.ATermAppl;
017    import aterm.ATermList;
018    
019    /**
020     * Environments maintain a relation between variables and their values.
021     */
022    public class Environment{
023            private final TBTermFactory tbfactory;
024            private Bindings bindings;
025            
026            public Environment(TBTermFactory tbfactory){
027                    bindings = new ListBindings();
028                    this.tbfactory = tbfactory;
029            }
030            
031            public Environment copy(){
032                    Environment env = new Environment(tbfactory);
033                    env.bindings = bindings.clone();
034                    
035                    return env;
036            }
037            
038            public TBTermFactory getTBTermFactory(){
039                    return tbfactory;
040            }
041            
042            public int size(){
043                    return bindings.size();
044            }
045            
046            /**
047             * introduceVars adds a list of variables to the environment. They are initialized to Undefined.
048             * 
049             * @param vs
050             *            list of variables.
051             */
052            public void introduceVars(ATermList vs) throws ToolBusInternalError{
053                    ATermList vars = vs;
054                    
055                    // System.err.println(this.hashCode() + " introduceVars: " + vars);
056                    while(!vars.isEmpty()){
057                            ATerm t = vars.getFirst();
058                            if(tbfactory.isVar(t)){
059                                    TBTermVar var = (TBTermVar) t;
060                                    bindings.put(var.getVarName(), new Binding(var, tbfactory.Undefined));
061                            }else{
062                                    throw new ToolBusInternalError("introduceVar illegal var: " + t);
063                            }
064                            
065                            vars = vars.getNext();
066                    }
067                    // System.err.println(this.hashCode() + " introduceVars yields:" + this);
068            }
069            
070            /**
071             * introduceBinding adds a new variable for the case of formal/actual correspondence in process
072             * calls. Special care is taken for result variables.
073             * 
074             * @param formalVar
075             *            A formal parameter.
076             * @param actual
077             *            An actual value.
078             * @param isFormal
079             *            Indicate if the variable is a formal.
080             */
081            public void introduceBinding(TBTermVar formalVar, ATerm actual, boolean isFormal) throws ToolBusException{
082                    // System.err.println(this.hashCode() + " introduceBinding: " + formalVar + ", " + actual);
083                    ATerm a = tbfactory.replaceFormals(actual, this);
084                    // System.err.println("actual: " + actual);
085                    if(tbfactory.isAnyVar(a)){
086                            TBTermVar actualVar = (TBTermVar) a;
087                            if(!Functions.compatibleTypes(formalVar.getVarType(), actualVar.getVarType())){
088                                    throw new ToolBusError("incompatible types for " + formalVar + " and " + actualVar + " in " + this);
089                            }
090                    }
091                    if(formalVar.isResultVar() && !tbfactory.isResultVar(a)) throw new ToolBusInternalError("actual: " + a + " should be a result variable");
092                    
093                    bindings.put(formalVar.getVarName(), new Binding(formalVar, a, isFormal));
094                    // System.err.println("introduceBinding => " + this);
095            }
096            
097            public void introduceBinding(TBTermVar formalVar, ATerm actual) throws ToolBusException{
098                    introduceBinding(formalVar, actual, false);
099            }
100            
101            /**
102             * introduceBindings adds new (formal.actual) bindings
103             * 
104             * @param formals
105             *            list of formal parameters.
106             * @param actual
107             *            list of actual parameters.
108             * @param isFormal
109             *            Indicate if the variables are formals.
110             */
111            public void introduceBindings(ATermList formals, ATermList actual, boolean isFormal) throws ToolBusException{
112                    ATermList f = formals;
113                    ATermList a = actual;
114                    
115                    if(f.getLength() != a.getLength()){
116                            throw new ToolBusInternalError("formal/actuals list have unequal length: " + f + " and " + a);
117                    }
118                    for(; !f.isEmpty(); f = f.getNext(), a = a.getNext()){
119                            ATerm first = f.getFirst();
120                            if(!tbfactory.isAnyVar(first)){
121                                    throw new ToolBusInternalError("illegal formal: " + first);
122                            }
123                            introduceBinding((TBTermVar) first, a.getFirst(), isFormal);
124                    }
125            }
126            
127            /**
128             * removeBindings deletes variables introduced by introduceVars and introduceBindings.
129             * 
130             * @param formals
131             *            variables to be removed.
132             */
133            public void removeBindings(ATermList formals){
134                    ATermList f = formals;
135                    // System.err.println(this.hashCode() + "/" + bindings.hashCode() + " removeBindings: " +
136                    // formals);
137                    for(; !f.isEmpty(); f = f.getNext()){
138                            ATerm first = f.getFirst();
139                            if(!tbfactory.isAnyVar(first)){
140                                    throw new ToolBusInternalError("illegal formal: " + first);
141                            }
142                            TBTermVar formalVar = (TBTermVar) first;
143                            bindings.remove(formalVar.getVarName());
144                    }
145                    // System.err.println(this.hashCode() + "/" + bindings.hashCode() + " removeBindings yields:
146                    // " + this);
147            }
148            
149            /**
150             * assignVar assigns a value to a variable.
151             * 
152             * @param v
153             *            variable
154             * @param val
155             *            value to be assigned to variable
156             */
157            public void assignVar(TBTermVar v, ATerm val){
158                    TBTermVar var = v;
159                    
160                    // System.err.println("assignVar(" + var + ", " + val + ")");
161                    while(true){
162                            String name = var.getVarName();
163                            Binding b = bindings.get(name);
164                            if(b == null){
165                                    bindings.put(name, new Binding(var, val));
166                                    return;
167                            }
168                            String bname = b.var.getVarName();
169                            if(name.equals(bname)){
170                                    // System.err.println(name + " equals " + bname);
171                                    if(b.isFormal() && b.var.isResultVar()){
172                                            var = (TBTermVar) b.val; // TODO checkit
173                                    }else{
174                                            b.val = val;
175                                            b.setFormal(false);
176                                            return;
177                                    }
178                            }
179                    }
180            }
181            
182            public ATerm getVarType(TBTermVar var) throws ToolBusError{
183                    // System.err.println("getVarType(" + var + ");" + this);
184                    String name = var.getVarName();
185                    Binding b = bindings.get(name);
186                    
187                    ATerm res;
188                    if(b == null){
189                            // System.err.println("getVarType (b == null) => " + var.getVarType());
190                            res = var.getVarType();
191                    }else if(b.var.getVarName().equals(name)){
192                            // System.err.println("getVarType (equals) => " + b.var.getVarType());
193                            res = b.var.getVarType();
194                    }else{
195                            // System.err.println("getVarType => " + var.getVarType());
196                            res = var.getVarType();
197                    }
198                    
199                    if((res.getType() == ATerm.APPL) && ((ATermAppl) res).getName() == "none"){
200                            throw new ToolBusError("Undeclared variable " + var.getExternalVarName());
201                    }
202                    return res;
203            }
204            
205            /**
206             * getValue fetches the value of a variable.
207             * 
208             * @param var
209             *            variable whole value is needed.
210             */
211            public Binding getBinding(TBTermVar var){
212                    // System.err.println("getBinding(" + var + " in " + this + ")");
213                    String name = var.getVarName();
214                    return bindings.get(name);
215            }
216            
217            public List<Binding> getBindingsAsList(){
218                    return bindings.getBindingsAsList();
219            }
220            
221            public boolean isDeclaredAsStringVar(TBTermVar var){
222                    String name = var.getVarName();
223                    Binding b = bindings.get(name);
224                    // System.err.println("isDeclaredAsStringVar: " + var + "; " + b + "; " + this);
225                    if(b == null){
226                            return false;
227                    }
228                    return (b.var.getVarType() == tbfactory.StrType);
229            }
230            
231            /**
232             * getValue fetches the value of a variable.
233             * 
234             * @param v
235             *            variable whose value is needed.
236             */
237            public ATerm getValue(TBTermVar v){
238                    TBTermVar var = v;
239                    // System.err.println(this.hashCode() + " getValue(" + var + " in " + this + ")");
240                    
241                    while(true){
242                            String name = var.getVarName();
243                            Binding b = bindings.get(name);
244                            if(b == null){
245                                    throw new RuntimeException("variable " + var.getExternalVarName() + " has undefined value");
246                            }
247                            if(b.isFormal() && (tbfactory.isAnyVar(b.val))){
248                                    var = (TBTermVar) b.val;
249                            }else{
250                                    return b.val;
251                            }
252                    }
253            }
254            
255            public void setAssignable(TBTermVar var){
256                    Binding b = getBinding(var);
257                    if(b != null){
258                            b.setAssignable(true);
259                    }else{
260                            throw new ToolBusInternalError("setAssignable: variable " + var.getExternalVarName() + " does not exist!");
261                    }
262            }
263            
264            public ATerm replaceFormal(TBTermVar v) throws ToolBusException{
265                    TBTermVar var = v;
266                    
267                    // System.err.println("replaceFormal(" + v + "); " + this);
268                    var = var.setVarType(getVarType(var));
269                    Binding b = getBinding(var);
270                    // System.err.println(b);
271                    if(b == null || b.val == tbfactory.Undefined){
272                            return var;
273                            /* throw new ToolBusError("Undeclared variable " + v.getExternalVarName()); */
274                    }
275                    if(!b.isFormal()){
276                            // System.err.println("local var:. replaceFormals(" + v + ") => " + v);
277                            return var;
278                    }
279                    
280                    if(b.isAssignable() || tbfactory.isResultVar(b.val)){
281                            // System.err.println("2. replaceFormal(" + v + ") => " + v);
282                            return var;
283                    }
284                    if(tbfactory.isVar(b.val)){ // TODO: OK?
285                            ATerm tmp = tbfactory.replaceFormals(b.val, this);
286                            // System.err.println("3. replaceFormal(" + v + ") => " + tmp);
287                            return tmp;
288                    }else if(tbfactory.isResultVar(b.val)){ // TODO: OK?
289                            ATerm tmp = tbfactory.replaceFormals(b.val, this);
290                            // System.err.println("3. replaceFormal(" + v + ") => " + tmp);
291                            return tmp;
292                    }else{
293                            // System.err.println("4. replaceFormal(" + v + ") => " + b.val);
294                            return b.val;
295                    }
296                    
297                    // System.err.println("5. replaceFormal(" + v + ") => " + v);
298                    /* return v; */
299            }
300            
301            public String toString(){
302                    return this.hashCode() + ":" + bindings.toString();
303            }
304    }