*** empty log message ***
[m17n/m17n-lib-cs.git] / XmlExpr.cs
index 56d2293..18de2ec 100644 (file)
@@ -9,15 +9,14 @@ using System.Xml;
 
 namespace System.Xml
 {
+  /// <summary>
+  /// Static class to provide XmlExpression loader and evaluator.
+  /// </summary>
   public static class Xexpression
   {
-    private static int trace_depth = 0;
-
-    public static int TraceDepth {
-      get { return trace_depth; }
-      set { trace_depth = value; }
-    }
-
+    /// <summary>
+    /// Exception thrown while loading and evaluating XmlExpression.
+    /// </summary>
     public class Error : Exception
     {
       private readonly Symbol name;
@@ -61,7 +60,7 @@ namespace System.Xml
        string str = (InnerException != null ? InnerException.ToString ()
                      : base.ToString ()) + "\n" + "Xexpression:" + name + "\n";
        if (node != null)
-         str += "  at " + node.ParentNode.OuterXml + "\n";
+         str += "  at " + node.OuterXml + "\n";
        if (stack != null)
          {
            str += "Call stack: ";
@@ -86,6 +85,7 @@ namespace System.Xml
       public static Symbol VariableRangeConflict = "variable-range-conflict";
       public static Symbol VariableWrongRange = "variable-wrong-range";
       public static Symbol VariableWrongValue = "variable-wrong-value";
+
       public static Symbol UnknownFunction = "unknown-function";
       public static Symbol MacroExpansionError = "macro-expansion-error";
       public static Symbol NoVariableName = "no-variable-anme";
@@ -739,11 +739,15 @@ namespace System.Xml
 
       internal void ThrowSymbol (Term tag)
       {
+       int i = catch_count;
        foreach (CatchTag elt in catch_stack)
          {
-           catch_count--;
+           i--;
            if (elt.Tag.Matches (tag))
-             return;
+             {
+               catch_count = i;
+               return;
+             }
          }
        throw new Error (Error.UncaughtThrow,
                         "No corresponding catch: {0}", tag);
@@ -856,8 +860,20 @@ namespace System.Xml
 
       internal Variable Defvar (Symbol name)
       {
-       Variable vari = new Variable (this, name, Zero);
-       variables[name] = vari;
+       Variable vari;
+
+       if (variables.TryGetValue (name, out vari))
+         {
+           Variable.Typed typed = vari as Variable.Typed;
+           if (typed != null)
+             throw new Error (Error.VariableTypeConflict,
+                              "Not a non-typed variable: {0}", name);
+         }
+       else
+         {
+           vari = new Variable (this, name, Zero);
+           variables[name] = vari;
+         }
        return vari;
       }
 
@@ -971,7 +987,7 @@ namespace System.Xml
          domain.functions[kv.Key] = kv.Value;
       }
 
-      public Variable GetVar (Symbol name)
+      public Variable GetVarCreate (Symbol name)
       {
        Variable vari;
 
@@ -980,6 +996,15 @@ namespace System.Xml
        return vari;
       }
 
+      public Variable GetVar (Symbol name)
+      {
+       Variable vari;
+
+       if (! variables.TryGetValue (name, out vari))
+         return null;
+       return vari;
+      }
+
       public override string ToString ()
       {
        string str = "<(functions";
@@ -1381,16 +1406,26 @@ namespace System.Xml
     private static Term Fand (Domain domain, Variable vari, Term[] args)
     {
       foreach (Term arg in args)
-       if (! arg.Eval (domain).IsTrue)
-         return Zero;
+       {
+         Term result = arg.Eval (domain);
+         if (domain.Thrown ())
+           result;
+         if (! result.IsTrue)
+           return Zero;
+       }
       return One;
     }
 
     private static Term For (Domain domain, Variable vari, Term[] args)
     {
       foreach (Term arg in args)
-       if (arg.Eval (domain).IsTrue)
-         return One;
+       {
+         Term result = arg.Eval (domain);
+         if (domain.Thrown ())
+           return result;
+         if (result.IsTrue)
+           return One;
+       }
       return Zero;
     }
 
@@ -1404,13 +1439,21 @@ namespace System.Xml
       Term result = One;
 
       foreach (Term arg in args)
-       result = arg.Eval (domain);
+       {
+         result = arg.Eval (domain);
+         if (domain.Thrown ())
+           return result;
+       }
       return result;
     }
 
     private static Term Fif (Domain domain, Variable vari, Term[] args)
     {
-      if (args[0].Eval (domain).IsTrue)
+      Term result = args[0].Eval (domain);
+
+      if (domain.Thrown)
+       return result;
+      if (result.IsTrue)
        return args[1].Eval (domain);
       if (args.Length == 2)
        return Zero;
@@ -1419,11 +1462,19 @@ namespace System.Xml
 
     private static Term Fwhen (Domain domain, Variable vari, Term[] args)
     {
-      if (! args[0].Eval (domain).IsTrue)
+      Term result = args[0].Eval (domain);
+
+      if (domain.Thrown)
+       return result;
+      if (! result.IsTrue)
        return Zero;
-      Term result = One;
+      result = One;
       for (int i = 1; i < args.Length; i++)
-       result = args[i].Eval (domain);
+       {
+         result = args[i].Eval (domain);
+         if (domain.Thrown)
+           return result;
+       }
       return result;
     }
 
@@ -1556,7 +1607,7 @@ namespace System.Xml
        {
          result = terms[i].Eval (domain);
          if (domain.Thrown (out caught))
-           break;
+           return result;
        }
       return result;
     }
@@ -1633,7 +1684,7 @@ namespace System.Xml
       public virtual bool Matches (TermValue other) { return Equals (other); }
       public override abstract bool Equals (object obj);
       public override abstract int GetHashCode ();
-      public abstract string ToString (bool detail);
+      public virtual string ToString (bool detail) { return ToString (); }
     }
 
     private class Varref : TermValue
@@ -1649,7 +1700,7 @@ namespace System.Xml
       public override Term Eval (Domain domain)
       {
        if (vari == null || vari.domain != domain)
-         vari = domain.GetVar (vname);
+         vari = domain.GetVarCreate (vname);
        return vari.Value;
       }
 
@@ -1681,6 +1732,7 @@ namespace System.Xml
     {
       private static Symbol name = "funcall";
       public static Symbol Name { get { return name; } }
+      private static Term[] null_args = new Term[0];
 
       internal Function func;
       internal Variable vari;
@@ -1688,8 +1740,9 @@ namespace System.Xml
 
       public Funcall (Function func, Variable vari, Term[] args)
        {
+         if (args == null)
+           args = null_args;
          int nargs = args.Length;
-
          if (nargs < func.min_args
              || (func.max_args >= 0 && nargs > func.max_args))
            throw new Error (Error.WrongArgument,
@@ -1714,7 +1767,7 @@ namespace System.Xml
        Function func = domain.GetFunc (fname);
        Variable vari;
        attr = node.Attributes[Qvname];
-       vari = attr == null ? null : domain.GetVar (attr.Value);
+       vari = attr == null ? null : domain.GetVarCreate (attr.Value);
        XmlNodeList nlist = node.ChildNodes;
        int nargs = nlist.Count;
        Term[] args = new Term[nargs];
@@ -1917,37 +1970,93 @@ namespace System.Xml
       public override string ToString () { return str; }
     }
 
+    /// <summary> Structure of term object.</summary>
     public struct Term
     {
-      public int intval;
-      public object objval;
+      internal int intval;
+      internal object objval;
 
-      // <integer>...</integer>
+      /// <summary>Create an integer term.</summary>
+      /// <param name='i'>Integer value of the term.</param>
+      /// <returns>An integer term.</returns>
+      /// <remarks>Create an integer term that has the integer value
+      /// specified by <paramref name="i"/>.  This is an constant
+      /// term; i.e. the integer value never changes.</remarks>
       public Term (int i) { intval = i; objval = null; }
-      // <symbol>...</symbol>
+
+      /// <summary>Create a symbol term.</summary>
+      /// <param name='name'>Symbol value of the term.</param>
+      /// <returns>A symbol term.</returns>
+      /// <remarks>Create a symbol term that has the symbol value
+      /// specified by <paramref name="name"/>.  This is an constant
+      /// term; i.e. the symbol value never changes.  It is evaluated
+      /// to itself.</remarks>
       public Term (Symbol name) { intval = 0; objval = name; }
-      // <string>...</string>
+
+      /// <summary>Create a string term.</summary>
+      /// <param name='str'>String value of the term.</param>
+      /// <returns>A string term.</returns>
+      /// <remarks>Create a string term that has the string value
+      /// specified by <paramref name="str"/>.  It is evaluated to
+      /// itself.  The string value can be modified by "ins", "del",
+      /// and "concat" functions.</remarks>
       public Term (string str) { intval = 0; objval = new Str (str); }
       // <list>...</list>
+
+      /// <summary>Create a list term.</summary>
+      /// <param name='list'>List value of the term.</param>
+      /// <returns>A list term.</returns>
+      /// <remarks>Create a list term that has the list value
+      /// specified by <paramref name="list"/>.  It is evaluated to
+      /// itself.  The list value can be modified by "ins", "del", and
+      /// "append" functions.</remarks>
       public Term (List<Term> list) { intval = 0; objval = list; }
       // <error ename="ERROR-NAME">ERROR-MESSASGE</error>
+
+      /// <summary>Create an error term.</summary>
+      /// <param name='name'>Name of the error.</param>
+      /// <param name='message'>Error message.</param>
+      /// <returns>An error term.</returns>
+      /// <remarks>Create an error term whose error name is <paramref
+      /// name="name"/> and error message is <paramref
+      /// name="message"/>.  It is evaluated to itself.</remarks>
       public Term (Symbol name, string message)
        {
          intval = 0;
          objval = new ErrorTerm (name, message);
        }
 
-      public Term (Str str) { intval = 0; objval = str; }
-      public Term (TermValue obj) { intval = 0; objval = obj; }
+      /// <summary>Create a term of a specific value</summary>
+      /// <param name='val'>Value of the term.</param>
+      /// <returns>A term.</returns>
+      /// <remarks>Create a term whose value is <paramref name="val">.
+      /// It is evaluated to a term that is returned by "Eval" method
+      /// of <paramref name="val">.</remarks>
+      public Term (TermValue val) { intval = 0; objval = val; }
+
+      internal Term (Str str) { intval = 0; objval = str; }
 
-      // <varref vname="VNAME"/>
+      /// <summary>Create a varref term.</summary>
+      /// <param name='domain'>Domain to create the term in.</param>
+      /// <param name='vname'>Name of the referred variable.</param>
+      /// <returns>A varref term.</returns>
+      /// <remarks>Create a varref term that is evaluated to the value
+      /// of the referring variable.</remarks>
       public Term (Domain domain, Symbol vname)
        {
          intval = 0;
          objval = new Varref (vname);
        }
 
-      // <funcall fname="FNAME">...</funcall>
+      /// <summary>Create a funcall term.</summary>
+      /// <param name='domain'>Domain to create the term in.</param>
+      /// <param name='fname'>Name of the calling function.</param>
+      /// <param name='args'>Array of terms that are given to the
+      /// function as arguments.</param>
+      /// <returns>A funcall term.</returns>
+      /// <remarks>Create a funcall term that is evaluated to a term
+      /// returned by the function <paramref name="fname"> when called
+      /// with <paramref name="args">.<remarks>
       public Term (Domain domain, Symbol fname, Term[] args)
        : this (domain, fname, Qnull, args) { }
 
@@ -1957,7 +2066,7 @@ namespace System.Xml
          intval = 0;
 
          Function func = domain.GetFunc (fname);
-         Variable vari = vname == Qnull ? null : domain.GetVar (vname);
+         Variable vari = vname == Qnull ? null : domain.GetVarCreate(vname);
          Funcall funcall = new Funcall (func, vari, args);
          if (func is Function.Macro)
            {
@@ -2323,7 +2432,7 @@ namespace System.Xml
       return args;
     }
 
-    public static Symbol parse_defun_head (Domain domain, XmlNode node)
+    private static Symbol parse_defun_head (Domain domain, XmlNode node)
     {
       Symbol name = node.Attributes[Qfname].Value;
       int min_args, max_args;
@@ -2337,16 +2446,17 @@ namespace System.Xml
       return name;
     }
 
-    public static void parse_defun_body (Domain domain, XmlNode node)
+    private static void parse_defun_body (Domain domain, XmlNode node)
     {
       bool is_defun = node.Name == Qdefun;
       Symbol name = node.Attributes[Qfname].Value;
       Function func = domain.GetFunc (name);
 
       for (node = node.FirstChild; node != null; node = node.NextSibling)
-       if (node.Name != Qdescription
-           && node.Name != Qargs)
+       if (node.Name != Qdescription && node.Name != Qargs)
          break;
+      Console.WriteLine ("found body " + node.Name);
+      
       Term[] body = Parse (domain, node, null);
       if (is_defun)
        ((Function.Lambda) func).SetBody (body);
@@ -2404,17 +2514,17 @@ namespace System.Xml
       }
     }
 
-    private static bool default_stop (XmlNode n) { return n == null; }
-
-    public delegate bool ParseStop (XmlNode node);
+    /// <summary>Parse a node and the following siblings as "term"s in a
+    /// specific domain.</summary>
+    /// <param name='domain'></param>
+    /// <param name='start'></param>
+    /// <param name='stop'></param>
+    /// <returns>Array of terms.</returns>
 
-    public static Term[] Parse (Domain domain, XmlNode node, ParseStop stop)
+    public static Term[] Parse (Domain domain, XmlNode start, XmlNode stop)
     {
-      if (stop == null)
-       stop = default_stop;
-
       XmlNode n;
-      for (n = node; ! stop (n); n = n.NextSibling)
+      for (n = start; n != stop && n != null; n = n.NextSibling)
        if (n.NodeType == XmlNodeType.Element
            && (n.Name == Qdefun || n.Name == Qdefmacro))
          {
@@ -2422,30 +2532,30 @@ namespace System.Xml
              parse_defun_head (domain, n);
            } catch (Error e) {
              if (e.Node == null)
-               e.Node = node;
+               e.Node = n;
              throw e;
            } catch (Exception e) {
-             throw new Error (Error.UnknownError, node, e, "Parsing error");
+             throw new Error (Error.UnknownError, n, e, "Parsing error");
            }
          }
 
       List<Term> terms = new List<Term> ();
-      for (; node != n; node = node.NextSibling)
-       if (node.NodeType == XmlNodeType.Element)
+      for (; start != n; start = start.NextSibling)
+       if (start.NodeType == XmlNodeType.Element)
          {
            try {
-             if (node.Name == Qdefun || node.Name == Qdefmacro)
-               parse_defun_body (domain, node);
-             else if (node.Name == Qdefvar)
-               parse_defvar (domain, node);
+             if (start.Name == Qdefun || start.Name == Qdefmacro)
+               parse_defun_body (domain, start);
+             else if (start.Name == Qdefvar)
+               parse_defvar (domain, start);
              else
-               terms.Add (Parse (domain, node));
+               terms.Add (Parse (domain, start));
            } catch (Error e) {
              if (e.Node == null)
-               e.Node = node;
+               e.Node = start;
              throw e;
            } catch (Exception e) {
-             throw new Error (Error.UnknownError, node, e, "Parsing error");
+             throw new Error (Error.UnknownError, start, e, "Parsing error");
            }
          }
       return terms.ToArray ();
@@ -2456,8 +2566,9 @@ namespace System.Xml
       XmlDocument doc = new XmlDocument (Symbol.NameTable);
       XmlNode node;
 
-      using (XmlTextReader reader = new XmlTextReader (url, doc.NameTable))
+      using (XmlTextReader reader = new XmlTextReader (url, Symbol.NameTable))
        {
+         reader.WhitespaceHandling = WhitespaceHandling.None;
          do {
            reader.Read ();
          } while (reader.NodeType != XmlNodeType.None
@@ -2470,6 +2581,21 @@ namespace System.Xml
       return Parse (domain, node.FirstChild, null);
     }
 
+    /// <summary>Evaluate a term in a specific domain.</summary>
+    /// <param name='domain'>Domain to evaluate the term in.</param>
+    /// <param name='term'>The term to evaluate.</param>
+    /// <returns>The result of evaluating the <paramref
+    /// name="term"/>.</returns>
+    public static Term Eval (Domain domain, Term term)
+    {
+      return Eval (domain, new Term[] { term });
+    }
+
+    /// <summary>Evaluate terms in a specific domain.</summary>
+    /// <param name='domain'>Domain to evaluate terms in.</param>
+    /// <param name='terms'>Array of terms to evaluate.</param>
+    /// <returns>The result of evaluating the last term of <paramref
+    /// name="terms"/>.</returns>
     public static Term Eval (Domain domain, Term[] terms)
     {
       Term result = Zero;
@@ -2488,5 +2614,16 @@ namespace System.Xml
        throw new Error (Error.UnknownError, null, e, "Runtime error");
       }
     }
+
+    // TRACING
+
+    private static int trace_depth = 0;
+
+    /// <value> TraceDepth specifies the minimum depth of call stack
+    /// to suppress tracing.  0 means no tracing.  </value>
+    public static int TraceDepth {
+      get { return trace_depth; }
+      set { trace_depth = value; }
+    }
   }
 }