*** empty log message ***
authorhanda <handa>
Mon, 3 Aug 2009 02:23:22 +0000 (02:23 +0000)
committerhanda <handa>
Mon, 3 Aug 2009 02:23:22 +0000 (02:23 +0000)
MExpression.cs
MInputMethod.cs
MPlist.cs
MText.cs
Makefile
mtext.cs
plist.cs

index 959ea1d..560a6b1 100644 (file)
@@ -138,10 +138,13 @@ namespace M17N.Core
       {
        Console.Write ("(" + func.name);
        indent += "  ";
-       foreach (MExpression o in args)
+       foreach (object o in args)
          {
            Console.Write (" ");
-           o.pp (indent);
+           if (o is MExpression)
+             ((MExpression) o).pp (indent);
+           else
+             Console.Write (o);
          }
        Console.Write (")");
       }
@@ -542,7 +545,7 @@ namespace M17N.Core
        Console.Write (")");
       }
 
-      private static object define_function (object[] args, MPlist bindings)
+      public static object define_function (object[] args, MPlist bindings)
       {
        FunctionTable table = (FunctionTable) args[0];
        MSymbol sym = (MSymbol) args[1];
@@ -584,8 +587,30 @@ namespace M17N.Core
 
     public class FunctionTable
     {
-      internal Dictionary<MSymbol, Function> table
-       = new Dictionary<MSymbol, Function> ();
+      internal Dictionary<MSymbol, Function> table;
+
+      public FunctionTable ()
+      {
+       table = new Dictionary<MSymbol, Function> ();
+      }
+
+      public FunctionTable (FunctionTable table)
+      {
+       this.table = new Dictionary<MSymbol, Function> (table.table);
+      }
+
+      public void Copy (FunctionTable table)
+      {
+       foreach (KeyValuePair<MSymbol, Function> kv in this.table)
+         table.table[kv.Key] = kv.Value;
+      }
+
+      public void Copy (MSymbol name, FunctionTable table)
+      {
+       Function func;
+       if (this.table.TryGetValue (name, out func))
+         table.table[name] = func;
+      }
     }
 
     private static FunctionTable basic_table = new FunctionTable ();
@@ -598,19 +623,30 @@ namespace M17N.Core
       table.table[func.name] = func;
     }
 
+    public static void Defmacro (FunctionTable table, MSymbol name,
+                                MExpression expr)
+    {
+      object[] args = new object[4];
+      args[0] = table;
+      args[1] = name;
+      args[2] = new MPlist ();
+      args[3] = expr;
+      Function.define_function (args, null);
+    }
+
     private static Function Defun (string name, Evaluator evaluator,
                                   int min_arg, int max_arg,
                                   params Type[] arg_types)
     {
       MSymbol sym = MSymbol.Of (name);
       Function func = new Function (sym, evaluator, min_arg, max_arg,
-                                     arg_types);
+                                   arg_types);
       basic_table.table[sym] = func;
       return func;
     }
 
     private static Function Defun (string name, Evaluator evaluator,
-                                   int min_arg, int max_arg)
+                                  int min_arg, int max_arg)
     {
       return Defun (name, evaluator, min_arg, max_arg, typeof (MExpression));
     }
index cfd6d7f..ee0bfae 100644 (file)
@@ -10,54 +10,984 @@ namespace M17N.Input
 {
   public class MInputMethod
   {
-    private class MInputAction
+    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 ("@]");
+
+
+    internal class Variable
     {
-      MExpression[] expr_list;
+      public MSymbol name;
+      public MText description;
+      public Type type;
+      public object value;
+      public MPlist candidates;
     }
 
-    private class MInputMethodMap
+    internal class Command
     {
       public MSymbol name;
-      private MSymbol[] keys;
-      private MInputAction[] actions;
+      public MText description;
+      public MSymbol[][] keys;
+    }
+
+    internal class KeySeq
+    {
+      public MSymbol[] keys;
+
+      private static MSymbol char_to_symbol (int c)
+      {
+       return MSymbol.Of (String.Format ("#%X", c));
+      }
+
+      public KeySeq (MPlist plist)
+      {
+       keys = new MSymbol[plist.Count];
+       int i = 0;
+       foreach (MPlist p in plist)
+         {
+           if (p.IsSymbol)
+             keys[i++] = p.Symbol;
+           else if (p.IsInteger)
+             keys[i++] = char_to_symbol (p.Integer);
+           else
+             keys[i] = null;
+         }
+      }
+
+      public KeySeq (MText mt)
+      {
+       keys = new MSymbol[mt.Length];
+       for (int i = 0; i < mt.Length; i++)
+         keys[i] = char_to_symbol (mt[i]);
+      }
+
+      public MSymbol this[int i] { get { return keys[i]; } }
+
+      public int Length { get { return keys.Length; } }
     }
 
-    private class MInputMethodBranch
+    internal class Map
     {
       public MSymbol name;
-      private MInputAction[] actions;
+      public Dictionary<MSymbol, Map> submaps;
+      public MExpression actions;
+
+      public void Add (KeySeq keys, int index, MExpression actions)
+      {
+       Map sub = null;
+
+       if (submaps == null)
+         submaps = new Dictionary<MSymbol, Map> ();
+       else
+         submaps.TryGetValue (keys[index], out sub);
+       if (sub == null)
+         submaps[keys[index]] = sub = new Map ();
+       if (index + 1 < keys.Length)
+         sub.Add (keys, index + 1, actions);
+       else
+         this.actions = actions;
+      }
+
+      public MExpression Lookup (KeySeq keys, int index)
+      {
+       Map sub;
+
+       if (index + 1 == keys.Length)
+         return actions;
+       if (submaps.TryGetValue (keys[index], out sub))
+         return sub.Lookup (keys, index + 1);
+       return null;
+      }
     }
 
-    private class MInputMethodState
+    internal class State
     {
-      public MText title;
       public MSymbol name;
-      public MInputMethodBranch[] branches;
+      public MText title;
+      public Dictionary<MSymbol, MExpression> branches
+       = new Dictionary<MSymbol, MExpression> ();
     }
 
+    private static Dictionary<MDatabase.Tag, MInputMethod> im_table;
+
+    private static MExpression.FunctionTable global_table
+      = new MExpression.FunctionTable ();
+
     public readonly MSymbol language;
     public readonly MSymbol name;
     public readonly MSymbol subname;
 
-    private MDatabase mdb;
-    private MText description;
-    private MText title;
-    private MPlist commands;
-    private MPlist variables;
-    private MPlist maps;
-    private MPlist states;
-    private MPlist macros;
-    private MPlist externals;
+    internal MExpression.FunctionTable local_table
+      = new MExpression.FunctionTable (global_table);
+    internal MDatabase mdb;
+    internal MText description;
+    internal MText title;
+    internal Command[] commands;
+    internal Variable[] variables;
+    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;
 
-    public MInputMethod (MSymbol language, MSymbol name, MSymbol extra)
+    static MInputMethod ()
     {
-      MDatabase.Tag tag = new MDatabase.Tag (language, name, extra);
+      MExpression.Defun (global_table, "insert",
+                        new MExpression.Evaluator (insert),
+                        2, 2, typeof (MInputContext), typeof (MExpression));
+      MExpression.Defun (global_table, "candidates",
+                        new MExpression.Evaluator (insert_candidates),
+                        2, 2, typeof (MInputContext), typeof (object));
+      MExpression.Defun (global_table, "delete",
+                        new MExpression.Evaluator (delete),
+                        2, 2, typeof (MInputContext), typeof (object));
+      MExpression.Defun (global_table, "select",
+                        new MExpression.Evaluator (select),
+                        2, 2, typeof (MInputContext), typeof (object));
+      MExpression.Defun (global_table, "show",
+                        new MExpression.Evaluator (show),
+                        1, 1, typeof (MInputContext));
+      MExpression.Defun (global_table, "hide",
+                        new MExpression.Evaluator (hide),
+                        1, 1, typeof (MInputContext));
+      MExpression.Defun (global_table, "move",
+                        new MExpression.Evaluator (move),
+                        2, 2, typeof (MInputContext), typeof (object));
+      MExpression.Defun (global_table, "mark",
+                        new MExpression.Evaluator (mark),
+                        2, 2, typeof (MInputContext), typeof (MSymbol));
+      MExpression.Defun (global_table, "pushback",
+                        new MExpression.Evaluator (pushback),
+                        2, 2, typeof (MInputContext), typeof (object));
+      MExpression.Defun (global_table, "pop",
+                        new MExpression.Evaluator (pop),
+                        1, 1, typeof (MInputContext));
+      MExpression.Defun (global_table, "undo",
+                        new MExpression.Evaluator (undo),
+                        2, 2, typeof (MInputContext), typeof (object));
+      MExpression.Defun (global_table, "commit",
+                        new MExpression.Evaluator (commit),
+                        1, 1, typeof (MInputContext));
+      MExpression.Defun (global_table, "unhandle",
+                        new MExpression.Evaluator (unhandle),
+                        1, 1, typeof (MInputContext));
+      MExpression.Defun (global_table, "shift",
+                        new MExpression.Evaluator (shift),
+                        2, 2, typeof (MInputContext), typeof (MSymbol));
+      MExpression.Defun (global_table, "call",
+                        new MExpression.Evaluator (call),
+                        3, -1, typeof (MInputContext), typeof (MSymbol),
+                        typeof (MSymbol), typeof (object));
+    }
 
+    private MInputMethod (MDatabase.Tag tag)
+    {
       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];
       MPlist plist = (MPlist) mdb.Load ();
+      if (plist == null)
+       return;
+      for (; ! plist.IsEmpty; plist = plist.next)
+       if (plist.IsPlist)
+         {
+           MPlist pl = plist.Plist;
+           if (pl.IsSymbol)
+             {
+               MSymbol sym = pl.Symbol;
+               pl = pl.next;
+               if (sym == Mdescription)
+                 {
+                   description = parse_description (pl);
+                   if (description == null)
+                     description = new MText ("No description");
+                 }
+               else if (sym == Mtitle)
+                 {
+                   if (pl.IsMText)
+                     title = pl.Text;
+                 }
+               else if (sym == Mvariable)
+                 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);
+             }
+         }
+    }
+
+    private static MText parse_description (MPlist plist)
+    {
+      return (plist.IsMText ? plist.Text
+             : plist.IsPlist && plist.Plist.IsMText ? plist.Plist.Text
+             : null);
+    }
+
+    private void parse_variables (MPlist plist)
+    {
+      variables = new Variable[plist.Count];
+
+      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;
+         }
+    }
+
+    private void parse_commands (MPlist plist)
+    {
+      commands = new Command[plist.Count];
+
+      for (int i = 0; ! plist.IsEmpty; plist = plist.next)
+       if (plist.IsPlist && plist.Plist.IsSymbol)
+         {
+           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");
+           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;
+         }
+    }
+
+    private void parse_include (MPlist plist)
+    {
+      if (! plist.IsPlist)
+       return;
+      MPlist p = plist.Plist.Cons (MSymbol.symbol, Minput_method);
+      MDatabase.Tag tag = new MDatabase.Tag (ref p);
+      MInputMethod im;
+      try {
+       im_table.TryGetValue (tag, out im);
+      } catch {
+       return;
+      }
+      plist = plist.next;
+      if (! plist.IsSymbol)
+       return;
+      MSymbol target_type = plist.Symbol;
+      plist = plist.next;
+      MSymbol target_name = MSymbol.nil;
+      if (plist.IsSymbol)
+       target_name = plist.Symbol;
+      if (target_type == Mmacro)
+       {
+         if (target_name == MSymbol.nil)
+           im.local_table.Copy (local_table);
+         else
+           im.local_table.Copy (target_name, local_table);
+       }
+      else if (target_type == Mmap)
+       {
+         if (target_name == MSymbol.nil)
+           {
+             foreach (KeyValuePair<MSymbol, Map> kv in im.maps)
+               maps[kv.Key] = kv.Value;
+           }
+         else
+           {
+             Map map;
+             if (im.maps.TryGetValue (target_name, out map))
+               maps[target_name] = map;
+           }
+       }
+      else if (target_type == Mstate)
+       {
+         if (target_name == MSymbol.nil)
+           {
+             foreach (KeyValuePair<MSymbol, State> kv in im.states)
+               states[kv.Key] = kv.Value;
+           }
+         else
+           {
+             State state;
+             if (im.states.TryGetValue (target_name, out state))
+               states[target_name] = state;
+           }
+       }
+    }
+
+    private void parse_macros (MPlist plist)
+    {
+      for (; ! plist.IsEmpty; plist = plist.next)
+       if (plist.IsPlist)
+         {
+           MPlist pl = plist.Plist;
+
+           if (! pl.IsSymbol)
+             continue;
+           MSymbol name = pl.Symbol;
+           MExpression expr = new MExpression (pl.next, local_table);
+           MExpression.Defmacro (local_table, name, expr);
+         }
+    }
+
+    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;
+               KeySeq keys;
+               if (pl.IsMText)
+                 keys = new KeySeq (pl.Text);
+               else if (pl.IsPlist)
+                 keys = new KeySeq (pl.Plist);
+               else
+                 continue;
+               if (keys.keys.Length == 0
+                   && keys.keys[0] == null)
+                 continue;
+               pl = pl.next;
+               if (pl.IsEmpty)
+                 continue;
+               MExpression expr = new MExpression (pl, local_table);
+               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 ();
+           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;
+               state.branches[p.Symbol]
+                 = new MExpression (p.next, local_table);
+             }
+         }
+    }
+
+    public MInputMethod Get (MSymbol language, MSymbol name, MSymbol extra)
+    {
+      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 {
+       im = null;
+      }
+      return im;
+    }
+
+    private static void adjust_markers (MInputContext ic,
+                                       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)
+       {
+         int pos = plist.Integer;
+         if (pos > from)
+           {
+             if (pos >= to)
+               plist.val = pos + diff;
+             else
+               plist.val = from;
+           }
+       }
+      if (ic.cursor_pos >= to)
+       ic.cursor_pos += diff;
+      else if (ic.cursor_pos > from)
+       ic.cursor_pos = from;
+    }
+
+    private static void preedit_replace (MInputContext ic,
+                                        int from, int to, int c)
+    {
+      ic.preedit.Del (from, to);
+      ic.preedit.Ins (from, c);
+      adjust_markers (ic, from, to, c);
+    }
+
+    private static void preedit_replace (MInputContext ic,
+                                        int from, int to, MText mt)
+    {
+      ic.preedit[from, to] = mt;
+      adjust_markers (ic, from, to, mt);
+    }
+
+    private static object insert (object[] args, MPlist bindings)
+    {
+      MInputContext ic = (MInputContext) args[0];
+      object arg = ((MExpression) args[1]).Eval (bindings);
+
+      if (arg is int)
+       preedit_replace (ic, ic.cursor_pos, ic.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;
+    }
+
+    internal class Candidates
+    {
+      private class Block
+      {
+       public Block Prev, Next;
+       public int Index;
+       public object Data;
+
+       public Block (Block prev, int index, MPlist plist)
+       {
+         Prev = prev;
+         if (prev != null)
+           prev.Next = this;
+         Index = index;
+         Data = plist.IsMText ? plist.Text : plist.Plist;
+       }
+
+       public int Count
+       {
+         get { return (data is MText
+                       ? ((MText) data).Length
+                       : ((MPlist) data).Count); }
+       }
+
+       public Block First
+       {
+         get {
+           Block b;
+           for (b = this; b.Prev != null; b = n.Prev);
+           return b;
+         }
+       }
+
+       public Block Last
+       {
+         get {
+           Block b;
+           for (b = this; b.Next != null; b = n.Next);
+           return b;
+         }
+       }
+
+       public object this[int i]
+       {
+         get { return (data is MText
+                       ? ((MText) data)[i]
+                       : ((MPlist) data)[i]); }
+       }
+      }
+
+      private Block block;
+      private int current_index = -1;
+      private int total_count = 0;
+      private object[] group = null;
+
+      public Candidates (MPlist list, int column)
+      {
+       if (column > 0)
+         group = new object[column];
+       Block b = null;
+       int index;
+       for (index = 0; ! list.IsEmpty; list = list.next)
+         {
+           b = new Block (b, index, list);
+           if (index == 0)
+             block = b;
+           index += b.Count;
+         }
+       total_count = index;
+      }
+
+      public int InGroupIndex
+      {
+       get {
+         return (group == null
+                 ? current_index - block.Index
+                 : current_index % group.Length);
+       }
+      }
+
+      public static void Detach (MInputContext ic)
+      {
+       ic.preedit.PopProp (0, ic.preedit.Length, Mcandidates);
+       ic.candidates = null;
+       ic.preedit_changed = true;
+       ic.cursor_pos_changed = true;
+       ic.candidate_changed = true;
+      }
+
+      // Fill the array "group" by candidates stating from INDEX.
+      // INDEX must be a multiple of "column".  Set NTIMES to the
+      // number of valid candidates in "group".  Update "block" if
+      // necessary.  Return "group".
+
+      private object fill_group (int index, out int nitems)
+      {
+       int column = group.Length;
+
+       if (index > block.Index + column)
+         block = block.Last;
+       while (index < block.Index)
+         block = block.Prev;
+
+       Block b = block;
+       int count = b.Count;
+       int inblock = index - b.Index;
+       for (nitems = 0; ntimes < column; ntimes++)
+         {
+           group[nitems] = b[inblock++];
+           if (inblock >= count)
+             {
+               b = b.Next;
+               if (b == null)
+                 break;
+               count = b.Count;
+               inblock = 0;
+             }
+         }
+       return group;
+      }
+
+      // Update "block" to what contains the first candidate of the
+      // previous candidate-group, update "current_index", and update
+      // "group" if necessary.  Return the previous candidate-group.
+      // Set NITEMS to the number of valid candidates contained in
+      // that group.
+
+      public object PrevGroup (out int nitems, out int ingroup)
+      {
+       object val;
+
+       ingroup = InGroupIndex;
+       if (group == null)
+         {
+           block = (block.Prev != null) ? block.Prev : block.Last;
+           current_index = block.Index;
+           nitems = block.Count;
+           val = block.Data;
+         }
+       else
+         {
+           nitems = group.Length;
+           current_index -= ingroup + nitems;
+           if (current_index < 0)
+             current_index = (total_count / nitems) * nitems;
+           val = fill_group (current_index, out nitems);
+         }
+       if (ingroup >= nitems)
+         ingroup = nitems - 1;
+       current_index += ingroup;
+       return val;
+      }
+
+      public object NextGroup (out int nitems)
+      {
+       int ingroup = InGroupIndex;
+       object val;
+
+       nitems = 0;
+       if (group == null)
+         {
+           block = (block.Next != null) ? block.Next : block.First;
+           current_index = block.Index;
+           nitems = block.Count;
+           val = block.Data;
+         }
+       else
+         {
+           nitems = group.Length;
+           current_index += column - ingroup;
+           if (current_index >= total_count)
+             current_index = 0;
+           val = fill_group (current_index, out nitems);
+         }
+       if (ingroup >= nitems)
+         ingroup = nitems - 1;
+       current_index += ingroup;
+       return val;
+      }
+
+      public object Next (out int nitems)
+      {
+       int ingroup = InGroupIndex + 1;
+       object val;
+       int index = current_index + 1;
+
+       nitems = group == null ? block.Count : group.Length;
+
+       if (ingroup >= nitems)
+         {
+           val = NextGroup (out nitems);
+           current_index = index < total_count ? index : 0;
+           return val;
+         }
+       if (group == null)
+         {
+           nitems = block.Count;
+           val = block.Data;
+         }
+       else
+         {
+           nitems = group.Length;
+           val = group;
+         }
+       current_index++;
+       return val;
+      }
+
+      public object Prev (out int nitems)
+      {
+       int ingroup = InGroupIndex - 1;
+       object val;
+       int index = current_index - 1;
+
+       if (ingroup < 0)
+         {
+           val = PrevGroup (out nitems);
+           current_index = index >= 0 ? index : total_count - 1;
+           return val;
+         }
+       if (group == null)
+         {
+           nitems = block.Count;
+           val = block.Data;
+         }
+       else
+         {
+           nitems = group.Length;
+           val = group;
+         }
+       current_index--;
+       return val;
+      }
+
+      public object First (out int nitems)
+      {
+       if (group == null)
+         {
+           nitems = block.Count;
+           current_index = block.Index;
+           return block.Data;
+         }
+       nitems = group.Length;
+       current_index = (current_index / nitems) * nitems;
+       return group;
+      }
+
+      public object Last (out int nitems)
+      {
+       if (group == null)
+         {
+           nitems = block.Count;
+           current_index = block.Index + block.Count - 1;;
+           return block.Data;
+         }
+       nitems = group.Length;
+       current_index = (current_index / nitems) * nitems + nitems - 1;
+       return group;
+      }
+
+      public object Select (int index, out int ingroup, out int len)
+      {
+       if (index < current_index)
+
+
+       MPlist prev;
+       return find_group (index, out ingroup_index, out text_len, out prev);
+      }
+
+      private MPlist find_group (int index, out int ingroup_index,
+                                out int text_len, out MPlist previous)
+      {
+       MPlist p;
+       int i = 0;
+
+       for (p = list, previous = null; ! p.IsEmpty; previous = p, p = p.next)
+         {
+           int len = p.IsMText ? p.Text.Length : p.Plist.Count;
+           if (index < i + len)
+             break;
+           i += len;
+         }
+       ingroup_index = index - i;
+       if (p.IsMText)
+         text_len = 1;
+       else
+         text_len = p.Plist[ingroup_index].Text.Length;
+       return p;
+      }
+
+      private void Update (MInputContext ic, object group, int ingroup)
+      {
+       int from, to, len;
+
+       if (current_index == index)
+         return;
+       if (ic.candidates == null)
+         {
+           from = ic.cursor_pos;
+           to = ic.cursor_pos;
+           ic.candidates = this;
+         }
+       else
+         {
+           from = ic.candidate_from;
+           to = ic.candidate_to;
+         }
+       group = 
+       p = find_group (index, out ingroup, out len, out prev);
+       to = from + len;
+       if (p.IsMText)
+         preedit_replace (ic, from, to, p.Text[ingroup]);
+       else
+         preedit_replace (ic, from, to, p.Plist[ingroup].Text);
+       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;
+      }
+
+      private void Update (MInputContext ic, int index)
+      {
+       int from, to, len;
+       object group;
+
+       if (current_index == index)
+         return;
+       if (ic.candidates == null)
+         {
+           from = ic.cursor_pos;
+           to = ic.cursor_pos;
+           ic.candidates = this;
+         }
+       else
+         {
+           from = ic.candidate_from;
+           to = ic.candidate_to;
+         }
+       group = 
+       p = find_group (index, out ingroup, out len, out prev);
+       to = from + len;
+       if (p.IsMText)
+         preedit_replace (ic, from, to, p.Text[ingroup]);
+       else
+         preedit_replace (ic, from, to, p.Plist[ingroup].Text);
+       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;
+      }
+
+      public void Prev (MInputContext ic) {}
+      public void Next (MInputContext ic) {}
+      public void First (MInputContext ic) {}
+      public void Last (MInputContext ic) {}
+      public void PrevGroup (MInputContext ic) {}
+      public void NextGroup (MInputContext ic) {}
+      public void Select (MInputContext ic, int index) {}
+
+    }
+
+    private static object insert_candidates (object[] args, MPlist bindings)
+    {
+      MInputContext ic = (MInputContext) args[0];
+      MPlist list = (MPlist) args[1];
+      int column = 0;
+
+      MPlist slot = (MPlist) bindings.Find (Mcandidates_group_size);
+      if (slot != null)
+       column = slot.Integer;
+      Candidates candidtes = new Candidates (list);
+      candidates.Update (ic, 0);
+      return 1;
+    }
+
+    private static object select (object[] args, MPlist bindings)
+    {
+      MInputContext ic = (MInputContext) args[0];
+      object arg = args[1];
+
+      if (ic.candidates == null)
+       return 0;
+      if (arg is MSymbol)
+       {
+         MSymbol sym = (MSymbol) arg;
+
+         if (sym == Mat_less_than)
+           ic.candidates.Update (ic, 0);
+         else if (sym == Mat_greater_than)
+           ic.candidates.Update (ic, -1);
+         else if (sym == Mat_minus)
+           ic.candidates.Prev (ic);
+         else if (sym == Mat_plus)
+           ic.candidates.Next (ic);
+         else if (sym == Mat_open_square_bracket)
+           ic.candidates.PrevGroup (ic);
+         else if (sym == Mat_close_square_bracket)
+           ic.candidates.NextGroup (ic);
+       }
+      else if (arg is int)
+       ic.candidates.SelectInGroup (ic, (int) arg);
+      return 0;
+    }
+
+    private static object delete (object[] args, MPlist bindings) { return 1; }
+    private static object show (object[] args, MPlist bindings) { return 1; }
+    private static object hide (object[] args, MPlist bindings) { return 1; }
+    private static object move (object[] args, MPlist bindings) { return 1; }
+    private static object mark (object[] args, MPlist bindings) { return 1; }
+    private static object pushback (object[] args, MPlist bindings) { return 1; }
+    private static object pop (object[] args, MPlist bindings) { return 1; }
+    private static object undo (object[] args, MPlist bindings) { return 1; }
+    private static object commit (object[] args, MPlist bindings) { return 1; }
+    private static object unhandle (object[] args, MPlist bindings) { return 1; }
+    private static object shift (object[] args, MPlist bindings) { return 1; }
+    private static object call (object[] args, MPlist bindings) { return 1; }
+  }
+
+  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;
+
+    internal MPlist GetCandidates (out int ingroup_index, out int text_len)
+    {
+      ingroup_index = text_len = 0;
+      if (cursor_pos == 0)
+       return null;
+      MInputMethod.Candidates candidates
+       = (MInputMethod.Candidates) preedit.GetProp (cursor_pos - 1,
+                                                    MInputMethod.Mcandidates);
+      if (candidates == null)
+       return null;
+      return candidates.FindGroup (candidate_index,
+                                  out ingroup_index, out text_len);
     }
   }
 }
index e62fdb4..ab948f4 100644 (file)
--- a/MPlist.cs
+++ b/MPlist.cs
@@ -170,6 +170,15 @@ namespace M17N.Core
        }
     }
 
+    public MPlist this[int i]
+    {
+      get {
+       MPlist p;
+       for (p = this; ! p.IsEmpty && i > 0; i--, p = p.next);
+       return (i == 0 ? p : null);
+      }
+    }
+
     public MPlist Clone ()
     {
       MPlist plist = new MPlist (), pl = plist;
index 5c916f1..9180c62 100644 (file)
--- a/MText.cs
+++ b/MText.cs
@@ -124,7 +124,7 @@ namespace M17N.Core
       int len = str.Length, n = 0;
 
       for (int i = 0; i < len; i++, n++) 
-       if (surrogate_high_p (str[i]))
+       if (Char.IsHighSurrogate (str[i]))
          i++;
       return n;
     }
@@ -134,7 +134,7 @@ namespace M17N.Core
       int len = str.Length, n = 0;
 
       for (int i = 0; i < len; i++, n++) 
-       if (surrogate_high_p (str[i]))
+       if (Char.IsHighSurrogate (str[i]))
          i++;
       return n;
     }
@@ -173,6 +173,14 @@ namespace M17N.Core
        intervals = new MPlist ();
       }
 
+    public MText (int c, int len) : this ()
+      {
+       while (len-- > 0)
+         this.Cat (c);
+      }
+
+    public MText (int c) : this (c, 1) { }
+
     public static MText operator+ (object obj, MText mt)
     {
       if (obj is string)
@@ -219,24 +227,14 @@ namespace M17N.Core
 
     public override string ToString () { return sb.ToString (); }
 
-    private static bool surrogate_high_p (char c)
-    {
-      return (c >= 0xD800 && c < 0xDC00);
-    }
-
-    private static bool surrogate_low_p (char c)
-    {
-      return (c >= 0xDC00 && c < 0xE000);
-    }
-
     private static int inc_idx (StringBuilder sb, int i)
     {
-      return (i + (surrogate_high_p (sb[i]) ? 2 : 1));
+      return (i + (Char.IsHighSurrogate (sb[i]) ? 2 : 1));
     }
 
     private static int dec_idx (StringBuilder sb, int i)
     {
-      return (i - (surrogate_low_p (sb[i - 1]) ? 2 : 1));
+      return (i - (Char.IsLowSurrogate (sb[i - 1]) ? 2 : 1));
     }
 
     private static int pos_to_idx (MText mt, int pos)
@@ -250,7 +248,7 @@ namespace M17N.Core
       if (pos < mt.cache_pos)
        {
          if (mt.cache_pos == mt.cache_idx)
-           return mt.cache_idx;
+           return pos;
          if (pos < mt.cache_pos - pos)
            {
              p = i = 0;
@@ -371,7 +369,7 @@ namespace M17N.Core
        i = pos_to_idx (this, i);
        if (value < 0x10000)
          {
-           if (surrogate_high_p (sb[i]))
+           if (Char.IsHighSurrogate (sb[i]))
              sb.Remove (i, 1);
            sb[i] = (char) value;
          }
@@ -380,20 +378,32 @@ namespace M17N.Core
            char high = (char) (0xD800 + ((value - 0x10000) >> 10));
            char low = (char) (0xDC00 + ((value - 0x10000) & 0x3FF));
 
-           if (! surrogate_high_p (sb[i]))
+           if (! Char.IsHighSurrogate (sb[i]))
              sb.Insert (i, 0);
            sb[i] = high;
            sb[i + 1] = low;
          }
+       PopProp (i, i + 1);
       }
       get {
        i = pos_to_idx (this, i);
-       return (surrogate_high_p (sb[i])
+       return (Char.IsHighSurrogate (sb[i])
                ? ((sb[i] - 0xD800) << 10) + (sb[i + 1] - 0xDC00) + 0x10000
                : sb[i]);
       }
     }
 
+    public MText this[int from, int to]
+    {
+      set {
+       if (from < to)
+         Del (from, to);
+       if (value != null)
+         Ins (from, value);
+      }
+      get { return Dup (from, to); }
+    }
+
     public MText Dup ()
     {
       MText mt = new MText (sb.ToString ());
@@ -446,14 +456,18 @@ namespace M17N.Core
       return this;
     }
 
+    public MText Cat (MText mt, int from, int to)
+    {
+      insert (nchars, mt, from, to);
+      return this;
+    }
+
     public MText Del (int from, int to)
     {
       if (check_range (from, to, true))
        return this;
-
       sb.Remove (from, pos_to_idx (this, to) - pos_to_idx (this, from));
       nchars -= to - from;
-
       if (nchars > 0)
        foreach (MPlist plist in intervals)
          {
@@ -548,6 +562,29 @@ namespace M17N.Core
        }
     }
 
+    public void PopProp (int from, int to)
+    {
+      if (from < 0)
+       {
+         default_property = null;
+       }
+      else
+       {
+         if (check_range (from, to, true))
+           return;
+         for (MPlist p = intervals; ! p.IsEmpty; p = p.next)
+           {
+             MInterval root = (MInterval) p.Val;
+             root.PopAll (from, to);
+             root = (MInterval) p.Val;
+             if (M17n.debug)
+               DumpPropNested ();
+             root.MergeAfterChange (from, to);
+             root.Balance ();
+           }
+       }
+    }
+
     public void PopProp (int from, int to, MSymbol key)
     {
       if (from < 0)
@@ -582,6 +619,18 @@ namespace M17N.Core
        }
     }
 
+    public object FindProp (MSymbol key, int pos, out int from, out int to)
+    {
+      from = to = pos;
+      check_pos (pos, false);
+      
+      MInterval i = (MInterval) intervals.Get (key);
+      if (i != null
+         && (i = i.Find (pos, out from, out to)) != null)
+       return GetProp (from, key);
+      return null;
+    }
+
     public void DumpProp ()
     {
       Console.Write ("(");
@@ -899,6 +948,21 @@ namespace M17N.Core
        return Parent;
       }
 
+      public MInterval Find (int pos, out int from, out int to)
+      {
+       MInterval i = find_head (pos);
+
+       from = to = pos;
+       if (i.Stack.IsEmpty)
+         i = i.Next;
+       if (i != null)
+         {
+           from = i.From;
+           to = i.To;
+         }
+       return i;
+      }
+
       public MInterval Balance ()
       {
        MInterval i = this;
@@ -1646,6 +1710,46 @@ namespace M17N.Core
        Pop (head.From, tail.To);
       }
 
+      public void PopAll (int start, int end)
+      {
+       update_from_to ();
+       M17n.DebugPrint ("popall({0} {1}) at {2}\n", start, end, this);
+       if (start < From)
+         {
+           if (end <= From)
+             {
+               Left.PopAll (start, end);
+               return;
+             }
+           Left.PopAll (start, From);
+           start = From;
+         }
+       if (end > To)
+         {
+           if (start >= To)
+             {
+               Right.PopAll (start, end);
+               return;
+             }
+           Right.PopAll (To, end);
+           end = To;
+         }
+
+       if (! Stack.IsEmpty)
+         {
+           if (isSensitive)
+             Stack.Clear ();
+           else
+             {
+               if (start > From)
+                 divide_left (start);
+               if (end < To)
+                 divide_right (end);
+               Stack.Clear ();
+             }
+         }
+      }
+
       public override string ToString ()
       {
        string str = String.Format ("#{0}({1} {2} {3} [", ID, Length, From, To);
index 8185c20..9097d70 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,25 +3,34 @@ CS=gmcs
 M17N_SRC = M17N.cs
 CORE_SRC = MSymbol.cs MPlist.cs MCharTable.cs MText.cs MDatabase.cs
 EXPR_SRC = MExpression.cs
-DLL = M17N.dll M17NCore.dll M17NExpr.dll
+INPUT_SRC = MInputMethod.cs
+DLL = M17N.dll M17NCore.dll M17NExpr.dll M17NIM.dll
 EXAMPLE = symbol.exe plist.exe chartab.exe text.exe textprop.exe database.exe \
        expr.exe
 TEST = rearsticky.exe frontsticky.exe bothsticky.exe \
        sensitive.exe frontsensitive.exe rearsensitive.exe
 
+DEBUG_FLAG =
+
 all: ${DLL} ${EXAMPLE} ${TEST}
 
 M17N.dll: ${M17N_SRC}
-       $(CS) -out:$@ -t:library ${M17N_SRC}
+       $(CS) $(DEBUG_FLAG) -out:$@ -t:library ${M17N_SRC}
 
 M17NCore.dll: M17N.dll ${CORE_SRC}
-       $(CS) -out:$@ -t:library -r:M17N.dll ${CORE_SRC}
+       $(CS) $(DEBUG_FLAG) -out:$@ -t:library -r:M17N.dll ${CORE_SRC}
 
 M17NExpr.dll: M17N.dll M17NCore.dll ${EXPR_SRC}
-       $(CS) -out:$@ -t:library -r:M17N.dll -r:M17NCore.dll ${EXPR_SRC}
+       $(CS) $(DEBUG_FLAG) -out:$@ -t:library -r:M17N.dll -r:M17NCore.dll ${EXPR_SRC}
+
+M17NIM.dll: M17N.dll M17NCore.dll M17NExpr.dll ${INPUT_SRC}
+       $(CS) $(DEBUG_FLAG) -out:$@ -t:library -r:M17N.dll -r:M17NCore.dll -r:M17NExpr.dll ${INPUT_SRC}
+
+input.exe: %.cs
+       $(CS) $(DEBUG_FLAG) -codepage:65001 -r:M17N.dll -r:M17NCore -r:M17NExpr  -r:M17NIM.dll $<
 
 %.exe: %.cs
-       $(CS) -codepage:65001 -r:M17N.dll -r:M17NCore -r:M17NExpr $<
+       $(CS) $(DEBUG_FLAG) -codepage:65001 -r:M17N.dll -r:M17NCore $<
 
 clean:
        rm -f *.dll *.exe
index 5610dbd..59c3245 100644 (file)
--- a/mtext.cs
+++ b/mtext.cs
@@ -20,5 +20,12 @@ public class Test
     foreach (int c in mt)
       Console.WriteLine ("U+{0:X4}", c);
     Console.WriteLine (mt + new MText ("漢字"));
+    Console.WriteLine (mt[2,4]);
+    mt[0] = '日';             // == mt.Del (0, 1); mt.Ins (0, char)
+    Console.WriteLine (mt);
+    mt[1,3] = new MText ('本');       // == mt.Del (1, 3); mt.Ins (1, mt)
+    Console.WriteLine (mt);
+    mt[1,2] = null;            // == mt.Del (1, 2)
+    Console.WriteLine (mt);
   }
 }
index 4a305c8..c7eb7c2 100644 (file)
--- a/plist.cs
+++ b/plist.cs
@@ -36,6 +36,7 @@ public class Test
     plist.Push (123);
     plist.Push (4.5);
     Console.WriteLine (plist);
+    Console.WriteLine ("plist[3] = " + plist[3]);
 
     MSymbol tmp = MSymbol.nil;
     if (tmp == null)