*** empty log message ***
authorhanda <handa>
Thu, 10 Sep 2009 13:09:25 +0000 (13:09 +0000)
committerhanda <handa>
Thu, 10 Sep 2009 13:09:25 +0000 (13:09 +0000)
MDatabase.cs
MExpression.cs
MInputMethod.cs
MPlist.cs
MSymbol.cs
MText.cs
Makefile
XmlExpr.cs
input.cs
mtext.cs
symbol.cs

index e6cbd8b..5779783 100644 (file)
@@ -2,6 +2,8 @@ using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.IO;
+using System.Xml;
+
 using M17N;
 using M17N.Core;
 
@@ -48,11 +50,13 @@ namespace M17N.Core
       int len = pattern.Length;
       int i;
     
+      M17n.DebugPrint ("Listing {0} in {1} ...", pattern, dirinfo);
       for (i = 0; i < len && pattern[i] != sep; i++);
       try {
        if (i == len)
          {
            FileInfo[] listing = dirinfo.GetFiles (pattern);
+           i = listing.Length;
            foreach (FileInfo elt in listing)
              files.Add (elt);
          }
@@ -61,11 +65,14 @@ namespace M17N.Core
            string tail = pattern.Substring (i + 1);
            pattern = pattern.Substring (0, i);
            DirectoryInfo[] listing = dirinfo.GetDirectories (pattern);
+
+           i = listing.Length;
            foreach (DirectoryInfo elt in listing)
              list (ref files, elt, tail);
          }
       } catch {
       }
+      M17n.DebugPrint (" found {0} files\n", i);
     }
   }
 
@@ -270,9 +277,10 @@ namespace M17N.Core
 
       public MDatabaseInfo (MPlist plist)
       {
-       Format = MSymbol.plist;
+       Format = MSymbol.nil;
        if (plist.IsMText)
          {
+           Format = MSymbol.plist;
            Filename = plist.Text.ToString ();
            plist = plist.Next;
          }
@@ -281,22 +289,22 @@ namespace M17N.Core
            MPlist p = plist.Plist;
 
            if (p.IsMText)
-             Filename = p.Text.ToString ();
-           p = p.Next;
-           if (! p.IsEmpty)
              {
-               if (p.IsSymbol)
-                 Format = p.Symbol;
+               Filename = p.Text.ToString ();
+               p = p.Next;
+             }
+           if (p.IsSymbol)
+             {
+               Format = p.Symbol;
                p = p.Next;
-               if (! p.IsEmpty)
-                 {
-                   if (p.IsSymbol)
-                     Schema = p.Symbol;
-                   p = p.Next;
-                   if (p.IsMText)
-                     SchemaFile = p.Text.ToString ();
-                 }                 
              }
+           if (p.IsSymbol)
+             {
+               Schema = p.Symbol;
+               p = p.Next;
+             }
+           if (p.IsMText)
+             SchemaFile = p.Text.ToString ();
            plist = plist.Next;
          }
 
@@ -361,10 +369,13 @@ namespace M17N.Core
 
     private static DateTime LastUpdateTime = new DateTime (0);
 
-    private static readonly MSymbol Mversion = MSymbol.Of ("version");
-    private static readonly MSymbol Mwildcard = MSymbol.Of ("*");
-    private static readonly MSymbol Mchar_table = MSymbol.Of ("char-table");
-    private static readonly MSymbol Mcharset = MSymbol.Of ("charset");
+    private static readonly MSymbol Mversion = "version";
+    private static readonly MSymbol Mwildcard = "*";
+    private static readonly MSymbol Mchar_table = "char-table";
+    private static readonly MSymbol Mcharset = "charset";
+    private static readonly MSymbol Mxml = "xml";
+
+    private static TimeSpan CheckInterval = new TimeSpan (50000000);
 
     /// Type of database
     private enum MDBType
@@ -401,7 +412,7 @@ namespace M17N.Core
        // current system, or the validation was failed.
        DISABLED,
        // The database is deleted by the modificaiton of mdb.dir, or
-       // is overwritten by a new explicit definition..
+       // is overwritten by a new explicit definition.
        INVALID,
       };
 
@@ -417,10 +428,22 @@ namespace M17N.Core
     private MDBType DBType;
     private MDBStatus DBStatus;
     internal MDatabaseInfo Info;
-    // File in which the database contents is stored.
+    // File in which the database contents is stored.  This is null
+    // when DBStatus is NOT_READY.
     internal FileInfo FileInfo;
-    // When the database file is checked (or validated).
-    internal DateTime CheckedTime;
+    // When the database file is loaded last.
+    internal DateTime LastLoaded = DateTime.Now;
+
+    public enum LoadStatus
+    {
+      None,
+      InvalidLoadMethod,
+      NotAvailable,
+      NotReadable,
+      InvalidContents,
+    };
+
+    public LoadStatus LastLoadStatus = LoadStatus.None;
 
     static MDatabase ()
     {
@@ -441,19 +464,23 @@ namespace M17N.Core
       }
       DBDirs[2] = new MDatabaseDir (null);
       DBDirs[3] = new MDatabaseDir (Path.Combine (share_dir, "m17n"));
-      update_all ();
+      update_all (true);
     }
 
     public static string ApplicationDir
     {
       get { return (DBDirs[1].Dirname); }
-      set { DBDirs[2] = new MDatabaseDir (value); update_all (); }
+      set { DBDirs[2] = new MDatabaseDir (value); update_all (true); }
     }
 
-    private static bool update_all ()
+    // Update all listing and directories.  Return true iff some are
+    // really updated.
+    private static bool update_all (bool force)
     {
-      bool updated = false;
+      if (! force && DateTime.Now - LastUpdateTime < CheckInterval)
+       return false;
 
+      bool updated = false;
       for (int i = 1; i < 4; i++)
        if (DBDirs[i].Dirname != null)
          {
@@ -469,13 +496,14 @@ namespace M17N.Core
                updated = true;
              }
          }
-      LastUpdateTime = DateTime.Now;
+      if (updated)
+       LastUpdateTime = DateTime.Now;
       return updated;
     }
 
     public static void Dump ()
     {
-      update_all ();
+      update_all (false);
       Console.WriteLine ("[DBDirs]");
       for (int i = 1; i < 4; i++)
        if (DBDirs[i].Dirname != null)
@@ -542,10 +570,6 @@ namespace M17N.Core
            mdb = mdbs[i];
            if (mdb.ListIndex == list_idx && mdb.DirIndex >= dir_idx)
              {
-               if (mdb.DBStatus == MDBStatus.INVALID)
-                 M17n.DebugPrint ("registering: {0}\n", mdb);
-               else
-                 M17n.DebugPrint ("updating: {0}\n", mdb);
                mdb.DirIndex = dir_idx;
                if (dict == wdict)
                  {
@@ -563,6 +587,10 @@ namespace M17N.Core
                    mdb.DBStatus = MDBStatus.READY;
                  }
                mdb.Info = info;
+               if (mdb.DBStatus == MDBStatus.INVALID)
+                 M17n.DebugPrint ("registering: {0}\n", mdb);
+               else
+                 M17n.DebugPrint ("updating: {0}\n", mdb);
                return;
              }
          }
@@ -709,38 +737,91 @@ namespace M17N.Core
            if (mdb.DBStatus == MDBStatus.READY)
              {
                M17n.DebugPrint ("re-expanding: {0}\n", mdb);
-               register_files (DBDirs[dir_idx].Dirname, mdb.ListIndex,
-                               dir_idx, mdb.Info);
+               register_files (DBDirs[dir_idx].Dirname, dir_idx, mdb);
+             }
+    }
+
+    private static bool parse_plist_header (FileInfo fileinfo, MDatabase mdb,
+                                           out Tag tag, out MDatabaseInfo info)
+    {
+      MPlist plist = null;
+
+      tag = new Tag (MSymbol.nil);
+      info = null;
+      using (FileStream stream = fileinfo.OpenRead ())
+       {
+         try { plist = new MPlist (stream, 1); } catch { }
+       }
+      if (plist == null || ! plist.IsPlist)
+       return false;
+      plist = plist.Plist;
+      tag = new Tag (ref plist);
+      if (tag.HasWildcard || ! tag.Match (mdb.tag))
+       return false;
+      info = new MDatabaseInfo (plist);
+      return true;
+    }
+
+    private static bool parse_xml_header (FileInfo fileinfo, MDatabase mdb,
+                                         out Tag tag, out MDatabaseInfo info)
+    {
+      tag = new Tag (MSymbol.nil);
+      info = null;
+      using (FileStream stream = fileinfo.OpenRead ())
+       {
+         try {
+           MPlist plist = new MPlist ();
+           XmlTextReader reader = new XmlTextReader (stream);
+           
+           reader.WhitespaceHandling = WhitespaceHandling.None;
+           do {
+             reader.Read ();
+           } while (reader.NodeType != XmlNodeType.Element);
+           plist.Add (MSymbol.symbol, (MSymbol) reader.Name);
+           reader.Read ();
+           if (reader.NodeType == XmlNodeType.Element && reader.Name == "tags")
+             {
+               reader.Read ();
+               while (reader.NodeType == XmlNodeType.Element)
+                 {
+                   reader.Read ();
+                   plist.Add (MSymbol.symbol, (MSymbol) reader.Value);
+                   reader.Read ();
+                   reader.Read ();
+                 }
+               tag = new Tag (ref plist);
+               if (tag.HasWildcard || ! tag.Match (mdb.tag))
+                 return false;
+               info = new MDatabaseInfo (plist);
+               return true;
              }
+         } catch (Exception e) {
+           Console.WriteLine ("error {0}", e);
+         }
+       }
+      return false;
     }
 
-    private static void register_files (string dir, int list_idx, int dir_idx,
-                                       MDatabaseInfo base_info)
+    private static void register_files (string dir, int dir_idx, MDatabase mdb)
     {
+      int list_idx = mdb.ListIndex;
       List<FileInfo> files = new List<FileInfo> ();
-      MGlob.FileList (ref files, dir, base_info.Filename);
+      MGlob.FileList (ref files, dir, mdb.Info.Filename);
       foreach (FileInfo fileinfo in files)
        {
-         MPlist plist = null;
-         using (FileStream stream = fileinfo.OpenRead ())
-           {
-             plist = new MPlist (stream, 1);
-           }
-         if (plist != null && plist.IsPlist)
-           {
-             plist = plist.Plist;
-             Tag tag = new Tag (ref plist);
+         Tag tag;
+         MDatabaseInfo info;
 
-             if (! tag.HasWildcard && tag.Match (tag))
-               {
-                 MDatabaseInfo info = new MDatabaseInfo (plist);
-                 info.Merge (base_info);
-                 if (Path.IsPathRooted (base_info.Filename))
-                   info.Filename = fileinfo.FullName;
-                 else
-                   info.Filename = fileinfo.Name;
-                 register (list_idx, dir_idx, tag, info);
-               }
+         if (mdb.Info.Format == MSymbol.plist
+             ? parse_plist_header (fileinfo, mdb, out tag, out info)
+             : parse_xml_header (fileinfo, mdb, out tag, out info))
+           {
+             info.Merge (mdb.Info);
+             if (Path.IsPathRooted (mdb.Info.Filename))
+               info.Filename = fileinfo.FullName;
+             else
+               info.Filename = fileinfo.Name;
+             register (list_idx, dir_idx, tag, info);
            }
        }
     }
@@ -750,11 +831,11 @@ namespace M17N.Core
       M17n.DebugPrint ("expanding: {0}\n", this);
 
       if (DirIndex == 0)
-       register_files (null, ListIndex, DirIndex, Info);
+       register_files (null, DirIndex, this);
       else
        for (int i = 1; i < 4; i++)
          if (DBDirs[i].DirInfo != null)
-           register_files (DBDirs[i].DirInfo.FullName, ListIndex, i, Info);
+           register_files (DBDirs[i].DirInfo.FullName, i, this);
       DBStatus = MDBStatus.READY;
     }
 
@@ -774,20 +855,29 @@ namespace M17N.Core
        }
     }
 
+    // Update the status.  Return true iff the database file is
+    // readable but changed.
+
     private bool update_status ()
     {
       if (DBType == MDBType.UNKNOWN)
        return true;
+      update_all (false);
       if (DBStatus == MDBStatus.INVALID)
        return false;
-      if (DBStatus == MDBStatus.READY)
+      if (DBStatus != MDBStatus.NOT_READY)
        {
-         if (DirIndex > 0
-             || File.Exists (FileInfo.FullName))
-           return true;
-         DBStatus = MDBStatus.INVALID;
-         return false;
-       }
+         try {
+           FileInfo.Refresh ();
+         } catch {
+           DBStatus = MDBStatus.INVALID;
+           return false;
+         }
+         if (LastLoaded >= FileInfo.LastWriteTime)
+           return false;
+         DBStatus = MDBStatus.READY;
+         return true;
+       }         
       for (int i = 1; i < 4; i++)
        if (DBDirs[i] != null && DBDirs[i].Dirname != null)
          {
@@ -796,8 +886,9 @@ namespace M17N.Core
              {
                FileInfo = new FileInfo (filename);
                DirIndex = i;
+               DBStatus = MDBStatus.READY;
                return true;
-           }
+             }
          }
       return false;
     }
@@ -822,14 +913,18 @@ namespace M17N.Core
                break;
            }
        }
-      if (! update_all () && mdb != null)
+      if (! update_all (false) && mdb != null)
        return mdb;
       maybe_expand_wildcard (tag);
       if (! ndict.TryGetValue (tag, out mdbs))
        return null;
       for (int i = 0; i < mdbs.Count; i++)
-       if ((mdb = mdbs[i]).update_status ())
-         return mdb;
+       {
+         mdb = mdbs[i];
+         mdb.update_status ();
+         if (mdb.DBStatus == MDBStatus.READY)
+           return mdb;
+       }
       return null;
     }
 
@@ -837,7 +932,7 @@ namespace M17N.Core
     {
       List<MDatabase> list = new List<MDatabase> ();
 
-      update_all ();
+      update_all (false);
       maybe_expand_wildcard (tag);
 
       if (tag.HasWildcard)
@@ -845,56 +940,240 @@ namespace M17N.Core
          foreach (KeyValuePair<Tag, List<MDatabase>> kv in ndict)
            if (kv.Key.Match (tag))
              foreach (MDatabase mdb in kv.Value)
-               if (mdb.update_status ())
-                 {
-                   list.Add (mdb);
-                   break;
-                 }
+               {
+                 mdb.update_status ();
+                 if (mdb.DBStatus == MDBStatus.READY)
+                   {
+                     list.Add (mdb);
+                     break;
+                   }
+               }
        }
       else
        {
          List<MDatabase> mdbs;
          if (ndict.TryGetValue (tag, out mdbs))
            foreach (MDatabase mdb in mdbs)
-             if (mdb.update_status ())
-               {
-                 list.Add (mdb);
-                 break;
-               }
+             {
+               mdb.update_status ();
+               if (mdb.DBStatus == MDBStatus.READY)
+                 {
+                   list.Add (mdb);
+                   break;
+                 }
+             }
        }
       return list;
     }
 
+    private FileStream get_stream ()
+    {
+      if (loader != null
+         || (Info.Format != MSymbol.plist && Info.Format != Mxml))
+       {
+         LastLoadStatus = LoadStatus.InvalidLoadMethod;
+         return null;
+       }
+      if (DBStatus != MDBStatus.READY)
+       {
+         LastLoadStatus = LoadStatus.NotAvailable;
+         return null;
+       }
+
+      FileStream stream = null;
+      try {
+       stream = FileInfo.OpenRead ();
+      } catch {
+       LastLoadStatus = LoadStatus.NotReadable;          
+      }
+      return stream;
+    }
+
     public object Load ()
     {
       if (loader != null)
        return loader (tag, ExtraInfo);
-      if (Info.Format == Mchar_table)
-       throw new Exception ("Use Load (MCharTable) to load this database");
-      if (Info.Format == Mcharset)
-       throw new Exception ("Use Load (MCharset) to load this database");
-      if (! update_status ())
-       throw new Exception ("Database invalid");
+      if (Info.Format == Mxml)
+       {
+         XmlDocument doc = new XmlDocument ();
+         try {
+           XmlReader reader = XmlReader.Create (FileInfo.FullName);
+           doc.Load (reader);
+           LastLoaded = DateTime.Now;      
+         } catch (Exception e) {
+           Console.WriteLine (e);
+           LastLoadStatus = LoadStatus.InvalidContents;
+         }
+         return doc;
+       }
 
+      FileStream stream = get_stream ();
+      if (stream == null)
+       return null;
       MPlist plist = null;
-      using (FileStream stream = File.OpenRead (FileInfo.FullName))
+      try {
        plist = new MPlist (stream);
+       LastLoaded = DateTime.Now;
+      } catch {
+       LastLoadStatus = LoadStatus.InvalidContents;
+      } finally {
+       stream.Dispose ();
+      }
       return plist;
     }
 
     public object Load (MSymbol key, MSymbol stop)
     {
-      if (loader != null || Info.Format != MSymbol.plist)
-       throw new ArgumentException
-         ("Key can't be specified for loading this database");
-      if (! update_status ())
-       throw new Exception ("Database invalid");
+      FileStream stream = get_stream ();
+
+      if (stream == null)
+       return null;
+      if (Info.Format == Mxml)
+       {
+         XmlDocument doc = new XmlDocument ();
+         XmlTextReader reader = new XmlTextReader (stream);
+
+         reader.WhitespaceHandling = WhitespaceHandling.None;
+         try {
+           reader.Read ();
+           while (reader.NodeType != XmlNodeType.Element)
+             reader.Read ();
+           doc.LoadXml ("<" + reader.Name + "></" + reader.Name + ">");
+           reader.Read ();
+           XmlNode node = doc.DocumentElement;
+           while (reader.NodeType == XmlNodeType.Element
+                  ? reader.Name != stop.Name
+                  : reader.NodeType != XmlNodeType.EndElement)
+             if (reader.NodeType == XmlNodeType.Element
+                 && reader.Name == key.Name)
+               node = doc.DocumentElement.InsertAfter (doc.ReadNode (reader),
+                                                       node);
+         } finally {
+           reader.Close ();
+           stream.Dispose ();
+         }
+         return doc;
+       }
+
       MPlist plist = null;
-      using (FileStream stream = File.OpenRead (FileInfo.FullName))
+      try {
        plist = new MPlist (stream, key, stop);
+       LastLoaded = DateTime.Now;
+      } catch {
+       LastLoadStatus = LoadStatus.InvalidContents;
+      } finally {
+       stream.Dispose ();
+      }
       return plist;
     }
 
+    public object Load (MSymbol stop)
+    {
+      FileStream stream = get_stream ();
+
+      if (stream == null)
+       return null;
+      if (Info.Format == Mxml)
+       {
+         XmlDocument doc = new XmlDocument ();
+         XmlTextReader reader = new XmlTextReader (stream);
+
+         reader.WhitespaceHandling = WhitespaceHandling.None;
+         try {
+           reader.Read ();
+           while (reader.NodeType != XmlNodeType.Element)
+             reader.Read ();
+           doc.LoadXml ("<" + reader.Name + "></" + reader.Name + ">");
+           reader.Read ();
+           XmlNode node = null;
+           while (reader.NodeType == XmlNodeType.Element
+                  ? reader.Name != stop.Name
+                  : reader.NodeType != XmlNodeType.EndElement)
+             if (reader.NodeType == XmlNodeType.Element)
+                 node = doc.DocumentElement.InsertAfter (doc.ReadNode (reader),
+                                                         node);
+         } catch (Exception e) {
+           Console.WriteLine (e);
+         } finally {
+           reader.Close ();
+           stream.Dispose ();
+         }
+         return doc;
+       }
+
+      MPlist plist = null;
+      try {
+       plist = new MPlist (stream, stop);
+       LastLoaded = DateTime.Now;
+      } catch (Exception e) {
+       Console.WriteLine (e);
+       LastLoadStatus = LoadStatus.InvalidContents;
+      } finally {
+       stream.Dispose ();
+      }
+      return plist;
+    }
+
+    public XmlNode Load (string id, params string[] nodes)
+    {
+      FileStream stream = get_stream ();
+      if (stream == null)
+       return null;
+      if (Info.Format != Mxml)
+       throw new Exception ("Not an XML format");
+
+      XmlDocument doc = new XmlDocument ();
+      XmlTextReader reader = new XmlTextReader (stream);
+      int len = nodes.Length;
+
+      reader.WhitespaceHandling = WhitespaceHandling.None;
+      do {
+       reader.Read ();
+      } while (reader.NodeType != XmlNodeType.Element);
+
+      if (reader.Name != nodes[0])
+       return null;
+
+      string ns = reader.GetAttribute ("xmlns");
+      XmlNode top = doc.CreateNode (XmlNodeType.Element, nodes[0], ns);
+      XmlNode node = top;
+
+      try {
+       int i;
+
+       for (i = 1; i + 1 < len; i++)
+         {
+           if (! reader.ReadToDescendant (nodes[i]))
+             return null;
+           node = node.InsertAfter (doc.CreateNode (XmlNodeType.Element,
+                                                    nodes[i], ns), null);
+         }
+       if (! reader.ReadToDescendant (nodes[i]))
+         return null;
+       XmlNode ref_node = null;
+       while (reader.NodeType != XmlNodeType.EndElement)
+         {
+           if (reader.NodeType == XmlNodeType.Element)
+             {
+               if (reader.Name == nodes[i]
+                   && (id == null || id == reader.GetAttribute ("id")))
+                 ref_node = node.InsertAfter (doc.ReadNode (reader), ref_node);
+               else
+                 reader.Skip ();
+             }
+           else
+             reader.Read ();
+         }
+             
+      } catch (Exception e) {
+       Console.WriteLine (e);
+      } finally {
+       reader.Close ();
+       stream.Dispose ();
+      }
+      return top;
+    }
+
     /// <summary>Return a list of currently available database
     /// directory names</summary>.
     public static string[] DirectoryList ()
@@ -907,6 +1186,19 @@ namespace M17N.Core
       return dirs.ToArray ();
     }
 
+    public MSymbol Format { get { return Info.Format; } }
+
+    public static bool Changed (DateTime time)
+    {
+      update_all (false);
+      return (time < LastUpdateTime);
+    }
+
+    public bool NeedReload ()
+    {
+      return update_status ();
+    }  
+
     // For IComparable<MDatabase>
     public int CompareTo (MDatabase other)
     {
index 046fdfb..d8fbadf 100644 (file)
@@ -2,6 +2,8 @@ using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.IO;
+using System.Xml;
+
 using M17N;
 using M17N.Core;
 
@@ -9,39 +11,33 @@ namespace M17N.Core
 {
   public class MExpression
   {
-    private static MSymbol Mvarref = MSymbol.Of ("symbol-value");
-    private static MSymbol Mdefun = MSymbol.Of ("defun");
-    private static MSymbol Mcond = MSymbol.Of ("cond");
-    private static MSymbol Mprogn = MSymbol.Of ("progn");
-    private static MSymbol Mwhen = MSymbol.Of ("when");
+    private static MSymbol Mvarref = "symbol-value";
+    private static MSymbol Mdefun = "defun";
+    private static MSymbol Mcond = "cond";
+    private static MSymbol Mprogn = "progn";
+    private static MSymbol Mwhen = "when";
 
     public class Domain
     {
-      internal MPlist functions;
-      internal MPlist bindings;
+      private Domain parent;
+      internal MPlist functions = new MPlist ();
+      internal MPlist bindings = new MPlist ();
+      private Stack<MPlist> boundaries = new Stack<MPlist> ();
       internal Translator translator;
       public object context;
 
       internal Domain ()
       {
-       functions = new MPlist ();
-       bindings = new MPlist ();
+       boundaries.Push (bindings);
       }
 
-      public Domain (object context)
-      {
-       functions = basic.functions;
-       bindings = basic.bindings;
-       translator = basic.translator;
-       this.context = context;
-      }
+      public Domain (object context) : this (basic, context) { }
 
       public Domain (Domain parent, object context)
       {
-       functions = parent.functions;
-       bindings = parent.bindings;
-       translator = parent.translator;
+       this.parent = parent;
        this.context = context;
+       boundaries.Push (bindings);
       }
 
       public void AddTranslator (Translator translator)
@@ -57,27 +53,91 @@ namespace M17N.Core
        bindings = bindings.Cons (sym, value);
       }
 
-      public void Defun (string name, Evaluator evaluator,
-                        int min_arg, int max_arg)
+      public MPlist SetBoundary ()
+      {
+       boundaries.Push (bindings);
+       return bindings;
+      }
+
+      public void UnboundTo (MPlist boundary)
+      {
+       while (boundary != boundaries.Pop ());
+       while (bindings != boundary)
+         bindings = bindings.next;
+      }
+
+      public void Defun (string name, Builtin builtin, int min_arg, int max_arg)
       {
-       Defun (name, evaluator, min_arg, max_arg, false);
+       Defun (name, builtin, min_arg, max_arg, false);
       }
 
-      public void Defun (string name, Evaluator evaluator,
-                        int min_arg, int max_arg, bool special)
+      public void Defun (string name, Builtin builtin, int min_arg, int max_arg,
+                        bool specialp)
       {
-       MSymbol sym = MSymbol.Of (name);
-       Function func = new Function (sym, evaluator,
-                                     min_arg, max_arg, special);
+       MSymbol sym = name;
+       Function func = (Function) functions.Get (sym);
 
-       functions = functions.Cons (sym, func);
+       if (func != null)
+         {
+           if (func.min_arg < min_arg || func.max_arg > max_arg)
+             throw new Exception ("Incompatible argument numbers to override: "
+                                  + name);
+           func.builtin = builtin;
+           func.lambda = null;
+           func.min_arg = min_arg;
+           func.max_arg = max_arg;
+           func.specialp = specialp;
+         }
+       else
+         {
+           func = new Function (sym, builtin, min_arg, max_arg, specialp);
+           functions = functions.Cons (sym, func);
+         }
       }
 
       public void Defun (MSymbol sym, MPlist args, MPlist body)
       {
-       Function func = new Function (sym, args, body, this);
+       Function func = (Function) functions.Get (sym);
+
+       if (func != null)
+         {
+           int nargs = args == null ? 0 : args.Count;
+
+           if (func.min_arg < nargs || func.max_arg > nargs)
+             throw new Exception ("Incompatible argument numbers to override: "
+                                  + sym);
+           func.lambda.SetArgs (args);
+           func.lambda.SetBody (body, this);
+           func.builtin = null;
+         }
+       else
+         {
+           func = new Function (sym, args, body, this);
+           functions = functions.Cons (sym, func);
+         }         
+      }
+
+      public void Defun (XmlNode node)
+      {
+       MSymbol sym = node.Attributes["id"].Value;
+       Function func = (Function) functions.Get (sym);
 
-       functions = functions.Cons (sym, func);
+       if (func != null)
+         {
+           XmlNode args = node.FirstChild;
+           int nargs = args.Name == "args" ? args.ChildNodes.Count : 0;
+
+           if (func.min_arg < nargs || func.max_arg > nargs)
+             throw new Exception ("Incompatible argument numbers to override: "
+                                  + sym);
+           func.lambda.Set (node, this);
+           func.builtin = null;
+         }
+       else
+         {
+           func = new Function (sym, node, this);
+           functions = functions.Cons (sym, func);
+         }         
       }
 
       internal Function GetFunc (MSymbol name)
@@ -85,11 +145,15 @@ namespace M17N.Core
        Function func = (Function) functions.Get (name);
 
        if (func == null)
-         throw new Exception ("Unknown function: " + name);
+         {
+           if (parent != null)
+             return parent.GetFunc (name);
+           throw new Exception ("Unknown function: " + name);
+         }
        return func;
       }
 
-      public bool CopyFunc (MSymbol name, Domain domain)
+      public bool CopyFunc (Domain domain, MSymbol name)
       {
        Function func = (Function) functions.Get (name);
        if (func == null)
@@ -109,18 +173,25 @@ namespace M17N.Core
        MPlist slot = bindings.Find (name);
 
        if (slot == null)
-         throw new Exception ("Unbound variable: " + name);
+         {
+           if (parent != null)
+             return parent.GetValue (name);
+           throw new Exception ("Unbound variable: " + name);
+         }
        return slot.val;
       }
 
       public object SetValue (MSymbol name, object val)
       {
-       MPlist slot = bindings.Find (name);
+       MPlist boundary = boundaries.Peek ();
 
-       if (slot == null)
-         bindings = bindings.Cons (name, val);
-       else
-         slot.val = val;
+       for (MPlist plist = bindings; plist != boundary; plist = plist.next)
+         if (plist.key == name)
+           {
+             plist.val = val;
+             return val;
+           }
+       bindings = bindings.Cons (name, val);
        return val;
       }
 
@@ -131,6 +202,8 @@ namespace M17N.Core
 
       public void Translate (MPlist plist)
       {
+       if (parent != null)
+         parent.Translate (plist);
        if (translator != null)
          for (MPlist p = plist; ! p.IsEmpty; p = p.next)
            translator (p, this);
@@ -149,59 +222,105 @@ namespace M17N.Core
       }
     }
 
-    public delegate object Evaluator (MExpression[] args, Domain domain);
+    public delegate object Builtin (MExpression[] args, Domain domain);
     public delegate void Translator (MPlist plist, Domain domain);
 
     internal class Function
     {
-      private class Lambda
+      internal class Lambda
       {
-       internal readonly MSymbol[] args;
-       internal readonly MExpression[] body;
+       internal MSymbol[] args;
+       internal MExpression[] body;
 
        public Lambda (MPlist args, MPlist body, Domain domain)
        {
-         int len;
+         SetArgs (args);
+         SetBody (body, domain);
+       }
 
-         len = args.Count;
-         this.args = new MSymbol[len];
-         for (int i = 0; ! args.IsEmpty; i++, args = args.next)
+       public Lambda (XmlNode node, Domain domain)
+       {
+         Set (node, domain);
+       }
+
+       public void SetArgs (MPlist args)
+       {
+         int len = args == null ? 0 : args.Count;
+
+         if (this.args == null)
+           this.args = new MSymbol[len];
+         for (int i = 0; i < len; i++, args = args.next)
            this.args[i] = args.Symbol;
-         len = body.Count;
-         this.body = new MExpression[len];
-         for (int i = 0; ! body.IsEmpty; i++, body = body.next)
+       }
+
+       public void SetBody (MPlist body, Domain domain)
+       {
+         int len = body == null ? 0 : body.Count;
+         if (this.body == null)
+           this.body = new MExpression[len];
+         for (int i = 0; i < len; i++, body = body.next)
            {
              domain.Translate (body);
              this.body[i] = new MExpression (body.key, body.val, domain);
            }
        }
+
+       public void Set (XmlNode node, Domain domain)
+       {
+         XmlNodeList body = node.ChildNodes;
+         int idx = 0;
+
+         if (body[0].Name == "args")
+           {
+             XmlNodeList args = body[0].ChildNodes;
+             if (this.args == null)
+               this.args = new MSymbol[args.Count];
+             for (int i = 0; i < args.Count; i++)
+               this.args[i] = args[i].InnerText;
+             idx++;
+           }
+         else if (this.args == null)
+           this.args = new MSymbol[0];
+         if (this.body == null)
+           this.body = new MExpression[body.Count - idx];
+         for (int i = 0; idx < body.Count; i++, idx++)
+           this.body[i] = new MExpression (body[idx], domain);
+       }
       }
 
       public readonly MSymbol Name;
-      private readonly Evaluator eval;
-      public readonly int min_arg, max_arg;
-      private readonly Lambda lambda;
-      private readonly bool specialp = false;
+      public Builtin builtin;
+      public int min_arg, max_arg;
+      internal Lambda lambda;
+      public bool specialp = false;
 
       internal static Function ignore, varref, block;
 
-      public Function (MSymbol name, Evaluator eval,
+      public Function (MSymbol name, Builtin builtin,
                       int min_arg, int max_arg, bool specialp)
       {
        Name = name;
-       this.eval = eval;
+       this.builtin = builtin;
        this.min_arg = min_arg;
        this.max_arg = max_arg;
        this.specialp = specialp;
       }
 
-      internal Function (MSymbol name, MPlist args, MPlist body, Domain domain)
+      internal Function (MSymbol name, MPlist args, MPlist body,
+                        Domain domain)
       {
        Name = name;
        lambda = new Lambda (args, body, domain);
        this.min_arg = this.max_arg = lambda.args.Length;
       }
 
+      internal Function (MSymbol name, XmlNode node, Domain domain)
+      {
+       Name = name;
+       lambda = new Lambda (node, domain);
+       this.min_arg = this.max_arg = lambda.args.Length;
+      }
+
       private Function ()
       {
        Name = MSymbol.nil;
@@ -221,12 +340,12 @@ namespace M17N.Core
 
       public object Call (MExpression[] args, Domain domain)
       {
-       if (eval != null)
+       if (builtin != null)
          {
            if (! specialp)
              foreach (MExpression e in args)
                e.Eval (domain);
-           return eval (args, domain);
+           return builtin (args, domain);
          }
        if (lambda == null)
          return null;
@@ -253,6 +372,7 @@ namespace M17N.Core
 
       basic.Defun ("set", set_value, 2, 2, true);
       basic.Defun ("=", set_value, 2, 2, true);
+      basic.Defun ("!", not, 1, 1, false);
       basic.Defun ("+", plus, 2, -1, false);
       basic.Defun ("*", multi, 2, -1, false);
       basic.Defun ("-", minus, 1, -1, false);
@@ -279,7 +399,7 @@ namespace M17N.Core
       basic.Defun (">=", moreeq, 2, -1, false);
       basic.Defun ("progn", progn, 0, -1, true);
       basic.Defun ("if", ifclause, 2, -1, true);
-      basic.Defun ("when", whenclause, 2, -1, true);
+      basic.Defun ("when", whenclause, 1, -1, true);
       basic.Defun ("while", whileclause, 1, -1, true);
 
       basic.AddTranslator (new Translator (translate_cond));
@@ -291,6 +411,15 @@ namespace M17N.Core
                              args[1].Eval (domain));
     }
 
+    private static object not (MExpression[] args, Domain domain)
+    {
+      if (args[0].val is int)
+       return (int) args[0].val == 0;
+      if (args[0].val is bool)
+       return ! ((bool) args[0].val);
+      return true;
+    }
+
     private static object plus (MExpression[] args, Domain domain)
     {
       if (args[0].val is int)
@@ -587,7 +716,7 @@ namespace M17N.Core
       }
 
     // (cond (COND1 ...) (COND2 ...) ...)
-    //   => (progn (when COND1 ...) (when COND2 ...) ...)
+    //   => (cond (when COND1 ...) (when COND2 ...) ...)
     private static void translate_cond (MPlist plist, Domain domain)
     {
       if (plist.IsPlist)
@@ -694,6 +823,27 @@ namespace M17N.Core
        }
     }
 
+    public MExpression (XmlNode node, Domain domain)
+    {
+      MSymbol sym = node.Name;
+
+      function = domain.GetFunc (sym);
+
+      XmlAttributeCollection attrs = node.Attributes;
+
+      int nargs = attrs.Count + node.ChildNodes.Count;
+      if (nargs < function.min_arg
+         || (function.max_arg >= 0 && nargs > function.max_arg))
+       throw new Exception ("Invalid number of arguments: " + node.InnerXml);
+      args = new MExpression[nargs];
+      int i;
+      for (i = 0; i < attrs.Count; i++)
+       {
+         string str = sttrs[i].Value;
+
+       }
+    }
+
     public object Eval (Domain domain)
     {
       if (function == null)
@@ -719,7 +869,12 @@ namespace M17N.Core
          str += ")";
        }
       else if (val != null)
-       str = val.ToString ();
+       {
+         if (val is MText)
+           str = "\"" + (string) ((MText) val) + "\"";
+         else
+           str = val.ToString ();
+       }
       else
        str = "()";
       return str;
index 520ec66..5bba7b2 100644 (file)
@@ -1,7 +1,10 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Reflection;
 using System.IO;
+using System.Xml;
+
 using M17N;
 using M17N.Core;
 using M17N.Input;
@@ -10,95 +13,545 @@ namespace M17N.Input
 {
   public class MInputMethod
   {
+    // Delegaes
+    public delegate bool Callback (MInputContext ic, MPlist args);
+
+    // Class members
+    public static Callback PreeditStart, PreeditDone, PreeditDraw;
+    public static Callback StatusStart, StatusDone, StatusDraw;
+    public static Callback CandidateStart, CandidateDone, CandidateDraw;
+    public static Callback SetSpot;
+    public static Callback Toggle;
+    public static Callback Reset;
+    public static Callback GetSurroundingText;
+    public static Callback DeleteSurroundingText;
+
     internal static MExpression.Domain domain = new MExpression.Domain (null);
-    internal MExpression.Domain local_domain;
+    private static MSymbol Minput_method = "input-method";
+    private static MSymbol Mdescription = "description";
+    private static MSymbol Mvariable = "variable";
+    private static MSymbol Mcommand = "command";
+    private static MSymbol Mmodule = "module";
+    private static MSymbol Mmodule_list = "module-list";
+    private static MSymbol Mtitle = "title";
+    private static MSymbol Minclude = "include";
+    private static MSymbol Mmacro = "macro";
+    private static MSymbol Mmacro_list = "macro-list";
+    private static MSymbol Mmap = "map";
+    private static MSymbol Mmap_list = "map-list";
+    private static MSymbol Mstate = "state";
+    private static MSymbol Mstate_list = "state-list";
+    internal static MSymbol Mcandidates = "candidates";
+    private static MSymbol Minsert = "insert";
+    private static MSymbol Mdelete = "delete";
+    private static MSymbol Mmove = "move";
+    private static MSymbol Mmark = "mark";
+    private static MSymbol Mmarker = "marker";
+    private static MSymbol Madd = "add";
+    private static MSymbol Msub = "sub";
+    private static MSymbol Mmul = "mul";
+    private static MSymbol Mdiv = "div";
+    private static MSymbol Mif = "if";
+    private static MSymbol Mcond = "cond";
+    private static MSymbol Mchar_at = "char-at";
+    private static MSymbol Msurrounding_text_p = "surrounding-text-p";
+    private static MSymbol Mpushback = "pushback"; 
+    private static MSymbol Mkeyseq = "keyseq"; 
 
-    private static MSymbol Minput_method = MSymbol.Of ("input-method");
-    private static MSymbol Mdescription = MSymbol.Of ("description");
-    private static MSymbol Mvariable = MSymbol.Of ("variable");
-    private static MSymbol Mcommand = MSymbol.Of ("command");
-    private static MSymbol Mtitle = MSymbol.Of ("title");
-    private static MSymbol Minclude = MSymbol.Of ("include");
-    private static MSymbol Mmacro = MSymbol.Of ("macro");
-    private static MSymbol Mmap = MSymbol.Of ("map");
-    private static MSymbol Mstate = MSymbol.Of ("state");
-    internal static MSymbol Mcandidates = MSymbol.Of ("candidates");
-    internal static MSymbol Mcandidates_group_size = MSymbol.Of ("candidates-group-size");
-    private static MSymbol Mat_less_than = MSymbol.Of ("@<");
-    private static MSymbol Mat_greater_than = MSymbol.Of ("@>");
-    private static MSymbol Mat_minus = MSymbol.Of ("@-");
-    private static MSymbol Mat_plus = MSymbol.Of ("@+");
-    private static MSymbol Mat_open_square_bracket = MSymbol.Of ("@[");
-    private static MSymbol Mat_close_square_bracket = MSymbol.Of ("@]");
-    private static MSymbol Minsert = MSymbol.Of ("insert");
-    private static MSymbol Mdelete = MSymbol.Of ("delete");
-    private static MSymbol Mmove = MSymbol.Of ("move");
-    private static MSymbol Mmark = MSymbol.Of ("mark");
-    private static MSymbol Mmarker = MSymbol.Of ("marker");
-    private static MSymbol Madd = MSymbol.Of ("add");
-    private static MSymbol Msub = MSymbol.Of ("sub");
-    private static MSymbol Mmul = MSymbol.Of ("mul");
-    private static MSymbol Mdiv = MSymbol.Of ("div");
-    private static MSymbol Mif = MSymbol.Of ("if");
-    private static MSymbol Mcond = MSymbol.Of ("cond");
-    private static MSymbol Mchar_at = MSymbol.Of ("char-at");
-
-    internal class Variable
-    {
-      public MSymbol name;
-      public MText description;
-      public Type type;
-      public object value;
-      public MPlist candidates;
-    }
+    private static Dictionary<MDatabase.Tag, MInputMethod> im_table
+      = new Dictionary<MDatabase.Tag, MInputMethod> ();
 
-    internal class Command
+    // Sub classes
+    private class Exception : System.Exception
     {
-      public MSymbol name;
-      public MText description;
-      public MSymbol[][] keys;
+      bool error;
+
+      public Exception (string msg) : base (msg)
+       {
+         error = true;
+       }
+
+      public Exception (string fmt, params object[] args)
+       : base (String.Format (fmt, args))
+       {
+         error = true;
+       }
+
+      public Exception (string msg, bool error) : base (msg)
+       {
+         this.error = error;
+       }
     }
 
-    internal class KeySeq
+    [FlagsAttribute]
+    private enum LoadStatus
     {
-      public MSymbol[] keys;
+      None =   0x00,
+      Header = 0x01,
+      Body =   0x02,
+      Full =   0x03,
+      Error =  0x04,
+    };
+
+    [FlagsAttribute]
+    public enum KeyModifier
+      {
+       None =      0x00000000,
+       Shift_L =   0x00400000,
+       Shift_R =   0x00800000,
+       Shift =     0x00C00000,
+       Control_L = 0x01000000,
+       Control_R = 0x02000000,
+       Control   = 0x03000000,
+       Alt_L =     0x04000000,
+       Alt_R =     0x08000000,
+       Alt =       0x0C000000,
+       AltGr =     0x10000000,
+       Super =     0x20000000,
+       Hyper =     0x40000000,
+       High =      0x70000000,
+       All =       0x7FC00000,
+      };
+
+    public struct Key
+    {
+      internal uint key;
+
+      private static Dictionary<string, uint> keysyms
+       = new Dictionary<string, uint> ();
+      private static Dictionary<string, KeyModifier> keymodifiers
+       = new Dictionary<string, KeyModifier> ();
+      private static uint keysym_base = 0x200000;
+      private static uint char_mask = ~((uint) KeyModifier.All);
+
+      static Key ()
+      {
+       keysyms["bs"] = keysyms["backspace"] = 0x08;
+       keysyms["tab"] = 0x09;
+       keysyms["lf"] = keysyms["linefeed"] = 0x10;
+       keysyms["cr"] = keysyms["return"] = keysyms["enter"] = 0x13;
+       keysyms["esc"] = keysyms["escape"] = 0x1B;
+       keysyms["spc"] = keysyms["space"] = 0x20;
+       keysyms["del"] = keysyms["delete"] = 0x7F;
+       keymodifiers["shift-l"] = KeyModifier.Shift_L;
+       keymodifiers["shift-r"] = KeyModifier.Shift_R;
+       keymodifiers["shift"] = KeyModifier.Shift;
+       keymodifiers["control-l"] = KeyModifier.Control_L;
+       keymodifiers["control-r"] = KeyModifier.Control_R;
+       keymodifiers["control"] = KeyModifier.Control;
+       keymodifiers["alt-l"] = KeyModifier.Alt_L;
+       keymodifiers["alt-r"] = KeyModifier.Alt_R;
+       keymodifiers["alt"] = KeyModifier.Alt;
+       keymodifiers["altgr"] = KeyModifier.AltGr;
+       keymodifiers["super"] = KeyModifier.Super;
+       keymodifiers["hyper"] = KeyModifier.Hyper;
+      }
+
+      private static uint decode_keysym (MSymbol keysym)
+      {
+       uint key;
+       string name = keysym.Name;
+
+       if (name.Length == 1)
+         return name[0];
+       name = name.ToLower ();
+       if (! keysyms.TryGetValue (name, out key))
+         keysyms[name] = key = keysym_base++;
+       return key;
+      }
+
+      private static uint combine_modifiers (uint c, KeyModifier modifiers)
+      {
+       if (c < 0x7F && c != 0x20)
+         {
+           if ((modifiers & KeyModifier.Shift) != KeyModifier.None
+               && Char.IsLower ((char) c))
+             {
+               modifiers &= ~KeyModifier.Shift;
+               c = Char.ToUpper ((char) c);
+             }
+           if ((modifiers & KeyModifier.Control) != KeyModifier.None)
+             {
+               modifiers &= ~KeyModifier.Control;
+               c &= 0x1F;
+             }
+         }     
+       return c | (uint) modifiers;
+      }
+
+      public Key (uint c)
+      {
+       key = c;
+      }
+
+      public Key (uint c, KeyModifier modifiers)
+      {
+       key = combine_modifiers (c, modifiers);
+      }
+
+      public Key (MSymbol keysym, KeyModifier modifiers)
+      {
+       key = combine_modifiers (decode_keysym (keysym), modifiers);
+      }
+
+      public Key (MSymbol keysym)
+      {
+       string str = keysym.Name;
+       int len = str.Length;
+       int i;
+       KeyModifier modifiers = KeyModifier.None;
+
+       for (i = 0; i + 2 < len && str[i + 1] == '-'; i += 2)
+         {
+           if (str[i] == 'S')
+             modifiers |= KeyModifier.Shift;
+           else if (str[i] == 'C')
+             modifiers |= KeyModifier.Control;
+           else if (str[i] == 'A')
+             modifiers |= KeyModifier.Alt;
+           else if (str[i] == 'G')
+             modifiers |= KeyModifier.AltGr;
+           else if (str[i] == 's')
+             modifiers |= KeyModifier.Super;
+           else if (str[i] == 'H')
+             modifiers |= KeyModifier.Hyper;
+         }
+       if (i + 1 == len)
+         key = combine_modifiers (str[i], modifiers);
+       else
+         key = combine_modifiers (decode_keysym (keysym), modifiers);
+      }
+
+      public Key (MPlist plist)
+      {
+       KeyModifier modifiers = KeyModifier.None;
+       MPlist p;
+
+       for (p = plist; ! p.IsEmpty; p = p.next)
+         {
+           if (p.IsInteger)
+             {
+               if (! p.next.IsEmpty)
+                 throw new Exception ("Invalid Key: " + plist);
+               break;
+             }
+           else if (! p.IsSymbol)
+             throw new Exception ("Invalid Key: " + plist);
+           else
+             {
+               string name = p.Symbol.Name.ToLower ();
+               KeyModifier m;
+               
+               if (! keymodifiers.TryGetValue (name, out m))
+                 break;
+               modifiers |= m;
+             }
+         }
+       if (p.IsEmpty || ! p.next.IsEmpty)
+         throw new Exception ("Invalid Key: " + plist);
+       if (p.IsInteger)
+         key = combine_modifiers ((uint) p.Integer, modifiers);
+       else
+         key = combine_modifiers (decode_keysym (p.Symbol), modifiers);
+      }
 
-      private static MSymbol char_to_symbol (int c)
+      public bool HasModifier
       {
-       return MSymbol.Of (String.Format ("#{0:X}", c));
+       get { return ((key & (uint) KeyModifier.All) != 0); }
+      }
+
+      public bool Match (Key k)
+      {
+       if (k.key == key)
+         return true;
+       if ((k.key & char_mask) != (key & char_mask))
+         return false;
+       KeyModifier m1 = ((KeyModifier) key) & KeyModifier.All;
+       KeyModifier m2 = ((KeyModifier) k.key) & KeyModifier.All;
+       return (((m1 & KeyModifier.Shift) == (m2 & KeyModifier.Shift)
+                || ((m1 & KeyModifier.Shift) == KeyModifier.Shift
+                    && (m2 & KeyModifier.Shift) != KeyModifier.None))
+               && ((m1 & KeyModifier.Control) == (m2 & KeyModifier.Control)
+                   || ((m1 & KeyModifier.Control) == KeyModifier.Control
+                       && (m2 & KeyModifier.Control) != KeyModifier.None))
+               && ((m1 & KeyModifier.Alt) == (m2 & KeyModifier.Alt)
+                   || ((m1 & KeyModifier.Alt) == KeyModifier.Alt
+                       && (m2 & KeyModifier.Alt) != KeyModifier.None))
+               && ((m1 & KeyModifier.High) == (m2 & KeyModifier.High)));
+      }
+
+      public override string ToString ()
+      {
+       string str = Char.ToString ((char) key);
+       KeyModifier m = ((KeyModifier) key) & KeyModifier.All;
+
+       if (m != KeyModifier.None)
+         {
+           if ((m & KeyModifier.Shift) != KeyModifier.None)
+             str = "S-" + str;
+           if ((m & KeyModifier.Control) != KeyModifier.None)
+             str = "C-" + str;
+           if ((m & KeyModifier.Alt) != KeyModifier.None)
+             str = "A-" + str;
+           if ((m & KeyModifier.AltGr) != KeyModifier.None)
+             str = "G-" + str;
+           if ((m & KeyModifier.Super) != KeyModifier.None)
+             str = "s-" + str;
+           if ((m & KeyModifier.Hyper) != KeyModifier.None)
+             str = "H-" + str;
+         }
+       return str;
       }
+    }
+
+    public class KeySeq : List<Key>
+    {
+      public KeySeq () : base () { }
 
-      public KeySeq (MPlist plist)
+      public KeySeq (MPlist plist) : base ()
       {
-       keys = new MSymbol[plist.Count];
-       int i = 0;
        foreach (MPlist p in plist)
          {
            if (p.IsSymbol)
-             keys[i++] = p.Symbol;
+             this.Add (new Key (p.Symbol));
            else if (p.IsInteger)
-             keys[i++] = char_to_symbol (p.Integer);
+             this.Add (new Key ((char) p.Integer));
+           else if (p.IsPlist)
+             this.Add (new Key (p.Plist));
            else
-             keys[i] = null;
+             throw new Exception ("Invalid Key Sequence: " + plist);
          }
       }
 
-      public KeySeq (MText mt)
+      public KeySeq (MText mt) : base ()
       {
-       keys = new MSymbol[mt.Length];
        for (int i = 0; i < mt.Length; i++)
-         keys[i] = char_to_symbol (mt[i]);
+         this.Add (new Key ((uint) mt[i]));
+      }
+
+      private static uint parse_integer (string str)
+      {
+       if (Char.IsDigit (str[0]))
+         {
+           if (str[0] == '0' && str.Length > 2 && str[1] == 'x')
+             {
+               uint i = 0;
+               for (int idx = 2; idx < str.Length; idx++)
+                 {
+                   uint c = str[idx];
+                   if (c >= '0' && c <= '9')
+                     i = i * 16 + (c - '0');
+                   else if (c >= 'A' && c <= 'F')
+                     i = i * 16 + 10 + (c - 'A');
+                   else if (c >= 'a' && c <= 'f')
+                     i = i * 16 + 10 + (c - 'a');
+                   else
+                     break;
+                 }
+               return i;
+             }
+           return UInt32.Parse (str);
+         }
+       else if (str[0] == '?')
+         return str[1];
+       return 0;
+      }
+
+      public KeySeq (XmlNode node) : base ()
+       {
+         XmlAttributeCollection acol = node.Attributes;
+         XmlNode n;
+
+         if (acol != null && (n = acol["keys"]) != null)
+           {
+             foreach (char c in  n.Value)
+               this.Add (new Key ((uint) c));
+           }
+
+         for (node = node.FirstChild; node != null; node = node.NextSibling)
+           {
+             if (node.Name == "key-event")
+               this.Add (new Key ((MSymbol) node.InnerText));
+             else
+               this.Add (new Key (parse_integer (node.InnerText)));
+           }
+       }
+
+      public override string ToString ()
+      {
+       string str;
+
+       foreach (Key key in this)
+         if (key.HasModifier)
+           {
+             str = "(keyseq";
+             foreach (Key k in this)
+               str += " " + k.ToString ();
+             return str + ")";
+           }
+       str = "\"";
+       foreach (Key key in this)               
+         str += key.ToString ();
+       return str + "\"";
+      }
+    }
+
+    public class Variable
+    {
+      public MSymbol name;
+      public MText description;
+      public Type type;
+      public object value;
+      public object[] candidates;
+
+      public Variable (MPlist p)
+      {
+       name = p.Symbol;
+       p = p.Next;
+       description = parse_description (p);
+       if (description == null)
+         description = new MText ("No description");
+       else
+         p = p.next;
+       type = (p.IsMText ? typeof (MText)
+               : p.IsInteger ? typeof (int)
+               : p.IsSymbol ? typeof (MSymbol)
+               : typeof (object));
+       value = p.val;
+       p = p.next;
+       candidates = new object[p.Count];
+       for (int i = 0; ! p.IsEmpty; i++, p = p.next)
+         candidates[i] = p.val;
+      }
+
+      private static Type parse_value (XmlNode node, out object value)
+      {
+       string typename = node.Attributes["type"].Value;
+       Type type;
+
+       if (typename == "integer")
+         {
+           int i;
+           if (! Int32.TryParse (node.InnerText, out i))
+             i = 0;
+           value = i;
+           type = typeof (int);
+         }
+       else if (typename == "string")
+         {
+           MText mt = node.InnerText;
+           value = mt;
+           type = typeof (MText);
+         }
+       else if (typename == "symbol")
+         {
+           MSymbol sym = node.InnerText;
+           value = sym;
+           type = typeof (MSymbol);
+         }
+       else
+         {
+           value = null;
+           type = typeof (object);
+         }
+       return type;
+      }
+
+      public Variable (XmlNode node)
+      {
+       name = node.Attributes["id"].Value;
+       for (node = node.FirstChild; node != null; node = node.NextSibling)
+         if (node.NodeType == XmlNodeType.Element)
+           {
+             if (node.Name == "description")
+               description = parse_description (node);
+             else if (node.Name == "value")
+               type = parse_value (node, out value);
+             else if (node.Name == "valiable-value-candidate")
+               {
+                 XmlNodeList n_list = node.ChildNodes;
+                 candidates = new object[n_list.Count];
+                 for (int i = 0; i < n_list.Count; i++)
+                   {
+                     object val;
+                     parse_value (n_list[i], out val);
+                     candidates[i] = val;
+                   }
+               }
+           }
+      }
+
+      public override string ToString ()
+      {
+       return ("(" + name + " \"" + (string) description
+               + "\" " + type + " " + value + " " + candidates + ")");
+      }
+    }
+
+    public class Command
+    {
+      public MSymbol name;
+      public MText description;
+      public List<KeySeq> keys;
+
+      public Command (MPlist p)
+      {
+       name = p.Symbol;
+       p = p.Next;
+       description = parse_description (p);
+       if (description == null)
+         description = "No description";
+       keys = new List<KeySeq> ();
+       for (p = p.next; ! p.IsEmpty; p = p.next)
+         {
+           if (p.IsMText)
+             keys.Add (new KeySeq (p.Text));
+           else if (p.IsPlist)
+             keys.Add (new KeySeq (p.Plist));
+         }
       }
 
-      public MSymbol this[int i] { get { return keys[i]; } }
+      public Command (XmlNode node)
+      {
+       name = node.Attributes["id"].Value;
+       keys = new List<KeySeq> ();
+       for (node = node.FirstChild; node != null; node = node.NextSibling)
+         {
+           if (node.Name == "description")
+             description = parse_description (node);
+           else if (node.Name == "keyseq")
+             keys.Add (new KeySeq (node));
+         }
+      }
 
-      public int Length { get { return keys.Length; } }
+      public override string ToString ()
+      {
+       string str = "(" + name + " \"" + (string) description;
+       foreach (KeySeq keyseq in keys)
+         str += " " + keyseq;
+       return str + ")";
+      }
+    }
+
+    internal class Plugin
+    {
+      public string name;
+      public Assembly assembly;
+      public MPlist methods;
+
+      public override string ToString ()
+      {
+       string str = "(" + name;
+       for (MPlist p = methods; ! p.IsEmpty; p = p.next)
+         str += " " + p.key;
+       return str + ")";
+      }
     }
 
     internal class Map
     {
       public MSymbol name;
-      public Dictionary<MSymbol, Map> submaps;
+      public Dictionary<Key, Map> submaps;
       public MExpression actions;
 
       public void Add (KeySeq keys, int index, MExpression actions)
@@ -106,15 +559,15 @@ namespace M17N.Input
        Map sub = null;
 
        if (submaps == null)
-         submaps = new Dictionary<MSymbol, Map> ();
+         submaps = new Dictionary<Key, Map> ();
        else
          submaps.TryGetValue (keys[index], out sub);
        if (sub == null)
          {
-           MSymbol sym = keys[index];
-           submaps[sym] = sub = new Map ();
+           Key key = keys[index];
+           submaps[key] = sub = new Map ();
          }
-       if (index + 1 < keys.Length)
+       if (index + 1 < keys.Count)
          sub.Add (keys, index + 1, actions);
        else
          this.actions = actions;
@@ -124,77 +577,220 @@ namespace M17N.Input
       {
        Map sub;
 
-       if (index + 1 == keys.Length)
+       if (index + 1 == keys.Count)
          return actions;
        if (submaps.TryGetValue (keys[index], out sub))
          return sub.Lookup (keys, index + 1);
        return null;
       }
+
+      private void describe (MText mt, KeySeq keyseq)
+      {
+       if (keyseq.Count > 0)
+         {
+           mt.Cat (" (").Cat (keyseq.ToString ());
+           if (actions != null)
+             mt.Cat (' ').Cat (actions.ToString ());
+           mt.Cat (')');           
+         }
+       if (submaps != null)
+         foreach (KeyValuePair<Key, Map> kv in submaps)
+           {
+             keyseq.Add (kv.Key);
+             kv.Value.describe (mt, keyseq);
+             keyseq.RemoveAt (keyseq.Count - 1);
+           }
+      }
+
+      public override string ToString ()
+      {
+       MText mt = "(" + name.Name;
+       KeySeq keyseq = new KeySeq ();
+
+       describe (mt, keyseq);
+       mt.Cat (')');
+       return (string) mt;
+      }
     }
 
     internal class State
     {
       public MSymbol name;
       public MText title;
-      public Dictionary<MSymbol, MExpression> branches
-       = new Dictionary<MSymbol, MExpression> ();
+      public MPlist branches = new MPlist ();
+
+      public State (MSymbol name)
+      {
+       this.name = name;
+      }
+
+      public override string ToString ()
+      {
+       MText mt = "(" + name.Name;
+
+       if (title != null)
+         mt.Cat (" \"" + title + "\"");
+       for (MPlist p = branches; ! p.IsEmpty; p = p.next)
+         mt.Cat (" (" + p.Key + " " + (MExpression) p.Val + ")");
+       return (string) mt + ")";
+      }
     }
 
-    private static Dictionary<MDatabase.Tag, MInputMethod> im_table
-      = new Dictionary<MDatabase.Tag, MInputMethod> ();
+    // Instance members
+    internal MExpression.Domain local_domain;
 
-    public readonly MSymbol language;
-    public readonly MSymbol name;
-    public readonly MSymbol subname;
+    private LoadStatus load_status = LoadStatus.None;
+    private MDatabase.Tag tag;
+    private MDatabase mdb;
 
-    internal MDatabase mdb;
-    internal MText description;
+    private MText description;
     internal MText title;
     internal Command[] commands;
     internal Variable[] variables;
+    internal Dictionary<MSymbol, Plugin> plugins;
     internal MPlist bindings;
-    internal Dictionary<MSymbol, Map> maps
-      = new Dictionary<MSymbol, Map> ();
-    internal State init_state;
-    internal Dictionary<MSymbol, State> states
-      = new Dictionary<MSymbol, State> ();
-    internal MPlist externals;
+    internal Dictionary<MSymbol, Map> maps;
+    internal MPlist states;
 
     static MInputMethod ()
     {
       domain.Defun ("insert", insert, 1, 1);
-      domain.Defun ("candidates", insert_candidates, 1, 1);
+      domain.Defun ("candidates", insert_candidates, 1, -1);
       domain.Defun ("delete", delete, 1, 1);
       domain.Defun ("select", select, 1, 1);
       domain.Defun ("show", show, 0, 0);
       domain.Defun ("hide", hide, 0, 0);
       domain.Defun ("move", move, 1, 1);
-      domain.Defun ("mark", mark, 1, 1);
+      domain.Defun ("mark", mark, 1, 1, true);
       domain.Defun ("pushback", pushback, 1, 1);
       domain.Defun ("pop", pop, 0, 0);
       domain.Defun ("undo", undo, 0, 1);
       domain.Defun ("commit", commit, 0, 0);
       domain.Defun ("unhandle", unhandle, 0, 0);
-      domain.Defun ("shift", shift, 1, 1);
-      domain.Defun ("call", call, 2, -1);
+      domain.Defun ("shift", shift, 1, 1, true);
+      domain.Defun ("call", call, 2, -1, true);
+      // Pseudo functions to make arguments of special objects.
       domain.Defun ("marker", marker, 1, 1, true);
       domain.Defun ("char-at", char_at, 1, 1, true);
+      domain.Defun ("keyseq", keyseq, 1, -1, true);
+
+      MDatabase.Tag tag = new MDatabase.Tag (Minput_method, "*", "*", "*");
+      List<MDatabase> list = MDatabase.List (tag);
+      M17n.DebugPrint ("Found {0} input methods\n", list.Count);
+      foreach (MDatabase mdb in list)
+       im_table[mdb.tag] = new MInputMethod (mdb.tag);
     }
 
+    // Constructor
     private MInputMethod (MDatabase.Tag tag)
     {
+      this.tag = tag;
+    }
+
+    // Instance Properties
+    public MSymbol Language { get { return tag[1]; } }
+    public MSymbol Name { get { return tag[2]; } }
+    public MSymbol SubName { get { return tag[3]; } }
+
+    public bool Info (out MText description, out MText title,
+                     out Variable[] variables, out Command[] commands)
+    {
+      if ((load_status & LoadStatus.Header) != LoadStatus.Header
+         && ! load_header ())
+       {
+         description = null;
+         title = null;
+         variables = null;
+         commands = null;
+         return false;
+       }
+      description = this.description;
+      title = this.title;
+      variables = this.variables;
+      commands = this.commands;
+      return true;
+    }
+
+    public static MInputMethod Find (MSymbol language, MSymbol name)
+    {
+      return Find (language, name, MSymbol.nil);
+    }
+
+    public static MInputMethod Find (MSymbol language, MSymbol name,
+                                    MSymbol subname)
+    {
+      MDatabase.Tag tag = new MDatabase.Tag (Minput_method, language,
+                                            name, subname);
+      MInputMethod im;
+
+      return (im_table.TryGetValue (tag, out im) ? im : null);
+    }
+
+    public bool Open ()
+    {
+      return ((load_status == LoadStatus.Full) || load_body ());
+    }
+
+    public static MInputMethod[] List ()
+    {
+      MInputMethod[] array = new MInputMethod[im_table.Count];
+      int i = 0;
+
+      foreach (KeyValuePair<MDatabase.Tag, MInputMethod> kv in im_table)
+       array[i++] = kv.Value;
+      return array;
+    }
+
+    private bool load_header ()
+    {
       mdb = MDatabase.Find (tag);
       if (mdb == null)
-       throw new Exception (String.Format ("Input method {0} not available",
-                                           tag));
-      language = tag[1];
-      name = tag[2];
-      subname = tag[3];
+       return false;
+      try {
+       MSymbol format = mdb.Format;
+
+       if (format == MSymbol.plist)
+         load ((MPlist) mdb.Load (Mmap), false);
+       else
+         {
+           XmlDocument doc = (XmlDocument) mdb.Load (Mmap_list);
+           load (doc.DocumentElement, false);
+         }
+      } catch (Exception e) {
+       Console.WriteLine ("{0}\n", e);
+       load_status = LoadStatus.Error;
+       return false;
+      }
+      load_status |= LoadStatus.Header;
+      return true;
+    }
+
+    private bool load_body ()
+    {
       local_domain = new MExpression.Domain (domain, null);
+      mdb = MDatabase.Find (tag);
+      if (mdb == null)
+       return false;
+      try {
+       object obj = mdb.Load ();
+       if (obj is MPlist)
+         load ((MPlist) obj, true);
+       else
+         load ((XmlDocument) obj, true);
+      } catch (Exception e) {
+       Console.WriteLine (e);
+       load_status = LoadStatus.Error;
+       return false;
+      }
+      load_status = LoadStatus.Full;
+      return true;
+    }
+
+    private void load (MPlist plist, bool full)
+    {
+      maps = new Dictionary<MSymbol, Map> ();
+      states = new MPlist ();
 
-      MPlist plist = (MPlist) mdb.Load ();
-      if (plist == null)
-       return;
       for (; ! plist.IsEmpty; plist = plist.next)
        if (plist.IsPlist)
          {
@@ -202,6 +798,7 @@ namespace M17N.Input
            if (pl.IsSymbol)
              {
                MSymbol sym = pl.Symbol;
+
                pl = pl.next;
                if (sym == Mdescription)
                  {
@@ -218,16 +815,97 @@ namespace M17N.Input
                  parse_variables (pl);
                else if (sym == Mcommand)
                  parse_commands (pl);
-               else if (sym == Minclude)
-                 parse_include (pl);
-               else if (sym == Mmacro)
-                 parse_macros (pl);
-               else if (sym == Mmap)
-                 parse_maps (pl);
-               else if (sym == Mstate)
-                 parse_states (pl);
+               else if (full)
+                 {
+                   if (sym == Mmodule)
+                     parse_plugins (pl);
+                   else if (sym == Minclude)
+                     parse_include (pl);
+                   else if (sym == Mmacro)
+                     parse_macros (pl);
+                   else if (sym == Mmap)
+                     parse_maps (pl);
+                   else if (sym == Mstate)
+                     parse_states (pl);
+                 }
              }
          }
+      if (description == null)
+       description = (MText) "No description";
+      if (title == null)
+       title = new MText (tag[2].Name);
+      if (variables == null)
+       variables = new Variable[0];
+      if (commands == null)
+       commands = new Command[0];
+      if (! full)
+       return;
+      if (states.IsEmpty)
+       {
+         State state = new State ((MSymbol) "init");
+         plist = new MPlist ();
+         foreach (KeyValuePair<MSymbol, Map>kv in maps)
+           state.branches.Add (kv.Key, new MExpression (plist, local_domain));
+         states.Add (state.name, state);
+       }
+    }
+
+    private void load (XmlNode node, bool full)
+    {
+      bool skip_header = load_status == LoadStatus.Header;
+
+      maps = new Dictionary<MSymbol, Map> ();
+      states = new MPlist ();
+
+      if (node.NodeType == XmlNodeType.Document)
+       node = node.FirstChild;
+      while (node.NodeType != XmlNodeType.Element)
+       node = node.NextSibling;
+      for (node = node.FirstChild; node != null; node = node.NextSibling)
+       {
+         if (node.NodeType != XmlNodeType.Element)
+           continue;
+         if (! skip_header)
+           {
+             if (node.Name == "description")
+               description = parse_description (node);
+             else if (node.Name == "title")
+               title = parse_title (node);
+             else if (node.Name == "variable-list")
+               parse_variables (node);
+             else if (node.Name == "command-list")
+               parse_commands (node);
+           }
+         else if (full)
+           {
+             if (node.Name == "module-list")
+               parse_plugins (node);
+             else if (node.Name == "macro-list")
+               parse_macros (node);
+             else if (node.Name == "map-list")
+               parse_maps (node);
+             else if (node.Name == "state-list")
+               parse_states (node);
+           }
+       }
+      if (description == null)
+       description = (MText) "No description";
+      if (title == null)
+       title = new MText (tag[2].Name);
+      if (variables == null)
+       variables = new Variable[0];
+      if (commands == null)
+       commands = new Command[0];
+      if (! full)
+       return;
+      if (states.IsEmpty)
+       {
+         State state = new State ((MSymbol) "init");
+         MPlist plist = new MPlist ();
+         foreach (KeyValuePair<MSymbol, Map>kv in maps)
+           state.branches.Add (kv.Key, new MExpression (plist, local_domain));
+         states.Add (state.name, state);
+       }
     }
 
     private static void transform (MPlist plist)
@@ -255,13 +933,13 @@ namespace M17N.Input
              if (pl.IsSymbol)
                {
                  if (pl.Symbol == Madd)
-                   pl.Set (MSymbol.symbol, MSymbol.Of ("+="));
+                   pl.Set (MSymbol.symbol, (MSymbol) "+=");
                  else if (pl.Symbol == Msub)
-                   pl.Set (MSymbol.symbol, MSymbol.Of ("-="));
+                   pl.Set (MSymbol.symbol, (MSymbol) "-=");
                  else if (pl.Symbol == Mmul)
-                   pl.Set (MSymbol.symbol, MSymbol.Of ("*="));
+                   pl.Set (MSymbol.symbol, (MSymbol) "*=");
                  else if (pl.Symbol == Mdiv)
-                   pl.Set (MSymbol.symbol, MSymbol.Of ("/="));
+                   pl.Set (MSymbol.symbol, (MSymbol) "/=");
                  else if (pl.Symbol == Minsert)
                    {
                      // (insert (CANDIDATES ...))
@@ -309,17 +987,11 @@ namespace M17N.Input
                          pl.Set (MSymbol.plist, p);
                        }
                    }
-                 else if (pl.Symbol == Mstate)
+                 else if (pl.Symbol == Mpushback)
                    {
                      pl = pl.next;
-                     if (pl.IsSymbol)
-                       {
-                         MSymbol sym = pl.Symbol;
-                         MPlist p = new MPlist ();
-                         p.Add (MSymbol.symbol, Mstate);
-                         p.Add (MSymbol.symbol, sym);
-                         pl.Set (MSymbol.plist, p);
-                       }
+                     if (pl.IsPlist)
+                       pl.Plist.Push (MSymbol.symbol, Mkeyseq);
                    }
                }
              else if (pl.IsMText)
@@ -332,11 +1004,24 @@ namespace M17N.Input
            {
              MSymbol sym = plist.Symbol;
 
-             if (sym.Name[0] == '@')
+             if (sym.Name.Length >= 3
+                 && sym.Name[0] == '@'
+                 && (sym.Name[1] == '-' || sym.Name[1] == '+'))
                {
+                 int pos = int.Parse (sym.Name.Substring (1));
                  MPlist p = new MPlist ();
-                 p.Add (MSymbol.symbol, Mchar_at);
-                 p.Add (MSymbol.symbol, sym);
+
+                 if (pos == 0)
+                   {
+                     p.Add (MSymbol.symbol, Msurrounding_text_p);
+                   }
+                 else
+                   {
+                     if (sym.Name[1] == '+')
+                       pos--;
+                     p.Add (MSymbol.symbol, Mchar_at);
+                     p.Add (MSymbol.integer, pos);
+                   }
                  plist.Set (MSymbol.plist, p);
                }
            }
@@ -345,9 +1030,28 @@ namespace M17N.Input
 
     private static MText parse_description (MPlist plist)
     {
-      return (plist.IsMText ? plist.Text
-             : plist.IsPlist && plist.Plist.IsMText ? plist.Plist.Text
-             : null);
+      if (plist.IsMText)
+       return plist.Text;
+      if (plist.IsPlist)
+       {
+         plist = plist.Plist;
+         if (plist.IsSymbol && plist.Symbol == (MSymbol) "_"
+             && plist.next.IsMText)
+           return plist.next.Text;
+       }
+      return null;
+    }
+
+    private static MText parse_description (XmlNode node)
+    {
+      if (node.HasChildNodes)
+       node = node.FirstChild;
+      return node.InnerText;
+    }
+
+    private static MText parse_title (XmlNode node)
+    {
+      return node.InnerText;
     }
 
     private void parse_variables (MPlist plist)
@@ -356,25 +1060,17 @@ namespace M17N.Input
 
       for (int i = 0; ! plist.IsEmpty; plist = plist.next)
        if (plist.IsPlist && plist.Plist.IsSymbol)
-         {
-           Variable var = new Variable ();
-           MPlist p = plist.Plist;
-
-           var.name = p.Symbol;
-           p = p.Next;
-           var.description = parse_description (p);
-           if (var.description == null)
-             var.description = new MText ("No description");
-           else
-             p = p.next;
-           var.type = (p.IsMText ? typeof (MText)
-                       : p.IsInteger ? typeof (int)
-                       : p.IsSymbol ? typeof (MSymbol)
-                       : typeof (object));
-           var.value = p.val;
-           var.candidates = p.next;
-           variables[i++] = var;
-         }
+         variables[i++] = new Variable (plist.Plist);
+    }
+
+    private void parse_variables (XmlNode node)
+    {
+      XmlNodeList node_list = node.ChildNodes;
+
+      variables = new Variable[node_list.Count];
+      for (int i = 0; i < node_list.Count; i++)
+       if (node_list[i].NodeType == XmlNodeType.Element)
+         variables[i] = new Variable (node_list[i]);
     }
 
     private void parse_commands (MPlist plist)
@@ -383,27 +1079,97 @@ namespace M17N.Input
 
       for (int i = 0; ! plist.IsEmpty; plist = plist.next)
        if (plist.IsPlist && plist.Plist.IsSymbol)
+         commands[i++] = new Command (plist.Plist);
+    }
+
+    private void parse_commands (XmlNode node)
+    {
+      XmlNodeList node_list = node.ChildNodes;
+
+      commands = new Command[node_list.Count];
+      for (int i = 0; i < node_list.Count; i++)
+       {
+         if (node_list[i].NodeType == XmlNodeType.Element)
+           commands[i] = new Command (node_list[i]);
+       }
+    }
+
+    private void parse_plugins (MPlist plist)
+    {
+      plugins = new Dictionary<MSymbol, Plugin> ();
+
+      for (; ! plist.IsEmpty; plist = plist.Next)
+       {
+         MPlist p = plist.Plist;
+         MSymbol sym = p.Symbol;
+         Plugin plugin = new Plugin ();
+
+         plugin.name = sym.Name;
+         plugin.methods = new MPlist ();
+         for (p = p.next; ! p.IsEmpty; p = p.next)
+           plugin.methods.Add (p.Symbol, null);
+         plugins.Add (sym, plugin);
+       }
+    }
+
+    private void parse_plugins (XmlNode node)
+    {
+      plugins = new Dictionary<MSymbol, Plugin> ();
+
+      foreach (XmlNode n in node.ChildNodes)
+       {
+         Plugin plugin = new Plugin ();
+         plugin.name = n.Attributes["id"].Value;
+         plugin.methods = new MPlist ();
+         foreach (XmlNode nn in n.ChildNodes)
+           plugin.methods.Add ((MSymbol) nn.Attributes["id"].Value,
+                               null);
+         plugins.Add (plugin.name, plugin);
+       }
+    }
+
+    private void parse_macros (XmlNode node)
+    {
+      for (XmlNode nn = node.FirstChild; nn != null; nn = nn.NextSibling)
+       if (nn.NodeType == XmlNodeType.Element
+           && nn.Name == "xi:include")
          {
-           Command cmd = new Command ();
-           MPlist p = plist.Plist;
-
-           cmd.name = p.Symbol;
-           p = p.Next;
-           cmd.description = parse_description (p);
-           if (cmd.description == null)
-             cmd.description = new MText ("No description");
+           XmlNode n = nn.FirstChild.FirstChild;
+           MSymbol language = n.InnerText;
+           n = n.NextSibling;
+           MSymbol name = n.InnerText;
+           n = n.NextSibling;
+           MSymbol subname = (n != null ? n.InnerText : MSymbol.nil);
+           n = n.ParentNode.NextSibling;
+           MSymbol section = n.InnerText;
+           n = n.NextSibling;
+           MSymbol id = (n != null ? n.InnerText : MSymbol.nil);
+
+           MInputMethod im = MInputMethod.Find (language, name, subname);
+           if (im == null || ! im.Open ())
+             continue;
+           if (id == MSymbol.nil)
+             im.local_domain.CopyFunc (local_domain);
            else
-             p = p.next;
-           KeySeq[] keys = new KeySeq[p.Count];
-           for (int j = 0; ! p.IsEmpty; p = p.next)
-             {
-               if (p.IsMText)
-                 keys[j++] = new KeySeq (p.Text);
-               else if (p.IsPlist)
-                 keys[j++] = new KeySeq (p.Plist);
-             }
-           commands[i++] = cmd;
+             im.local_domain.CopyFunc (local_domain, id);
          }
+      for (XmlNode nn = node.FirstChild; nn != null; nn = nn.NextSibling)
+       if (nn.NodeType == XmlNodeType.Element
+           && nn.Name != "xi:include")
+         local_domain.Defun ((MSymbol) node.GetAttribute ("id"));
+      for (XmlNode nn = node.FirstChild; nn != null; nn = nn.NextSibling)
+       if (nn.NodeType == XmlNodeType.Element
+           && nn.Name != "xi:include")
+         local_domain.Defun ((MSymbol) node.GetAttribute ("id"), null,
+                             nn.FirstChild);
+    }
+
+    private void parse_maps (XmlNode node)
+    {
+    }
+
+    private void parse_states (XmlNode node)
+    {
     }
 
     private void parse_include (MPlist plist)
@@ -411,24 +1177,26 @@ namespace M17N.Input
       if (! plist.IsPlist)
        return;
       MPlist p = plist.Plist;
-      MSymbol language, name, extra;
+      MSymbol language, name, subname;
       language = p.Symbol;
       p = p.next;
       if (! p.IsSymbol)
-       name = extra = MSymbol.nil;
+       name = subname = MSymbol.nil;
       else
        {
          name = p.Symbol;
          p = p.next;
          if (! p.IsSymbol)
-           extra = MSymbol.nil;
+           subname = MSymbol.nil;
          else
-           extra = p.Symbol;
+           subname = p.Symbol;
        }
 
-      MInputMethod im = MInputMethod.Get (language, name, extra);
+      MInputMethod im = MInputMethod.Find (language, name, subname);
       if (im == null)
        return;
+      if (! im.Open ())
+       return;
       plist = plist.next;
       if (! plist.IsSymbol)
        return;
@@ -442,7 +1210,7 @@ namespace M17N.Input
          if (target_name == MSymbol.nil)
            im.local_domain.CopyFunc (local_domain);
          else
-           im.local_domain.CopyFunc (target_name, local_domain);
+           im.local_domain.CopyFunc (local_domain, target_name);
        }
       else if (target_type == Mmap)
        {
@@ -462,132 +1230,366 @@ namespace M17N.Input
        {
          if (target_name == MSymbol.nil)
            {
-             foreach (KeyValuePair<MSymbol, State> kv in im.states)
-               states[kv.Key] = kv.Value;
+             for (p = im.states; ! p.IsEmpty; p = p.next)
+               states.Add (p.key, p.val);
            }
          else
            {
-             State state;
-             if (im.states.TryGetValue (target_name, out state))
-               states[target_name] = state;
+             object state = im.states.Get (target_name);
+             if (state != null)
+               states.Add (target_name, state);
            }
        }
     }
 
-    private void parse_macros (MPlist plist)
+    private void parse_macros (MPlist plist)
+    {
+      for (MPlist pl = plist; ! pl.IsEmpty; pl = pl.next)
+       if (pl.IsPlist)
+         {
+           MPlist p = pl.Plist;
+
+           if (! p.IsSymbol)
+             continue;
+           local_domain.Defun (p.Symbol, null, null);
+         }
+      for (MPlist pl = plist; ! pl.IsEmpty; pl = pl.next)
+       if (pl.IsPlist)
+         {
+           MPlist p = pl.Plist;
+
+           if (! p.IsSymbol)
+             continue;
+           transform (p.next);
+           local_domain.Defun (p.Symbol, null, p.next);
+         }
+    }
+
+    private void parse_maps (MPlist plist)
+    {
+      for (; ! plist.IsEmpty; plist = plist.next)
+       if (plist.IsPlist)
+         {
+           MPlist pl = plist.Plist;
+         
+           if (! pl.IsSymbol)
+             continue;
+           Map map = new Map ();
+           map.name = pl.Symbol;
+           maps[map.name] = map;
+           for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
+             {
+               if (! pl.IsPlist)
+                 continue;
+               MPlist p = pl.Plist;
+               KeySeq keys;
+               if (p.IsMText)
+                 keys = new KeySeq (p.Text);
+               else if (p.IsPlist)
+                 keys = new KeySeq (p.Plist);
+               else
+                 continue;
+               p = p.next;
+               if (p.IsEmpty)
+                 continue;
+               transform (p);
+               MExpression expr = new MExpression (p, local_domain);
+               map.Add (keys, 0, expr);
+             }
+         }
+    }
+
+    private void parse_states (MPlist plist)
+    {
+      for (; ! plist.IsEmpty; plist = plist.next)
+       if (plist.IsPlist)
+         {
+           MPlist pl = plist.Plist;
+           MText title = null;
+         
+           if (pl.IsMText)
+             {
+               title = pl.Text;
+               pl = pl.next;
+             }
+           if (! pl.IsSymbol)
+             continue;
+
+           State state = new State (pl.Symbol);
+           state.title = title;
+           if (states == null)
+             states = new MPlist ();
+           states.Add (state.name, state);
+           for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
+             {
+               if (! pl.IsPlist)
+                 continue;
+               MPlist p = pl.Plist;
+               if (! p.IsSymbol)
+                 continue;
+               MSymbol map_name = p.Symbol;
+               p = p.next;
+               transform (p);
+               state.branches.Add (map_name,
+                                   new MExpression (p, local_domain));
+             }
+         }
+    }
+
+    private static object insert (MExpression[] args, MExpression.Domain domain)
+    {
+      ((MInputContext) domain.context).insert (args[0].Val);
+
+      return true;
+    }
+
+    private static object insert_candidates (MExpression[] args,
+                                            MExpression.Domain domain)
+    {
+      ((MInputContext) domain.context).insert_candidates ((MPlist) args[0].Val);
+
+      return true;
+    }
+
+    private static object marker (MExpression[] args, MExpression.Domain domain)
+    {
+      MSymbol sym = (MSymbol) args[0].Args[0].Val;
+
+      return ((MInputContext) domain.context).marker (sym);
+    }
+
+    private static object char_at (MExpression[] args,
+                                  MExpression.Domain domain)
+    {
+      return ((MInputContext) domain.context).char_at ((int) args[0].Val);
+    }
+
+    private static object delete (MExpression[] args, MExpression.Domain domain)
+    {
+      ((MInputContext) domain.context).delete ((int) args[0].Val);
+      return true;
+    }
+
+    private static object select (MExpression[] args, MExpression.Domain domain)
+    {
+      MInputContext ic = (MInputContext) domain.context;
+      object val = args[0].Val;
+
+      if (val is int)
+       ic.select ((int) val);
+      else
+       ic.select ((MSymbol) val);
+      return true;
+    }
+
+    private static object show (MExpression[] args, MExpression.Domain domain)
+    {
+      ((MInputContext) domain.context).show ();
+
+      return true;
+    }
+
+    private static object hide (MExpression[] args, MExpression.Domain domain)
+    {
+      ((MInputContext) domain.context).hide ();
+
+      return true;
+    }
+
+    private static object move (MExpression[] args, MExpression.Domain domain)
+    {
+      ((MInputContext) domain.context).move ((int) args[0].Val);
+
+      return true;
+    }
+
+    private static object mark (MExpression[] args, MExpression.Domain domain)
+    {
+      MSymbol sym = (MSymbol) args[0].Val;
+
+      ((MInputContext) domain.context).mark (sym);
+      return true;
+    }
+
+    private static object keyseq (MExpression[] args, MExpression.Domain domain)
+    {
+      MPlist p = new MPlist ();
+
+      for (int i = 0; i < args.Length; i++)
+       p.Add (MSymbol.symbol, (MSymbol) args[i].Val);
+      return new KeySeq (p);
+    }
+
+    private static object pushback (MExpression[] args,
+                                   MExpression.Domain domain)
+    {
+      MInputContext ic = (MInputContext) domain.context;
+      object val = args[0].Val;
+
+      if (val is int)
+       ic.pushback ((int) val);
+      else if (val is MText)
+       ic.pushback (new KeySeq ((MText) val));
+      else if (val is KeySeq)
+       ic.pushback ((KeySeq) val);
+      else
+       throw new Exception ("Invalid keyseq: " + val);
+      return true;
+    }
+
+    private static object pop (MExpression[] args, MExpression.Domain domain)
+    {
+      ((MInputContext) domain.context).pop ();
+      return true;
+    }
+
+    private static object undo (MExpression[] args, MExpression.Domain domain)
+    {
+      int n = args.Length == 0 ? -2 : (int) args[0].Val;
+      ((MInputContext) domain.context).undo (n);
+      return true;
+    }
+
+    private static object commit (MExpression[] args, MExpression.Domain domain)
+    {
+      ((MInputContext) domain.context).commit ();
+      return true;
+    }
+
+    private static object unhandle (MExpression[] args,
+                                   MExpression.Domain domain)
+    {
+      ((MInputContext) domain.context).commit ();
+      return false;
+    }
+
+    private static object shift (MExpression[] args, MExpression.Domain domain)
+    {
+      MSymbol sym = (MSymbol) args[0].Args[0].Val;
+
+      ((MInputContext) domain.context).shift (sym);
+      return true;
+    }
+
+    private static object call (MExpression[] args, MExpression.Domain domain)
     {
-      for (; ! plist.IsEmpty; plist = plist.next)
-       if (plist.IsPlist)
-         {
-           MPlist pl = plist.Plist;
+      MSymbol module = (MSymbol) args[0].Args[0].Val;
+      MSymbol method = (MSymbol) args[1].Args[0].Val;
+      MPlist arglist = new MPlist ();
 
-           if (! pl.IsSymbol)
-             continue;
-           transform (pl.next);
-           local_domain.Defun (pl.Symbol, new MPlist (), pl.next);
-         }
+      for (int i = 2; i < args.Length; i++)
+       {
+         object val = args[i].Eval (domain);
+
+         if (val is int)
+           arglist.Add (MSymbol.integer, val);
+         else if (val is MSymbol)
+           arglist.Add (MSymbol.symbol, val);
+         else if (val is MText)
+           arglist.Add (MSymbol.mtext, val);
+         else if (val is MPlist)
+           arglist.Add (MSymbol.plist, val);
+         else
+           throw new Exception ("Invalid argument to {0}/{1}: {2}",
+                                module, method, val);
+       }
+      return ((MInputContext) domain.context).call (module, method, arglist);
     }
 
-    private void parse_maps (MPlist plist)
+    public override string ToString ()
     {
-      for (; ! plist.IsEmpty; plist = plist.next)
-       if (plist.IsPlist)
-         {
-           MPlist pl = plist.Plist;
-         
-           if (! pl.IsSymbol)
-             continue;
-           Map map = new Map ();
-           map.name = pl.Symbol;
-           maps[map.name] = map;
-           for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
-             {
-               if (! pl.IsPlist)
-                 continue;
-               MPlist p = pl.Plist;
-               KeySeq keys;
-               if (p.IsMText)
-                 keys = new KeySeq (p.Text);
-               else if (p.IsPlist)
-                 keys = new KeySeq (p.Plist);
-               else
-                 continue;
-               if (keys.keys.Length == 0
-                   && keys.keys[0] == null)
-                 continue;
-               p = p.next;
-               if (p.IsEmpty)
-                 continue;
-               transform (p);
-               MExpression expr = new MExpression (p, local_domain);
-               map.Add (keys, 0, expr);
-             }
-         }
+      string str = (String.Format ("({0} (title \"{1}\")", tag, title));
+      if (commands != null)
+       {
+         str += " (commands";
+         foreach (Command cmd in commands)
+           str += " " + cmd;
+         str += ")";
+       }
+      if (variables != null)
+       {
+         str += " (variables";
+         foreach (Variable var in variables)
+           str += " " + var;
+         str += ")";
+       }
+      if (plugins != null)
+       {
+         str += " (modules";
+         foreach (KeyValuePair<MSymbol, Plugin> kv in plugins)
+           str += " " + kv.Value;
+         str += ")";
+       }
+      str += " (maps";
+      foreach (KeyValuePair<MSymbol, Map> kv in maps)
+       str += " " + kv.Value;
+      str += ") (states";
+      foreach (MPlist p in states)
+       str += " " + p.val;
+      return str + "))";
     }
+  }
 
-    private void parse_states (MPlist plist)
-    {
-      for (; ! plist.IsEmpty; plist = plist.next)
-       if (plist.IsPlist)
-         {
-           MPlist pl = plist.Plist;
-           MText title = null;
-         
-           if (pl.IsMText)
-             {
-               title = pl.Text;
-               pl = pl.next;
-             }
-           if (! pl.IsSymbol)
-             continue;
+  public class MInputContext
+  {
+    internal static MSymbol Mcandidates_group_size = "candidates-group-size";
+    private static MSymbol Mat_less_than = "@<";
+    private static MSymbol Mat_greater_than = "@>";
+    private static MSymbol Mat_minus = "@-";
+    private static MSymbol Mat_plus = "@+";
+    private static MSymbol Mat_open_square_bracket = "@[";
+    private static MSymbol Mat_close_square_bracket = "@]";
 
-           State state = new State ();
-           state.name = pl.Symbol;
-           state.title = title;
-           states[state.name] = state;
-           if (init_state == null)
-             init_state = state;
-           for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
-             {
-               if (! pl.IsPlist)
-                 continue;
-               MPlist p = pl.Plist;
-               if (! p.IsSymbol)
-                 continue;
-               MSymbol map_name = p.Symbol;
-               p = p.next;
-               transform (p);
-               state.branches[map_name] = new MExpression (p, local_domain);
-             }
-         }
-    }
+    public MInputMethod im;
+    private MText produced;
+    private bool active;
+    private MText status;
+    private bool status_changed;
+    private MText preedit;
+    private bool preedit_changed;
+    private int cursor_pos;
+    private bool cursor_pos_changed;
+    private Candidates candidates;
+    private MPlist candidate_group;
+    private int candidate_index;
+    private int candidate_from, candidate_to;
+    private bool candidate_show;
+    private bool candidate_changed;
+
+    private Stack<MInputMethod.State> states;
+    internal MInputMethod.KeySeq keys;
+    private int key_head;
+    private int state_key_head;
+    private MPlist state_boundary;
+    private int commit_key_head;
+    private MText state_preedit;
+    private int state_pos;
+    internal MPlist markers = new MPlist ();
+    private MPlist vars;
+    private MPlist vars_saved;
+    internal MText preceding_text = new MText ();
+    internal MText following_text = new MText ();
+    private bool key_unhandled;
+
+    internal MExpression.Domain domain;
 
-    public static MInputMethod Get (MSymbol language, MSymbol name,
-                                   MSymbol extra)
+    public MInputContext (MInputMethod im)
     {
-      MDatabase.Tag tag
-       = new MDatabase.Tag (Minput_method, language, name, extra);
-      MInputMethod im;
-      if (im_table.TryGetValue (tag, out im))
-       return im;
-      try {
-       im = new MInputMethod (tag);
-      } catch (Exception e) {
-       Console.WriteLine (e);
-       im = null;
-      }
-      return im;
+      this.im = im;
+      domain = new MExpression.Domain (im.local_domain, this);
+      states = new Stack<MInputMethod.State> ();
+      states.Push ((MInputMethod.State) im.states.val);
+      keys = new MInputMethod.KeySeq ();
     }
 
-    private static void adjust_markers (MInputContext ic,
-                                       int from, int to, object inserted)
+    private void adjust_markers (int from, int to, object inserted)
     {
       int ins = (inserted == null ? 0
                 : inserted is int ? 1
                 : ((MText) inserted).Length);
       int diff = ins - (to - from);
 
-      for (MPlist plist = ic.markers; ! plist.IsEmpty; plist = plist.next)
+      for (MPlist plist = markers; ! plist.IsEmpty; plist = plist.next)
        {
          int pos = plist.Integer;
          if (pos > from)
@@ -598,43 +1600,36 @@ namespace M17N.Input
                plist.val = from;
            }
        }
-      if (ic.cursor_pos >= to)
-       ic.cursor_pos += diff;
-      else if (ic.cursor_pos > from)
-       ic.cursor_pos = from;
+      if (cursor_pos >= to)
+       cursor_pos += diff;
+      else if (cursor_pos > from)
+       cursor_pos = from;
     }
 
-    private static void preedit_replace (MInputContext ic,
-                                        int from, int to, int c)
+    private void preedit_replace (int from, int to, int c)
     {
-      ic.preedit.Del (from, to);
-      ic.preedit.Ins (from, c);
-      adjust_markers (ic, from, to, c);
+      preedit.Del (from, to);
+      preedit.Ins (from, c);
+      adjust_markers (from, to, c);
     }
 
-    private static void preedit_replace (MInputContext ic,
-                                        int from, int to, MText mt)
+    private void preedit_replace (int from, int to, MText mt)
     {
-      ic.preedit[from, to] = mt;
-      adjust_markers (ic, from, to, mt);
+      preedit[from, to] = mt;
+      adjust_markers (from, to, mt);
     }
 
-    private static object insert (MExpression[] args,
-                                 MExpression.Domain domain)
+    internal void insert (object arg)
     {
-      MInputContext ic = (MInputContext) domain.context;
-      object arg = args[0].Val;
-
       if (arg is int)
-       preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (int) arg);
+       preedit_replace (cursor_pos, cursor_pos, (int) arg);
       else
-       preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (MText) arg);
-      ic.preedit_changed = true;
-      ic.cursor_pos_changed = true;
-      return 1;
+       preedit_replace (cursor_pos, cursor_pos, (MText) arg);
+      preedit_changed = true;
+      cursor_pos_changed = true;
     }
 
-    internal class Candidates
+    private class Candidates
     {
       private class Block
       {
@@ -721,7 +1716,7 @@ namespace M17N.Input
 
       public static void Detach (MInputContext ic)
       {
-       ic.preedit.PopProp (0, ic.preedit.Length, Mcandidates);
+       ic.preedit.PopProp (0, ic.preedit.Length, MInputMethod.Mcandidates);
        ic.candidates = null;
        ic.preedit_changed = true;
        ic.cursor_pos_changed = true;
@@ -859,44 +1854,32 @@ namespace M17N.Input
          col = maxcol;
        index = index - Column + col;
       }
+    }
 
-      public void Update (MInputContext ic)
-      {
-       int from, to;
-
-       if (ic.candidates == null)
-         {
-           from = ic.cursor_pos;
-           to = ic.cursor_pos;
-           ic.candidates = this;
-         }
-       else
-         {
-           from = ic.candidate_from;
-           to = ic.candidate_to;
-         }
-
-       object candidate = ic.candidates.Current;
+    private void update_candidate ()
+    {
+      object candidate = candidates.Current;
 
-       if (candidate is MText)
-         preedit_replace (ic, from, to, (MText) candidate);
-       else
-         preedit_replace (ic, from, to, (int) candidate);
-       ic.preedit.PushProp (from, to, Mcandidates, this);
-       ic.cursor_pos = to;
-       ic.candidate_from = from;
-       ic.candidate_to = to;
-       ic.preedit_changed = true;
-       ic.cursor_pos_changed = true;
-       ic.candidate_changed = true;
-      }
+      if (candidate is MText)
+       {
+         preedit_replace (candidate_from, candidate_to, (MText) candidate);
+         candidate_to = candidate_from + ((MText) candidate).Length;
+       }
+      else
+       {
+         preedit_replace (candidate_from, candidate_to, (int) candidate);
+         candidate_to = candidate_from + 1;
+       }
+      preedit.PushProp (candidate_from, candidate_to,
+                       MInputMethod.Mcandidates, this);
+      cursor_pos = candidate_from;
+      preedit_changed = true;
+      cursor_pos_changed = true;
+      candidate_changed = true;
     }
 
-    private static object insert_candidates (MExpression[] args,
-                                            MExpression.Domain domain)
+    internal void insert_candidates (MPlist list)
     {
-      MInputContext ic = (MInputContext) domain.context;
-      MPlist list = (MPlist) args[0].Val;
       int column = 0;
 
       if (domain.IsBound (Mcandidates_group_size))
@@ -905,54 +1888,70 @@ namespace M17N.Input
          if (val is int)
            column = (int) val;
        }
-      Candidates candidates = new Candidates (list, column);
-      candidates.Update (ic);
-      return 1;
+      candidates = new Candidates (list, column);
+      candidate_from = candidate_to = cursor_pos;
+      update_candidate ();
     }
 
-    private static object state (MExpression[] args,
-                                MExpression.Domain domain)
+    internal void select (int n)
     {
-      MInputContext ic = (MInputContext) domain.context;
-      MSymbol sym = (MSymbol) args[0].Args[0].Val;
+      if (candidates != null)
+       {
+         candidates.Select (n);
+         update_candidate ();
+       }
+    }
 
-      if (sym == MSymbol.t)
-       return MSymbol.t;
-      return ic.im.states[sym];
+    internal void select (MSymbol sym)
+    {
+      if (candidates != null)
+       {
+         if (sym == Mat_less_than)
+           candidates.First ();
+         else if (sym == Mat_greater_than)
+           candidates.Last ();
+         else if (sym == Mat_minus)
+           candidates.Prev ();
+         else if (sym == Mat_plus)
+           candidates.Next ();
+         else if (sym == Mat_open_square_bracket)
+           candidates.PrevGroup ();
+         else if (sym == Mat_close_square_bracket)
+           candidates.NextGroup ();
+       }
     }
 
-    private static object marker (MExpression[] args,
-                                 MExpression.Domain domain)
+    internal int marker (MSymbol sym)
     {
-      MInputContext ic = (MInputContext) domain.context;
-      MSymbol sym = (MSymbol) args[0].Args[0].Val;
-      int pos = ic.cursor_pos;
+      int pos = cursor_pos;
 
       if (sym.Name.Length == 2 && sym.Name[0] == '@')
        {
          switch (sym.Name[0])
            {
            case '<': pos = 0; break;
-           case '>': pos = ic.preedit.Length; break;
-           case '-': pos = ic.cursor_pos - 1; break;
-           case '+': pos = ic.cursor_pos + 1; break;
+           case '>': pos = preedit.Length; break;
+           case '-': pos = cursor_pos - 1; break;
+           case '+': pos = cursor_pos + 1; break;
            case '[':
              if (pos > 0)
                {
                  int to;
-                 ic.preedit.FindProp (Mcandidates, pos - 1, out pos, out to);
+                 preedit.FindProp (MInputMethod.Mcandidates, pos - 1,
+                                   out pos, out to);
                }
              else
                pos = 0;
              break;
            case ']':
-             if (ic.cursor_pos < ic.preedit.Length - 1)
+             if (cursor_pos < preedit.Length - 1)
                {
                  int from;
-                 ic.preedit.FindProp (Mcandidates, pos, out from, out pos);
+                 preedit.FindProp (MInputMethod.Mcandidates, pos,
+                                   out from, out pos);
                }
              else
-               pos = ic.preedit.Length;
+               pos = preedit.Length;
              break;
            default:
              if (sym.Name[0] >= '0' && sym.Name[0] <= '9')
@@ -962,12 +1961,11 @@ namespace M17N.Input
        }
       else if (sym.Name.Length >= 3 && sym.Name[0] == '@')
        {
-         
-
+         pos = int.Parse (sym.Name.Substring (2));
        }
       else
        {
-         object val = ic.markers.Get (sym);
+         object val = markers.Get (sym);
 
          if (val is int)
            pos = (int) val;
@@ -975,130 +1973,231 @@ namespace M17N.Input
       return pos;
     }
 
-    private static object char_at (MExpression[] args,
-                                  MExpression.Domain domain)
+    internal int char_at (int pos)
     {
-      MInputContext ic = (MInputContext) domain.context;
-      MSymbol sym = (MSymbol) args[0].Args[0].Val;
+      int c;
+
+      pos += cursor_pos;
+      if (pos < 0)
+       {
+         if (preceding_text.Length < -pos)
+           {
+             MPlist plist = new MPlist ();
+             plist.Push (MSymbol.integer, pos);
+             if (MInputMethod.GetSurroundingText != null
+                 && MInputMethod.GetSurroundingText (this, plist)
+                 && plist.IsMText
+                 && preceding_text.Length < plist.Text.Length)
+               preceding_text = plist.Text;
+           }
+         c = (-pos < preceding_text.Length
+              ? preceding_text[preceding_text.Length + pos] : -1);
+       }
+      else if (pos >= 0 && pos < preedit.Length)
+       c = preedit[pos];
+      else
+       {
+         pos -= preedit.Length;
+         if (pos >= following_text.Length)
+           {
+             MPlist plist = new MPlist ();
+             plist.Push (MSymbol.integer, pos + 1);
+             if (MInputMethod.GetSurroundingText != null
+                 && MInputMethod.GetSurroundingText (this, plist)
+                 && plist.IsMText
+                 && following_text.Length < plist.Text.Length)
+               following_text = plist.Text;
+           }
+         c = (pos < following_text.Length ? following_text[pos] : -1);
+       }
+      return c;
+    }
 
-      if (
+    internal void delete (int pos)
+    {
+      if (pos < cursor_pos)
+       preedit_replace (pos, cursor_pos, null);
+      else
+       preedit_replace (cursor_pos, pos, null);
+      preedit_changed = true;
+      cursor_pos_changed = true;
+    }
 
+    internal void show ()
+    {
+      candidate_show = true;
+      candidate_changed = true;
+    }
 
-      return 0;
+    internal void hide ()
+    {
+      candidate_show = false;
+      candidate_changed = true;
     }
 
-    private static object delete (MExpression[] args,
-                                 MExpression.Domain domain)
+    internal void move (int pos)
     {
-      MInputContext ic = (MInputContext) domain.context;
-      int pos = (int) args[0].Val;
+      if (pos < 0)
+       pos = 0;
+      else if (pos > preedit.Length)
+       pos = preedit.Length;
+      if (pos != cursor_pos)
+       {
+         cursor_pos = pos;
+         preedit_changed = true;
+       }
+    }
 
-      if (pos < ic.cursor_pos)
-       preedit_replace (ic, pos, ic.cursor_pos, null);
+    internal void mark (MSymbol sym)
+    {
+      MPlist slot = markers.Find (sym);
+
+      if (slot == null)
+       markers.Push (sym, cursor_pos);
       else
-       preedit_replace (ic, ic.cursor_pos, pos, null);
-      ic.preedit_changed = true;
-      ic.cursor_pos_changed = true;
-      return true;
+       slot.val = cursor_pos;
     }
 
-    private static object select (MExpression[] args,
-                                 MExpression.Domain domain)
+    internal void pushback (int n)
     {
-      MInputContext ic = (MInputContext) domain.context;
-      object arg = args[0].Val;
-
-      if (ic.candidates == null)
-       return 0;
-      if (arg is MSymbol)
+      if (n > 0)
+       {
+         key_head -= n;
+         if (key_head < 0)
+           key_head = 0;
+       }
+      else if (n == 0)
+       key_head = 0;
+      else
        {
-         MSymbol sym = (MSymbol) arg;
+         key_head = - n;
+         if (key_head > keys.Count)
+           key_head = keys.Count;
+       }
+    }
 
-         if (sym == Mat_less_than)
-           ic.candidates.First ();
-         else if (sym == Mat_greater_than)
-           ic.candidates.Last ();
-         else if (sym == Mat_minus)
-           ic.candidates.Prev ();
-         else if (sym == Mat_plus)
-           ic.candidates.Next ();
-         else if (sym == Mat_open_square_bracket)
-           ic.candidates.PrevGroup ();
-         else if (sym == Mat_close_square_bracket)
-           ic.candidates.NextGroup ();
+    internal void pushback (MInputMethod.KeySeq keyseq)
+    {
+      if (key_head > 0)
+       key_head--;
+      if (key_head < keys.Count)
+       keys.RemoveRange (key_head, keys.Count - key_head);
+      for (int i = 0; i < keyseq.Count; i++)
+       keys.Add (keyseq[i]);
+    }
+
+    internal void pop ()
+    {
+      if (key_head < keys.Count)
+       keys.RemoveRange (key_head, 1);
+    }
+
+    internal void undo (int n)
+    {
+      if (n < 0)
+       keys.RemoveRange (keys.Count + n, - n);
+      else
+       keys.RemoveRange (n, keys.Count  - n);
+      reset ();
+    }
+
+    internal void commit ()
+    {
+      produced.Cat (preedit);
+      preedit.Del ();
+      preedit_changed = true;
+    }
+
+    internal void shift (MSymbol sym)
+    {
+      MInputMethod.State state;
+
+      if (sym == MSymbol.t)
+       {
+         if (states.Count > 1)
+           state = states.Pop ();
          else
-           return 0;
+           state = states.Peek ();
+       }
+      else
+       {
+         state = (MInputMethod.State) im.states.Get (sym);
+         if (state == null)
+           throw new Exception ("Unknown state: " + state.name);
+       }
+      if (state == null)
+       state = states.Pop ();
+      if (state == (MInputMethod.State) im.states.val)
+       {
+         commit ();
+         reset ();
+       }
+      else
+       {
+         state_key_head = key_head;
+         state_pos = cursor_pos;
+         state_preedit = preedit.Dup ();
+         if (state != states.Peek ())
+           {
+             states.Push (state);
+             state_boundary = domain.SetBoundary ();
+             status = state.title;
+             if (status == null)
+               status = im.title;
+             status_changed = true;
+             MExpression on_entry
+               = (MExpression) state.branches.Get (MSymbol.t);
+             if (on_entry != null)
+               on_entry.Eval (domain);
+           }
        }
-      else if (arg is int)
-       ic.candidates.Select ((int) arg);
-      ic.candidates.Update (ic);
-      return 0;
     }
 
-    private static object show (MExpression[] args, MExpression.Domain domain)
-    { return 1; }
-    private static object hide (MExpression[] args, MExpression.Domain domain)
-    { return 1; }
-    private static object move (MExpression[] args, MExpression.Domain domain)
-    { return 1; }
-    private static object mark (MExpression[] args, MExpression.Domain domain)
-    { return 1; }
-    private static object pushback (MExpression[] args, MExpression.Domain domain)
-    { return 1; }
-    private static object pop (MExpression[] args, MExpression.Domain domain)
-    { return 1; }
-    private static object undo (MExpression[] args, MExpression.Domain domain)
-    { return 1; }
-    private static object commit (MExpression[] args, MExpression.Domain domain)
-    { return 1; }
-    private static object unhandle (MExpression[] args, MExpression.Domain domain)
-    { return 1; }
-    private static object shift (MExpression[] args, MExpression.Domain domain)
-    { return 1; }
-    private static object call (MExpression[] args, MExpression.Domain domain)
-    { return 1; }
-  }
+    internal bool call (MSymbol module, MSymbol method, MPlist arglist)
+    {
+      MInputMethod.Plugin plugin;
 
-  public class MInputContext
-  {
-    public MInputMethod im;
-    public MText produced;
-    public bool active;
-    public MText status;
-    public bool status_changed;
-    public MText preedit;
-    public bool preedit_changed;
-    public int cursor_pos;
-    public bool cursor_pos_changed;
-    internal MInputMethod.Candidates candidates;
-    public MPlist candidate_group;
-    public int candidate_index;
-    public int candidate_from, candidate_to;
-    public bool candidate_show;
-    public bool candidate_changed;
-    public MPlist callback_args;
-
-    private MInputMethod.State state;
-    private MInputMethod.State prev_state;
-    private Stack<MSymbol> keys;
-    private int state_key_head;
-    private int key_head;
-    private int commit_key_head;
-    private MText preedit_saved;
-    private int state_pos;
-    internal MPlist markers = new MPlist ();
-    private MPlist vars;
-    private MPlist vars_saved;
-    private MText preceding_text;
-    private MText following_text;
-    private bool key_unhandled;
+      if (! im.plugins.TryGetValue (module, out plugin))
+       return false;
+      if (plugin.assembly == null)
+       {
+         Assembly assembly;
 
-    internal MExpression.Domain domain;
+         try {
+           assembly = Assembly.LoadFrom (module.Name + ".dll");
+         } catch {
+           return false;
+         }
+         Type t = assembly.GetType ("Plugin");
+         for (MPlist p = plugin.methods; ! p.IsEmpty; p = p.next)
+           p.Set (p.key, t.GetMethod (p.key.Name));
+       }
 
-    public MInputContext (MInputMethod im)
+      MethodInfo method_info = (MethodInfo) plugin.methods.Get (method);
+      if (method_info == null)
+       return false;
+      object[] method_arg = new object[1];
+      method_arg[0] = arglist;
+      bool result = (bool) method_info.Invoke (null, method_arg);
+      if (! result)
+       return false;
+      if (! arglist.IsEmpty)
+       (new MExpression (arglist, domain)).Eval (domain);
+      return true;
+    }
+
+    internal void reset ()
     {
-      this.im = im;
-      domain = new MExpression.Domain (im.local_domain, this);
+      preedit.Del ();
+      state_preedit.Del ();
+      produced.Del ();
+      markers.Clear ();
+      cursor_pos = 0;
+      key_head = commit_key_head = 0;
+      states.Clear ();
+      states.Push ((MInputMethod.State) im.states.Val);
+      state_key_head = 0;
+      state_pos = 0;
     }
 
     internal object GetCandidates (out int column)
@@ -1106,13 +2205,20 @@ namespace M17N.Input
       column = 0;
       if (cursor_pos == 0)
        return null;
-      MInputMethod.Candidates candidates
-       = (MInputMethod.Candidates) preedit.GetProp (cursor_pos - 1,
-                                                    MInputMethod.Mcandidates);
+      Candidates candidates
+       = (Candidates) preedit.GetProp (cursor_pos - 1,
+                                       MInputMethod.Mcandidates);
       if (candidates == null)
        return null;
       column = candidates.Column;
       return candidates.Current;
     }
+
+    internal void HandleKey ()
+    {
+      MInputMethod.State state = states.Peek ();
+
+      
+    }
   }
 }
index ab948f4..35cafd8 100644 (file)
--- a/MPlist.cs
+++ b/MPlist.cs
@@ -54,6 +54,34 @@ namespace M17N.Core
          next = new MPlist ();
       }
 
+    public MPlist (FileStream stream, MSymbol stop)
+      {
+       MStreamReader reader = new MStreamReader (stream);
+       bool result;
+
+       key = MSymbol.nil;
+       val = null;
+       while (true)
+         {
+           MSymbol this_key;
+           object this_val;
+
+           result = reader.ReadElement (out this_key, out this_val);
+           if (! result)
+             return;
+           if (this_key == MSymbol.plist)
+             {
+               MPlist plist = (MPlist) this_val;
+
+               if (plist.IsSymbol && plist.Symbol == stop)
+                 return;
+             }
+           key = this_key;
+           val = this_val;
+           next = new MPlist (reader, stop);
+         }
+      }
+
     public MPlist (FileStream stream, MSymbol target, MSymbol stop)
       {
        MStreamReader reader = new MStreamReader (stream);
@@ -107,6 +135,44 @@ namespace M17N.Core
          next = new MPlist ();
       }
 
+    private MPlist (MStreamReader reader, MSymbol stop)
+      {
+       bool result;
+       MPlist next_plist = null;
+
+       key = MSymbol.nil;
+       val = null;
+       while (true)
+         {
+           MSymbol this_key;
+           object this_val;
+
+           result = reader.ReadElement (out this_key, out this_val);
+           if (! result)
+             return;
+           if (this_key == MSymbol.plist)
+             {
+               MPlist plist = (MPlist) this_val;
+
+               if (plist.IsSymbol && plist.Symbol == stop)
+                 return;
+             }
+           if (next_plist == null)
+             {
+               key = this_key;
+               val = this_val;
+               next = next_plist = new MPlist ();
+             }
+           else
+             {
+               next_plist.key = this_key;
+               next_plist.val = this_val;
+               next_plist.next = new MPlist ();
+               next_plist = next_plist.next;
+             }
+         }
+      }
+
     private MPlist (MStreamReader reader, MSymbol target, MSymbol stop)
       {
        bool result;
index 39f9958..c687a0f 100644 (file)
@@ -26,6 +26,16 @@ namespace M17N.Core
       Name = name;
     }
 
+    public static implicit operator MSymbol (string name)
+    {
+      return Of (name);
+    }
+
+    public static explicit operator string (MSymbol sym)
+    {
+      return sym.Name;
+    }
+
     public static MSymbol Of (string name)
     {
       lock (pool)
index a48b556..b281808 100644 (file)
--- a/MText.cs
+++ b/MText.cs
@@ -227,6 +227,16 @@ namespace M17N.Core
 
     public override string ToString () { return sb.ToString (); }
 
+    public static implicit operator MText (string str)
+    {
+      return new MText (str);
+    }
+
+    public static explicit operator string (MText mt)
+    {
+      return mt.ToString ();
+    }
+
     private static int inc_idx (StringBuilder sb, int i)
     {
       return (i + (Char.IsHighSurrogate (sb[i]) ? 2 : 1));
@@ -462,6 +472,11 @@ namespace M17N.Core
       return this;
     }
 
+    public MText Del ()
+    {
+      return Del (0, Length);
+    }
+
     public MText Del (int from, int to)
     {
       if (check_range (from, to, true))
index 35875fd..d4ffc59 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,7 @@ M17N.dll: ${M17N_SRC}
        $(RUNCS) -out:$@ -t:library ${M17N_SRC}
 
 M17NCore.dll: M17N.dll ${CORE_SRC}
-       $(RUNCS) -out:$@ -t:library -r:M17N.dll ${CORE_SRC}
+       $(RUNCS) -out:$@ -t:library -r:M17N.dll -r:Mvp.Xml-bin-2.0/Mvp.Xml.dll ${CORE_SRC}
 
 M17NExpr.dll: M17N.dll M17NCore.dll ${EXPR_SRC}
        $(RUNCS) -out:$@ -t:library -r:M17N.dll -r:M17NCore.dll ${EXPR_SRC}
@@ -38,7 +38,7 @@ expr.exe: expr.cs
        $(RUNCS) -r:M17N.dll -r:M17NCore $<
 
 clean:
-       rm -f *.dll *.exe
+       rm -f $(DLL) *.exe
 
 temp.exe: temp.cs
        $(CS) temp.cs
index 6eb42d0..ba46189 100644 (file)
@@ -6,7 +6,7 @@ using System.Xml;
 
 namespace System.Xml.Expression
 {
-  public class XmlName : IEquatable<XmlName>
+public class XmlName : IEquatable<XmlName>
   {
     private static NameTable nt = new NameTable ();
 
@@ -22,14 +22,14 @@ namespace System.Xml.Expression
       return new XmlName (str);
     }
 
-    public static bool operator== (object obj, XmlName nam)
+    public static bool operator== (XmlName n1, XmlName n2)
       {
-       return obj == nam.name;
+       return n1.name == n2.name;
       }
 
-    public static bool operator!= (object obj, XmlName nam)
+    public static bool operator!= (XmlName n1, XmlName n2)
       {
-       return obj != nam.name;
+       return n1.name != n2.name;
       }
 
     public bool Equals (XmlName other) { return name == other.name; }
index c4305d7..4871598 100644 (file)
--- a/input.cs
+++ b/input.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using System.Xml;
 using M17N;
 using M17N.Core;
 using M17N.Input;
@@ -9,11 +10,40 @@ public class Test
 {
   public static void Main()
   {
-    MDatabase.ApplicationDir = "/usr/local/share/m17n";
-    MInputMethod im = MInputMethod.Get (MSymbol.Of ("vi"),
-                                       MSymbol.Of ("telex"),
-                                       MSymbol.nil);
-    Console.WriteLine (im);
+    //M17n.debug = true;
+    MDatabase.ApplicationDir = "/usr/local/share/m17n-xml";
+    MText desc, title;
+    MInputMethod.Variable[] vars;
+    MInputMethod.Command[] cmds;
+
+#if false
+    MDatabase.Tag tag = new MDatabase.Tag ("input-method",
+                                          "t", "nil", "vi-base");
+    MDatabase mdb = MDatabase.Find (tag);
+    XmlNode node = mdb.Load ("map-alnum", "input-method", "map-list", "map");
+
+    Console.WriteLine (node.OuterXml);
+
+#elif true
+    MInputMethod im = MInputMethod.Find ("vi", "telex");
+
+    if (im.Info (out desc, out title, out vars, out cmds))
+      {
+       Console.WriteLine ("{0}, {1}, {2}, {3}", desc, title, vars, cmds);
+       im.Open ();
+      }
+#else
+    MInputMethod[] ims = MInputMethod.List ();
+    foreach (MInputMethod im in ims)
+      {
+
+       Console.Write ("(<{0}, {1}, {2}>", im.Language, im.Name, im.SubName);
+       if (im.Info (out desc, out title, out vars, out cmds))
+         Console.Write (" {0}, {1}, {2}, {3}", desc, title, vars, cmds);
+       if (! im.Open ())
+         Console.Write (" open fail");
+       Console.WriteLine (")");
+      }
+#endif
   }
 }
-
index 59c3245..bc8dad5 100644 (file)
--- a/mtext.cs
+++ b/mtext.cs
@@ -27,5 +27,13 @@ public class Test
     Console.WriteLine (mt);
     mt[1,2] = null;            // == mt.Del (1, 2)
     Console.WriteLine (mt);
+    // explicit casting (MText -> string)
+    string str2 = " " + (string) mt + (string) ((MText) "abc").Cat ('a');
+    Console.WriteLine (str2);
+    // implicit casting (string -> MText)
+    mt2 = "abc";
+    mt2.Cat ("def");
+    mt2 += "ghi";
+    Console.WriteLine (mt2);
   }
 }
index d6b8b18..4f0336d 100644 (file)
--- a/symbol.cs
+++ b/symbol.cs
@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Reflection;
 using M17N;
 using M17N.Core;