+ public override string ToString ()
+ {
+ return String.Format ("(module {0}", name);
+ }
+ }
+
+ internal class PluginMethod : Xex.Function
+ {
+ private Plugin plugin;
+ private MethodInfo method_info;
+ object[] parameters = new object[2];
+
+ public PluginMethod (Plugin plugin, string name)
+ : base ((Xex.Symbol) name, 0, -1)
+ {
+ this.plugin = plugin;
+ }
+
+ public override Xex.Term Call (Xex.Domain domain, Xex.Variable vari,
+ Xex.Term[] args)
+ {
+ args = (Xex.Term[]) args.Clone ();
+ for (int i = 0; i < args.Length; i++)
+ {
+ args[i] = args[i].Eval (domain);
+ if (domain.Thrown)
+ return args[i];
+ }
+ if (method_info == null)
+ method_info = plugin.GetMethod (name);
+ parameters[0] = domain.context;
+ parameters[1] = args;
+ return (Xex.Term) method_info.Invoke (null, parameters);
+ }
+ }
+
+ internal abstract class Marker : Xex.TermValue
+ {
+ private MSymbol name;
+
+ private Marker (MSymbol name)
+ {
+ this.name = name;
+ }
+
+ public abstract int Position (Context ic);
+
+ public virtual void Mark (Context ic)
+ {
+ throw new Exception ("Can't set predefined marker: " + name);
+ }
+ public virtual int CharAt (Context ic)
+ {
+ return ic.preedit[Position (ic)];
+ }
+
+ public override string ToString ()
+ {
+ return "<marker>" + name.Name + "</marker>";
+ }
+
+ public static Xex.TermValue parser (Xex.Domain domain, XmlNode node)
+ {
+ return Get ((MSymbol) node.InnerText);
+ }
+
+ public class Named : Marker
+ {
+ public Named (MSymbol name) : base (name) { }
+
+ public override int Position (Context ic)
+ {
+ MPlist p = ic.marker_positions.Find (name);
+ return (p == null ? 0 : p.Integer);
+ }
+
+ public override void Mark (Context ic)
+ {
+ ic.marker_positions.Put (name, ic.cursor_pos);
+ }
+ }
+
+ public class Predefined : Marker
+ {
+ char tag;
+
+ public Predefined (MSymbol name) : base (name)
+ {
+ tag = ((string) name)[1];
+ }
+
+ public override int Position (Context ic)
+ {
+ switch (tag) {
+ case '<': return 0;
+ case '>': return ic.preedit.Length;
+ case '-': return ic.cursor_pos - 1;
+ case '+': return ic.cursor_pos + 1;
+ case '[':
+ if (ic.cursor_pos > 0)
+ {
+ int pos = ic.cursor_pos;
+ int to;
+ ic.preedit.FindProp (Mcandidates, pos - 1, out pos, out to);
+ return pos;
+ }
+ return 0;
+ case ']':
+ if (ic.cursor_pos < ic.preedit.Length - 1)
+ {
+ int pos = ic.cursor_pos;
+ int from;
+ ic.preedit.FindProp (Mcandidates, pos, out from, out pos);
+ return pos;
+ }
+ return ic.preedit.Length;
+ default:
+ return tag - '0';
+ }
+ }
+ }
+
+ public class PredefinedAbsolute : Marker
+ {
+ private int pos;
+
+ public PredefinedAbsolute (MSymbol name) : base (name)
+ {
+ if (! int.TryParse (((string) name).Substring (1), out pos))
+ throw new Exception ("Invalid marker name: " + name);
+ }
+
+ public override int Position (Context ic)
+ {
+ return (pos < ic.preedit.Length ? pos : ic.preedit.Length);
+ }
+ }
+
+ public class PredefinedSurround : Marker
+ {
+ private int distance;
+
+ public PredefinedSurround (MSymbol name) : base (name)
+ {
+ if (! int.TryParse (((string) name).Substring (2), out distance))
+ throw new Exception ("Invalid marker name: " + name);
+ if (distance > 0)
+ distance--;
+ }
+
+ public override int Position (Context ic)
+ {
+ return ic.cursor_pos + distance;
+ }
+
+ public override int CharAt (Context ic)
+ {
+ int pos = ic.cursor_pos + distance;
+ if (pos < 0)
+ return ic.GetSurroundingChar (pos);
+ else if (pos >= ic.preedit.Length)
+ return ic.GetSurroundingChar (pos - ic.preedit.Length);
+ return ic.preedit[pos];
+ }
+ }
+
+ static internal Dictionary<MSymbol,Predefined> predefined_markers;
+
+ static Marker ()
+ {
+ predefined_markers = new Dictionary<MSymbol, Predefined> ();
+ MSymbol[] symlist = new MSymbol[] {"@<", "@>", "@-", "@+", "@[", "@]" };
+ foreach (MSymbol s in symlist)
+ predefined_markers[s] = new Predefined (s);
+ }
+
+ public static Marker Get (MSymbol name)
+ {
+ string str = name.Name;
+ if (str[0] == '@')
+ {
+ Predefined pred;
+ if (predefined_markers.TryGetValue (name, out pred))
+ return pred;
+ if (str.Length == 1)
+ throw new Exception ("Invalid marker name: " + name);
+ if (Char.IsDigit (str[1]))
+ return new PredefinedAbsolute (name);
+ if (str.Length == 2 || name == Mat_minus_zero
+ || ! (str[1] == '-' || str[1] == '+'))
+ throw new Exception ("Invalid marker name: " + name);
+ return new PredefinedSurround (name);
+ }
+ return new Named (name);
+ }
+ }
+
+ internal class Candidates
+ {
+ private class Block
+ {
+ public int Index;
+ public object Data;
+
+ public Block (int index, Xex.Term term)
+ {
+ Index = index;
+ if (term.IsStr)
+ Data = (MText) term.Strval;
+ else
+ {
+ MPlist plist = new MPlist ();
+ MPlist p = plist;
+ foreach (Xex.Term t in term.Listval)
+ p = p.Add (MSymbol.mtext, (MText) t.Strval);
+ Data = plist;
+ }
+ }
+
+ public Block (int index, MPlist plist)
+ {
+ Index = index;
+ if (plist.IsMText)
+ Data = plist.Text;
+ else if (plist.IsPlist)
+ Data = plist.Plist;
+ else
+ throw new Exception ("Invalid candidate: " + plist);
+ }
+
+ public int Count
+ {
+ get { return (Data is MText
+ ? ((MText) Data).Length
+ : ((MPlist) Data).Count); }
+ }
+
+ public object this[int i]
+ {
+ get {
+ if (Data is MText) return ((MText) Data)[i];
+ return ((MPlist) Data)[i];
+ }
+ }
+ }
+
+ private Block[] blocks;
+ private int row = 0;
+ private int index = 0;
+ public object[] group;
+
+ private bool IsFixed { get { return group != null; } }
+ private int Total {
+ get {
+ Block last = blocks[blocks.Length - 1];
+ return last.Index + last.Count; }
+ }
+
+ public int Column {
+ get { return (IsFixed ? index % group.Length
+ : index - blocks[row].Index); }
+ }
+
+ public object Group {
+ get { return (IsFixed ? group : blocks[row].Data); }
+ }
+
+ public int GroupLength
+ {
+ get {
+ if (IsFixed)
+ {
+ int nitems = group.Length;
+ int start = index - (index % nitems);
+ int total = Total;
+ return (start + nitems <= total ? nitems : total - start);
+ }
+ return blocks[row].Count;
+ }
+ }
+
+ public object Current {
+ get {
+ return (IsFixed ? group[index % group.Length]
+ : blocks[row][index - blocks[row].Index]);
+ }
+ }
+
+ public Candidates (MPlist list, int column)
+ {
+ int nblocks = list.Count;
+
+ blocks = new Block[nblocks];
+ for (int i = 0, start = 0; i < nblocks; i++, list = list.next)
+ start += (blocks[i] = new Block (index, list)).Count;
+ if (column > 0)
+ group = new object[column];
+ }
+
+ public Candidates (Xex.Term[] candidates, int column)
+ {
+ int nblocks = candidates.Length;
+
+ blocks = new Block[nblocks];
+ for (int i = 0, start = 0; i < nblocks; i++)
+ start += (blocks[i] = new Block (index, candidates[i])).Count;
+ if (column > 0)
+ group = new object[column];
+ }
+
+ public static void Detach (Context ic)
+ {
+ ic.preedit.PopProp (0, ic.preedit.Length, Mcandidates);
+ ic.candidates = null;
+ ic.changed |= (ChangedStatus.Preedit | ChangedStatus.CursorPos
+ | CandidateAll);
+ }
+
+ // 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 int fill_group (int start)
+ {
+ int nitems = group.Length;
+ int r = row;
+ Block b = blocks[r];
+
+ if (start < b.Index)
+ while (start < b.Index)
+ b = blocks[--r];
+ else
+ while (start >= b.Index + b.Count)
+ b = blocks[++r];
+ row = r;
+
+ int count = b.Count;
+ start -= b.Index;
+ for (int i = 0; i < nitems; i++, start++)
+ {
+ if (start >= count)
+ {
+ r++;
+ if (r == blocks.Length)
+ return i;
+ b = blocks[r];
+ count = b.Count;
+ start = 0;
+ }
+ group[i] = b[start];
+ }
+ return nitems;
+ }
+
+ // Update "row" 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 int PrevGroup ()
+ {
+ int nitems;
+ int col = Column;
+
+ if (IsFixed)
+ {
+ nitems = group.Length;
+ if ((index -= col + nitems) < 0)
+ index = (Total / nitems) * nitems;
+ nitems = fill_group (index);
+ }
+ else
+ {
+ row = row > 0 ? row-- : blocks.Length - 1;
+ nitems = blocks[row].Count;
+ index = blocks[row].Index;
+ }
+ index += col < nitems ? col : nitems - 1;
+ return nitems;
+ }
+
+ public int NextGroup ()
+ {
+ int nitems;
+ int col = Column;
+
+ if (IsFixed)
+ {
+ nitems = group.Length;
+ if ((index += nitems - col) >= Total)
+ index = 0;
+ nitems = fill_group (index);
+ }
+ else
+ {
+ row = row < blocks.Length - 1 ? row + 1 : 0;
+ nitems = blocks[row].Count;
+ index = blocks[row].Count;
+ }
+ index += col < nitems ? col : nitems - 1;
+ return nitems;
+ }
+
+ public void Prev ()
+ {
+ int col = Column;
+
+ if (col == 0)
+ {
+ int nitems = PrevGroup ();
+ index += col < nitems - 1 ? col : nitems - 1;
+ }
+ else
+ index--;
+ }
+
+ public void Next ()
+ {
+ int col = Column;
+ int nitems = GroupLength;
+
+ if (col == nitems - 1)
+ {
+ nitems = NextGroup ();
+ index -= Column;
+ }
+ else
+ index++;
+ }
+
+ public void First ()
+ {
+ index -= Column;
+ }
+
+ public void Last ()
+ {
+ index += GroupLength - (Column + 1);
+ }
+
+ public void Select (int col)
+ {
+ int maxcol = GroupLength - 1;
+ if (col > maxcol)
+ col = maxcol;
+ index = index - Column + col;
+ }
+ }
+
+ internal class Selector : Xex.TermValue
+ {
+ static new Dictionary<MSymbol, Selector> selectors;
+
+ static Selector ()
+ {
+ selectors = new Dictionary<MSymbol, Selector> ();
+ MSymbol[] symlist = new MSymbol[] { "@<", "@=", "@>", "@-", "@+",
+ "@[", "@]" };
+ foreach (MSymbol s in symlist)
+ selectors[s] = new Selector (s);
+ selectors["@first"] = new Selector ('<');
+ selectors["@current"] = new Selector ('=');
+ selectors["@last"] = new Selector ('>');
+ selectors["@previous"] = new Selector ('-');
+ selectors["@next"] = new Selector ('+');
+ selectors["@previous-candidate-change"] = new Selector ('[');
+ selectors["@next-candidate-change"] = new Selector (']');
+ }
+
+ private char tag;
+
+ private Selector (MSymbol sym) { tag = sym.Name[1]; }
+
+ private Selector (char tag) { this.tag = tag; }
+
+ public static Xex.TermValue parser (Xex.Domain domain, XmlNode node)
+ {
+ return Get ((MSymbol) node.InnerText);
+ }
+
+ public static Xex.TermValue Get (MSymbol name)
+ {
+ Selector selector;
+ if (! selectors.TryGetValue (name, out selector))
+ throw new Exception ("Invalid selector name: " + name);
+ return selector;
+ }
+
+ public void Select (Candidates candidates)
+ {
+ switch (tag)
+ {
+ case '<': candidates.First (); break;
+ case '>': candidates.Last (); break;
+ case '-': candidates.Prev (); break;
+ case '+': candidates.Next (); break;
+ case '[': candidates.PrevGroup (); break;
+ case ']': candidates.NextGroup (); break;
+ default: break;
+ }
+ }
+ }
+
+ internal class Map
+ {
+ public MSymbol name;
+ public List<Entry> entries = new List<Entry> ();
+
+ public Map (MSymbol name) { this.name = name; }
+
+ public class Entry
+ {
+ public KeySeq keyseq;
+ public Xex.Term[] actions;
+
+ public Entry (Xex.Domain domain, KeySeq keyseq, Xex.Term[] actions)
+ {
+ this.keyseq = keyseq;
+ this.actions = actions;
+ }
+ }
+
+ public override string ToString ()
+ {
+ string str = "(" + name;
+ foreach (Entry e in entries)
+ str += " " + e.keyseq.ToString ();
+ return str + ")";
+ }
+ }
+
+ internal class Keymap
+ {
+ public Dictionary<Key, Keymap> submaps;
+ public Xex.Term[] map_actions, branch_actions;
+
+ public Keymap () { }
+
+ public void Add (KeySeq keys, int index,
+ Xex.Term[] map_actions, Xex.Term[] branch_actions)
+ {
+ if (index == keys.keyseq.Count)
+ {
+ this.map_actions = map_actions;
+ this.branch_actions = branch_actions;
+ }
+ else
+ {
+ Key key = keys.keyseq[index];
+ Keymap sub = null;
+
+ if (submaps == null)
+ submaps = new Dictionary<Key, Keymap> ();
+ else
+ submaps.TryGetValue (key, out sub);
+ if (sub == null)
+ submaps[key] = sub = new Keymap ();
+ sub.Add (keys, index + 1, map_actions, branch_actions);
+ }
+ }
+
+ public void AddMap (Map map, Xex.Term[] branch_actions)
+ {
+ foreach (Map.Entry entry in map.entries)
+ Add (entry.keyseq, 0, entry.actions, branch_actions);
+ }
+
+ public Keymap Lookup (KeySeq keys, ref int index)
+ {
+ Keymap sub;
+
+ if (index < keys.keyseq.Count
+ && submaps != null
+ && submaps.TryGetValue (keys.keyseq[index], out sub))
+ {
+ index++;
+ return sub.Lookup (keys, ref index);
+ }
+ return this;
+ }
+
+ private void describe (MText mt, KeySeq keyseq)
+ {
+ if (map_actions != null || branch_actions != null)
+ {
+ if (mt.Length > 0)
+ mt.Cat (" ");
+ mt.Cat ('(').Cat (keyseq.ToString ());
+ if (map_actions != null)
+ foreach (Xex.Term term in map_actions)
+ mt.Cat (' ').Cat (term.ToString ());
+ if (branch_actions != null)
+ foreach (Xex.Term term in branch_actions)
+ mt.Cat (' ').Cat (term.ToString ());
+ mt.Cat (')');
+ }
+ if (submaps != null)
+ foreach (KeyValuePair<Key, Keymap> kv in submaps)
+ {
+ keyseq.keyseq.Add (kv.Key);
+ kv.Value.describe (mt, keyseq);
+ keyseq.keyseq.RemoveAt (keyseq.keyseq.Count - 1);
+ }
+ }
+
+ public override string ToString ()
+ {
+ MText mt = "";
+ KeySeq keyseq = new KeySeq ();
+
+ describe (mt, keyseq);
+ return (string) mt;
+ }
+ }
+
+ internal class State
+ {
+ public Xex.Symbol name;
+ public MText title;
+ public Xex.Term[] enter_actions, fallback_actions;
+ public Keymap keymap = new Keymap ();
+
+ public State (Xex.Symbol name, MText title)
+ {
+ this.name = name;
+ this.title = title;
+ }
+
+ public State (MInputMethod im, XmlNode node)
+ {
+ this.name = node.Attributes[Qsname].Value;
+ XmlAttribute attr = node.Attributes[Qtitle];
+ if (attr != null)
+ title = (MText) attr.Value;
+ else
+ title = im.title;
+ keymap = new Keymap ();
+ for (node = node.FirstChild; node != null; node = node.NextSibling)
+ {
+ if (node.Name == Qstate_hook)
+ enter_actions = Xex.ParseTerms (im.domain, node.FirstChild);
+ else if (node.Name == Qcatch_all_branch)
+ fallback_actions = Xex.ParseTerms (im.domain, node.FirstChild);
+ else if (node.Name == Qbranch)
+ {
+ MSymbol mapname = node.Attributes[Qmname].Value;
+ Map map;
+ if (im.maps.TryGetValue (mapname, out map))
+ keymap.AddMap (map, Xex.ParseTerms (im.domain,
+ node.FirstChild));
+ else
+ throw new Exception ("Unknown map: " + mapname);
+ }
+ }
+ }
+
+ public State (MInputMethod im, MPlist plist)
+ {
+ if (! plist.IsSymbol)
+ throw new Exception ("Invalid state: " + plist);
+ this.name = plist.Symbol.Name;
+ plist = plist.next;
+ if (plist.IsMText)
+ {
+ title = plist.Text;
+ plist = plist.next;
+ }
+ else
+ title = im.title;
+ keymap = new Keymap ();
+ for (; ! plist.IsEmpty; plist = plist.next)
+ {
+ if (! plist.IsPlist)
+ throw new Exception ("Invalid branch: " + plist);
+ MPlist p = plist.Plist;
+ if (! p.IsSymbol)
+ throw new Exception ("Invalid branch: " + p);
+ MSymbol mapname = p.Symbol;
+ if (mapname == MSymbol.t)
+ enter_actions = im.parse_actions (p.next, false);
+ else if (mapname == MSymbol.nil)
+ fallback_actions = im.parse_actions (p.next, false);
+ else
+ {
+ Map map;
+ if (im.maps.TryGetValue (mapname, out map))
+ keymap.AddMap (map, im.parse_actions (p.next, false));
+ else
+ throw new Exception ("Unknown map: " + mapname);
+ }
+ }
+ }
+
+ public override string ToString ()
+ {
+ MText mt = "(" + name;
+
+ if (title != null)
+ mt.Cat (" \"" + title + "\"");
+ mt.Cat (keymap.ToString ());
+ return (string) mt + ")";
+ }
+ }
+
+ // Instance members
+ internal Xex.Domain domain;
+
+ protected LoadStatus load_status = LoadStatus.None;
+ protected MDatabase.Tag tag;
+ private MDatabase mdb;
+
+ private MText description;
+ internal MText title;
+ internal Command[] commands;
+ internal Xex.Symbol[] var_names;
+ internal Dictionary<MSymbol, Plugin> plugins;
+ internal Dictionary<MSymbol, Map> maps;
+ internal Dictionary<Xex.Symbol, State> states;
+ internal State initial_state;
+
+ static MInputMethod ()
+ {
+ im_domain.DefTerm ("keyseq", KeySeq.parser);
+ im_domain.DefTerm ("marker", Marker.parser);
+ im_domain.DefTerm ("selector", Selector.parser);
+
+ im_domain.DefSubr (Finsert, "insert", false, 1, 1);
+ im_domain.DefSubr (Finsert_candidates, "insert-candidates", false, 1, -1);
+ im_domain.DefSubr (Fdelete, "delete", false, 1, 1);
+ im_domain.DefSubr (Fselect, "select", false, 1, 1);
+ im_domain.DefSubr (Fshow, "show", false, 0, 0);
+ im_domain.DefSubr (Fhide, "hide", false, 0, 0);
+ im_domain.DefSubr (Fmove, "move", false, 1, 1);
+ im_domain.DefSubr (Fmark, "mark", false, 1, 1);
+ im_domain.DefSubr (Fpushback, "pushback", false, 1, 1);
+ im_domain.DefSubr (Fpop, "pop", false, 0, 0);
+ im_domain.DefSubr (Fundo, "undo", false, 0, 1);
+ im_domain.DefSubr (Fcommit, "commit", false, 0, 0);
+ im_domain.DefSubr (Funhandle, "unhandle", false, 0, 0);
+ im_domain.DefSubr (Fshift, "shift", false, 1, 1);
+ im_domain.DefSubr (Fshiftback, "shiftback", false, 0, 0);
+ im_domain.DefSubr (Fchar_at, "char-at", false, 1, 1);
+ im_domain.DefSubr (Fkey_count, "key-count", false, 1, 1);
+ im_domain.DefSubr (Fsurrounding_flag, "surrounding-text-flag",
+ false, 0, 0);
+
+ 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;
+ domain = new Xex.Domain (tag[1].Name, im_domain, null);
+ }
+
+ // 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 Xex.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;
+ if (var_names == null)
+ variables = null;
+ else
+ {
+ variables = new Xex.Variable[var_names.Length];
+ int i = 0;
+ foreach (Xex.Symbol name in var_names)
+ variables[i++] = domain.GetVar (name, false);
+ }
+ 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);
+ }
+
+ private 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)
+ return false;
+ mdb.name_table = Xex.Symbol.Table;
+ 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 ()
+ {
+ mdb = MDatabase.Find (tag);
+ if (mdb == null)
+ return false;
+ mdb.name_table = Xex.Symbol.Table;
+ 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 add_default_state ()
+ {
+ Xex.Symbol Qinit = "init";
+ State state = new State (Qinit, title);
+ foreach (KeyValuePair<MSymbol, Map>kv in maps)
+ state.keymap.AddMap (kv.Value, null);
+ states[Qinit] = initial_state = state;
+ }
+
+ private void load (MPlist plist, bool full)
+ {
+ maps = new Dictionary<MSymbol, Map> ();
+ states = new Dictionary<Xex.Symbol, State> ();
+
+ 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);
+ 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 (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 (commands == null)
+ commands = new Command[0];
+ if (! full)
+ return;
+ if (states.Count == 0)
+ add_default_state ();
+ }
+
+ private void load (XmlNode node, bool full)
+ {
+ bool skip_header = load_status == LoadStatus.Header;
+
+ maps = new Dictionary<MSymbol, Map> ();
+ states = new Dictionary<Xex.Symbol, State> ();
+
+ 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 (commands == null)
+ commands = new Command[0];
+ if (! full)
+ return;
+ if (states.Count == 0)
+ add_default_state ();
+ }
+
+ private static MText parse_description (MPlist plist)
+ {
+ 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 new_variable (Xex.Symbol name, string desc, int val,
+ MPlist pl, Xex.Variable vari)
+ {
+ int[] range;
+
+ if (pl.IsEmpty)
+ range = null;
+ else
+ {
+ int nrange = pl.Count;
+ range = new int[nrange * 2];
+ for (int i = 0; i < nrange; i++)
+ {
+ if (pl.IsPlist)
+ {
+ MPlist p = pl.Plist;
+
+ if (! p.IsInteger || ! p.next.IsInteger)
+ throw new Exception ("Invalid range: " + p);
+ range[i * 2] = p.Integer;
+ range[i * 2 + 1] = p.next.Integer;
+ }
+ else if (pl.IsInteger)
+ range[i * 2] = range[i * 2 + 1] = pl.Integer;
+ else
+ throw new Exception ("Invalid range: " + pl);
+ }
+ }
+ if (vari == null)
+ domain.Defvar (new Xex.Variable.Int (name, desc, val, range));
+ else
+ {
+ Xex.Term term = new Xex.Term (val);
+ vari.Value = term;
+ vari.DefaultValue = term;
+ vari.Range = range;
+ }
+ }
+
+ private void new_variable (Xex.Symbol name, string desc, MText val,
+ MPlist pl, Xex.Variable vari)
+ {
+ string[] range;
+
+ if (pl.IsEmpty)
+ range = null;
+ else
+ {
+ range = new string[pl.Count * 2];
+ for (int i = 0; i < range.Length; i++)
+ {
+ if (pl.IsMText)
+ range[i] = (string) pl.Text;
+ else
+ throw new Exception ("Invalid range: " + pl);
+ }
+ }
+ if (vari == null)
+ domain.Defvar (new Xex.Variable.Str (name, desc, (string) val, range));
+ else
+ {
+ Xex.Term term = new Xex.Term ((string) val);
+ vari.Value = term;
+ vari.DefaultValue = term;
+ vari.Range = range;
+ }
+ }
+
+ private void new_variable (Xex.Symbol name, string desc, MSymbol val,
+ MPlist pl, Xex.Variable vari)
+ {
+ Xex.Symbol[] range;
+ Xex.Symbol sym = val.Name;
+
+ if (pl.IsEmpty)
+ range = null;
+ else
+ {
+ range = new Xex.Symbol[pl.Count * 2];
+ for (int i = 0; i < range.Length; i++)
+ {
+ if (pl.IsSymbol)
+ range[i] = pl.Symbol.Name;
+ else
+ throw new Exception ("Invalid range: " + pl);
+ }
+ }
+ if (vari == null)
+ domain.Defvar (new Xex.Variable.Sym (name, desc, sym, range));
+ else
+ {
+ Xex.Term term = new Xex.Term (sym);
+ vari.Value = term;
+ vari.DefaultValue = term;
+ vari.Range = range;
+ }
+ }
+
+ private Xex.Variable get_global_var (Xex.Symbol name)
+ {
+ if (im_global == null || this != im_global)
+ {
+ tag = new MDatabase.Tag (Minput_method, MSymbol.t, MSymbol.nil,
+ "global");
+ im_global = im_table[tag];
+ if (! im_global.Open ())
+ throw new Exception ("Failed to load global");
+ }
+ return im_global.domain.GetVar (name, false);
+ }
+
+ private void parse_variables (MPlist plist)
+ {
+ var_names = new Xex.Symbol[plist.Count];
+
+ for (int i = 0; ! plist.IsEmpty; i++, plist = plist.next)
+ {
+ if (! plist.IsPlist || ! plist.Plist.IsSymbol)
+ throw new Exception ("Invalid variable: " + plist);
+
+ MPlist p = plist.Plist;
+ Xex.Symbol name = (Xex.Symbol) p.Symbol.Name;
+ var_names[i] = name;
+ p = p.next;
+ string desc = (string) parse_description (p);
+ Xex.Variable vari = get_global_var (name);
+ if (vari != null)
+ domain.Defvar (vari);
+ if (desc != null)
+ p = p.next;
+ if (! p.IsEmpty)
+ {
+ if (p.IsInteger)
+ new_variable (name, desc, p.Integer, p.next, vari);
+ else if (p.IsMText)
+ new_variable (name, desc, p.Text, p.next, vari);
+ else if (p.IsSymbol)
+ new_variable (name, desc, p.Symbol, p.next, vari);
+ else
+ throw new Exception ("Invalid variable type: " + p.val);
+ }
+ }
+ }
+
+ private void parse_variables (XmlNode node)
+ {
+ XmlNodeList node_list = node.ChildNodes;