*** empty log message ***
[m17n/m17n-lib-cs.git] / MPlist.cs
index 27e23e3..57005c9 100644 (file)
--- a/MPlist.cs
+++ b/MPlist.cs
@@ -1,5 +1,6 @@
 using System;
 using System.Collections;
+using System.Collections.Generic;
 using System.IO;
 using M17N;
 using M17N.Core;
@@ -8,46 +9,221 @@ namespace M17N.Core
 {
   public class MPlist : IEnumerable
   {
-    public MSymbol Key;
-    public object Val;
-    private MPlist next;
+    public MSymbol key;
+    public object val;
+    public MPlist next;
+
+    public MSymbol Key { get { return key; } }
+    public object Val { get { return val; } }
+    public MPlist Next { get { return next; } }
+
+    private static List<MSymbol> wf_keys;
+
+    static MPlist ()
+      {
+       wf_keys = new List<MSymbol> ();
+       wf_keys.Add (MSymbol.symbol);
+       wf_keys.Add (MSymbol.mtext);
+       wf_keys.Add (MSymbol.plist);
+       wf_keys.Add (MSymbol.integer);
+      }
 
     public MPlist ()
       {
-       Key = MSymbol.nil;
-       Val = null;
+       key = MSymbol.nil;
+       val = null;
       }
 
-    public MPlist (MStreamReader reader)
+    public MPlist (FileStream stream)
       {
-       MSymbol key;
-       object val;
+       MStreamReader reader = new MStreamReader (stream);
        bool result = reader.ReadElement (out key, out val);
 
-       Key = key;
-       Val = val;
        if (result)
          next = new MPlist (reader);
       }
 
-    private MPlist (MSymbol key, object val)
+    public MPlist (FileStream stream, int count)
       {
-       Key = key;
-       Val = val;
+       MStreamReader reader = new MStreamReader (stream);
+       bool result = reader.ReadElement (out key, out val);
+
+       if (result && --count > 0)
+         next = new MPlist (reader, count);
+       else
+         next = new MPlist ();
+      }
+
+    public MPlist (FileStream stream, MSymbol stop)
+      {
+       MStreamReader reader = new MStreamReader (stream);
+       bool result;
+
+       key = MSymbol.nil;
+       val = null;
+       while (true)
+         {
+           MSymbol this_key;
+           object this_val;
+
+           result = reader.ReadElement (out this_key, out this_val);
+           if (! result)
+             return;
+           if (this_key == MSymbol.plist)
+             {
+               MPlist plist = (MPlist) this_val;
+
+               if (plist.IsSymbol && plist.Symbol == stop)
+                 return;
+             }
+           key = this_key;
+           val = this_val;
+           next = new MPlist (reader, stop);
+         }
+      }
+
+    public MPlist (FileStream stream, MSymbol target, MSymbol stop)
+      {
+       MStreamReader reader = new MStreamReader (stream);
+       bool result;
+
+       key = MSymbol.nil;
+       val = null;
+       while (true)
+         {
+           MSymbol this_key;
+           object this_val;
+
+           result = reader.ReadElement (out this_key, out this_val);
+           if (! result)
+             return;
+           if (this_key == MSymbol.plist)
+             {
+               MPlist plist = (MPlist) this_val;
+
+               if (plist.IsSymbol)
+                 {
+                   if (plist.Symbol == stop)
+                     return;
+                   if (plist.Symbol == target)
+                     {
+                       key = target;
+                       val = this_val;
+                       next = new MPlist ();
+                       return;
+                     }
+                 }
+             }
+         }
+      }
+
+    internal MPlist (MStreamReader reader)
+      {
+       bool result = reader.ReadElement (out key, out val);
+
+       if (result)
+         next = new MPlist (reader);
+      }
+
+    private MPlist (MStreamReader reader, int count)
+      {
+       bool result = reader.ReadElement (out key, out val);
+
+       if (result && --count > 0)
+         next = new MPlist (reader, count);
+       else
+         next = new MPlist ();
+      }
+
+    private MPlist (MStreamReader reader, MSymbol stop)
+      {
+       bool result;
+       MPlist next_plist = null;
+
+       key = MSymbol.nil;
+       val = null;
+       while (true)
+         {
+           MSymbol this_key;
+           object this_val;
+
+           result = reader.ReadElement (out this_key, out this_val);
+           if (! result)
+             return;
+           if (this_key == MSymbol.plist)
+             {
+               MPlist plist = (MPlist) this_val;
+
+               if (plist.IsSymbol && plist.Symbol == stop)
+                 return;
+             }
+           if (next_plist == null)
+             {
+               key = this_key;
+               val = this_val;
+               next = next_plist = new MPlist ();
+             }
+           else
+             {
+               next_plist.key = this_key;
+               next_plist.val = this_val;
+               next_plist.next = new MPlist ();
+               next_plist = next_plist.next;
+             }
+         }
+      }
+
+    private MPlist (MStreamReader reader, MSymbol target, MSymbol stop)
+      {
+       bool result;
+
+       key = MSymbol.nil;
+       val = null;
+       while (true)
+         {
+           MSymbol this_key;
+           object this_val;
+
+           result = reader.ReadElement (out this_key, out this_val);
+           if (! result)
+             return;
+           if (this_key == MSymbol.plist)
+             {
+               MPlist plist = (MPlist) this_val;
+
+               if (plist.IsSymbol)
+                 {
+                   if (plist.Symbol == stop)
+                     return;
+                   if (plist.Symbol == target)
+                     {
+                       key = target;
+                       val = this_val;
+                       next = new MPlist ();
+                       return;
+                     }
+                 }
+             }
+         }
+      }
+
+    protected MPlist (MSymbol key, object val)
+      {
+       this.key = key;
+       this.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 bool IsSymbol { get { return Key == MSymbol.symbol; } }
+    public bool IsMText { get { return Key == MSymbol.mtext; } }
+    public bool IsPlist { get { return Key == MSymbol.plist; } }
+    public bool IsInteger { get { return Key == MSymbol.integer; } }
 
-    internal MSymbol Symbol { get { return (MSymbol) Val; } }
-    internal MText Text { get { return (MText) Val; } }
-    internal MPlist Plist { get { return (MPlist) Val; } }
-    internal int Integer { get { return (int) Val; } }
+    public MSymbol Symbol { get { return (MSymbol) val; } }
+    public MText Text { get { return (MText) val; } }
+    public MPlist Plist { get { return (MPlist) val; } }
+    public int Integer { get { return (int) val; } }
 
     public int Count
     {
@@ -60,12 +236,21 @@ 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;
 
       for (MPlist p = this; p.next != null; p = p.next)
-       pl = pl.Add (p.Key, p.Val);
+       pl = pl.Add (p.key, p.val);
       return plist;
     }
 
@@ -77,12 +262,12 @@ namespace M17N.Core
          {
            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;
+           if (! wf_keys.Contains (p.key))
+             str += p.key + ":";
+           if (p.key == MSymbol.mtext)
+             str += "\"" + p.val + "\"";
+           else
+             str += p.val;
          }
        return str + ")";
     }
@@ -92,7 +277,7 @@ namespace M17N.Core
       MPlist p;
 
       for (p = this; ! p.IsEmpty; p = p.next)
-       if (p.Key == key)
+       if (p.key == key)
          break;
       return p;
     }
@@ -106,7 +291,15 @@ namespace M17N.Core
 
     public object Get (MSymbol key)
     {
-      return find (key).Val;
+      return find (key).val;
+    }
+
+    internal MPlist Assq (MSymbol key)
+    {
+      foreach (MPlist p in this)
+       if (p.IsPlist && p.Plist.IsSymbol && p.Plist.Symbol == key)
+         return p;
+      return null;
     }
 
     private delegate MPlist MPlistDelegate (MSymbol key, object val);
@@ -131,7 +324,10 @@ namespace M17N.Core
       if (IsEmpty)
        Push (key, val);
       else
-       Val = val;
+       {
+         this.key = key;
+         this.val = val;
+       }
       return this;
     }
 
@@ -152,11 +348,11 @@ namespace M17N.Core
 
     public MPlist Push (MSymbol key, object val)
     {
-      MPlist p = new MPlist (Key, Val);
+      MPlist p = new MPlist (this.key, this.val);
 
       p.next = this.next;
-      Key = key;
-      Val = val;
+      this.key = key;
+      this.val = val;
       next = p;
       return this;
     }
@@ -168,22 +364,22 @@ namespace M17N.Core
 
     public object Pop (out MSymbol key)
     {
-      key = Key;
+      key = this.key;
       if (IsEmpty)
        return null;
 
-      object val = Val;
+      object this_val = val;
 
-      Key = next.Key;
-      Val = next.Val;
+      this.key = next.key;
+      this.val = next.val;
       next = next.next;
-      return val;
+      return this_val;
     }
 
     public object Pop ()
     {
-      MSymbol key;
-      return Pop (out key);
+      MSymbol temp;
+      return Pop (out temp);
     }
 
     public MPlist Add (MSymbol key, object val)
@@ -194,6 +390,23 @@ namespace M17N.Core
       return p.Push (key, val);
     }
 
+    public MPlist Cons (MSymbol key, object val)
+    {
+      MPlist plist = new MPlist ();
+      plist.key = key;
+      plist.val = val;
+      plist.next = this;
+      return plist;
+    }
+
+    public MPlist Clear ()
+    {
+      key = MSymbol.nil;
+      val = null;
+      next = null;
+      return this;
+    }
+
     // Implement IEnumerable interface.
     //   foreach (MPlist p in plist) { ... }
 
@@ -241,22 +454,35 @@ namespace M17N.Core
   {
     private static char[] escaped_char = new char[128];
     private static int[] hexadecimal = new int[128];
+    private char comment_start;
+    private bool line_oriented;
 
     public MStreamReader (Stream stream) : base (stream)
       {
+       comment_start = ';';
+       line_oriented = false;
+      }
+
+    public MStreamReader (Stream stream, char comment_start,
+                         bool line_oriented) : base (stream)
+      {
+       this.comment_start = comment_start;
+       this.line_oriented = line_oriented;
       }
 
     static MStreamReader ()
       {
        for (int i = 0; i < 128; i++)
          escaped_char[i] = (char) i;
+       escaped_char['0'] = (char) 0;
        escaped_char['e'] = (char) 27;
+       escaped_char['a'] = '\a';
        escaped_char['b'] = '\b';
        escaped_char['f'] = '\f';
        escaped_char['n'] = '\n';
        escaped_char['r'] = '\r';
        escaped_char['t'] = '\t';
-       escaped_char['\\'] = '\\';
+       escaped_char['v'] = '\v';
        for (int i = 0; i < 128; i++)
          hexadecimal[i] = -1;
        for (int i = '0'; i <= '9'; i++)
@@ -265,117 +491,199 @@ namespace M17N.Core
          hexadecimal[i] = hexadecimal[i + 'a' - 'A'] = i -'A' + 10;
       }
 
-    internal int PeekChar ()
+    private int ReadHexadecimal (int max)
     {
-      bool comment = false;
-      int c;
+      int i = 0, c;
 
-      while ((c = Peek ()) != -1)
+      while ((c = Peek ()) >= 0 && c < 128 && (c = hexadecimal[c]) >= 0)
        {
-         if (comment)
-           {
-             if ((c = Read ()) == '\n')
-               comment = false;
-           }
-         else
-           {
-             if (c == ';')
-               comment = true;
-             else if (c != ' ' && c != '\t' && c != '\n')
-               return c;
-             Read ();
-           }
+         if (max >= 0 && (i * 16) + c >= max)
+           break;
+         Read ();
+         i = (i * 16) + c;
        }
-      return c;
+      return i;
     }
 
-    internal int ReadHexadecimal ()
+    public bool ForwardLine ()
     {
-      int i = 0, c;
+      int c;
+      while ((c = Read ()) >=0 && c != '\n');
+      return (c == '\n');
+    }
 
-      while ((c = Peek ()) >= 0 && c < 128 && (c = hexadecimal[c]) >= 0)
+    public bool SkipSpace (out int c)
+    {
+      while ((c = Peek ()) == ' ' && c == '\t' && c == '\f')
+       Read ();
+      return (c >= 0);
+    }
+
+    public bool PeekChar (out int c)
+    {
+      while ((c = Peek ()) != -1)
        {
-         Read ();
-         i = (i * 16) + c;
+         if (c == comment_start)
+           ForwardLine ();
+         else if (c != ' ' && c != '\t' && c != '\n')
+           return true;
+         else if (c == '\n' && line_oriented)
+           return false;
+         else
+           Read ();
        }
-      return i;
+      return false;
     }
 
-    internal int ReadInteger ()
+    public bool ReadInteger (out int i)
     {
-      int i = 0, c;
+      int c = Peek ();
 
+      i = 0;
+      if (c < 0)
+       return false;
+      if (c == '0')
+       {
+         Read ();
+         c = Peek ();
+         if (c == 'x')
+           {
+             Read ();
+             i = ReadHexadecimal (-1);
+             return true;
+           }
+       }
       while ((c = Peek ()) >= '0' && c <= '9')
        i = (i * 10) + (Read () - '0');
-      return i;
+      return true;
     }
 
-    internal int ReadChar ()
+    public bool ReadChar (out int c)
     {
-      int c = Read ();
-
+      c = Read ();
+      if (c < 0 || (line_oriented && c == '\n'))
+       return false;
       if (c == '\\')
        {
          c = Read ();
-         if (c == -1)
-           return -1;
-         if (c == 'x' || c == 'u')
-           return ReadHexadecimal ();
-         if (c < 128)
+         if (c == '\n')
+           return ReadChar (out c);
+         if (c < 0)
+           c = '\\';
+         else if (c == 'x' || c == 'u' || c == 'U')
+           c = ReadHexadecimal (0x10FFFF);
+         else if (c < 128)
            c = escaped_char[c];
        }
-      return c;
+      return true;
     }
 
-    internal MText ReadMtext ()
+    private bool read_string (out string str, int prefix, bool for_symbol)
     {
-      MText mt = new MText ();
+      char[] buf = new char[256];
       int c;
-
-      while ((c = Peek ()) != -1 && c != '"')
+      int i = 0;
+
+      str = null;
+      if (prefix >= 0)
+       buf[i++] = (char) prefix;
+      while ((c = Peek ()) >= 0
+            && c != '\"'
+            && (! for_symbol
+                || (c != '(' && c != ')' && c != ' ' && c != '\t' && c != '\n')))
        {
-         if (c == '\\')
+         if (! ReadChar (out c))
+           break;
+         if (c < 0x10000)
            {
-             c = ReadChar ();
-             if (Peek () == '\n')
-               {
-                 ReadChar ();
-                 continue;
-               }
-             if (c == -1)
-               {
-                 mt.Cat ('\\');
-                 break;
-               }
-             mt.Cat (c);
+             buf[i++] = (char) c;
            }
          else
-           mt.Cat (Read ());
+           {
+             buf[i++] = (char) (0xD800 + ((c - 0x10000) >> 10));
+             buf[i++] = (char) (0xDC00 + ((c - 0x10000) & 0x3FF));
+           }
+         if (i >= 255)
+           {
+             if (str == null)
+               str = new string (buf, 0, i);
+             else
+               str += new string (buf, 0, i);
+             i = 0;
+           }
        }
-      if (c == '"')
+      if (c == '\"' && ! for_symbol)
        Read ();
-      return mt;
+      if (i > 0)
+       {
+         if (str == null)
+           str = new string (buf, 0, i);
+         else
+           str += new string (buf, 0, i);
+       }
+      return (str != null);
     }
 
-    internal string ReadSymbolName ()
+    public bool ReadString (out string str)
+    {
+      return read_string (out str, -1, false);
+    }
+
+    public bool ReadMText (out MText mt)
     {
       int c = Peek ();
 
-      if (c == -1 || c == '(' || c == ' ' || c == '\n' || c == '"')
-       return "";
-      Read ();
+      if (c == '"')
+       {
+         string str;
+
+         Read ();
+         if (read_string (out str, -1, false))
+           mt = new MText (str);
+         else
+           mt = new MText ();
+         return true;
+       }
+      mt = new MText ();
       if (c == '\\')
        {
-         c = Read ();
-         if (c == -1)
-           c = '\\';
+         while ((c = Peek ()) == '\\')
+           {
+             Read ();
+             c = Peek ();
+             if (c != 'x')
+               break;
+             Read ();
+             mt.Cat (ReadHexadecimal (0x10FFFF));
+           }
+         return true;
+       }
+      return false;
+    }
+
+    public bool ReadSymbol (out MSymbol sym, int prefix)
+    {
+      string str;
+
+      if (read_string (out str, prefix, true))
+       {
+         sym = MSymbol.Of (str);
+         return true;
        }
-      return (char) c + ReadSymbolName ();
+      sym = MSymbol.nil;
+      return false;
     }
 
     internal bool ReadElement (out MSymbol key, out object val)
     {
-      int c = PeekChar ();
+      int c;
+
+      if (! PeekChar (out c))
+       {
+         val = null;
+         key = MSymbol.nil;
+         return false;
+       }
 
       if (c == '(')
        {
@@ -385,14 +693,15 @@ namespace M17N.Core
        }
       else if (c == '"')
        {
-         Read ();
-         val = ReadMtext ();
+         MText mt;
+         ReadMText (out mt);
+         val = mt;
          key = MSymbol.mtext;
        }
       else if (c >= '0' && c <= '9')
        {
-         int i = ReadInteger ();
-
+         int i;
+         ReadInteger (out i);
          val = i;
          key = MSymbol.integer;
        }
@@ -402,23 +711,33 @@ namespace M17N.Core
          c = Peek ();
          if (c >= '0' && c <= '9')
            {
-             int i = ReadInteger ();
+             int i;
+             ReadInteger (out i);
              val = - i;
              key = MSymbol.integer;
            }
          else
            {
-             string str = ReadSymbolName ();
+             MSymbol sym;
 
-             val = new MSymbol ("-" + str);
+             ReadSymbol (out sym, '-');
+             val = sym;
              key = MSymbol.symbol;
            }
        }
       else if (c == '?')
        {
          Read ();
-         val = ReadChar ();
-         key = MSymbol.integer;
+         if (ReadChar (out c))
+           {
+             val = c;
+             key = MSymbol.integer;
+           }
+         else
+           {
+             val = null;
+             key = MSymbol.nil;
+           }
        }
       else if (c == '#')
        {
@@ -426,28 +745,31 @@ namespace M17N.Core
          if ((c = Peek ()) == 'x' || c == 'u')
            {
              Read ();
-             val = ReadHexadecimal ();
+             val = ReadHexadecimal (-1);
              key = MSymbol.integer;
            }
          else
            {
-             string str = ReadSymbolName ();
+             MSymbol sym;
 
-             val = new MSymbol ("#" + (char) c + str);
+             ReadSymbol (out sym, '#');
+             val = sym;
              key = MSymbol.symbol;
            }
        }
-      else if (c == -1 || c == ')')
+      else if (c == ')')
        {
-         if (c == ')')
-           Read ();
+         Read ();
          val = null;
          key = MSymbol.nil;
          return false;
        }
       else
        {
-         val = new MSymbol (ReadSymbolName ());
+         MSymbol sym;
+
+         ReadSymbol (out sym, -1);
+         val = sym;
          key = MSymbol.symbol;
        }
       return true;