*** empty log message ***
[m17n/m17n-lib-js.git] / xex2.js
diff --git a/xex2.js b/xex2.js
new file mode 100644 (file)
index 0000000..144e9af
--- /dev/null
+++ b/xex2.js
@@ -0,0 +1,1383 @@
+// xex.js -- Xex (XML Expression) interpreter
+// Copyright (C) 2010
+//   National Institute of Advanced Industrial Science and Technology (AIST)
+//   Registration Number H15PRO112
+
+// This file is part of the m17n database; a sub-part of the m17n
+// library.
+
+// The m17n library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public License
+// as published by the Free Software Foundation; either version 2.1 of
+// the License, or (at your option) any later version.
+
+// The m17n library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+
+// You should have received a copy of the GNU Lesser General Public
+// License along with the m17n library; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+// Please note that the code is not yet matured.
+
+var Xex = {};
+
+(function () {                 // Logging
+  // The logging node containing tracing information.
+  var log = null;
+  // Number of lines.
+  var lines;
+  // Style properties of the logging node.
+  var styles = { border: '1px solid black',
+                font: 'normal normal normal small monospace',
+                width: '100%',
+                minHeight: '300px',
+                maxHeight: '300px',
+                overflow: 'auto' };
+  
+  // Toggle logging on and off.  PARENT if non-null specifies the
+  // parent of the log node.  The log node is appended to PARENT.
+  // If PARENT is null, 'body' node is assumed.
+  Xex.LogToggle = function (parent)
+  {
+    if (log)
+      {
+       log.parentNode.removeChild (log);
+       log = null;
+       return;
+      }
+    if (! parent)
+      parent = document.getElementsByTagName ('body')[0];
+    log = document.createElement ('ol');
+    for (var prop in styles)
+      log.style[prop] = styles[prop];
+    parent.appendChild (log);
+    lines = 0;
+    return log;
+  }
+
+  // Log ARG (string).  INDENT if specified is a number of columns to
+  // indent.  If INDENT is -1, ARG is appended to the last log.
+  Xex.Log = function (arg, indent)
+  {
+    if (! log)
+      return;
+    if (! arg)
+      {
+       while (log.childNodes.length > 0)
+         log.removeChild (log.firstChild);
+       lines = 0;
+      }
+    else
+      {
+       var node;
+       if (indent == -1)
+         log.lastChild.innerText += arg;
+       else
+         {
+           lines++;
+           if (lines >= 256)
+             {
+               node = log.firstElement ();
+               log.start = lines - 254;
+             }
+           else
+             node = document.createElement ('li');
+           if (indent != undefined)
+             node.style.textIndent = (indent + 1) + 'em';
+           else
+             node.style.textIndent = '0px';
+           node.innerText = arg;
+           log.appendChild (node);
+           log.scrollTop = log.scrollHeight;
+         }
+      }
+  }
+}) ();
+
+Xex.Error = {
+  UnknownError: "unknown-error",
+  WrongArgument: "wrong-argument",
+  // Load time errors.
+  InvalidInteger: "invalid-integer",
+  TermTypeInvalid: "term-type-invalid",
+  FunctionConflict: "function-conflict",
+  VariableTypeConflict: "variable-type-conflict",
+  VariableRangeConflict: "variable-range-conflict",
+  VariableWrongRange: "variable-wrong-range",
+  VariableWrongValue: "variable-wrong-value",
+
+  UnknownFunction: "unknown-function",
+  MacroExpansionError: "macro-expansion-error",
+  NoVariableName: "no-variable-name",
+  NoFunctionName: "no-funcion-name",
+
+  // Run time errors.
+  ArithmeticError: "arithmetic-error",
+  WrongType: "wrong-type",
+  IndexOutOfRange: "index-out-of-range",
+  ValueOutOfRange: "value-out-of-range",
+  NoLoopToBreak: "no-loop-to-break",
+  UncaughtThrow: "uncaught-throw"
+};
+
+Xex.Variable = function (name, desc, val, range)
+{
+  if (name)
+    this.name = name;
+  if (desc)
+    this.desc = desc;
+  this.val = val;
+  if (range)
+    this.range = range;
+}
+
+Xex.Variable.prototype = {
+  clone: function ()
+  {
+    return new Xex.Variable (this.name, this.desc, this.val, this.range);
+  },
+  equals: function (obj)
+  {
+    return ((obj instanceof Xex.Variable)
+           && obj.name == this.name);
+  },
+  SetValue: function (term)
+  {
+    this.val = term;
+    return term;
+  }
+}  
+
+
+Xex.Function = function (name, with_var, min_args, max_args)
+{
+  this.name = name;
+  this.with_var = with_var;
+  this.min_args = min_args;
+  this.max_args = max_args;
+};  
+
+Xex.Subrountine = function (builtin, name, with_var, min_args, max_args)
+{
+  Xex.Function.apply (this, [name, with_var, min_args, max_args]);
+  this.builtin = builtin;
+}
+
+Xex.Subrountine.prototype.Call = function (domain, vari, args)
+{
+  var newargs = new Array ();
+  for (var i = 0; i < args.length; i++)
+    {
+      newargs[i] = args[i].Eval (domain);
+      if (domain.Thrown ())
+       return newargs[i];
+    }
+  return this.builtin (domain, vari, newargs)
+}
+
+Xex.SpecialForm = function (builtin, name, with_var, min_args, max_args)
+{
+  Xex.Function.apply (this, [name, with_var, min_args, max_args]);
+  this.builtin = builtin;
+}
+
+Xex.SpecialForm.prototype.Call = function (domain, vari, args)
+{
+  return this.builtin (domain, vari, args)
+}
+
+Xex.Lambda = function (name, min_args, max_args, args, body)
+{
+  Xex.Function.apply (this, [name, false, min_args, max_args]);
+  this.args = args;
+  this.body = body;
+}
+
+Xex.Lambda.prototype.Call = function (domain, vari, args)
+{
+  var current = domain.bindings;
+  var result = Xex.Zero;
+  var limit = max_args >= 0 ? args.length : args.length - 1;
+  var i;
+  
+  try {
+    for (i = 0; i < limit; i++)
+      {
+       result = args[i].Eval (domain);
+       if (domain.Thrown ())
+         return result;
+       domain.Bind (this.args[i], result);
+      }
+    if (max_args < 0)
+      {
+       var list = new Array ();
+       for (i = 0; i < args[limit].length; i++)
+         {
+           result = args[limit].Eval (domain);
+           if (domain.Thrown ())
+             return result;
+           list[i] = result;
+         }
+       domain.Bind (this.args[limit], list);
+      }
+    try {
+      domain.Catch (Xex.CatchTag.Return);
+      for (var term in this.body)
+        {
+         result = term.Eval (domain);
+         if (domain.Thrown ())
+           return result;
+       }
+    } finally {
+      domain.Uncatch ();
+    }
+  } finally {
+    domain.UnboundTo (current);
+  }
+  return result;
+}
+
+Xex.Macro = function (name, min_args, max_args, args, body)
+{
+  Xex.Function.apply (this, [name, false, min_args, max_args]);
+  this.args = args;
+  this.body = body;
+}
+
+Xex.Macro.prototype.Call = function (domain, vari, args)
+{
+  var current = domain.bindings;
+  var result = Xex.Zero;
+  var i;
+
+  try {
+    for (i = 0; i < args.length; i++)
+      domain.Bind (this.args[i], args[i]);
+    try {
+      domain.Catch (Xex.CatchTag.Return);
+      for (var i in this.body)
+        {
+         result = this.body[i].Eval (domain);
+         if (domain.Thrown ())
+           break;
+       }
+    } finally {
+      domain.Uncatch ();
+    }
+  } finally {
+    domain.UnboundTo (current);
+  }
+  return result;
+}
+
+Xex.Bindings = function (vari)
+{
+  this.vari = vari;
+  this.old_value = vari.val;
+}
+
+Xex.Bindings.prototype.UnboundTo = function (boundary)
+{
+  for (var b = this; b != boundary; b = b.next)
+    b.vari.val = b.old_value;
+  return boundary;
+}
+
+Xex.Bind = function (bindings, vari, val)
+{
+  var b = new Xex.Bindings (vari);
+  b.vari.val = val;
+  b.next = bindings;
+  return b;
+}
+
+Xex.CatchTag = {
+  Return: 0,
+  Break: 1
+}
+
+Xex.Domain = function (name, parent, context)
+{
+  this.name = name;
+  this.context = context;
+  this.depth = 0;
+
+  if (name != 'basic' && ! parent)
+    parent = Xex.BasicDomain
+  this.parent = parent;
+  this.termtypes = {};
+  this.functions = {};
+  this.variables = {};
+  if (parent)
+    {
+      var elt;
+      for (elt in parent.termtypes)
+       this.termtypes[elt] = parent.termtypes[elt];
+      for (elt in parent.functions)
+       this.functions[elt] = parent.functions[elt];
+      for (elt in parent.variables)
+       {
+         var vari = parent.variables[elt];
+         this.variables[elt] = new Xex.Variable (vari.name, vari.desc,
+                                                 vari.val, vari.range);
+       }
+    }
+
+  this.call_stack = new Array ();
+  this.bindings = null;
+  this.catch_stack = new Array ();
+  this.catch_count = 0;
+  this.caught = false;
+};
+
+Xex.Domain.prototype = {
+  CallStackCount: function () { return this.call_stack.length; },
+  CallStackPush: function (term) { this.call_stack.push (term); },
+  CallStackPop: function () { this.call_stack.pop (); },
+  Bind: function (vari, val)
+  {
+    this.bindings = Xex.Bind (this.bindings, vari, val);
+  },
+  UnboundTo: function (boundary)
+  {
+    if (this.bindings)
+      this.bindings = this.bindings.UnboundTo (boundary);
+  },
+  Catch: function (tag) { this.catch_stack.push (tag); this.catch_count++; },
+  Uncatch: function ()
+  {
+    this.catch_stack.pop ();
+    if (this.catch_count > this.catch_stack.length)
+      this.catch_count--;
+  },
+  Thrown: function ()
+  {
+    if (this.catch_count < this.catch_stack.length)
+      {
+       this.caught = (this.catch_count == this.catch_stack.length - 1);
+       return true;
+      }
+    this.caught = false;
+    return false;
+  },
+  ThrowReturn: function ()
+  {
+    for (var i = this.catch_stack.length - 1; i >= 0; i--)
+      {
+       this.catch_count--;
+       if (this.catch_stack[i] == Xex.CatchTag.Return)
+         break;
+      }
+  },
+  ThrowBreak: function ()
+  {
+    if (this.catch_stack[this.catch_stack.length - 1] != Xex.CatchTag.Break)
+      throw new Xex.ErrTerm (Xex.Error.NoLoopToBreak,
+                          "No surrounding loop to break");
+    this.catch_count--;
+  },
+  ThrowSymbol: function (tag)
+  {
+    var i = this.catch_count;
+    for (var j = this.catch_stack.length - 1; j >= 0; j--)
+      {
+       i--;
+       if (Xex.CatchTag.Matches (this.catch_stack[i], tag))
+          {
+           this.catch_count = i;
+           return;
+         }
+      }
+    throw new Xex.ErrTerm (Xex.Error.UncaughtThrow,
+                        "No corresponding catch: " + tag);
+  },
+  DefType: function (obj)
+  {
+    var type = obj.type;
+    if (this.termtypes[type])
+      throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
+                          "Already defined: " + type);
+    if (this.functions[type])
+      throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
+                          "Already defined as a funciton or a macro: "
+                          + type);
+    this.termtypes[type] = obj.Parser;
+  },
+  DefSubr: function (builtin, name, with_var, min_args, max_args)
+  {
+    this.functions[name] = new Xex.Subrountine (builtin, name, with_var,
+                                               min_args, max_args);
+  },
+  DefSpecial: function (builtin, name, with_var, min_args, max_args)
+  {
+    this.functions[name] = new Xex.SpecialForm (builtin, name, with_var,
+                                               min_args, max_args);
+  },
+  Defun: function (name, min_args, max_args, args, body)
+  {
+    this.functions[name] =  new Xex.Lambda (name, min_args, max_args,
+                                           args, body);
+  },
+  DefunByFunc: function (func) { this.functions[func.name] = func; },
+  Defmacro: function (name, min_args, max_args, args, body)
+  {
+    this.functions[name] = new Xex.Macro (name, min_args, max_args,
+                                         args, body);
+  },
+  DefAlias: function (alias, fname)
+  {
+    var func = this.functions[fname];
+
+    if (! func)
+      throw new Xex.ErrTerm (Xex.Error.UnknownFunction, fname);
+    if (this.termtypes[alias])
+      throw new Xex.ErrTerm (Xex.Error.FunctionConflict,
+                          "Already defined as a term type: " + alias);
+    if (this.functions[alias])
+      throw new Xex.ErrTerm (Xex.Error.FunctionConflict,
+                          "Already defined as a function: " + alias);
+    this.functions[alias] = func;
+  },
+  Defvar: function (name, desc, val, range)
+  {
+    var vari = new Xex.Variable (name, desc, val, range);
+    this.variables[name] = vari;
+    return vari;
+  },
+  GetFunc: function (name)
+  {
+    var func = this.functions[name];
+    if (! func)
+      throw new Xex.ErrTerm (Xex.Error.UnknownFunction,
+                            "Unknown function: " + name);
+    return func;
+  },
+  CopyFunc: function (domain, name)
+  {
+    var func = this.functions[name];
+    domain.DefunByFunc (func);
+    return true;
+  },
+  CopyFuncAll: function (domain)
+  {
+    for (var elt in this.functions)
+      domain.DefunByFunc (this.functions[elt]);
+  },
+  GetVarCreate: function (name)
+  {
+    var vari = this.variables[name];
+    if (! vari)
+      vari = this.variables[name] = new Xex.Variable (name, null,
+                                                     Xex.Zero, null);
+    return vari;
+  },
+  GetVar: function (name) { return this.variables[name]; },
+  SaveValues: function ()
+  {
+    values = {};
+    for (var elt in this.variables)
+      values[elt] = this.variables[elt].val.Clone ();
+    return values;
+  },
+  RestoreValues: function (values)
+  {
+    var name;
+    for (name in values)
+      {
+       var vari = this.variables[name];
+       vari.val = values[name];
+      }
+  }
+};
+
+Xex.Term = function (type) { this.type = type; }
+Xex.Term.prototype = {
+  IsTrue: function () { return true; },
+  Eval: function (domain) { return this.Clone (); },
+  Clone: function (domain) { return this; },
+  equals: function (obj)
+  {
+    return (this.type == obj.type
+           && this.val != undefined
+           && obj.val == this.val);
+  },
+  Matches: function (obj) { return this.equals (obj); },
+  toString: function ()
+  {
+    if (this.val != undefined)
+      return '<' + this.type + '>' + this.val + '</' + this.type + '>';
+    return '<' + this.type + '/>';
+  },
+  Intval: function () { throw new Xex.ErrTerm (Xex.Error.WrongType,
+                                              "Not an integer"); },
+  Strval: function () { throw new Xex.ErrTerm (Xex.Error.WrongType,
+                                              "Not a string"); }
+};
+
+Node.prototype.firstElement = function ()
+{
+  for (var n = this.firstChild; n; n = n.nextSibling)
+    if (n.nodeType == 1)
+      return n;
+  return null;
+}
+
+Node.prototype.nextElement = function ()
+{
+  for (var n = this.nextSibling; n; n = n.nextSibling)
+    if (n.nodeType == 1)
+      return n;
+  return null;
+};
+
+(function () {
+  function parse_defvar (domain, node)
+  {
+    var name = node.attributes['vname'].nodeValue;
+    if (! name)
+      throw new Xex.ErrTerm (Xex.Error.NoVariableName, node, '');
+    var vari = domain.variables[name];
+    var desc, val = null, range;
+    if (vari)
+      {
+       desc = vari.description;
+       val = vari.val;
+       range = vari.range;
+      }
+    node = node.firstElement ();
+    if (node && node.nodeName == 'description')
+      {
+       desc = node.firstChild.nodeValue;
+       node = node.nextElement ();
+      }
+    if (node)
+      {
+       val = Xex.Term.Parse (domain, node);
+       node = node.nextElement ();
+       if (node && node.nodeName == 'possible-values')
+         for (node = node.firstElement (); node; node = node.nextElement ())
+           {
+             var pval;
+             if (node.nodeName == 'range')
+               {
+                 if (! val.IsInt)
+                   throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
+                                          'Range not allowed for ' + name);
+                 pval = new Array ();
+                 for (var n = node.firstElement (); n; n = n.nextElement ())
+                   {
+                     var v = Xex.Term.Parse (domain, n);
+                     if (! v.IsInt)
+                       throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
+                                              'Invalid range value: ' + val);
+                     pval.push (v);
+                   }
+                 }
+             else
+               {
+                 pval = Xex.Term.Parse (domain, node);
+                 if (val.type != pval.type)
+                   throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
+                                          'Invalid possible value: ' + pval);
+               }
+             if (! range)
+               range = new Array ();
+             range.push (pval);
+         }
+      }
+    if (val == null)
+      val = Xex.Zero;
+    domain.Defvar (name, desc, val, range);
+    return name;
+  }
+
+  function parse_defun_head (domain, node)
+  {
+    var name = node.attributes['fname'].nodeValue;
+    if (! name)
+      throw new Xex.ErrTerm (Xex.Error.NoFunctionName, node, '');
+    var args = new Array ();
+    var nfixed = 0, noptional = 0, nrest = 0;
+
+    node = node.firstElement ();
+    if (node && node.nodeName == 'args')
+      {
+       var n;
+       for (n = n.firstElement (); n; n = n.nextElement ())
+         {
+           if (n.nodeName == 'fixed')
+             nfixed++;
+           else if (n.nodeName == 'optional')
+             noptional++;
+           else if (n.nodeName == 'rest')
+             nrest++;
+           else
+             throw new Xex.ErrTerm (Xex.Error.WrongType, n, n.nodeName);
+         }
+       if (nrest > 1)
+         throw new Xex.ErrTerm (Xex.Error.WrongType, n, 'Too many <rest>');
+       for (n = node.firstElement (); n; n = n.nextElement ())
+         args.push (domain.DefVar (n.attributes['vname'].nodeValue));
+      }
+    args.min_args = nfixed;
+    args.max_args = nrest == 0 ? nfixed + noptional : -1;
+
+    if (node.nodeName == 'defun')
+      domain.Defun (name, args, null);
+    else
+      domain.Defmacro (name, args, null);
+    return name;
+  }
+
+  function parse_defun_body (domain, node)
+  {
+    var name = node.attributes['fname'].nodeValue;
+    var func = domain.GetFunc (name);
+    var body;
+    for (node = node.firstElement (); node; node = node.nextElement ())
+      if (node.nodeName != 'description' && node.nodeName != 'args')
+       break;
+    body = Xex.Term.Parse (domain, node, null);
+    func.body = body;
+  }
+
+  Xex.Term.Parse = function (domain, node, stop)
+  {
+    if (arguments.length == 2)
+      {
+       var name = node.nodeName;
+       var parser = domain.termtypes[name];
+
+       if (parser)
+         return parser (domain, node);
+       if (name == 'defun' || name == 'defmacro')
+         {
+           name = parse_defun_head (domain, node);
+           parse_defun_body (domain, node);
+           return new Xex.StrTerm (name);
+         }
+       if (name == 'defvar')
+         {
+           name = parse_defvar (domain, node);
+           return new Xex.StrTerm (name);
+         }
+       return new Xex.Funcall.prototype.Parser (domain, node);
+      }
+    for (var n = node; n && n != stop; n = n.nextElement ())
+      if (n.nodeName == 'defun' || n.nodeName == 'defmacro')
+       parse_defun_head (domain, n);
+    var terms = null;
+    for (var n = node; n && n != stop; n = n.nextElement ())
+      {
+       if (n.nodeName == 'defun' || n.nodeName == 'defmacro')
+         parse_defun_body (domain, n);
+       else if (n.nodeName == 'defvar')
+         parse_defvar (domain, n);
+       else
+         {
+           if (! terms)
+             terms = new Array ();
+           terms.push (Xex.Term.Parse (domain, n));
+         }
+      }
+    return terms;
+  }
+}) ();
+
+Xex.Varref = function (vname)
+{
+  this.val = vname;
+};
+
+(function () {
+  var proto = new Xex.Term ('varref');
+
+  proto.Clone = function () { return new Xex.Varref (this.val); }
+  proto.Eval = function (domain)
+  {
+    var vari = domain.GetVarCreate (this.val);
+    Xex.Log (this.ToString () + '=>' + vari.val, domain.depth);
+    return vari.val;
+  }
+
+  proto.Parser = function (domain, node)
+  {
+    return new Xex.Varref (node.attributes['vname'].nodeValue);
+  }
+
+  proto.ToString = function ()
+  {
+    return '<varref vname="' + this.val + '"/>';
+  }
+
+  Xex.Varref.prototype = proto;
+}) ();
+
+var null_args = new Array ();
+  
+Xex.Funcall = function (func, vname, args)
+{
+  this.func = func;
+  this.vname = vname;
+  this.args = args || null_args;
+};
+
+(function () {
+  var proto = new Xex.Term ('funcall');
+
+  proto.Parser = function (domain, node)
+  {
+    var fname = node.nodeName;
+    var attr;
+
+    if (fname == 'funcall')
+      fname = node.attributes['fname'].nodeValue;
+    var func = domain.GetFunc (fname);
+    var vname;
+    attr = node.attributes['vname'];
+    vname = attr != undefined ? attr.nodeValue : null;
+    var args = Xex.Term.Parse (domain, node.firstElement (), null);
+    return new Xex.Funcall (func, vname, args);
+  }
+
+  proto.New = function (domain, fname, vname, args)
+  {
+    var func = domain.GetFunc (fname);
+    var funcall = new Xex.Funcall (func, vname, args);
+    if (func instanceof Xex.Macro)
+      funcall = funcall.Eval (domain);
+    return funcall;
+  }
+
+  proto.Eval = function (domain)
+  {
+    Xex.Log (this, domain.depth);
+    var vari;
+    if (this.vname)
+      vari = domain.GetVarCreate (this.vname);
+    domain.depth++;
+    var result;
+    try {
+      result = this.func.Call (domain, vari, this.args);
+    } finally {
+      Xex.Log (' => ' + result, --domain.depth,
+              this.func instanceof Xex.Subrountine);
+    }
+    return result;
+  }
+
+  proto.Clone = function ()
+  {
+    return new Xex.Funcall (this.func, this.vari, this.args);
+  }
+
+  proto.equals = function (obj)
+  {
+    return (obj.type == 'funcall'
+           && obj.func == this.func
+           && obj.vari.equals (this.vari)
+           && obj.args.length == this.func.length);
+  }
+
+  proto.toString = function ()
+  {
+    var arglist = ''
+    var len = this.args.length;
+    var str = '<' + this.func.name;
+    if (this.vname)
+      str += ' vname="' + this.vname + '"';
+    if (len == 0)
+      return str + '/>';
+    if (this.func instanceof Xex.Subrountine)
+      for (var i = 0; i < len; i++)
+       arglist += this.args[i].toString ();
+    else
+      for (var i = 0; i < len; i++)
+       arglist += '.';
+    return str + '>' + arglist + '</' + this.func.name + '>';
+  }
+
+  Xex.Funcall.prototype = proto;
+}) ();
+
+Xex.ErrTerm = function (ename, message, stack)
+{
+  this.ename = ename;
+  this.message = message;
+  this.stack = stack;
+};
+
+(function () {
+  var proto = new Xex.Term ('error');
+
+  proto.IsError = true;
+
+  proto.Parser = function (domain, node)
+  {
+    return new Xex.ErrTerm (node.attributes['ename'].nodeValue,
+                           node.innerText, false);
+  }
+
+  proto.CallStack = function () { return stack; }
+
+  proto.SetCallStack = function (value) { statck = value; }
+
+  proto.Clone = function ()
+  {
+    return new Xex.ErrTerm (ename, message, false);
+  }
+
+  proto.equals = function (obj)
+  {
+    return (obj.IsError
+           && obj.ename == ename && obj.message == message
+           && (obj.stack ? (stack && stack.length == obj.stack.length)
+               : ! stack));
+  }
+
+  proto.Matches = function (obj)
+  {
+    return (obj.IsError && obj.ename == ename);
+  }
+
+  proto.toString = function ()
+  {
+    return '<error ename="' + this.ename + '">' + this.message + '</error>';
+  }
+
+  Xex.ErrTerm.prototype = proto;
+}) ();
+
+Xex.IntTerm = function (num) { this.val = num; };
+(function () {
+  var proto = new Xex.Term ('integer');
+  proto.IsInt = true;
+  proto.Intval = function () { return this.val; };
+  proto.IsTrue = function () { return this.val != 0; }
+  proto.Clone = function () { return new Xex.IntTerm (this.val); }
+  proto.Parser = function (domain, node)
+  {
+    var str = node.firstChild.nodeValue;
+
+    if (str.charAt (0) == '?' && str.length == 2)
+      return new Xex.IntTerm (str.charCodeAt (1));
+    return new Xex.IntTerm (parseInt (node.firstChild.nodeValue));
+  }
+  Xex.IntTerm.prototype = proto;
+}) ();
+
+Xex.StrTerm = function (str) { this.val = str; };
+(function () {
+  var proto = new Xex.Term ('string');
+  proto.IsStr = true;
+  proto.Strval = function () { return this.val; };
+  proto.IsTrue = function () { return this.val.length > 0; }
+  proto.Clone = function () { return new Xex.StrTerm (this.val); }
+  proto.Parser = function (domain, node)
+  {
+    return new Xex.StrTerm (node.firstChild ? node.firstChild.nodeValue : '');
+  }
+  Xex.StrTerm.prototype = proto;
+}) ();
+
+Xex.SymTerm = function (str) { this.val = str; };
+(function () {
+  var proto = new Xex.Term ('symbol');
+  proto.IsSymbol = true;
+  proto.IsTrue = function () { return this.val != 'nil'; }
+  proto.Clone = function () { return new Xex.SymTerm (this.val); }
+  proto.Parser = function (domain, node)
+  {
+    return new Xex.SymTerm (node.firstChild.nodeValue);
+  }
+  Xex.SymTerm.prototype = proto;
+}) ();
+
+Xex.LstTerm = function (list) { this.val = list; };
+(function () {
+  var proto = new Xex.Term ('list');
+  proto.IsList = true;
+  proto.IsTrue = function () { return this.val.length > 0; }
+  proto.Clone = function () { return new Xex.LstTerm (this.val.slice (0)); }
+
+  proto.equals = function (obj)
+  {
+    if (obj.type != 'list' || obj.val.length != this.val.length)
+      return false;
+    var i, len = this.val.length;
+    for (i = 0; i < len; i++)
+      if (! this.val[i].equals (obj.val[i]))
+       return false;
+    return true;
+  }
+
+  proto.Parser = function (domain, node)
+  {
+    var list = Xex.Term.Parse (domain, node.firstElement (), null);
+    return new Xex.LstTerm (list);
+  }
+
+  proto.toString = function ()
+  {
+    var len = this.val.length;
+
+    if (len == 0)
+      return '<list/>';
+    var str = '<list>';
+    for (var i = 0; i < len; i++)
+      str += this.val[i].toString ();
+    return str + '</list>';
+  }
+  Xex.LstTerm.prototype = proto;
+}) ();
+
+(function () {
+  var basic = new Xex.Domain ('basic', null, null);
+
+  function Fset (domain, vari, args)
+  {
+    if (! vari)
+      throw new Xex.ErrTerm (Xex.Error.NoVariableName,
+                            'No variable name to set');
+    vari.SetValue (args[0]);
+    return args[0];
+  }
+
+  function Fnot (domain, vari, args)
+  {
+    return (args[0].IsTrue () ? Xex.Zero : Xex.One);
+  }
+
+  function maybe_set_intvar (vari, n)
+  {
+    var term = new Xex.IntTerm (n);
+    if (vari)
+      vari.SetValue (term);
+    return term;
+  }
+
+  function Fadd (domain, vari, args)
+  {
+    var n = vari ? vari.val.Intval () : 0;
+    var len = args.length;
+
+    for (var i = 0; i < len; i++)
+      n += args[i].Intval ();
+    return maybe_set_intvar (vari, n);
+  }
+
+  function Fmul (domain, vari, args)
+  {
+    var n = vari ? vari.val.Intval () : 1;
+    for (var i = 0; i < args.length; i++)
+      n *= args[i].Intval ();
+    return maybe_set_intvar (vari, n);
+  }
+
+  function Fsub (domain, vari, args)
+  {
+    var n, i;
+
+    if (! vari)
+      {
+       n = args[0].Intval ();
+       i = 1;
+      }
+    else
+      {
+       n = vari.val.Intval ();
+       i = 0;
+      }
+    while (i < args.length)
+      n -= args[i++].Intval ();
+    return maybe_set_intvar (vari, n);
+  }
+
+  function Fdiv (domain, vari, args)
+  {
+    var n, i;
+
+    if (! vari == null)
+      {
+       n = args[0].Intval ();
+       i = 1;
+      }
+    else
+      {
+       n = vari.val.Intval ();
+       i = 0;
+      }
+    while (i < args.length)
+      n /= args[i++].Intval ();
+    return maybe_set_intvar (vari, n);
+  }
+
+  function Fmod (domain, vari, args)
+  {
+    return maybe_set_intvar (vari, args[0].Intval () % args[1].Intval ());
+  }
+
+  function Flogior (domain, vari, args)
+  {
+    var n = vari == null ? 0 : vari.val;
+    for (var i = 0; i < args.length; i++)
+      n |= args[i].val;
+    return maybe_set_intvar (vari, n);
+  }
+
+  function Flogand (domain, vari, args)
+  {
+    var n, i;
+    if (vari == null)
+      {
+       Xex.Log ("logand arg args[0]" + args[0]);
+       n = args[0].Intval ()
+       i = 1;
+      }
+    else
+      {
+       Xex.Log ("logand arg var " + vari);
+       n = vari.val.Intval ();
+       i = 0;
+      }
+    while (n > 0 && i < args.length)
+      {
+       Xex.Log ("logand arg " + args[i]);
+       n &= args[i++].val;
+      }
+    return maybe_set_intvar (vari, n);
+  }
+
+  function Flsh (domain, vari, args)
+  {
+    return maybe_set_intvar (vari, args[0].Intval () << args[1].Intval ());
+  }
+
+  function Frsh (domain, vari, args)
+  {
+    return maybe_set_intvar (vari, args[0].Intval () >> args[1].Intval ());
+  }
+
+  function Fand (domain, vari, args)
+  {
+    var len = args.length;
+    for (var i = 0; i < len; i++)
+    {
+      var result = args[i].Eval (domain);
+      if (domain.Thrown ())
+       return result;
+      if (! result.IsTrue ())
+       return Xex.Zero;
+    }
+    return Xex.One;
+  }
+
+  function For (domain, vari, args)
+  {
+    var len = args.length;
+    for (var i = 0; i < len; i++)
+    {
+      var result = args[i].Eval (domain);
+      if (domain.Thrown ())
+       return result;
+      if (result.IsTrue ())
+       return Xex.One;
+    }
+    return Xex.Zero;
+  }
+
+  function Feq (domain, vari, args)
+  {
+    for (var i = 1; i < args.length; i++)
+      if (! args[i - 1].equals (args[i]))
+       return Xex.Zero;
+    return Xex.One;
+  }
+
+  function Fnoteq (domain, vari, args)
+  {
+    return (Feq (domain, vari, args) == Xex.One ? Xex.Zero : Xex.One);
+  }
+
+  function Flt (domain, vari, args)
+  {
+    var n = args[0].Intval ();
+
+    for (var i = 1; i < args.length; i++)
+      {
+       var n1 = args[i].Intval ();
+       if (n >= n1)
+         return Xex.Zero;
+       n = n1;
+      }
+    return Xex.One;
+  }
+
+  function Fle (domain, vari, args)
+  {
+    var n = args[0].Intval ();
+    for (var i = 1; i < args.length; i++)
+      {
+       var n1 = args[i].Intval ();
+       if (n > n1)
+         return Xex.Zero;
+       n = n1;
+      }
+    return Xex.One;
+  }
+
+  function Fgt (domain, vari, args)
+  {
+    var n = args[0].Intval ();
+    for (var i = 1; i < args.length; i++)
+      {
+       var n1 = args[i].Intval ();
+       if (n <= n1)
+         return Xex.Zero;
+       n = n1;
+      }
+    return Xex.One;
+  }
+
+  function Fge (domain, vari, args)
+  {
+    var n = args[0].Intval ();
+    for (var i = 1; i < args.length; i++)
+      {
+       var n1 = args[i].Intval ();
+       if (n < n1)
+         return Xex.Zero;
+       n = n1;
+      }
+    return Xex.One;
+  }
+
+  function Fprogn (domain, vari, args)
+  {
+    var result = Xex.One;
+    var len = args.length;
+
+    for (var i = 0; i < len; i++)
+      {
+       result = args[i].Eval (domain);
+       if (domain.Thrown ())
+         return result;
+      }
+    return result;
+  }
+
+  function Fif (domain, vari, args)
+  {
+    var result = args[0].Eval (domain);
+
+    if (domain.Thrown ())
+      return result;
+    if (result.IsTrue ())
+      return args[1].Eval (domain);
+    if (args.length == 2)
+      return Xex.Zero;
+    return args[2].Eval (domain);
+  }
+
+  function Fcond (domain, vari, args)
+  {
+    for (var i = 0; i < args.length; i++)
+      {
+       var list = args[i];
+       var result = list.val[0].Eval (domain);
+       if (result.IsTrue ())
+         {
+           for (var j = 1; j < list.val.length; j++)
+             {
+               domain.depth++;
+               result = list.val[j].Eval (domain);
+               domain.depth--;
+               if (domain.Thrown ())
+                 return result;
+               }
+           return result;
+         }
+      }
+    return Xex.Zero;
+  }
+
+  function eval_terms (domain, terms, idx)
+  {
+    var result = Xex.Zero;
+    domain.caught = false;
+    for (var i = idx; i < terms.length; i++)
+      {
+       result = terms[i].Eval (domain);
+       if (domain.Thrown ())
+         return result;
+      }
+    return result;
+  }
+
+  function Fcatch (domain, vari, args)
+  {
+    var caught = false;
+    var result;
+
+    if (args[0].IsError)
+      {
+       try {
+         result = eval_terms (domain, args, 1);
+       } catch (e) {
+         if (e instanceof Xex.ErrTerm)
+           {
+             if (! args[0].Matches (e))
+               throw e;
+             if (vari)
+               vari.SetValue (e);
+             return Xex.One;
+           }
+       }
+      }
+    else if (args[0].IsSymbol)
+      {
+       try {
+         domain.Catch (args[0].val);
+         result = eval_terms (domain, args, 1);
+         if (domain.caught)
+           {
+             if (vari != null)
+               vari.SetValue (result);
+             return Xex.One;
+           }
+         return Xex.Zero;
+       } finally {
+         domain.Uncatch ();
+       }
+      }
+    throw new Xex.ErrTerm (Xex.Error.WrongArgument,
+                          "Not a symbol nor an error: " + args[0]);
+  }
+
+  function Fthrow (domain, vari, args)
+  {
+    if (args[0].IsSymbl)
+      {
+       domain.ThrowSymbol (args[0]);
+       return (args[args.length - 1]);
+      }
+    if (args[0].IsError)
+      {
+       throw args[0];
+      }
+    throw new Xex.ErrTerm (Xex.Error.WrongArgument,
+                          "Not a symbol nor an error:" + args[0]);
+  }
+
+  Xex.BasicDomain = basic;
+
+  basic.DefSubr (Fset, "set", true, 1, 1);
+  basic.DefAlias ("=", "set");
+  basic.DefSubr (Fnot, "not", false, 1, 1);
+  basic.DefAlias ("!", "not");
+  basic.DefSubr (Fadd, "add", true, 1, -1);
+  basic.DefSubr (Fmul, "mul", true, 1, -1);
+  basic.DefAlias ("*", "mul");
+  basic.DefSubr (Fsub, "sub", true, 1, -1);
+  basic.DefAlias ("-", "sub");
+  basic.DefSubr (Fdiv, "div", true, 1, -1);
+  basic.DefAlias ("/", "div");
+  basic.DefSubr (Fmod, "mod", true, 1, 2);
+  basic.DefAlias ("%", "mod");
+  basic.DefSubr (Flogior, "logior", true, 1, -1);
+  basic.DefAlias ('|', "logior");
+  basic.DefSubr (Flogand, "logand", true, 1, -1);
+  basic.DefAlias ("&", "logand");
+  basic.DefSubr (Flsh, "lsh", true, 1, 2);
+  basic.DefAlias ("<<", "lsh");
+  basic.DefSubr (Frsh, "rsh", true, 1, 2);
+  basic.DefAlias (">>", "rsh");
+  basic.DefSubr (Feq, "eq", false, 2, -1);
+  basic.DefAlias ("==", "eq");
+  basic.DefSubr (Fnoteq, "noteq", false, 2, 2);
+  basic.DefAlias ("!=", "noteq");
+  basic.DefSubr (Flt, "lt", false, 2, -1);
+  basic.DefAlias ("<", "lt");
+  basic.DefSubr (Fle, "le", false, 2, -1);
+  basic.DefAlias ("<=", "le");
+  basic.DefSubr (Fgt, "gt", false, 2, -1);
+  basic.DefAlias (">", "gt");
+  basic.DefSubr (Fge, "ge", false, 2, -1);
+  basic.DefAlias (">=", "ge");
+  basic.DefSubr (Fthrow, "throw", false, 1, 2);
+
+  //basic.DefSubr (Fappend, "append", true, 0, -1);
+  //basic.DefSubr (Fconcat, "concat", true, 0, -1);
+  //basic.DefSubr (Fnth, "nth", false, 2, 2);
+  //basic.DefSubr (Fcopy, "copy", false, 1, 1);
+  //basic.DefSubr (Fins, "ins", true, 2, 2);
+  //basic.DefSubr (Fdel, "del", true, 2, 2);
+  //basic.DefSubr (Feval, "eval", false, 1, 1);
+  //basic.DefSubr (Fbreak, "break", false, 0, 1);
+  //basic.DefSubr (Freturn, "return", false, 0, 1);
+  //basic.DefSubr (Fthrow, "throw", false, 1, 2);
+
+  basic.DefSpecial (Fand, "and", false, 1, -1);
+  basic.DefAlias ("&&", "and");
+  basic.DefSpecial (For, "or", false, 1, -1);
+  basic.DefAlias ("||", "or");
+  basic.DefSpecial (Fprogn, "progn", false, 1, -1);
+  basic.DefAlias ("expr", "progn");
+  basic.DefSpecial (Fif, "if", false, 2, 3);
+  //basic.DefSpecial (Fwhen, "when", false, 1, -1);
+  //basic.DefSpecial (Floop, "loop", false, 1, -1);
+  //basic.DefSpecial (Fwhile, "while", false, 1, -1);
+  basic.DefSpecial (Fcond, "cond", false, 1, -1);
+  //basic.DefSpecial (Fforeach, "foreach", true, 2, -1);
+  //basic.DefSpecial (Fquote, "quote", false, 1, 1);
+  //basic.DefSpecial (Ftype, "type", false, 1, 1);
+  basic.DefSpecial (Fcatch, "catch", true, 2, -1);
+
+  basic.DefType (Xex.Funcall.prototype);
+  basic.DefType (Xex.Varref.prototype);
+  basic.DefType (Xex.ErrTerm.prototype);
+  basic.DefType (Xex.IntTerm.prototype);
+  basic.DefType (Xex.StrTerm.prototype);
+  basic.DefType (Xex.SymTerm.prototype);
+  basic.DefType (Xex.LstTerm.prototype);
+
+}) ();
+
+Xex.Zero = new Xex.IntTerm (0);
+Xex.One = new Xex.IntTerm (1);
+Xex.nil = new Xex.SymTerm ('nil');
+
+Xex.LoadOld = function (server, file)
+{
+  var obj = new XMLHttpRequest ();
+  var url = server ? server + '/' + file : file;
+  obj.open ('GET', url, false);
+  obj.overrideMimeType ('text/xml');
+  obj.send ('');
+  return (obj.responseXML && obj.responseXML.firstChild);
+};
+
+Xex.Load = function (server, file, callback)
+{
+  var body = document.getElementsByTagName ('body')[0];
+  var iframe = document.createElement ('iframe');
+  function receiver (event)
+  {
+    alert ('receiver called:'+event.data);
+    var parser = new DOMParser ();
+    var xml = parser.parseFromString (event.data, 'text/xml');
+    window.removeEventListener ('message', receiver, false);
+    body.removeChild (iframe);
+    callback (xml);
+  };
+  Xex.xml = undefined;
+  window.addEventListener ('message', receiver, false);
+  iframe.src = server + '/loadxml.html#' + file;
+  alert ('iframe created');
+  body.appendChild (iframe);
+};
+