001    package toolbus;
002    
003    import java.util.Hashtable;
004    
005    import toolbus.environment.Environment;
006    import toolbus.exceptions.ToolBusError;
007    import toolbus.exceptions.ToolBusException;
008    import toolbus.exceptions.ToolBusInternalError;
009    import toolbus.process.ProcessInstance;
010    import aterm.AFun;
011    import aterm.ATerm;
012    import aterm.ATermAppl;
013    import aterm.ATermInt;
014    import aterm.ATermList;
015    import aterm.ATermPlaceholder;
016    import aterm.ATermReal;
017    
018    abstract class FunctionDescriptor{
019            private final TBTermFactory tbfactory;
020            private final String name;
021            private final ATerm argtypes[];
022            private final ATerm resultType;
023            
024            public FunctionDescriptor(TBTermFactory tbfactory, String name, ATerm resultType){
025                    this.tbfactory = tbfactory;
026                    this.name = name;
027                    this.resultType = resultType;
028                    argtypes = new ATerm[0];
029            }
030            
031            public FunctionDescriptor(TBTermFactory tbfactory, String name, ATerm arg0, ATerm resultType){
032                    this.tbfactory = tbfactory;
033                    this.name = name;
034                    this.resultType = resultType;
035                    argtypes = new ATerm[1];
036                    argtypes[0] = arg0;
037            }
038            
039            public FunctionDescriptor(TBTermFactory tbfactory, String name, ATerm arg0, ATerm arg1, ATerm resultType){
040                    this.tbfactory = tbfactory;
041                    this.name = name;
042                    this.resultType = resultType;
043                    argtypes = new ATerm[2];
044                    argtypes[0] = arg0;
045                    argtypes[1] = arg1;
046            }
047            
048            public FunctionDescriptor(TBTermFactory tbfactory, String name, ATerm arg0, ATerm arg1, ATerm arg2, ATerm resultType){
049                    this.tbfactory = tbfactory;
050                    this.name = name;
051                    this.resultType = resultType;
052                    argtypes = new ATerm[3];
053                    argtypes[0] = arg0;
054                    argtypes[1] = arg1;
055                    argtypes[2] = arg2;
056            }
057            
058            public String getName(){
059                    return name;
060            }
061            
062            public ATerm getResultType(){
063                    return resultType;
064            }
065            
066            /**
067             * checkStatic performs a static type check for calls to built-in functions
068             * 
069             * @param actual
070             *            an array of types of actual parameters
071             * @return boolean
072             * @throws ToolBusError
073             */
074            public boolean checkStatic(ATerm actual[]) throws ToolBusError{
075                    if(argtypes.length != actual.length) throw new ToolBusError("funcion " + name + " has wrong number of arguments");
076                    
077                    for(int i = 0; i < argtypes.length; i++){
078                            if(!Functions.compatibleTypes(actual[i], argtypes[i])) throw new ToolBusError(arg(i) + " of " + name + " has type " + actual[i] + " but should have type " + argtypes[i]);
079                    }
080                    return true;
081            }
082            
083            private String[] argNumberStrings = new String[]{"first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "nineth"};
084            
085            public String arg(int n){
086                    if(n < argNumberStrings.length) return argNumberStrings[n] + " argument";
087                    
088                    return "some argument";
089            }
090            
091            public boolean checkRunTime(ATerm actual[]) throws ToolBusException{
092                    if(argtypes.length != actual.length) throw new ToolBusError(name + " has wrong number of arguments");
093                    for(int i = 0; i < argtypes.length; i++){
094                            if(argtypes[i] == tbfactory.BoolType){
095                                    if(!tbfactory.isBoolean(actual[i])) throw new ToolBusError(arg(i) + " of " + name + " should have type boolean, got " + actual[i]);
096                            }else if(argtypes[i] == tbfactory.IntType){
097                                    if(!(actual[i] instanceof ATermInt)) throw new ToolBusError(arg(i) + " of " + name + " should have type integer, got " + actual[i]);
098                            }else if(argtypes[i] == tbfactory.RealType){
099                                    if(!(actual[i] instanceof ATermReal)) throw new ToolBusError(arg(i) + " of " + name + " should have type real, got " + actual[i]);
100                            }else if(argtypes[i] == tbfactory.StrType){
101                                    if(!(actual[i] instanceof ATermAppl) || ((ATermAppl) actual[i]).getArity() > 0){
102                                            throw new ToolBusError(arg(i) + " of " + name + " should have type string, got " + actual[i]);
103                                    }
104                            }else if(argtypes[i] == tbfactory.TermType){
105                                    /* Ignore */
106                            }else if(argtypes[i] == tbfactory.ListType){
107                                    if(!(actual[i] instanceof ATermList)) throw new ToolBusError(arg(i) + " of " + name + " should be a list type, got " + actual[i]);
108                            }else throw new ToolBusInternalError("check: wrong type " + argtypes[i]);
109                    }
110                    return true;
111            }
112            
113            abstract public ATerm apply(ATerm args[], ProcessInstance pi) throws ToolBusException;
114    }
115    
116    public class Functions{
117            private static Hashtable<String, FunctionDescriptor> Funs;
118            private static TBTermFactory tbfactory;
119            
120            public static void init(TBTermFactory tbfac){
121                    tbfactory = tbfac;
122                    Funs = new Hashtable<String, FunctionDescriptor>();
123                    defineFuns();
124            }
125            
126            /**
127             * Declaration
128             */
129            private static void define(FunctionDescriptor fd){
130                    Funs.put(fd.getName(), fd);
131            }
132            
133            private static void defineFuns(){
134                    define(new FunctionDescriptor(tbfactory, "is-bool", tbfactory.TermType, tbfactory.BoolType){
135                            public ATerm apply(ATerm args[], ProcessInstance pi){
136                                    return tbfactory.isBoolean(args[0]) ? tbfactory.True : tbfactory.False;
137                            }
138                    });
139                    
140                    define(new FunctionDescriptor(tbfactory, "is-int", tbfactory.TermType, tbfactory.BoolType){
141                            public ATerm apply(ATerm args[], ProcessInstance pi){
142                                    return tbfactory.isInt(args[0]) ? tbfactory.True : tbfactory.False;
143                            }
144                    });
145                    
146                    define(new FunctionDescriptor(tbfactory, "is-real", tbfactory.TermType, tbfactory.BoolType){
147                            public ATerm apply(ATerm args[], ProcessInstance pi){
148                                    return tbfactory.isReal(args[0]) ? tbfactory.True : tbfactory.False;
149                            }
150                    });
151                    
152                    define(new FunctionDescriptor(tbfactory, "is-str", tbfactory.TermType, tbfactory.BoolType){
153                            public ATerm apply(ATerm args[], ProcessInstance pi){
154                                    return tbfactory.isStr(args[0]) ? tbfactory.True : tbfactory.False;
155                            }
156                    });
157                    
158                    // is-bstr
159                    define(new FunctionDescriptor(tbfactory, "is-appl", tbfactory.TermType, tbfactory.BoolType){
160                            public ATerm apply(ATerm args[], ProcessInstance pi){
161                                    return tbfactory.isAppl(args[0]) ? tbfactory.True : tbfactory.False;
162                            }
163                    });
164                    
165                    define(new FunctionDescriptor(tbfactory, "is-list", tbfactory.TermType, tbfactory.BoolType){
166                            public ATerm apply(ATerm args[], ProcessInstance pi){
167                                    return tbfactory.isList(args[0]) ? tbfactory.True : tbfactory.False;
168                            }
169                    });
170                    
171                    define(new FunctionDescriptor(tbfactory, "is-empty", tbfactory.TermType, tbfactory.BoolType){
172                            public ATerm apply(ATerm args[], ProcessInstance pi){
173                                    // if(args[0] == null){
174                                    // return tbfactory.True;
175                                    // }
176                                    if(tbfactory.isList(args[0])){
177                                            if(((ATermList) args[0]).getLength() == 0) return tbfactory.True;
178                                    }
179                                    return tbfactory.False;
180                            }
181                    });
182                    
183                    define(new FunctionDescriptor(tbfactory, "int-to-real", tbfactory.IntType, tbfactory.RealType){
184                            public ATerm apply(ATerm args[], ProcessInstance pi){
185                                    return tbfactory.makeReal(((ATermInt) args[0]).getInt());
186                            }
187                    });
188                    
189                    define(new FunctionDescriptor(tbfactory, "real-to-int", tbfactory.RealType, tbfactory.IntType){
190                            public ATerm apply(ATerm args[], ProcessInstance pi){
191                                    return tbfactory.makeInt((int) ((ATermReal) args[0]).getReal());
192                            }
193                    });
194                    
195                    // is-var, see method eval
196                    // is-result-var, see method eval
197                    // TODO: is-formal
198                    define(new FunctionDescriptor(tbfactory, "fun", tbfactory.TermType, tbfactory.StrType){
199                            public ATerm apply(ATerm args[], ProcessInstance pi){
200                                    String fname = ((ATermAppl) args[0]).getName();
201                                    return tbfactory.make("<str>", fname);
202                            }
203                    });
204                    
205                    define(new FunctionDescriptor(tbfactory, "args", tbfactory.TermType, tbfactory.ListType){
206                            public ATerm apply(ATerm args[], ProcessInstance pi){
207                                    return tbfactory.getArgs(args[0]);
208                            }
209                    });
210                    
211                    define(new FunctionDescriptor(tbfactory, "true", tbfactory.BoolType){
212                            public ATerm apply(ATerm args[], ProcessInstance pi){
213                                    return tbfactory.True;
214                            }
215                    });
216                    
217                    define(new FunctionDescriptor(tbfactory, "false", tbfactory.BoolType){
218                            public ATerm apply(ATerm args[], ProcessInstance pi){
219                                    return tbfactory.False;
220                            }
221                    });
222                    
223                    define(new FunctionDescriptor(tbfactory, "not", tbfactory.BoolType, tbfactory.BoolType){
224                            public ATerm apply(ATerm args[], ProcessInstance pi){
225                                    return args[0] == tbfactory.True ? tbfactory.False : tbfactory.True;
226                            }
227                    });
228                    
229                    define(new FunctionDescriptor(tbfactory, "and", tbfactory.BoolType, tbfactory.BoolType, tbfactory.BoolType){
230                            public ATerm apply(ATerm args[], ProcessInstance pi){
231                                    return (args[0] == tbfactory.True) && (args[1] == tbfactory.True) ? tbfactory.True : tbfactory.False;
232                            }
233                    });
234                    
235                    define(new FunctionDescriptor(tbfactory, "or", tbfactory.BoolType, tbfactory.BoolType, tbfactory.BoolType){
236                            public ATerm apply(ATerm args[], ProcessInstance pi){
237                                    return (args[0] == tbfactory.True) || (args[1] == tbfactory.True) ? tbfactory.True : tbfactory.False;
238                            }
239                    });
240                    
241                    define(new FunctionDescriptor(tbfactory, "equal", tbfactory.TermType, tbfactory.TermType, tbfactory.BoolType){
242                            public ATerm apply(ATerm args[], ProcessInstance pi){
243                                    ATerm res = (args[0] == args[1]) ? tbfactory.True : tbfactory.False;
244                                    // System.err.println("equal: " + args[0] + " == " + args[1] + " ==> " + res);
245                                    return res;
246                            }
247                    });
248                    
249                    define(new FunctionDescriptor(tbfactory, "not-equal", tbfactory.TermType, tbfactory.TermType, tbfactory.BoolType){
250                            public ATerm apply(ATerm args[], ProcessInstance pi){
251                                    ATerm res = (args[0] != args[1]) ? tbfactory.True : tbfactory.False;
252                                    // System.err.println("not-equal: " + args[0] + " == " + args[1] + " ==> " + res);
253                                    return res;
254                            }
255                    });
256                    
257                    define(new FunctionDescriptor(tbfactory, "add", tbfactory.IntType, tbfactory.IntType, tbfactory.IntType){
258                            public ATerm apply(ATerm args[], ProcessInstance pi){
259                                    return tbfactory.makeInt(((ATermInt) args[0]).getInt() + ((ATermInt) args[1]).getInt());
260                            }
261                    });
262                    
263                    define(new FunctionDescriptor(tbfactory, "sub", tbfactory.IntType, tbfactory.IntType, tbfactory.IntType){
264                            public ATerm apply(ATerm args[], ProcessInstance pi){
265                                    return tbfactory.makeInt(((ATermInt) args[0]).getInt() - ((ATermInt) args[1]).getInt());
266                            }
267                    });
268                    
269                    define(new FunctionDescriptor(tbfactory, "mul", tbfactory.IntType, tbfactory.IntType, tbfactory.IntType){
270                            public ATerm apply(ATerm args[], ProcessInstance pi){
271                                    return tbfactory.makeInt(((ATermInt) args[0]).getInt() * ((ATermInt) args[1]).getInt());
272                            }
273                    });
274                    
275                    define(new FunctionDescriptor(tbfactory, "div", tbfactory.IntType, tbfactory.IntType, tbfactory.IntType){
276                            public ATerm apply(ATerm args[], ProcessInstance pi){
277                                    return tbfactory.makeInt(((ATermInt) args[0]).getInt() / ((ATermInt) args[1]).getInt());
278                            }
279                    });
280                    
281                    define(new FunctionDescriptor(tbfactory, "mod", tbfactory.IntType, tbfactory.IntType, tbfactory.IntType){
282                            public ATerm apply(ATerm args[], ProcessInstance pi){
283                                    return tbfactory.makeInt(((ATermInt) args[0]).getInt() % ((ATermInt) args[1]).getInt());
284                            }
285                    });
286                    
287                    define(new FunctionDescriptor(tbfactory, "abs", tbfactory.IntType, tbfactory.IntType){
288                            public ATerm apply(ATerm args[], ProcessInstance pi){
289                                    return tbfactory.makeInt(Math.abs(((ATermInt) args[0]).getInt()));
290                            }
291                    });
292                    
293                    define(new FunctionDescriptor(tbfactory, "less", tbfactory.IntType, tbfactory.IntType, tbfactory.BoolType){
294                            public ATerm apply(ATerm args[], ProcessInstance pi){
295                                    return ((ATermInt) args[0]).getInt() < ((ATermInt) args[1]).getInt() ? tbfactory.True : tbfactory.False;
296                            }
297                    });
298                    define(new FunctionDescriptor(tbfactory, "less-equal", tbfactory.IntType, tbfactory.IntType, tbfactory.BoolType){
299                            public ATerm apply(ATerm args[], ProcessInstance pi){
300                                    return ((ATermInt) args[0]).getInt() <= ((ATermInt) args[1]).getInt() ? tbfactory.True : tbfactory.False;
301                            }
302                    });
303                    
304                    define(new FunctionDescriptor(tbfactory, "greater", tbfactory.IntType, tbfactory.IntType, tbfactory.BoolType){
305                            public ATerm apply(ATerm args[], ProcessInstance pi){
306                                    return ((ATermInt) args[0]).getInt() > ((ATermInt) args[1]).getInt() ? tbfactory.True : tbfactory.False;
307                            }
308                    });
309                    
310                    define(new FunctionDescriptor(tbfactory, "greater-equal", tbfactory.IntType, tbfactory.IntType, tbfactory.BoolType){
311                            public ATerm apply(ATerm args[], ProcessInstance pi){
312                                    return ((ATermInt) args[0]).getInt() >= ((ATermInt) args[1]).getInt() ? tbfactory.True : tbfactory.False;
313                            }
314                    });
315                    
316                    define(new FunctionDescriptor(tbfactory, "radd", tbfactory.RealType, tbfactory.RealType, tbfactory.RealType){
317                            public ATerm apply(ATerm args[], ProcessInstance pi){
318                                    return tbfactory.makeReal(((ATermReal) args[0]).getReal() + ((ATermReal) args[1]).getReal());
319                            }
320                    });
321                    
322                    define(new FunctionDescriptor(tbfactory, "rsub", tbfactory.RealType, tbfactory.RealType, tbfactory.RealType){
323                            public ATerm apply(ATerm args[], ProcessInstance pi){
324                                    return tbfactory.makeReal(((ATermReal) args[0]).getReal() - ((ATermReal) args[1]).getReal());
325                            }
326                    });
327                    
328                    define(new FunctionDescriptor(tbfactory, "rmul", tbfactory.RealType, tbfactory.RealType, tbfactory.RealType){
329                            public ATerm apply(ATerm args[], ProcessInstance pi){
330                                    return tbfactory.makeReal(((ATermReal) args[0]).getReal() * ((ATermReal) args[1]).getReal());
331                            }
332                    });
333                    define(new FunctionDescriptor(tbfactory, "rdiv", tbfactory.RealType, tbfactory.RealType, tbfactory.RealType){
334                            public ATerm apply(ATerm args[], ProcessInstance pi){
335                                    return tbfactory.makeReal(((ATermReal) args[0]).getReal() / ((ATermReal) args[1]).getReal());
336                            }
337                    });
338                    
339                    define(new FunctionDescriptor(tbfactory, "rless", tbfactory.RealType, tbfactory.RealType, tbfactory.BoolType){
340                            public ATerm apply(ATerm args[], ProcessInstance pi){
341                                    return ((ATermReal) args[0]).getReal() < ((ATermReal) args[1]).getReal() ? tbfactory.True : tbfactory.False;
342                            }
343                    });
344                    
345                    define(new FunctionDescriptor(tbfactory, "rless-equal", tbfactory.RealType, tbfactory.RealType, tbfactory.BoolType){
346                            public ATerm apply(ATerm args[], ProcessInstance pi){
347                                    return ((ATermReal) args[0]).getReal() <= ((ATermReal) args[1]).getReal() ? tbfactory.True : tbfactory.False;
348                            }
349                    });
350                    
351                    define(new FunctionDescriptor(tbfactory, "rgreater", tbfactory.RealType, tbfactory.RealType, tbfactory.BoolType){
352                            public ATerm apply(ATerm args[], ProcessInstance pi){
353                                    return ((ATermReal) args[0]).getReal() > ((ATermReal) args[1]).getReal() ? tbfactory.True : tbfactory.False;
354                            }
355                    });
356                    define(new FunctionDescriptor(tbfactory, "rgreater-equal", tbfactory.RealType, tbfactory.RealType, tbfactory.BoolType){
357                            public ATerm apply(ATerm args[], ProcessInstance pi){
358                                    return ((ATermReal) args[0]).getReal() >= ((ATermReal) args[1]).getReal() ? tbfactory.True : tbfactory.False;
359                            }
360                    });
361                    
362                    // sin, cos, atan atan2 exp log log10 sqrt
363                    define(new FunctionDescriptor(tbfactory, "rabs", tbfactory.RealType, tbfactory.RealType){
364                            public ATerm apply(ATerm args[], ProcessInstance pi){
365                                    return tbfactory.makeReal(Math.abs(((ATermReal) args[0]).getReal()));
366                            }
367                    });
368                    
369                    define(new FunctionDescriptor(tbfactory, "size", tbfactory.ListType, tbfactory.IntType){
370                            public ATerm apply(ATerm args[], ProcessInstance pi){
371                                    // if(args[0] == null){
372                                    // return factory.makeInt(0);
373                                    // }
374                                    return tbfactory.makeInt(tbfactory.size(args[0]));
375                            }
376                    });
377                    
378                    define(new FunctionDescriptor(tbfactory, "index", tbfactory.ListType, tbfactory.IntType, tbfactory.TermType){
379                            public ATerm apply(ATerm args[], ProcessInstance pi){
380                                    return tbfactory.index(args[0], ((ATermInt) args[1]).getInt());
381                            }
382                    });
383                    
384                    define(new FunctionDescriptor(tbfactory, "replace", tbfactory.ListType, tbfactory.IntType, tbfactory.TermType, tbfactory.ListType){
385                            public ATerm apply(ATerm args[], ProcessInstance pi){
386                                    return tbfactory.replace(args[0], ((ATermInt) args[1]).getInt(), args[2]);
387                            }
388                    });
389                    
390                    define(new FunctionDescriptor(tbfactory, "get", tbfactory.ListType, tbfactory.TermType, tbfactory.TermType){
391                            public ATerm apply(ATerm args[], ProcessInstance pi){
392                                    return tbfactory.get(args[0], args[1]);
393                            }
394                    });
395                    
396                    define(new FunctionDescriptor(tbfactory, "put", tbfactory.ListType, tbfactory.TermType, tbfactory.TermType, tbfactory.ListType){
397                            public ATerm apply(ATerm args[], ProcessInstance pi){
398                                    return tbfactory.put(args[0], args[1], args[2]);
399                            }
400                    });
401                    
402                    define(new FunctionDescriptor(tbfactory, "first", tbfactory.ListType, tbfactory.TermType){
403                            public ATerm apply(ATerm args[], ProcessInstance pi){
404                                    // if(args[0] == null){
405                                    // return null;
406                                    // }
407                                    return tbfactory.first(args[0]);
408                            }
409                    });
410                    
411                    define(new FunctionDescriptor(tbfactory, "next", tbfactory.ListType, tbfactory.ListType){
412                            public ATerm apply(ATerm args[], ProcessInstance pi){
413                                    // if(args[0] == null){
414                                    // return null;
415                                    // }
416                                    return tbfactory.next(args[0]);
417                            }
418                    });
419                    
420                    define(new FunctionDescriptor(tbfactory, "member", tbfactory.TermType, tbfactory.ListType, tbfactory.BoolType){
421                            public ATerm apply(ATerm args[], ProcessInstance pi){
422                                    return tbfactory.member(args[0], args[1]) ? tbfactory.True : tbfactory.False;
423                            }
424                    });
425                    
426                    define(new FunctionDescriptor(tbfactory, "subset", tbfactory.ListType, tbfactory.ListType, tbfactory.BoolType){
427                            public ATerm apply(ATerm args[], ProcessInstance pi){
428                                    return tbfactory.subset(args[0], args[1]) ? tbfactory.True : tbfactory.False;
429                            }
430                    });
431                    
432                    define(new FunctionDescriptor(tbfactory, "diff", tbfactory.ListType, tbfactory.ListType, tbfactory.ListType){
433                            public ATerm apply(ATerm args[], ProcessInstance pi){
434                                    return tbfactory.diff(args[0], args[1]);
435                            }
436                    });
437                    
438                    define(new FunctionDescriptor(tbfactory, "inter", tbfactory.ListType, tbfactory.ListType, tbfactory.ListType){
439                            public ATerm apply(ATerm args[], ProcessInstance pi){
440                                    return tbfactory.inter(args[0], args[1]);
441                            }
442                    });
443                    
444                    define(new FunctionDescriptor(tbfactory, "join", tbfactory.TermType, tbfactory.TermType, tbfactory.ListType){
445                            public ATerm apply(ATerm args[], ProcessInstance pi){
446                                    return tbfactory.join(args[0], args[1]);
447                            }
448                    });
449                    
450                    define(new FunctionDescriptor(tbfactory, "concat", tbfactory.StrType, tbfactory.StrType, tbfactory.StrType){
451                            public ATerm apply(ATerm args[], ProcessInstance pi){
452                                    return tbfactory.concat(args[0], args[1]);
453                            }
454                    });
455                    
456                    // functions
457                    define(new FunctionDescriptor(tbfactory, "process-id", tbfactory.IntType){
458                            public ATerm apply(ATerm args[], ProcessInstance pi){
459                                    return tbfactory.makeInt(pi.getProcessId());
460                            }
461                    });
462                    
463                    define(new FunctionDescriptor(tbfactory, "process-name", tbfactory.StrType){
464                            public ATerm apply(ATerm args[], ProcessInstance pi){
465                                    AFun afun = tbfactory.makeAFun(pi.getProcessName(), 0, true);
466                                    return tbfactory.makeAppl(afun);
467                            }
468                    });
469                    
470                    define(new FunctionDescriptor(tbfactory, "get-property", tbfactory.StrType, tbfactory.StrType){
471                            public ATerm apply(ATerm args[], ProcessInstance pi){
472                                    ToolBus tb = pi.getToolBus();
473                                    String arg = ((ATermAppl) args[0]).getName();
474                                    String val = tb.getProperty(arg);
475                                    if(val == null){
476                                            val = "";
477                                    }
478                                    AFun afun = tbfactory.makeAFun(val, 0, true);
479                                    return tbfactory.makeAppl(afun);
480                            }
481                    });
482                    
483                    // quote
484                    // current-time
485                    define(new FunctionDescriptor(tbfactory, "current-time", tbfactory.IntType){
486                            public ATerm apply(ATerm args[], ProcessInstance pi){
487                                    int n = (int) pi.getRunTime();
488                                    return tbfactory.makeInt(n);
489                            }
490                    });
491                    
492                    // sec
493                    // msec
494            }
495            
496            public static ATerm checkStatic(String fun, ATerm args[]) throws ToolBusError{
497                    FunctionDescriptor fd = Funs.get(fun);
498                    if(fd == null) throw new ToolBusError("Unknown function: " + fun);
499                    
500                    if(fd.checkStatic(args)) return fd.getResultType();
501                    
502                    return null;
503            }
504            
505            private static ATerm apply(String fun, ATerm args[], ProcessInstance process) throws ToolBusException{
506                    FunctionDescriptor fd = Funs.get(fun);
507                    
508                    if(fd == null){
509                            throw new ToolBusError("Unknown function: " + fun);
510                    }
511                    fd.checkRunTime(args); // redundant after typecheck!
512                    return fd.apply(args, process);
513            }
514            
515            public static ATerm eval(ATerm t, ProcessInstance pi, Environment env) throws ToolBusException{
516                    // System.err.println("eval: " + t + "; env = " + env);
517                    switch(t.getType()){
518                            
519                            case ATerm.BLOB:
520                            case ATerm.INT:
521                            case ATerm.PLACEHOLDER:
522                            case ATerm.REAL:
523                                    return t;
524                                    
525                            case TBTermFactory.VAR:
526                                    return env.getValue((TBTermVar) t);
527                                    
528                            case ATerm.APPL:
529                                    if(tbfactory.isBoolean(t)) return t;
530                                    if(tbfactory.isStr(t)) return t;
531                                    String fun = ((ATermAppl) t).getName();
532                                    ATerm args[] = ((ATermAppl) t).getArgumentArray();
533                                    if(fun == "quote"){ // TODO: check # of args
534                                            // System.err.println("quote: " + t + " ==> " + tbfactory.substitute(args[0],
535                                            // env));
536                                            return tbfactory.substitute(args[0], env);
537                                    }
538                                    if(fun == "is-var") return tbfactory.isVar(args[0]) ? tbfactory.True : tbfactory.False;
539                                    if(fun == "is-result-var") return tbfactory.isResultVar(args[0]) ? tbfactory.True : tbfactory.False;
540                                    if(args.length == 0 && !((fun == "process-id") || fun == "process-name")) return t;
541                                    ATerm vargs[] = new ATerm[args.length];
542                                    for(int i = 0; i < args.length; i++){
543                                            vargs[i] = eval(args[i], pi, env);
544                                    }
545                                    return apply(fun, vargs, pi);
546                                    
547                            case ATerm.LIST:
548                                    ATermList lst = tbfactory.makeList();
549                                    ATermList tlst = (ATermList) t;
550                                    for(int i = tlst.getLength() - 1; i >= 0; i--){
551                                            lst = lst.insert(eval(tlst.elementAt(i), pi, env));
552                                    }
553                                    return lst;
554                    }
555                    throw new ToolBusInternalError("illegal ATerm in eval: " + t);
556            }
557            
558            public static ATerm checkType(ATerm t, Environment env, boolean quoted) throws ToolBusException{
559                    // System.err.println("checkType(" + t + ")");
560                    switch(t.getType()){
561                            case ATerm.INT:
562                                    return tbfactory.IntType;
563                                    
564                            case ATerm.PLACEHOLDER:
565                                    return ((ATermPlaceholder) t).getPlaceholder();
566                                    
567                            case ATerm.REAL:
568                                    return tbfactory.RealType;
569                            
570                            case ATerm.BLOB:
571                                    return tbfactory.BlobType;
572                                    
573                            case TBTermFactory.VAR:
574                                    return ((TBTermVar) t).getVarType();
575                                    
576                            case ATerm.APPL:
577                                    ATermAppl apt = (ATermAppl) t;
578                                    
579                                    if(apt == tbfactory.Undefined) return tbfactory.Undefined;
580                                    
581                                    if(tbfactory.isBoolean(apt)){
582                                            return tbfactory.BoolType;
583                                    }
584                                    if(apt.isQuoted() && apt.getArity() == 0){
585                                            return tbfactory.StrType;
586                                    }
587                                    String name = apt.getName();
588                                    ATerm args[] = apt.getArgumentArray();
589                                    if(args.length == 0 && Funs.get(name) == null) return t;
590                                    if(name == "quote") return checkType(args[0], env, true);
591                                    
592                                    ATerm vargs[] = new ATerm[args.length];
593                                    for(int i = 0; i < args.length; i++){
594                                            vargs[i] = checkType(args[i], env, quoted);
595                                    }
596                                    if(quoted){
597                                            AFun afun = apt.getAFun();
598                                            return tbfactory.makeAppl(afun, vargs);
599                                    }
600                                    
601                                    return Functions.checkStatic(name, vargs);
602                                    
603                            case ATerm.LIST:
604                                    ATermList lst = tbfactory.makeList();
605                                    ATermList tlst = (ATermList) t;
606                                    for(int i = tlst.getLength() - 1; i >= 0; i--){
607                                            lst = lst.insert(checkType(tlst.elementAt(i), env, quoted));
608                                    }
609                                    return lst;
610                    }
611                    throw new ToolBusInternalError("illegal ATerm in checkType: " + t);
612            }
613            
614            /**
615             * compatibleTypeList checks that a list of types is compatible with a given element type
616             * 
617             * @param list
618             *            the list to be checked
619             * @param elmtype
620             *            the required element type
621             * @return boolean
622             */
623            public static boolean compatibleTypeList(ATermList list, ATerm elmtype){
624                    ATermList lst = list;
625                    while(!lst.isEmpty()){
626                            if(!compatibleTypes(lst.getFirst(), elmtype)) return false;
627                            
628                            lst = lst.getNext();
629                    }
630                    return true;
631            }
632            
633            /**
634             * compatibleTypes checks that two types are compatible
635             * 
636             * @param t1
637             *            first type
638             * @param t2
639             *            second type
640             * @return boolean
641             */
642            public static boolean compatibleTypes(ATerm t1, ATerm t2){
643                    ATerm term1 = t1;
644                    ATerm term2 = t2;
645                    
646                    // System.err.println("compatibleType(" + t1 + ", " + t2 + ")");
647                    
648                    if(term2 == tbfactory.Undefined) return true;
649                    
650                    if(term1.getType() == ATerm.PLACEHOLDER) term1 = ((ATermPlaceholder) term1).getPlaceholder();
651                    if(tbfactory.isAnyVar(term1)) term1 = ((TBTermVar) term1).getVarType();
652                    
653                    if(term2.getType() == ATerm.PLACEHOLDER) term2 = ((ATermPlaceholder) term2).getPlaceholder();
654                    if(tbfactory.isAnyVar(term2)) term2 = ((TBTermVar) term2).getVarType();
655                    
656                    if(term1.equals(term2)) return true;
657                    
658                    if(term1.equals(tbfactory.TermType) || term2.equals(tbfactory.TermType)) return true;
659                    
660                    switch(term1.getType()){
661                            case ATerm.INT:
662                                    return term2.equals(tbfactory.IntType);
663                            case ATerm.REAL:
664                                    return term2.equals(tbfactory.RealType);
665                            case ATerm.BLOB:
666                                    return term2.equals(tbfactory.BlobType);
667                                    
668                            case ATerm.PLACEHOLDER:
669                                    return compatibleTypes(((ATermPlaceholder) term1).getPlaceholder(), term2);
670                                    
671                            case ATerm.APPL:
672                                    ATermAppl ap1 = (ATermAppl) term1;
673                                    if(term2.getType() != ATerm.APPL){
674                                            if(ap1.getName() == "list" && term2.getType() == ATerm.LIST){
675                                                    if(ap1.getArity() == 0) return true;
676                                                    
677                                                    return compatibleTypeList((ATermList) term2, ap1.getArgument(0));
678                                            }else if(term2.getType() == ATerm.INT && term1.equals(tbfactory.IntType))
679                                                    return true;
680                                            else if(term2.getType() == ATerm.REAL && term1.equals(tbfactory.RealType))
681                                                    return true;
682                                            else if(term2.getType() == ATerm.BLOB && term1.equals(tbfactory.BlobType))
683                                                    return true;
684                                            else return false;
685                                    }
686                                    
687                                    ATermAppl ap2 = (ATermAppl) term2;
688                                    // System.err.println("compatibleType: ap1.getName() = " + ap1.getName() + ";" +
689                                    // "ap2.getName() = " + ap2.getName());
690                                    if(term1.equals(tbfactory.StrType) && ap2.getArity() == 0 && ap2.isQuoted()) return true;
691                                    if(term2.equals(tbfactory.StrType) && ap1.getArity() == 0 && ap1.isQuoted()) return true;
692                                    if(term1.equals(tbfactory.BoolType) && tbfactory.isBoolean(term2)) return true;
693                                    if(term2.equals(tbfactory.BoolType) && tbfactory.isBoolean(term1)) return true;
694                                    if(ap1.getName() != ap2.getName()) return false;
695                                    if(ap1.getArity() == 0 || ap2.getArity() == 0) return true;
696                                    if(ap1.getArity() != ap2.getArity()) return false;
697                                    for(int i = 0; i < ap1.getArity() - 1; i++){
698                                            if(!compatibleTypes(ap1.getArgument(i), ap2.getArgument(i))) return false;
699                                    }
700                                    return true;
701                                    
702                            case ATerm.LIST:
703                                    if(term2.getType() == ATerm.LIST){
704                                            ATermList lst1 = (ATermList) term1;
705                                            ATermList lst2 = (ATermList) term2;
706                                            if(lst1.getLength() != lst2.getLength()) return false;
707                                            for(; !lst1.isEmpty(); lst1 = lst1.getNext(), lst2 = lst2.getNext()){
708                                                    if(!compatibleTypes(lst1.getFirst(), lst2.getFirst())) return false;
709                                            }
710                                            return true;
711                                    }
712                                    if(term2.getType() == ATerm.APPL){
713                                            ATermAppl ap = (ATermAppl) term2;
714                                            if(ap.getName() == "list"){
715                                                    if(ap.getArity() == 0) return true;
716                                                    
717                                                    return compatibleTypeList((ATermList) term1, ap.getArgument(0));
718                                            }
719                                            
720                                            return false;
721                                    }
722                    }
723                    throw new ToolBusInternalError("Illegal ATerm in compareType: " + term1);
724            }
725    }