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