using System; using System.Collections; using System.IO; using M17N; using M17N.Core; namespace M17N.Core { public class MPlist : IEnumerable { public MSymbol Key; public object Val; private MPlist next; public MPlist () { Key = MSymbol.nil; Val = null; } public MPlist (MStreamReader reader) { MSymbol key; object val; bool result = reader.ReadElement (out key, out val); Key = key; Val = val; if (result) next = new MPlist (reader); } private MPlist (MSymbol key, object val) { Key = key; Val = val; } public bool IsEmpty { get { return next == null; } } public MPlist Next { get { return next; } } internal bool IsSymbol { get { return Key == MSymbol.symbol; } } internal bool IsMText { get { return Key == MSymbol.mtext; } } internal bool IsPlist { get { return Key == MSymbol.plist; } } internal bool IsInteger { get { return Key == MSymbol.integer; } } public int Count { get { int i = 0; for (MPlist p = this; p.next != null; i++, p = p.next); return i; } } public MPlist Clone () { MPlist plist = new MPlist (), pl = plist; for (MPlist p = this; p.next != null; p = p.next) pl = pl.Add (p.Key, p.Val); return plist; } public override string ToString () { string str = "("; for (MPlist p = this; ! p.IsEmpty; p = p.next) { if (p != this) str += " "; if (p.Key != MSymbol.symbol && p.Key != MSymbol.integer && p.Key != MSymbol.plist && p.Key != MSymbol.mtext) str += p.Key + ":"; str += p.Val; } return str + ")"; } private MPlist find (MSymbol key) { MPlist p; for (p = this; ! p.IsEmpty; p = p.next) if (p.Key == key) break; return p; } public MPlist Find (MSymbol key) { MPlist p = find (key); return (p.IsEmpty ? null : p); } public object Get (MSymbol key) { return find (key).Val; } private delegate MPlist MPlistDelegate (MSymbol key, object val); private MPlist mplist_op (MPlistDelegate op, object val) { Type type = val.GetType (); if (Object.ReferenceEquals (type, typeof (MSymbol))) return op (MSymbol.symbol, val); if (Object.ReferenceEquals (type, typeof (MText))) return op (MSymbol.mtext, val); if (Object.ReferenceEquals (type, typeof (MPlist))) return op (MSymbol.plist, val); if (Object.ReferenceEquals (type, typeof (int))) return op (MSymbol.integer, val); return op (MSymbol.t, val); } public MPlist Set (MSymbol key, object val) { if (IsEmpty) Push (key, val); else Val = val; return this; } public MPlist Set (object val) { return mplist_op (Set, val); } public MPlist Put (MSymbol key, object val) { return find (key).Set (key, val); } public MPlist Put (object val) { return mplist_op (Put, val); } public MPlist Push (MSymbol key, object val) { MPlist p = new MPlist (Key, Val); p.next = this.next; Key = key; Val = val; next = p; return this; } public MPlist Push (object val) { return mplist_op (Push, val); } public object Pop (out MSymbol key) { key = Key; if (IsEmpty) return null; object val = Val; Key = next.Key; Val = next.Val; next = next.next; return val; } public object Pop () { MSymbol key; return Pop (out key); } public MPlist Add (MSymbol key, object val) { MPlist p; for (p = this; ! p.IsEmpty; p = p.next); return p.Push (key, val); } // Implement IEnumerable interface. // foreach (MPlist p in plist) { ... } public virtual IEnumerator GetEnumerator () { return new Enumerator (this); } private class Enumerator : IEnumerator { private MPlist plist; private MPlist current; internal Enumerator (MPlist plist) { this.plist = plist; } public object Current { get { if (current == null || current.IsEmpty) throw new InvalidOperationException (); return current; } } public void Reset () { current = null; } public bool MoveNext () { if (current == null) current = plist; else current = current.next; return (! current.IsEmpty); } } } public class MStreamReader : StreamReader { private static char[] escaped_char = new char[128]; private static int[] hexadecimal = new int[128]; public MStreamReader (Stream stream) : base (stream) { } static MStreamReader () { for (int i = 0; i < 128; i++) escaped_char[i] = (char) i; escaped_char['e'] = (char) 27; escaped_char['b'] = '\b'; escaped_char['f'] = '\f'; escaped_char['n'] = '\n'; escaped_char['r'] = '\r'; escaped_char['t'] = '\t'; escaped_char['\\'] = '\\'; for (int i = 0; i < 128; i++) hexadecimal[i] = -1; for (int i = '0'; i <= '9'; i++) hexadecimal[i] = i - '0'; for (int i = 'A'; i <= 'F'; i++) hexadecimal[i] = hexadecimal[i + 'a' - 'A'] = i -'A' + 10; } internal int PeekChar () { bool comment = false; int c; while ((c = Peek ()) != -1) { if (comment) { if ((c = Read ()) == '\n') comment = false; } else { if (c == ';') comment = true; else if (c != ' ' && c != '\t' && c != '\n') return c; Read (); } } return c; } internal int ReadHexadecimal () { int i = 0, c; while ((c = Peek ()) >= 0 && c < 128 && (c = hexadecimal[c]) >= 0) { Read (); i = (i * 16) + c; } return i; } internal int ReadInteger () { int i = 0, c; while ((c = Peek ()) >= '0' && c <= '9') i = (i * 10) + (Read () - '0'); return i; } internal int ReadChar () { int c = Read (); if (c == '\\') { c = Read (); if (c == -1) return -1; if (c == 'x' || c == 'u') return ReadHexadecimal (); if (c < 128) c = escaped_char[c]; } return c; } internal MText ReadMtext () { MText mt = new MText (); int c; while ((c = Peek ()) != -1 && c != '"') { if (c == '\\') { c = ReadChar (); if (Peek () == '\n') { ReadChar (); continue; } if (c == -1) { mt.Cat ('\\'); break; } mt.Cat (c); } else mt.Cat (Read ()); } if (c == '"') Read (); return mt; } internal string ReadSymbolName () { int c = Peek (); if (c == -1 || c == '(' || c == ' ' || c == '\n' || c == '"') return ""; Read (); if (c == '\\') { c = Read (); if (c == -1) c = '\\'; } return (char) c + ReadSymbolName (); } internal bool ReadElement (out MSymbol key, out object val) { int c = PeekChar (); if (c == '(') { Read (); val = new MPlist (this); key = MSymbol.plist; } else if (c == '"') { Read (); val = ReadMtext (); key = MSymbol.mtext; } else if (c >= '0' && c <= '9') { int i = ReadInteger (); val = i; key = MSymbol.integer; } else if (c == '-') { Read (); c = Peek (); if (c >= '0' && c <= '9') { int i = ReadInteger (); val = - i; key = MSymbol.integer; } else { string str = ReadSymbolName (); val = new MSymbol ("-" + str); key = MSymbol.symbol; } } else if (c == '?') { Read (); val = ReadChar (); key = MSymbol.integer; } else if (c == '#') { Read (); if ((c = Peek ()) == 'x' || c == 'u') { Read (); val = ReadHexadecimal (); key = MSymbol.integer; } else { string str = ReadSymbolName (); val = new MSymbol ("#" + (char) c + str); key = MSymbol.symbol; } } else if (c == -1 || c == ')') { if (c == ')') Read (); val = null; key = MSymbol.nil; return false; } else { val = new MSymbol (ReadSymbolName ()); key = MSymbol.symbol; } return true; } } }