From 471297b4ac15917a2500cc391f2f1e28311e4308 Mon Sep 17 00:00:00 2001 From: handa Date: Mon, 3 Aug 2009 02:23:22 +0000 Subject: [PATCH] *** empty log message *** --- MExpression.cs | 50 ++- MInputMethod.cs | 972 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- MPlist.cs | 9 + MText.cs | 144 +++++++-- Makefile | 19 +- mtext.cs | 7 + plist.cs | 1 + 7 files changed, 1149 insertions(+), 53 deletions(-) diff --git a/MExpression.cs b/MExpression.cs index 959ea1d..560a6b1 100644 --- a/MExpression.cs +++ b/MExpression.cs @@ -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 table - = new Dictionary (); + internal Dictionary table; + + public FunctionTable () + { + table = new Dictionary (); + } + + public FunctionTable (FunctionTable table) + { + this.table = new Dictionary (table.table); + } + + public void Copy (FunctionTable table) + { + foreach (KeyValuePair 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)); } diff --git a/MInputMethod.cs b/MInputMethod.cs index cfd6d7f..ee0bfae 100644 --- a/MInputMethod.cs +++ b/MInputMethod.cs @@ -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 submaps; + public MExpression actions; + + public void Add (KeySeq keys, int index, MExpression actions) + { + Map sub = null; + + if (submaps == null) + submaps = new Dictionary (); + 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 branches + = new Dictionary (); } + private static Dictionary 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 maps + = new Dictionary (); + internal State init_state; + internal Dictionary states + = new Dictionary (); + 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 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 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 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); } } } diff --git a/MPlist.cs b/MPlist.cs index e62fdb4..ab948f4 100644 --- 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; diff --git a/MText.cs b/MText.cs index 5c916f1..9180c62 100644 --- 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); diff --git a/Makefile b/Makefile index 8185c20..9097d70 100644 --- 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 diff --git a/mtext.cs b/mtext.cs index 5610dbd..59c3245 100644 --- 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); } } diff --git a/plist.cs b/plist.cs index 4a305c8..c7eb7c2 100644 --- 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) -- 1.7.10.4