*** empty log message *** load_sync
authorhanda <handa>
Mon, 8 Feb 2010 04:05:51 +0000 (04:05 +0000)
committerhanda <handa>
Mon, 8 Feb 2010 04:05:51 +0000 (04:05 +0000)
index.html
mim.js
xex.js
xex.xml

index 794f865..b3f66f6 100644 (file)
@@ -9,7 +9,7 @@
       -->
   </style>
   <title>M17N Input Method by JavaScript</title>
-  <script src="xex.js"></script>
+  <!-- <script src="xex.js"></script> -->
   <script src="mim.js"></script>
 </head>
 
 <br>
 <h3>debug Info</h3>
 <table border="1">
-<tr><th>down</th><th>press</th><th>status</th><th>keyseq</th><th>range</th></tr>
-<tr><td id="keydown">0</td><td id="keypress">0</td><td id="status"><td id="keyseq"></td><td id="range"></td></tr>
+<tr><th>key</th><th>keycode</th><th>status</th><th>keyseq</th><th>range</th></tr>
+<tr><th>down</th><td id="keydown">0</td><td id="status0"><td id="keyseq0"></td><td id="range0"></td></tr>
+<tr><th>press</th><td id="keypress">0</td><td id="status1"><td id="keyseq1"></td><td id="range1"></td></tr>
 </table>
 <br>
-<form><input type="text" name="TEXT"></form>
+<form><input type="text" id="text" style="width:100mm"></form>
 </body>
diff --git a/mim.js b/mim.js
index 25d59e4..bb7f551 100644 (file)
--- a/mim.js
+++ b/mim.js
@@ -314,85 +314,65 @@ MIM.debug_print = function (event, ic)
   if (! MIM.debug_nodes)
     {
       MIM.debug_nodes = new Array ();
-      MIM.debug_nodes['status'] = document.getElementById ('status');
-      MIM.debug_nodes['range'] = document.getElementById ('range');
       MIM.debug_nodes['keydown'] = document.getElementById ('keydown');
       MIM.debug_nodes['keypress'] = document.getElementById ('keypress');
-      MIM.debug_nodes['keyseq'] = document.getElementById ('keyseq');
+      MIM.debug_nodes['status0'] = document.getElementById ('status0');
+      MIM.debug_nodes['status1'] = document.getElementById ('status1');
+      MIM.debug_nodes['keyseq0'] = document.getElementById ('keyseq0');
+      MIM.debug_nodes['keyseq1'] = document.getElementById ('keyseq1');
+      MIM.debug_nodes['range0'] = document.getElementById ('range0');
+      MIM.debug_nodes['range1'] = document.getElementById ('range1');
     }
   var target = event.target;
   var code = event.keyCode;
   var ch = event.type == 'keydown' ? 0 : event.charCode;
   var key = MIM.decode_key (event);
   var keyseq = "";
+  var index;
 
   MIM.debug_nodes[event.type].innerHTML = "" + code + "/" + ch + " : " + key;
-  MIM.debug_nodes['status'].innerHTML = ic.im.status;
+  index = (event.type == 'keydown' ? '0' : '1');
+  MIM.debug_nodes['status' + index].innerHTML = ic.im.status;
   for (var i = 0; i < ic.keyseq.length; i++)
     keyseq += ic.keyseq[i];
-  MIM.debug_nodes['keyseq'].innerHTML = keyseq + ":" + ic.keyseq.length;
-  MIM.debug_nodes['range'].innerHTML = "" + ic.range[0] + "," + ic.range[1];;
+  MIM.debug_nodes['keyseq' + index].innerHTML
+    = keyseq + ":" + ic.keyseq.length;
+  MIM.debug_nodes['range' + index].innerHTML
+    = "" + ic.range[0] + ":" + ic.range[1];
 };
 
 MIM.get_range = function (target, range)
 {
-  if (target.selectionStart != null)
+  if (target.selectionStart != null) // for Mozilla
     {
-      // for Mozilla
       range[0] = target.selectionStart;
       range[1] = target.selectionEnd;
-      return true;
     }
-  if (document.selection != null)
+  else                         // for IE
     {
-      target.focus();
-
-      var range = document.selection.createRange ();
-      var bookmark = range.getBookmark ();
-      var value = target.value;
-      var saved_value = value;
-      var marker = "_#_MARKER_#_";
-      while (value.indexOf (marker) != -1)
-        marker += "#_";
-      var parent = range.parentElement ();
-      if (parent == null || parent.type != "textarea")
-       {
-         range[0] = range[1] = 0;
-       }
-      else
-       {
-         range.text = marker + range.text + marker;
-         contents = this.element.value;
-         range[0] = contents.indexOf (marker);
-         contents = contents.replace(marker, "");
-         range[1] = contents.indexOf(marker);
-         target.value = originalContents;
-         range.moveToBookmark (bookmark);
-         range.select ();
-       }
-      return true;
+      var r = document.selection.createRange ();
+      var rr = r.duplicate ();
+
+      rr.moveToElementText (target);
+      rr.setEndPoint ('EndToEnd', range);
+      range[0] = rr.text.length - r.text.length;
+      range[1] = rr.text.length;
     }
-  return false;
-};
+}
 
 MIM.set_caret = function (target, pos)
 {
-  if(/*@cc_on ! @*/ false)     // IE
-    {
-      var range = target.createTextRange ();
-      range.move ('character', pos);
-      ranges.elect ();
-      return true;
-    }
   if (target.selectionStart != null) // Mozilla
     {
       target.focus ();
       target.setSelectionRange (pos, pos);
-      return true;
     }
-  // Unknown
-  target.focus ();
-  return false;
+  else                         // IE
+    {
+      var range = target.createTextRange ();
+      range.move ('character', pos);
+      range.select ();
+    }
 };
 
 MIM.ic = function (im, target)
@@ -401,7 +381,7 @@ MIM.ic = function (im, target)
   this.target = target;
   this.key = false;
   this.keyseq = new Array ();
-  this.range = new Array (-1, -1);
+  this.range = new Array (0, 0);
   return this;
 };
 
@@ -410,22 +390,17 @@ MIM.ic.prototype.reset = function ()
   this.key = false;
   while (this.keyseq.length > 0)
     this.keyseq.pop ();
-  this.range[0] = this.range[1] = -1;
 };
 
-MIM.ic.prototype.check_caret = function ()
+MIM.ic.prototype.check_range = function ()
 {
-  var from = this.range[0];
-  var to = this.range[1];
+  var from = this.range[0], to = this.range[1];
 
   MIM.get_range (this.target, this.range);
-  if (from >= 0)
-    {
-      if (this.range[0] != this.range[1] || to != this.range[0])
-       this.reset ();
-      else
-       this.range[0] = from;
-    }
+  if (this.range[0] != this.range[1] || to != this.range[1])
+    this.reset ();
+  else
+    this.range[0] = from;
 };
 
 MIM.insert = function (ic, insert)
@@ -453,7 +428,10 @@ MIM.handle_keyseq = function (event, ic)
     {
       MIM.insert (ic, map['_target_text']);
       if (! ('_has_child' in map))
-       ic.reset ();
+       {
+         ic.reset ();
+         ic.range[0] = ic.range[1];
+       }
       event.preventDefault ();
       //document.getElementById ('text').value
       //= keyseq_string (ic.keyseq) + " handled";
@@ -487,20 +465,21 @@ MIM.reset_ic = function (event)
 
 MIM.keydown = function (event)
 {
-  if (! (event.target.type == "text" || event.target.type == "textarea"))
+  var target = event.target;
+  if (! (target.type == "text" || target.type == "textarea"))
     return;
 
-  var ic = event.target.mim_ic;
+  var ic = target.mim_ic;
   if (! ic || ic.im != MIM.current_im)
     {
-      ic = new MIM.ic (MIM.current_im, event.target);
-      event.target.mim_ic = ic;
+      ic = new MIM.ic (MIM.current_im, target);
+      target.mim_ic = ic;
+      MIM.add_event_listener (target, 'blur', MIM.reset_ic);
     }
-  MIM.add_event_listener (event.target, 'blur', MIM.reset_ic);
-  MIM.debug_print (event, ic);
   if (ic.im.status < 0)
     return;
-  ic.check_caret ();
+  ic.check_range ();
+  MIM.debug_print (event, ic);
   ic.key = MIM.decode_key (event);
 };
 
@@ -512,20 +491,23 @@ MIM.keypress = function (event)
   var ic = event.target.mim_ic;
   var i;
 
-  MIM.debug_print (event, ic);
-  if (ic.im.status < 0)
-    return;
-  if (! ic.key)
-    ic.key = MIM.decode_key (event);
-  if (! ic.key)
-    {
-      ic.reset ();
+  try {
+    if (ic.im.status < 0)
       return;
-    }
-  ic.keyseq.push (ic.key);
-  if (ic.im.status == 1) // Still loading.
-    return;
-  MIM.handle_keyseq (event, ic);
+    if (! ic.key)
+      ic.key = MIM.decode_key (event);
+    if (! ic.key)
+      {
+       ic.reset ();
+       return;
+      }
+    ic.keyseq.push (ic.key);
+    if (ic.im.status == 1) // Still loading.
+      return;
+    MIM.handle_keyseq (event, ic);
+  } finally {
+    MIM.debug_print (event, ic);
+  }
   return;
 };
 
@@ -596,7 +578,7 @@ MIM.init = function ()
     MIM.server = 'http://localhost/mim';
   MIM.current_im = MIM.register ('latin', 'post', 'latn-post.js');
   MIM.register ('th', 'kesmanee', 'th-kesmanee.js');
-  MIM.load_sync (MIM.current_im);
+  MIM.load (MIM.current_im);
 };
 
 MIM.init_debug = function ()
diff --git a/xex.js b/xex.js
index 91135b2..9c49da3 100644 (file)
--- a/xex.js
+++ b/xex.js
@@ -1,40 +1,44 @@
 // -* coding: utf-8; -*
 
-var XEX = {};
+var Xex = {};
 
-XEX.Variable = function (domain, name, val)
+Xex.Variable = function (domain, name, val)
 {
   this.domain = domain;
   this.name = name;
   this.val = val;
 }
 
-XEX.Variable.prototype = {
-  value: function () { return this.val; },
-  set_value: function (val) {
-    this.val = val;
-    return val;
-  },
-  clone: function () {
-    {
-      return new XEX.Variable (this.domain, this.name, this.value);
-    }
-  }
+Xex.Variable.prototype.clone = function () {
+  return new Xex.Variable (this.domain, this.name, this.value);
 }
     
-XEX.Function = function (name, with_var, min_args, max_args) {
+Xex.Variable.prototype.Equals = function (obj) {
+  return ((obj instanceof Xex.Variable)
+         && obj.name == this.name);
+}
+
+Xex.Variable.prototype.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) {
-  this.prototype = new XEX.Function (name, with_var, min_args, max_args);
+Xex.Subrountine = function (builtin, 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;
   this.builtin = builtin;
-};
+}
 
-XEX.Subrountine.prototype.Call = function (domain, vari, args)
+Xex.Subrountine.prototype.Call = function (domain, vari, args)
 {
   newargs = new Array ();
   for (var i = 0; i < args.length; i++)
@@ -46,28 +50,34 @@ XEX.Subrountine.prototype.Call = function (domain, vari, args)
   return this.builtin (domain, vari, newargs)
 }
 
-XEX.SpecialForm = function (builtin, name, with_var, min_args, max_args)
+Xex.SpecialForm = function (builtin, name, with_var, min_args, max_args)
 {
-  this.prototype = new XEX.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;
   this.builtin = builtin;
 }
 
-XEX.SpecialForm.prototype.Call = function (domain, vari, args)
+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.Lambda = function (name, min_args, max_args, args, body)
 {
-  this.prototype = new XEX.Function (name, false, min_args, max_args);
+  this.name = name;
+  this.with_var = with_var;
+  this.min_args = min_args;
+  this.max_args = max_args;
   this.args = args;
   this.body = body;
 }
 
-XEX.Lambda.prototype.Call = function (domain, vari, args)
+Xex.Lambda.prototype.Call = function (domain, vari, args)
 {
   var current = domain.bindings;
-  var result = XEX.Term.zero;
+  var result = Xex.Zero;
   var limit = max_args >= 0 ? args.length : args.length - 1;
   var i;
   
@@ -92,7 +102,7 @@ XEX.Lambda.prototype.Call = function (domain, vari, args)
        domain.Bind (this.args[limit], list);
       }
     try {
-      domain.Catch (XEX.CatchTag.Return);
+      domain.Catch (Xex.CatchTag.Return);
       for (var term in this.body)
         {
          result = term.Eval (domain);
@@ -108,24 +118,27 @@ XEX.Lambda.prototype.Call = function (domain, vari, args)
   return result;
 }
 
-XEX.Macro = function (name, min_args, max_args, args, body)
+Xex.Macro = function (name, min_args, max_args, args, body)
 {
-  this.prototype = new XEX.Function (name, false, min_args, max_args);
+  this.name = name;
+  this.with_var = with_var;
+  this.min_args = min_args;
+  this.max_args = max_args;
   this.args = args;
   this.body = body;
 }
 
-XEX.Macro.prototype.Call = function (domain, vari, args)
+Xex.Macro.prototype.Call = function (domain, vari, args)
 {
   var current = domain.bindings;
-  var result = XEX.Term.Zero;
+  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);
+      domain.Catch (Xex.CatchTag.Return);
       for (var term in body)
         {
          result = term.Eval (domain);
@@ -141,40 +154,573 @@ XEX.Macro.prototype.Call = function (domain, vari, args)
   return result;
 }
 
-XEX.EvalConstant = function (domain)
+Xex.Bindings = function (vari)
 {
-  if (this.nodeName == 'integer')
-    alert ("integer:" + this.attributes[0].nodeValue);
-  else if (this.nodeName == 'string')
-    alert ("string:" + this.firstChild.nodeValue);
-  return this;
+  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;
+  this.termtypes = parent ? parent.termtypes.slice () : new Array ();
+  this.functions = parent ? parent.functions.slice () : new Array ();
+  this.variables = parent ? parent.variable.slice () : new Array ();
+
+  var call_stack = new Array ();
+  var bindings;
+  var catch_stack = new Array ();
+  var catch_count = 0;
+
+  if (this.CallStackCount)
+    return;
+
+  var proto = Xex.Domain.prototype;
+
+  proto.CallStackCount = function () { return call_stack.length; }
+  proto.CallStackPush = function (term) { call_stack.push (term); }
+  proto.CallStackPop = function () { call_stack.pop (); }
+  proto.Bind = function (vari, val)
+  {
+    bindings = Xex.Bind (bindings, vari, val);
+  }
+  proto.UnboundTo = function (boundary)
+  {
+    if (bindings)
+      bindings = bindings.UnboundTo (boundary);
+  }
+  proto.Catch = function (tag) { catch_stack.push (tag); catch_count++; }
+  proto.Uncatch = function ()
+  {
+    catch_stack.pop ();
+    if (catch_count > catch_stack.length)
+      catch_count--;
+  }
+  proto.Thrown = function ()
+  {
+    var i = catch_stack.length - catch_count;
+    return (i > 0 ? i - 1 : false);
+  }
+  proto.ThrowReturn = function ()
+  {
+    for (var i = catch_stack.length - 1; i >= 0; i--)
+      {
+       catch_count--;
+       if (catch_stack[i] == Xex.CatchTag.Return)
+         break;
+      }
+  }
+  proto.ThrowBreak = function ()
+  {
+    if (catch_stack[catch_stack.length - 1] != Xex.CatchTag.Break)
+      throw new Xex.Error (Xex.Error.NoLoopToBreak,
+                          "No surrounding loop to break");
+    catch_count--;
+  }
+  proto.ThrowSymbol = function (tag)
+  {
+    var i = catch_count;
+    for (var j = catch_stack.length - 1; j >= 0; j--)
+      {
+       i--;
+       if (Xex.CatchTag.Matches (catch_stack[i], tag))
+          {
+           catch_count = i;
+           return;
+         }
+      }
+    throw new Xex.Error (Xex.Error.UncaughtThrow,
+                        "No corresponding catch: " + tag);
+  }
+  proto.DefType = function (obj)
+  {
+    var type = obj.type;
+    if (this.termtypes[type])
+      throw new Xex.Error (Xex.Error.TermTypeInvalid,
+                          "Already defined: " + type);
+    if (this.functions[type])
+      throw new Xex.Error (Xex.Error.TermTypeInvalid,
+                          "Already defined as a funciton or a macro: "
+                          + type);
+    this.termtypes[type] = obj.Parser;
+  }
+
+  proto.DefSubr = function (builtin, name, with_var, min_args, max_args)
+  {
+    this.functions[name] = new Xex.Subrountine (builtin, name, with_var,
+                                               min_args, max_args);
+  }
+  proto.DefSpecial = function (builtin, name, with_var, min_args, max_args)
+  {
+    this.functions[name] = new Xex.SpecialForm (builtin, name, with_var,
+                                               min_args, max_args);
+  }
+  proto.Defun = function (name, min_args, max_args, args, body)
+  {
+    this.functions[name] = new Xex.Lambda (name, min_args, max_args,
+                                          args, body);
+  }
+  proto.DefunByFunc = function (func) { this.functions[func.Name] = func; }
+  proto.Defmacro = function (name, min_args, max_args, args, body)
+  {
+    this.functions[name] = new Xex.Macro (name, min_args, max_args, args, body);
+  }
+  proto.DefAlias = function (alias, fname)
+  {
+    var func = this.functions[fname];
+
+    if (this.termtypes[alias])
+      throw new Xex.Error (Xex.Error.FunctionConflict,
+                          "Already defined as a term type: " + alias);
+    if (this.functions[alias])
+      throw new Xex.Error (Xex.Error.FunctionConflict,
+                          "Already defined as a function: " + alias);
+    if (! func)
+      throw new Xex.Error (Xex.Error.UnknownFunction, fname);
+    this.functions[alias] = func;
+  }
+  proto.Defvar = function (name)
+  {
+    var vari = this.variables[name];
+    if (vari)
+      {
+       if (vari.Typed)
+         throw new Xex.Error (Xex.Error.VariableTypeConflict,
+                              "Not a non-typed variable: " + name);
+      }
+    else
+      {
+       vari = new Xex.Variable (this, name, Xex.Zero);
+       this.variables[name] = vari;
+      }
+    return vari;
+  }
+  proto.GetFunc = function (name) { return this.functions[name]; }
+  proto.CopyFunc = function (domain, name)
+  {
+    var func = this.functions[name];
+    domain.DefunByFunc (func);
+    return true;
+  }
+  proto.CopyFuncAll = function (domain)
+  {
+    for (var i = this.functions.length - 1; i >= 0; i--)
+      domain.DefunByFunc (this.functions[i]);
+  }
+  proto.GetVarCreate = function (name)
+  {
+    var vari = this.variables[name];
+    if (! vari)
+      this.variables[name] = vari
+        = new Xex.Variable (this, name, Xex.Zero);
+    return vari;
+  }
+  proto.GetVar = function (name) { return this.variables[name]; }
+  proto.SaveValues = function ()
+  {
+    values = new Array ()
+    for (var i = this.variables.length - 1; i >= 0; i--)
+      {
+       var vari = this.variables[i];
+       values[vari.Name] = vari.val.Clone ();
+      }
+    return values;
+  }
+  proto.RestoreValues = function (values)
+  {
+    var name;
+    for (name in values)
+      {
+       var vari = this.variables[name];
+       vari.val = values[name];
+      }
+  }
 };
 
-XEX.EvalFuncall = function (domain)
+Xex.Term = function () { }
+Xex.Term.prototype = {
+  IsTrue: true,
+  Eval: function (domain) { return this.Clone (); },
+  Equals: function (obj)
+  {
+    return (this.type == obj.type
+           && this.val
+           && 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 + '/>';
+  }
+};
+
+Xex.ParseTerm = function (domain, node)
 {
-  try {
-    domain.CallStackPush (this);
-  } catch (e) {
-    alert (e);
+  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 (doamin, node);
+      return new Xex.StrTerm (nanme);
+    }
+
+  return new Xex.Funcall.prototype.Parser (domain, node);
+}
+
+Xex.Varref = function (vname)
+{
+  var vari;
+
+  this.val = vname;
+  if (this.type)
+    return;
+  var proto = Xex.Varref.prototype;
+
+  proto.type = 'varref';
+
+  proto.Clone = function () { return new Xex.Varref (this.val); }
+  proto.Eval = function (domain)
+  {
+    if (! vari || vari.domain != domain)
+      vari = domain.GetVarCreate (this.val);
+    return vari.val;
+  }
+
+  proto.Parser = function (domain, node)
+  {
+    return new Xex.Varref (node.attributes['vname'].nodeValue);
   }
 }
 
-var obj = new XMLHttpRequest ();
-obj.open ('GET', 'xex.xml', false);
-obj.send ('');
-var body = obj.responseXML.firstChild;
-for (var i = body.childNodes.length - 1; i >= 0 ; i--)
+Xex.Varref.prototype = new Xex.Term ();
+
+var null_args = new Array ();
+  
+Xex.Funcall = function (func, vari, args)
+{
+  this.func = func;
+  if (! args)
+    args = null_args;
+  this.vari = vari;
+  this.args = args;
+  if (this.type)
+    return;
+
+  var proto = Xex.Funcall.prototype;
+
+  proto.type = 'funcall';
+
+  proto.Parser = function (domain, node)
   {
-    var node =  body.childNodes[i];
-    if (node.nodeType == 1)
-      node.Eval = XEX.EvalConstant;
-    else
-      body.removeChild (node);
+    var fname = node.nodeName;
+    var attr;
+
+    if (fname == 'funcall')
+      fname = node.attributes['fname']
+    var func = domain.GetFunc (fname);
+    var vari;
+    attr = node.attributes['vname'];
+    vari = attr != undefined ? domain.GetVarCreate (attr.nodeValue) : false;
+    var args = new Array ();
+    for (node = node.firstChild; node; node = node.nextSibling)
+      if (node.nodeType == 1)
+       args.push (Xex.ParseTerm (domain, node));
+    return new Xex.Funcall (func, vari, args);
+  }
+
+  proto.Eval = function (domain)
+  {
+    return this.func.Call (domain, this.vari, this.args);
+  }
+
+  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;
+    if (len == 0)
+      return '<' + this.func.name + '/>';
+    for (var i = 0; i < len; i++)
+      arglist += this.args[i].toString ();
+    return '<' + this.func.name + '>' + arglist + '</' + this.func.name + '>';
+  }
+}
+
+Xex.Funcall.prototype = new Xex.Term ();
+
+Xex.ErrTerm = function (ename, message, stack)
+{
+  if (this.type)
+    return;
+
+  var proto = Xex.ErrTerm.prototype;
+
+  proto.type = 'error';
+
+  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.type == 'error'
+           && obj.ename == ename && obj.message == message
+           && (obj.stack ? (stack && stack.length == obj.stack.length)
+               : ! stack));
+  }
+
+  proto.Matches = function (obj)
+  {
+    return (obj.type == 'error' && obj.ename == ename);
+  }
+}
+
+Xex.ErrTerm.prototype = new Xex.Term ();
+
+Xex.IntTerm = function (num)
+{
+  this.val = num;
+  this.IsTrue = num != 0;
+  if (this.type)
+    return;
+  var proto = Xex.IntTerm.prototype;
+  proto.type = 'integer';
+  proto.Clone = function () { return new Xex.IntTerm (this.val); }
+  proto.Parser = function (domain, node)
+  {
+    return new Xex.IntTerm (parseInt (node.firstChild.nodeValue));
+  }
+}
+
+Xex.IntTerm.prototype = new Xex.Term ();
+
+Xex.StrTerm = function (str)
+{
+  this.val = str;
+  this.IsTrue = str && str.length > 0;
+  if (this.type)
+    return;
+  var proto = Xex.StrTerm.prototype;
+  proto.type = 'string';
+  proto.Clone = function () { return new Xex.StrTerm (this.val); }
+  proto.Parser = function (domain, node)
+  {
+    return new Xex.StrTerm (node.firstChild.nodeValue);
+  }
+}
+
+Xex.StrTerm.prototype = new Xex.Term ();
+
+Xex.LstTerm = function (list)
+{
+  this.val = list;
+  this.IsTrue = list && list.length > 0;
+  if (this.type)
+    return;
+
+  var proto = Xex.LstTerm.prototype;
+
+  proto.type = 'list';
+  
+  proto.Clone = function () { return new Xex.LstTerm (this.val.slice ()); }
+
+  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 = new Array ();
+
+    for (node = node.firstChild; node; node = node.nextSibling)
+      if (node.nodeType == 1)
+       list.push (Xex.ParseTerm (domain, node));
+    return new Xex.LstTerm (list);
+  }
+
+  proto.toString = function ()
+  {
+    var len = this.val.length;
+
+    if (len == 0)
+      return '<list/>';
+    var str = '<list>';
+    for (i = 0; i < len; i++)
+      str += this.val[i].toString ();
+    return str + '</list>';
   }
-for (var i = 0; i < body.childNodes.length; i++)
+}
+
+Xex.LstTerm.prototype = new Xex.Term ();
+
+(function () {
+  var basic = new Xex.Domain ('basic');
+
+  function Fset (domain, vari, args)
   {
-    var node =  body.childNodes[i];
-    node.Eval ();
+    return vari.SetValue (args[0]);
   }
 
+  function maybe_set_intvar (vari, n)
+  {
+    var term = new Xex.IntTerm (n);
+    if (vari != null)
+      vari.SetValue (term);
+    return term;
+  }
 
+  function Fadd (domain, vari, args)
+  {
+    var n = vari ? vari.val.val : 0;
+    var len = args.length;
+
+    for (var i = 0; i < len; i++)
+      n += args[i].val;
+    return maybe_set_intvar (vari, n);
+  }
+
+  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 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 Zero;
+    return args[2].Eval (domain);
+  }
+
+  basic.DefSubr (Fset, "set", true, 1, 1);
+  basic.DefSubr (Fadd, "add", true, 1, -1);
+  basic.DefSpecial (Fand, "and", false, 1, -1);
+  basic.DefSpecial (For, "or", false, 1, -1);
+  basic.DefAlias ("=", "set");
+  basic.DefSpecial (Fprogn, "progn", false, 1, -1);
+  basic.DefSpecial (Fif, "if", false, 2, 3);
+  basic.DefType (new Xex.Funcall ());
+  basic.DefType (new Xex.Varref ());
+  basic.DefType (new Xex.ErrTerm ());
+  basic.DefType (new Xex.IntTerm ());
+  basic.DefType (new Xex.StrTerm ());
+  basic.DefType (new Xex.LstTerm ());
+
+  Xex.Domain.basic = basic;
+}) ();
+
+Xex.Zero = new Xex.IntTerm (0);
+Xex.One = new Xex.IntTerm (1);
+
+var obj = new XMLHttpRequest ();
+obj.open ('GET', 'xex.xml', false);
+obj.send ('');
+var body = obj.responseXML.firstChild;
+var domain = Xex.Domain.basic;
+document.ATTR = body;
+alert (Xex.ParseTerm (domain, body).Eval (domain));
diff --git a/xex.xml b/xex.xml
index bcadbbc..e5be038 100644 (file)
--- a/xex.xml
+++ b/xex.xml
@@ -1,5 +1,7 @@
 <?xml version='1.0'?>
-<temp>
-  <integer value="10"/>
-  <string>This is a test</string>
-</temp>
+<if>
+  <integer>0</integer>
+  <set vname="test"><integer>10</integer></set>
+  <set vname="test"><integer>5</integer></set>
+</if>
+