705a99b1aa14972c9932bfd496ad6fa11cc06961
[m17n/m17n-lib-js.git] / xex.js
1 // xex.js -- Xex (XML Expression) interpreter
2 // Copyright (C) 2010
3 //   National Institute of Advanced Industrial Science and Technology (AIST)
4 //   Registration Number H15PRO112
5
6 // This file is part of the m17n database; a sub-part of the m17n
7 // library.
8
9 // The m17n library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public License
11 // as published by the Free Software Foundation; either version 2.1 of
12 // the License, or (at your option) any later version.
13
14 // The m17n library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 // Lesser General Public License for more details.
18
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with the m17n library; if not, write to the Free
21 // Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 // Boston, MA 02110-1301, USA.
23
24 var Xex = {};
25
26 (function () {                  // Tracing
27   // The logging node containing tracing information.
28   var log = null;
29   // Number of lines.
30   var lines;
31   // Style properties of the logging node.
32   var styles = { border: '1px solid black',
33                  font: 'normal normal normal small monospace',
34                  width: '100%',
35                  minHeight: '300px',
36                  maxHeight: '300px',
37                  overflow: 'auto' };
38   
39   // Toggle logging on and off.  PARENT if non-null specifies the
40   // parent of the log node.  The log node is appended to PARENT.
41   // If PARENT is null, 'body' node is assumed.
42   Xex.LogToggle = function (parent)
43   {
44     if (log)
45       {
46         log.parentNode.removeChild (log);
47         log = null;
48         return;
49       }
50     if (! parent)
51       parent = document.getElementsByTagName ('body')[0];
52     log = document.createElement ('ol');
53     for (var prop in styles)
54       log.style[prop] = styles[prop];
55     parent.appendChild (log);
56     lines = 0;
57     return log;
58   }
59
60   // Log ARG (string).  INDENT if specified is a number of columns to
61   // indent.  If INDENT is -1, ARG is appended to the last log.
62   Xex.Log = function (arg, indent)
63   {
64     if (! log)
65       return;
66     if (! arg)
67       {
68         while (log.childNodes.length > 0)
69           log.removeChild (log.firstChild);
70         lines = 0;
71       }
72     else
73       {
74         var node;
75         if (indent == -1)
76           log.lastChild.innerText += arg;
77         else
78           {
79             lines++;
80             if (lines >= 256)
81               {
82                 node = log.firstElement ();
83                 log.start = lines - 254;
84               }
85             else
86               node = document.createElement ('li');
87             if (indent != undefined)
88               node.style.textIndent = (indent + 1) + 'em';
89             else
90               node.style.textIndent = '0px';
91             node.innerText = arg;
92             log.appendChild (node);
93             log.scrollTop = log.scrollHeight;
94           }
95       }
96   }
97 }) ();
98
99 Xex.Error = {
100   UnknownError: "unknown-error",
101   WrongArgument: "wrong-argument",
102   // Load time errors.
103   InvalidInteger: "invalid-integer",
104   TermTypeInvalid: "term-type-invalid",
105   FunctionConflict: "function-conflict",
106   VariableTypeConflict: "variable-type-conflict",
107   VariableRangeConflict: "variable-range-conflict",
108   VariableWrongRange: "variable-wrong-range",
109   VariableWrongValue: "variable-wrong-value",
110
111   UnknownFunction: "unknown-function",
112   MacroExpansionError: "macro-expansion-error",
113   NoVariableName: "no-variable-name",
114   NoFunctionName: "no-funcion-name",
115
116   // Run time errors.
117   ArithmeticError: "arithmetic-error",
118   WrongType: "wrong-type",
119   IndexOutOfRange: "index-out-of-range",
120   ValueOutOfRange: "value-out-of-range",
121   NoLoopToBreak: "no-loop-to-break",
122   UncaughtThrow: "uncaught-throw"
123 };
124
125 Xex.Variable = function (domain, name, desc, val, range)
126 {
127   this.domain = domain;
128   this.name = name;
129   this.desc = desc;
130   this.val = val;
131   this.range = range;
132 }
133
134 Xex.Variable.prototype.clone = function ()
135 {
136   return new Xex.Variable (this.domain, this.name, this.desc,
137                            this.val, this.range);
138 }
139     
140 Xex.Variable.prototype.Equals = function (obj)
141 {
142   return ((obj instanceof Xex.Variable)
143           && obj.name == this.name);
144 }
145
146 Xex.Variable.prototype.SetValue = function (term)
147 {
148   this.val = term;
149   return term;
150 }
151
152 Xex.Function = function (name, with_var, min_args, max_args)
153 {
154   this.name = name;
155   this.with_var = with_var;
156   this.min_args = min_args;
157   this.max_args = max_args;
158 };  
159
160 Xex.Subrountine = function (builtin, name, with_var, min_args, max_args)
161 {
162   Xex.Function.apply (this, [name, with_var, min_args, max_args]);
163   this.builtin = builtin;
164 }
165
166 Xex.Subrountine.prototype.Call = function (domain, vari, args)
167 {
168   var newargs = new Array ();
169   for (var i = 0; i < args.length; i++)
170     {
171       newargs[i] = args[i].Eval (domain);
172       if (domain.Thrown ())
173         return newargs[i];
174     }
175   return this.builtin (domain, vari, newargs)
176 }
177
178 Xex.SpecialForm = function (builtin, name, with_var, min_args, max_args)
179 {
180   Xex.Function.apply (this, [name, with_var, min_args, max_args]);
181   this.builtin = builtin;
182 }
183
184 Xex.SpecialForm.prototype.Call = function (domain, vari, args)
185 {
186   return this.builtin (domain, vari, args)
187 }
188
189 Xex.Lambda = function (name, min_args, max_args, args, body)
190 {
191   Xex.Function.apply (this, [name, false, min_args, max_args]);
192   this.args = args;
193   this.body = body;
194 }
195
196 Xex.Lambda.prototype.Call = function (domain, vari, args)
197 {
198   var current = domain.bindings;
199   var result = Xex.Zero;
200   var limit = max_args >= 0 ? args.length : args.length - 1;
201   var i;
202   
203   try {
204     for (i = 0; i < limit; i++)
205       {
206         result = args[i].Eval (domain);
207         if (domain.Thrown ())
208           return result;
209         domain.Bind (this.args[i], result);
210       }
211     if (max_args < 0)
212       {
213         var list = new Array ();
214         for (i = 0; i < args[limit].length; i++)
215           {
216             result = args[limit].Eval (domain);
217             if (domain.Thrown ())
218               return result;
219             list[i] = result;
220           }
221         domain.Bind (this.args[limit], list);
222       }
223     try {
224       domain.Catch (Xex.CatchTag.Return);
225       for (var term in this.body)
226         {
227           result = term.Eval (domain);
228           if (domain.Thrown ())
229             return result;
230         }
231     } finally {
232       domain.Uncatch ();
233     }
234   } finally {
235     domain.UnboundTo (current);
236   }
237   return result;
238 }
239
240 Xex.Macro = function (name, min_args, max_args, args, body)
241 {
242   Xex.Function.apply (this, [name, false, min_args, max_args]);
243   this.args = args;
244   this.body = body;
245 }
246
247 Xex.Macro.prototype.Call = function (domain, vari, args)
248 {
249   var current = domain.bindings;
250   var result = Xex.Zero;
251   var i;
252
253   try {
254     for (i = 0; i < args.length; i++)
255       domain.Bind (this.args[i], args[i]);
256     try {
257       domain.Catch (Xex.CatchTag.Return);
258       for (var i in this.body)
259         {
260           result = this.body[i].Eval (domain);
261           if (domain.Thrown ())
262             break;
263         }
264     } finally {
265       domain.Uncatch ();
266     }
267   } finally {
268     domain.UnboundTo (current);
269   }
270   return result;
271 }
272
273 Xex.Bindings = function (vari)
274 {
275   this.vari = vari;
276   this.old_value = vari.val;
277 }
278
279 Xex.Bindings.prototype.UnboundTo = function (boundary)
280 {
281   for (var b = this; b != boundary; b = b.next)
282     b.vari.val = b.old_value;
283   return boundary;
284 }
285
286 Xex.Bind = function (bindings, vari, val)
287 {
288   var b = new Xex.Bindings (vari);
289   b.vari.val = val;
290   b.next = bindings;
291   return b;
292 }
293
294 Xex.CatchTag = {
295   Return: 0,
296   Break: 1
297 }
298
299 Xex.Domain = function (name, parent, context)
300 {
301   this.name = name;
302   this.context = context;
303   this.depth = 0;
304
305   if (name != 'basic' && ! parent)
306     parent = Xex.BasicDomain
307   this.parent = parent;
308   this.termtypes = {};
309   this.functions = {};
310   this.variables = {};
311   if (parent)
312     {
313       var elt;
314       for (elt in parent.termtypes)
315         this.termtypes[elt] = parent.termtypes[elt];
316       for (elt in parent.functions)
317         this.functions[elt] = parent.functions[elt];
318       for (elt in parent.variables)
319         {
320           var vari = parent.variables[elt];
321           this.variables[elt] = new Xex.Variable (this, vari.name, vari.desc,
322                                                   vari.val, vari.range);
323         }
324     }
325
326   this.call_stack = new Array ();
327   this.bindings = null;
328   this.catch_stack = new Array ();
329   this.catch_count = 0;
330   this.caught = false;
331 };
332
333 Xex.Domain.prototype = {
334   CallStackCount: function () { return this.call_stack.length; },
335   CallStackPush: function (term) { this.call_stack.push (term); },
336   CallStackPop: function () { this.call_stack.pop (); },
337   Bind: function (vari, val)
338   {
339     this.bindings = Xex.Bind (this.bindings, vari, val);
340   },
341   UnboundTo: function (boundary)
342   {
343     if (this.bindings)
344       this.bindings = this.bindings.UnboundTo (boundary);
345   },
346   Catch: function (tag) { this.catch_stack.push (tag); this.catch_count++; },
347   Uncatch: function ()
348   {
349     this.catch_stack.pop ();
350     if (this.catch_count > this.catch_stack.length)
351       this.catch_count--;
352   },
353   Thrown: function ()
354   {
355     if (this.catch_count < this.catch_stack.length)
356       {
357         this.caught = (this.catch_count == this.catch_stack.length - 1);
358         return true;
359       }
360     this.caught = false;
361     return false;
362   },
363   ThrowReturn: function ()
364   {
365     for (var i = this.catch_stack.length - 1; i >= 0; i--)
366       {
367         this.catch_count--;
368         if (this.catch_stack[i] == Xex.CatchTag.Return)
369           break;
370       }
371   },
372   ThrowBreak: function ()
373   {
374     if (this.catch_stack[this.catch_stack.length - 1] != Xex.CatchTag.Break)
375       throw new Xex.ErrTerm (Xex.Error.NoLoopToBreak,
376                            "No surrounding loop to break");
377     this.catch_count--;
378   },
379   ThrowSymbol: function (tag)
380   {
381     var i = this.catch_count;
382     for (var j = this.catch_stack.length - 1; j >= 0; j--)
383       {
384         i--;
385         if (Xex.CatchTag.Matches (this.catch_stack[i], tag))
386           {
387             this.catch_count = i;
388             return;
389           }
390       }
391     throw new Xex.ErrTerm (Xex.Error.UncaughtThrow,
392                          "No corresponding catch: " + tag);
393   },
394   DefType: function (obj)
395   {
396     var type = obj.type;
397     if (this.termtypes[type])
398       throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
399                            "Already defined: " + type);
400     if (this.functions[type])
401       throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
402                            "Already defined as a funciton or a macro: "
403                            + type);
404     this.termtypes[type] = obj.Parser;
405   },
406   DefSubr: function (builtin, name, with_var, min_args, max_args)
407   {
408     this.functions[name] = new Xex.Subrountine (builtin, name, with_var,
409                                                 min_args, max_args);
410   },
411   DefSpecial: function (builtin, name, with_var, min_args, max_args)
412   {
413     this.functions[name] = new Xex.SpecialForm (builtin, name, with_var,
414                                                 min_args, max_args);
415   },
416   Defun: function (name, min_args, max_args, args, body)
417   {
418     this.functions[name] =  new Xex.Lambda (name, min_args, max_args,
419                                             args, body);
420   },
421   DefunByFunc: function (func) { this.functions[func.name] = func; },
422   Defmacro: function (name, min_args, max_args, args, body)
423   {
424     this.functions[name] = new Xex.Macro (name, min_args, max_args,
425                                           args, body);
426   },
427   DefAlias: function (alias, fname)
428   {
429     var func = this.functions[fname];
430
431     if (! func)
432       throw new Xex.ErrTerm (Xex.Error.UnknownFunction, fname);
433     if (this.termtypes[alias])
434       throw new Xex.ErrTerm (Xex.Error.FunctionConflict,
435                            "Already defined as a term type: " + alias);
436     if (this.functions[alias])
437       throw new Xex.ErrTerm (Xex.Error.FunctionConflict,
438                            "Already defined as a function: " + alias);
439     this.functions[alias] = func;
440   },
441   Defvar: function (name, desc, val, range)
442   {
443     var vari = new Xex.Variable (this, name, desc, val, range);
444     this.variables[name] = vari;
445     return vari;
446   },
447   GetFunc: function (name)
448   {
449     var func = this.functions[name];
450     if (! func)
451       throw new Xex.ErrTerm (Xex.Error.UnknownFunction,
452                              "Unknown function: " + name);
453     return func;
454   },
455   CopyFunc: function (domain, name)
456   {
457     var func = this.functions[name];
458     domain.DefunByFunc (func);
459     return true;
460   },
461   CopyFuncAll: function (domain)
462   {
463     for (var elt in this.functions)
464       domain.DefunByFunc (this.functions[elt]);
465   },
466   GetVarCreate: function (name)
467   {
468     var vari = this.variables[name];
469     if (! vari)
470       vari = this.variables[name] = new Xex.Variable (this, name, null,
471                                                       Xex.Zero, null);
472     return vari;
473   },
474   GetVar: function (name) { return this.variables[name]; },
475   SaveValues: function ()
476   {
477     values = {};
478     for (var elt in this.variables)
479       values[elt] = this.variables[elt].val.Clone ();
480     return values;
481   },
482   RestoreValues: function (values)
483   {
484     var name;
485     for (name in values)
486       {
487         var vari = this.variables[name];
488         vari.val = values[name];
489       }
490   }
491 };
492
493 Xex.Term = function (type) { this.type = type; }
494 Xex.Term.prototype = {
495   IsTrue: function () { return true; },
496   Eval: function (domain) { return this.Clone (); },
497   Clone: function (domain) { return this; },
498   Equals: function (obj)
499   {
500     return (this.type == obj.type
501             && this.val != undefined
502             && obj.val == this.val);
503   },
504   Matches: function (obj) { return this.Equals (obj); },
505   toString: function ()
506   {
507     if (this.val != undefined)
508       return '<' + this.type + '>' + this.val + '</' + this.type + '>';
509     return '<' + this.type + '/>';
510   },
511   Intval: function () { throw new Xex.ErrTerm (Xex.Error.WrongType,
512                                                "Not an integer"); },
513   Strval: function () { throw new Xex.ErrTerm (Xex.Error.WrongType,
514                                                "Not a string"); }
515 };
516
517 Node.prototype.firstElement = function ()
518 {
519   for (var n = this.firstChild; n; n = n.nextSibling)
520     if (n.nodeType == 1)
521       return n;
522   return null;
523 }
524
525 Node.prototype.nextElement = function ()
526 {
527   for (var n = this.nextSibling; n; n = n.nextSibling)
528     if (n.nodeType == 1)
529       return n;
530   return null;
531 };
532
533 (function () {
534   function parse_defvar (domain, node)
535   {
536     var name = node.attributes['vname'].nodeValue;
537     if (! name)
538       throw new Xex.ErrTerm (Xex.Error.NoVariableName, node, '');
539     var vari = domain.variables[name];
540     var desc, val = null, range;
541     if (vari)
542       {
543         desc = vari.description;
544         val = vari.val;
545         range = vari.range;
546       }
547     node = node.firstElement ();
548     if (node && node.nodeName == 'description')
549       {
550         desc = node.firstChild.nodeValue;
551         node = node.nextElement ();
552       }
553     if (node)
554       {
555         val = Xex.Term.Parse (domain, node);
556         node = node.nextElement ();
557         if (node && node.nodeName == 'possible-values')
558           for (node = node.firstElement (); node; node = node.nextElement ())
559             {
560               var pval;
561               if (node.nodeName == 'range')
562                 {
563                   if (! val.IsInt)
564                     throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
565                                            'Range not allowed for ' + name);
566                   pval = new Array ();
567                   for (var n = node.firstElement (); n; n = n.nextElement ())
568                     {
569                       var v = Xex.Term.Parse (domain, n);
570                       if (! v.IsInt)
571                         throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
572                                                'Invalid range value: ' + val);
573                       pval.push (v);
574                     }
575                   }
576               else
577                 {
578                   pval = Xex.Term.Parse (domain, node);
579                   if (val.type != pval.type)
580                     throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
581                                            'Invalid possible value: ' + pval);
582                 }
583               if (! range)
584                 range = new Array ();
585               range.push (pval);
586           }
587       }
588     if (val == null)
589       val = Xex.Zero;
590     domain.Defvar (name, desc, val, range);
591     return name;
592   }
593
594   function parse_defun_head (domain, node)
595   {
596     var name = node.attributes['fname'].nodeValue;
597     if (! name)
598       throw new Xex.ErrTerm (Xex.Error.NoFunctionName, node, '');
599     var args = new Array ();
600     var nfixed = 0, noptional = 0, nrest = 0;
601
602     node = node.firstElement ();
603     if (node && node.nodeName == 'args')
604       {
605         var n;
606         for (n = n.firstElement (); n; n = n.nextElement ())
607           {
608             if (n.nodeName == 'fixed')
609               nfixed++;
610             else if (n.nodeName == 'optional')
611               noptional++;
612             else if (n.nodeName == 'rest')
613               nrest++;
614             else
615               throw new Xex.ErrTerm (Xex.Error.WrongType, n, n.nodeName);
616           }
617         if (nrest > 1)
618           throw new Xex.ErrTerm (Xex.Error.WrongType, n, 'Too many <rest>');
619         for (n = node.firstElement (); n; n = n.nextElement ())
620           args.push (domain.DefVar (n.attributes['vname'].nodeValue));
621       }
622     args.min_args = nfixed;
623     args.max_args = nrest == 0 ? nfixed + noptional : -1;
624
625     if (node.nodeName == 'defun')
626       domain.Defun (name, args, null);
627     else
628       domain.Defmacro (name, args, null);
629     return name;
630   }
631
632   function parse_defun_body (domain, node)
633   {
634     var name = node.attributes['fname'].nodeValue;
635     var func = domain.GetFunc (name);
636     var body;
637     for (node = node.firstElement (); node; node = node.nextElement ())
638       if (node.nodeName != 'description' && node.nodeName != 'args')
639         break;
640     body = Xex.Term.Parse (domain, node, null);
641     func.body = body;
642   }
643
644   Xex.Term.Parse = function (domain, node, stop)
645   {
646     if (arguments.length == 2)
647       {
648         var name = node.nodeName;
649         var parser = domain.termtypes[name];
650
651         if (parser)
652           return parser (domain, node);
653         if (name == 'defun' || name == 'defmacro')
654           {
655             name = parse_defun_head (domain, node);
656             parse_defun_body (domain, node);
657             return new Xex.StrTerm (name);
658           }
659         if (name == 'defvar')
660           {
661             name = parse_defvar (domain, node);
662             return new Xex.StrTerm (name);
663           }
664         return new Xex.Funcall.prototype.Parser (domain, node);
665       }
666     for (var n = node; n && n != stop; n = n.nextElement ())
667       if (n.nodeName == 'defun' || n.nodeName == 'defmacro')
668         parse_defun_head (domain, n);
669     var terms = null;
670     for (var n = node; n && n != stop; n = n.nextElement ())
671       {
672         if (n.nodeName == 'defun' || n.nodeName == 'defmacro')
673           parse_defun_body (domain, n);
674         else if (n.nodeName == 'defvar')
675           parse_defvar (domain, n);
676         else
677           {
678             if (! terms)
679               terms = new Array ();
680             terms.push (Xex.Term.Parse (domain, n));
681           }
682       }
683     return terms;
684   }
685 }) ();
686
687 Xex.Varref = function (vname)
688 {
689   this.val = vname;
690 };
691
692 (function () {
693   var proto = new Xex.Term ('varref');
694
695   proto.Clone = function () { return new Xex.Varref (this.val); }
696   proto.Eval = function (domain)
697   {
698     var vari = domain.GetVarCreate (this.val);
699     Xex.Log (this.ToString () + '=>' + vari.val, domain.depth);
700     return vari.val;
701   }
702
703   proto.Parser = function (domain, node)
704   {
705     return new Xex.Varref (node.attributes['vname'].nodeValue);
706   }
707
708   proto.ToString = function ()
709   {
710     return '<varref vname="' + this.val + '"/>';
711   }
712
713   Xex.Varref.prototype = proto;
714 }) ();
715
716 var null_args = new Array ();
717   
718 Xex.Funcall = function (func, vname, args)
719 {
720   this.func = func;
721   this.vname = vname;
722   this.args = args || null_args;
723 };
724
725 (function () {
726   var proto = new Xex.Term ('funcall');
727
728   proto.Parser = function (domain, node)
729   {
730     var fname = node.nodeName;
731     var attr;
732
733     if (fname == 'funcall')
734       fname = node.attributes['fname'].nodeValue;
735     var func = domain.GetFunc (fname);
736     var vname;
737     attr = node.attributes['vname'];
738     vname = attr != undefined ? attr.nodeValue : null;
739     var args = Xex.Term.Parse (domain, node.firstElement (), null);
740     return new Xex.Funcall (func, vname, args);
741   }
742
743   proto.New = function (domain, fname, vname, args)
744   {
745     var func = domain.GetFunc (fname);
746     var funcall = new Xex.Funcall (func, vname, args);
747     if (func instanceof Xex.Macro)
748       funcall = funcall.Eval (domain);
749     return funcall;
750   }
751
752   proto.Eval = function (domain)
753   {
754     Xex.Log (this, domain.depth);
755     var vari;
756     if (this.vname)
757       vari = domain.GetVarCreate (this.vname);
758     domain.depth++;
759     var result;
760     try {
761       result = this.func.Call (domain, vari, this.args);
762     } finally {
763       Xex.Log (' => ' + result, --domain.depth,
764                this.func instanceof Xex.Subrountine);
765     }
766     return result;
767   }
768
769   proto.Clone = function ()
770   {
771     return new Xex.Funcall (this.func, this.vari, this.args);
772   }
773
774   proto.Equals = function (obj)
775   {
776     return (obj.type == 'funcall'
777             && obj.func == this.func
778             && obj.vari.Equals (this.vari)
779             && obj.args.length == this.func.length);
780   }
781
782   proto.toString = function ()
783   {
784     var arglist = ''
785     var len = this.args.length;
786     var str = '<' + this.func.name;
787     if (this.vname)
788       str += ' vname="' + this.vname + '"';
789     if (len == 0)
790       return str + '/>';
791     if (this.func instanceof Xex.Subrountine)
792       for (var i = 0; i < len; i++)
793         arglist += this.args[i].toString ();
794     else
795       for (var i = 0; i < len; i++)
796         arglist += '.';
797     return str + '>' + arglist + '</' + this.func.name + '>';
798   }
799
800   Xex.Funcall.prototype = proto;
801 }) ();
802
803 Xex.ErrTerm = function (ename, message, stack)
804 {
805   this.ename = ename;
806   this.message = message;
807   this.stack = stack;
808 };
809
810 (function () {
811   var proto = new Xex.Term ('error');
812
813   proto.IsError = true;
814
815   proto.Parser = function (domain, node)
816   {
817     return new Xex.ErrTerm (node.attributes['ename'].nodeValue,
818                             node.innerText, false);
819   }
820
821   proto.CallStack = function () { return stack; }
822
823   proto.SetCallStack = function (value) { statck = value; }
824
825   proto.Clone = function ()
826   {
827     return new Xex.ErrTerm (ename, message, false);
828   }
829
830   proto.Equals = function (obj)
831   {
832     return (obj.IsError
833             && obj.ename == ename && obj.message == message
834             && (obj.stack ? (stack && stack.length == obj.stack.length)
835                 : ! stack));
836   }
837
838   proto.Matches = function (obj)
839   {
840     return (obj.IsError && obj.ename == ename);
841   }
842
843   proto.toString = function ()
844   {
845     return '<error ename="' + this.ename + '">' + this.message + '</error>';
846   }
847
848   Xex.ErrTerm.prototype = proto;
849 }) ();
850
851 Xex.IntTerm = function (num) { this.val = num; };
852 (function () {
853   var proto = new Xex.Term ('integer');
854   proto.IsInt = true;
855   proto.Intval = function () { return this.val; };
856   proto.IsTrue = function () { return this.val != 0; }
857   proto.Clone = function () { return new Xex.IntTerm (this.val); }
858   proto.Parser = function (domain, node)
859   {
860     var str = node.firstChild.nodeValue;
861
862     if (str.charAt (0) == '?' && str.length == 2)
863       return new Xex.IntTerm (str.charCodeAt (1));
864     return new Xex.IntTerm (parseInt (node.firstChild.nodeValue));
865   }
866   Xex.IntTerm.prototype = proto;
867 }) ();
868
869 Xex.StrTerm = function (str) { this.val = str; };
870 (function () {
871   var proto = new Xex.Term ('string');
872   proto.IsStr = true;
873   proto.Strval = function () { return this.val; };
874   proto.IsTrue = function () { return this.val.length > 0; }
875   proto.Clone = function () { return new Xex.StrTerm (this.val); }
876   proto.Parser = function (domain, node)
877   {
878     return new Xex.StrTerm (node.firstChild ? node.firstChild.nodeValue : '');
879   }
880   Xex.StrTerm.prototype = proto;
881 }) ();
882
883 Xex.SymTerm = function (str) { this.val = str; };
884 (function () {
885   var proto = new Xex.Term ('symbol');
886   proto.IsSymbol = true;
887   proto.IsTrue = function () { return this.val != 'nil'; }
888   proto.Clone = function () { return new Xex.SymTerm (this.val); }
889   proto.Parser = function (domain, node)
890   {
891     return new Xex.SymTerm (node.firstChild.nodeValue);
892   }
893   Xex.SymTerm.prototype = proto;
894 }) ();
895
896 Xex.LstTerm = function (list) { this.val = list; };
897 (function () {
898   var proto = new Xex.Term ('list');
899   proto.IsList = true;
900   proto.IsTrue = function () { return this.val.length > 0; }
901   proto.Clone = function () { return new Xex.LstTerm (this.val.slice (0)); }
902
903   proto.Equals = function (obj)
904   {
905     if (obj.type != 'list' || obj.val.length != this.val.length)
906       return false;
907     var i, len = this.val.length;
908     for (i = 0; i < len; i++)
909       if (! this.val[i].Equals (obj.val[i]))
910         return false;
911     return true;
912   }
913
914   proto.Parser = function (domain, node)
915   {
916     var list = Xex.Term.Parse (domain, node.firstElement (), null);
917     return new Xex.LstTerm (list);
918   }
919
920   proto.toString = function ()
921   {
922     var len = this.val.length;
923
924     if (len == 0)
925       return '<list/>';
926     var str = '<list>';
927     for (var i = 0; i < len; i++)
928       str += this.val[i].toString ();
929     return str + '</list>';
930   }
931   Xex.LstTerm.prototype = proto;
932 }) ();
933
934 (function () {
935   var basic = new Xex.Domain ('basic', null, null);
936
937   function Fset (domain, vari, args)
938   {
939     if (! vari)
940       throw new Xex.ErrTerm (Xex.Error.NoVariableName,
941                              'No variable name to set');
942     vari.SetValue (args[0]);
943     return args[0];
944   }
945
946   function Fnot (domain, vari, args)
947   {
948     return (args[0].IsTrue () ? Xex.Zero : Xex.One);
949   }
950
951   function maybe_set_intvar (vari, n)
952   {
953     var term = new Xex.IntTerm (n);
954     if (vari)
955       vari.SetValue (term);
956     return term;
957   }
958
959   function Fadd (domain, vari, args)
960   {
961     var n = vari ? vari.val.Intval () : 0;
962     var len = args.length;
963
964     for (var i = 0; i < len; i++)
965       n += args[i].Intval ();
966     return maybe_set_intvar (vari, n);
967   }
968
969   function Fmul (domain, vari, args)
970   {
971     var n = vari ? vari.val.Intval () : 1;
972     for (var i = 0; i < args.length; i++)
973       n *= args[i].Intval ();
974     return maybe_set_intvar (vari, n);
975   }
976
977   function Fsub (domain, vari, args)
978   {
979     var n, i;
980
981     if (! vari)
982       {
983         n = args[0].Intval ();
984         i = 1;
985       }
986     else
987       {
988         n = vari.val.Intval ();
989         i = 0;
990       }
991     while (i < args.length)
992       n -= args[i++].Intval ();
993     return maybe_set_intvar (vari, n);
994   }
995
996   function Fdiv (domain, vari, args)
997   {
998     var n, i;
999
1000     if (! vari == null)
1001       {
1002         n = args[0].Intval ();
1003         i = 1;
1004       }
1005     else
1006       {
1007         n = vari.val.Intval ();
1008         i = 0;
1009       }
1010     while (i < args.length)
1011       n /= args[i++].Intval ();
1012     return maybe_set_intvar (vari, n);
1013   }
1014
1015   function Fmod (domain, vari, args)
1016   {
1017     return maybe_set_intvar (vari, args[0].Intval () % args[1].Intval ());
1018   }
1019
1020   function Flogior (domain, vari, args)
1021   {
1022     var n = vari == null ? 0 : vari.val;
1023     for (var i = 0; i < args.length; i++)
1024       n |= args[i].val;
1025     return maybe_set_intvar (vari, n);
1026   }
1027
1028   function Flogand (domain, vari, args)
1029   {
1030     var n, i;
1031     if (vari == null)
1032       {
1033         Xex.Log ("logand arg args[0]" + args[0]);
1034         n = args[0].Intval ()
1035         i = 1;
1036       }
1037     else
1038       {
1039         Xex.Log ("logand arg var " + vari);
1040         n = vari.val.Intval ();
1041         i = 0;
1042       }
1043     while (n > 0 && i < args.length)
1044       {
1045         Xex.Log ("logand arg " + args[i]);
1046         n &= args[i++].val;
1047       }
1048     return maybe_set_intvar (vari, n);
1049   }
1050
1051   function Flsh (domain, vari, args)
1052   {
1053     return maybe_set_intvar (vari, args[0].Intval () << args[1].Intval ());
1054   }
1055
1056   function Frsh (domain, vari, args)
1057   {
1058     return maybe_set_intvar (vari, args[0].Intval () >> args[1].Intval ());
1059   }
1060
1061   function Fand (domain, vari, args)
1062   {
1063     var len = args.length;
1064     for (var i = 0; i < len; i++)
1065     {
1066       var result = args[i].Eval (domain);
1067       if (domain.Thrown ())
1068         return result;
1069       if (! result.IsTrue ())
1070         return Xex.Zero;
1071     }
1072     return Xex.One;
1073   }
1074
1075   function For (domain, vari, args)
1076   {
1077     var len = args.length;
1078     for (var i = 0; i < len; i++)
1079     {
1080       var result = args[i].Eval (domain);
1081       if (domain.Thrown ())
1082         return result;
1083       if (result.IsTrue ())
1084         return Xex.One;
1085     }
1086     return Xex.Zero;
1087   }
1088
1089   function Feq (domain, vari, args)
1090   {
1091     for (var i = 1; i < args.length; i++)
1092       if (! args[i - 1].Equals (args[i]))
1093         return Xex.Zero;
1094     return Xex.One;
1095   }
1096
1097   function Fnoteq (domain, vari, args)
1098   {
1099     return (Feq (domain, vari, args) == Xex.One ? Xex.Zero : Xex.One);
1100   }
1101
1102   function Flt (domain, vari, args)
1103   {
1104     var n = args[0].Intval ();
1105
1106     for (var i = 1; i < args.length; i++)
1107       {
1108         var n1 = args[i].Intval ();
1109         if (n >= n1)
1110           return Xex.Zero;
1111         n = n1;
1112       }
1113     return Xex.One;
1114   }
1115
1116   function Fle (domain, vari, args)
1117   {
1118     var n = args[0].Intval ();
1119     for (var i = 1; i < args.length; i++)
1120       {
1121         var n1 = args[i].Intval ();
1122         if (n > n1)
1123           return Xex.Zero;
1124         n = n1;
1125       }
1126     return Xex.One;
1127   }
1128
1129   function Fgt (domain, vari, args)
1130   {
1131     var n = args[0].Intval ();
1132     for (var i = 1; i < args.length; i++)
1133       {
1134         var n1 = args[i].Intval ();
1135         if (n <= n1)
1136           return Xex.Zero;
1137         n = n1;
1138       }
1139     return Xex.One;
1140   }
1141
1142   function Fge (domain, vari, args)
1143   {
1144     var n = args[0].Intval ();
1145     for (var i = 1; i < args.length; i++)
1146       {
1147         var n1 = args[i].Intval ();
1148         if (n < n1)
1149           return Xex.Zero;
1150         n = n1;
1151       }
1152     return Xex.One;
1153   }
1154
1155   function Fprogn (domain, vari, args)
1156   {
1157     var result = Xex.One;
1158     var len = args.length;
1159
1160     for (var i = 0; i < len; i++)
1161       {
1162         result = args[i].Eval (domain);
1163         if (domain.Thrown ())
1164           return result;
1165       }
1166     return result;
1167   }
1168
1169   function Fif (domain, vari, args)
1170   {
1171     var result = args[0].Eval (domain);
1172
1173     if (domain.Thrown ())
1174       return result;
1175     if (result.IsTrue ())
1176       return args[1].Eval (domain);
1177     if (args.length == 2)
1178       return Xex.Zero;
1179     return args[2].Eval (domain);
1180   }
1181
1182   function Fcond (domain, vari, args)
1183   {
1184     for (var i = 0; i < args.length; i++)
1185       {
1186         var list = args[i];
1187         var result = list.val[0].Eval (domain);
1188         if (result.IsTrue ())
1189           {
1190             for (var j = 1; j < list.val.length; j++)
1191               {
1192                 domain.depth++;
1193                 result = list.val[j].Eval (domain);
1194                 domain.depth--;
1195                 if (domain.Thrown ())
1196                   return result;
1197                 }
1198             return result;
1199           }
1200       }
1201     return Xex.Zero;
1202   }
1203
1204   function eval_terms (domain, terms, idx)
1205   {
1206     var result = Xex.Zero;
1207     domain.caught = false;
1208     for (var i = idx; i < terms.length; i++)
1209       {
1210         result = terms[i].Eval (domain);
1211         if (domain.Thrown ())
1212           return result;
1213       }
1214     return result;
1215   }
1216
1217   function Fcatch (domain, vari, args)
1218   {
1219     var caught = false;
1220     var result;
1221
1222     if (args[0].IsError)
1223       {
1224         try {
1225           result = eval_terms (domain, args, 1);
1226         } catch (e) {
1227           if (e instanceof Xex.ErrTerm)
1228             {
1229               if (! args[0].Matches (e))
1230                 throw e;
1231               if (vari)
1232                 vari.SetValue (e);
1233               return Xex.One;
1234             }
1235         }
1236       }
1237     else if (args[0].IsSymbol)
1238       {
1239         try {
1240           domain.Catch (args[0].val);
1241           result = eval_terms (domain, args, 1);
1242           if (domain.caught)
1243             {
1244               if (vari != null)
1245                 vari.SetValue (result);
1246               return Xex.One;
1247             }
1248           return Xex.Zero;
1249         } finally {
1250           domain.Uncatch ();
1251         }
1252       }
1253     throw new Xex.ErrTerm (Xex.Error.WrongArgument,
1254                            "Not a symbol nor an error: " + args[0]);
1255   }
1256
1257   function Fthrow (domain, vari, args)
1258   {
1259     if (args[0].IsSymbl)
1260       {
1261         domain.ThrowSymbol (args[0]);
1262         return (args[args.length - 1]);
1263       }
1264     if (args[0].IsError)
1265       {
1266         throw args[0];
1267       }
1268     throw new Xex.ErrTerm (Xex.Error.WrongArgument,
1269                            "Not a symbol nor an error:" + args[0]);
1270   }
1271
1272   Xex.BasicDomain = basic;
1273
1274   basic.DefSubr (Fset, "set", true, 1, 1);
1275   basic.DefAlias ("=", "set");
1276   basic.DefSubr (Fnot, "not", false, 1, 1);
1277   basic.DefAlias ("!", "not");
1278   basic.DefSubr (Fadd, "add", true, 1, -1);
1279   basic.DefSubr (Fmul, "mul", true, 1, -1);
1280   basic.DefAlias ("*", "mul");
1281   basic.DefSubr (Fsub, "sub", true, 1, -1);
1282   basic.DefAlias ("-", "sub");
1283   basic.DefSubr (Fdiv, "div", true, 1, -1);
1284   basic.DefAlias ("/", "div");
1285   basic.DefSubr (Fmod, "mod", true, 1, 2);
1286   basic.DefAlias ("%", "mod");
1287   basic.DefSubr (Flogior, "logior", true, 1, -1);
1288   basic.DefAlias ('|', "logior");
1289   basic.DefSubr (Flogand, "logand", true, 1, -1);
1290   basic.DefAlias ("&", "logand");
1291   basic.DefSubr (Flsh, "lsh", true, 1, 2);
1292   basic.DefAlias ("<<", "lsh");
1293   basic.DefSubr (Frsh, "rsh", true, 1, 2);
1294   basic.DefAlias (">>", "rsh");
1295   basic.DefSubr (Feq, "eq", false, 2, -1);
1296   basic.DefAlias ("==", "eq");
1297   basic.DefSubr (Fnoteq, "noteq", false, 2, 2);
1298   basic.DefAlias ("!=", "noteq");
1299   basic.DefSubr (Flt, "lt", false, 2, -1);
1300   basic.DefAlias ("<", "lt");
1301   basic.DefSubr (Fle, "le", false, 2, -1);
1302   basic.DefAlias ("<=", "le");
1303   basic.DefSubr (Fgt, "gt", false, 2, -1);
1304   basic.DefAlias (">", "gt");
1305   basic.DefSubr (Fge, "ge", false, 2, -1);
1306   basic.DefAlias (">=", "ge");
1307   basic.DefSubr (Fthrow, "throw", false, 1, 2);
1308
1309   //basic.DefSubr (Fappend, "append", true, 0, -1);
1310   //basic.DefSubr (Fconcat, "concat", true, 0, -1);
1311   //basic.DefSubr (Fnth, "nth", false, 2, 2);
1312   //basic.DefSubr (Fcopy, "copy", false, 1, 1);
1313   //basic.DefSubr (Fins, "ins", true, 2, 2);
1314   //basic.DefSubr (Fdel, "del", true, 2, 2);
1315   //basic.DefSubr (Feval, "eval", false, 1, 1);
1316   //basic.DefSubr (Fbreak, "break", false, 0, 1);
1317   //basic.DefSubr (Freturn, "return", false, 0, 1);
1318   //basic.DefSubr (Fthrow, "throw", false, 1, 2);
1319
1320   basic.DefSpecial (Fand, "and", false, 1, -1);
1321   basic.DefAlias ("&&", "and");
1322   basic.DefSpecial (For, "or", false, 1, -1);
1323   basic.DefAlias ("||", "or");
1324   basic.DefSpecial (Fprogn, "progn", false, 1, -1);
1325   basic.DefAlias ("expr", "progn");
1326   basic.DefSpecial (Fif, "if", false, 2, 3);
1327   //basic.DefSpecial (Fwhen, "when", false, 1, -1);
1328   //basic.DefSpecial (Floop, "loop", false, 1, -1);
1329   //basic.DefSpecial (Fwhile, "while", false, 1, -1);
1330   basic.DefSpecial (Fcond, "cond", false, 1, -1);
1331   //basic.DefSpecial (Fforeach, "foreach", true, 2, -1);
1332   //basic.DefSpecial (Fquote, "quote", false, 1, 1);
1333   //basic.DefSpecial (Ftype, "type", false, 1, 1);
1334   basic.DefSpecial (Fcatch, "catch", true, 2, -1);
1335
1336   basic.DefType (Xex.Funcall.prototype);
1337   basic.DefType (Xex.Varref.prototype);
1338   basic.DefType (Xex.ErrTerm.prototype);
1339   basic.DefType (Xex.IntTerm.prototype);
1340   basic.DefType (Xex.StrTerm.prototype);
1341   basic.DefType (Xex.SymTerm.prototype);
1342   basic.DefType (Xex.LstTerm.prototype);
1343
1344 }) ();
1345
1346 Xex.Zero = new Xex.IntTerm (0);
1347 Xex.One = new Xex.IntTerm (1);
1348 Xex.nil = new Xex.SymTerm ('nil');
1349
1350 Xex.Load = function (server, file)
1351 {
1352   var obj = new XMLHttpRequest ();
1353   var url = server ? server + '/' + file : file;
1354   obj.open ('GET', url, false);
1355   obj.overrideMimeType ('text/xml');
1356   obj.send ('');
1357   return (obj.responseXML && obj.responseXML.firstChild);
1358 }