*** empty log message ***
[m17n/m17n-lib-cs.git] / MPlist.cs
1 using System;
2 using System.Collections;
3 using System.IO;
4 using M17N;
5 using M17N.Core;
6
7 namespace M17N.Core
8 {
9   public class MPlist : IEnumerable
10   {
11     public MSymbol Key;
12     public object Val;
13     private MPlist next;
14
15     public MPlist ()
16       {
17         Key = MSymbol.nil;
18         Val = null;
19       }
20
21     public MPlist (FileStream stream)
22       {
23         MStreamReader reader = new MStreamReader (stream);
24         MSymbol key;
25         object val;
26         bool result = reader.ReadElement (out key, out val);
27
28         Key = key;
29         Val = val;
30         if (result)
31           next = new MPlist (reader);
32       }
33
34     public MPlist (FileStream stream, int count)
35       {
36         MStreamReader reader = new MStreamReader (stream);
37         MSymbol key;
38         object val;
39         bool result = reader.ReadElement (out key, out val);
40
41         Key = key;
42         Val = val;
43         if (result && --count > 0)
44           next = new MPlist (reader, count);
45         else
46           next = new MPlist ();
47       }
48
49     public MPlist (FileStream stream, MSymbol target, MSymbol stop)
50       {
51         MStreamReader reader = new MStreamReader (stream);
52         MSymbol key;
53         object val;
54         bool result;
55
56         Key = MSymbol.nil;
57         Val = null;
58         while (true)
59           {
60             result = reader.ReadElement (out key, out val);
61             if (! result)
62               return;
63             if (key == MSymbol.plist)
64               {
65                 MPlist plist = (MPlist) val;
66
67                 if (plist.IsSymbol)
68                   {
69                     if (plist.Symbol == stop)
70                       return;
71                     if (plist.Symbol == target)
72                       {
73                         Key = target;
74                         Val = val;
75                         next = new MPlist ();
76                         return;
77                       }
78                   }
79               }
80           }
81       }
82
83     internal MPlist (MStreamReader reader)
84       {
85         MSymbol key;
86         object val;
87         bool result = reader.ReadElement (out key, out val);
88
89         Key = key;
90         Val = val;
91         if (result)
92           next = new MPlist (reader);
93       }
94
95     private MPlist (MStreamReader reader, int count)
96       {
97         MSymbol key;
98         object val;
99         bool result = reader.ReadElement (out key, out val);
100
101         Key = key;
102         Val = val;
103         if (result && --count > 0)
104           next = new MPlist (reader, count);
105         else
106           next = new MPlist ();
107       }
108
109     private MPlist (MStreamReader reader, MSymbol target, MSymbol stop)
110       {
111         MSymbol key;
112         object val;
113         bool result;
114
115         Key = MSymbol.nil;
116         Val = null;
117         while (true)
118           {
119             result = reader.ReadElement (out key, out val);
120             if (! result)
121               return;
122             if (key == MSymbol.plist)
123               {
124                 MPlist plist = (MPlist) val;
125
126                 if (plist.IsSymbol)
127                   {
128                     if (plist.Symbol == stop)
129                       return;
130                     if (plist.Symbol == target)
131                       {
132                         Key = target;
133                         Val = val;
134                         next = new MPlist ();
135                         return;
136                       }
137                   }
138               }
139           }
140       }
141
142     private MPlist (MSymbol key, object val)
143       {
144         Key = key;
145         Val = val;
146       }
147
148     public bool IsEmpty { get { return next == null; } }
149     public MPlist Next { get { return next; } }
150
151     internal bool IsSymbol { get { return Key == MSymbol.symbol; } }
152     internal bool IsMText { get { return Key == MSymbol.mtext; } }
153     internal bool IsPlist { get { return Key == MSymbol.plist; } }
154     internal bool IsInteger { get { return Key == MSymbol.integer; } }
155
156     internal MSymbol Symbol { get { return (MSymbol) Val; } }
157     internal MText Text { get { return (MText) Val; } }
158     internal MPlist Plist { get { return (MPlist) Val; } }
159     internal int Integer { get { return (int) Val; } }
160
161     public int Count
162     {
163       get
164         {
165           int i = 0;
166
167           for (MPlist p = this; p.next != null; i++, p = p.next);
168           return i;
169         }
170     }
171
172     public MPlist Clone ()
173     {
174       MPlist plist = new MPlist (), pl = plist;
175
176       for (MPlist p = this; p.next != null; p = p.next)
177         pl = pl.Add (p.Key, p.Val);
178       return plist;
179     }
180
181     public override string ToString ()
182     {
183         string str = "(";
184
185         for (MPlist p = this; ! p.IsEmpty; p = p.next)
186           {
187             if (p != this)
188               str += " ";
189             if (p.Key != MSymbol.symbol
190                 && p.Key != MSymbol.integer
191                 && p.Key != MSymbol.plist
192                 && p.Key != MSymbol.mtext)
193               str += p.Key + ":";
194             if (p.Key == MSymbol.mtext)
195               str += "\"" + p.Val + "\"";
196             else
197               str += p.Val;
198           }
199         return str + ")";
200     }
201
202     private MPlist find (MSymbol key)
203     {
204       MPlist p;
205
206       for (p = this; ! p.IsEmpty; p = p.next)
207         if (p.Key == key)
208           break;
209       return p;
210     }
211
212     public MPlist Find (MSymbol key)
213     {
214       MPlist p = find (key);
215
216       return (p.IsEmpty ? null : p);
217     }
218
219     public object Get (MSymbol key)
220     {
221       return find (key).Val;
222     }
223
224     internal MPlist Assq (MSymbol key)
225     {
226       foreach (MPlist p in this)
227         if (p.IsPlist && p.Plist.IsSymbol && p.Plist.Symbol == key)
228           return p;
229       return null;
230     }
231
232     private delegate MPlist MPlistDelegate (MSymbol key, object val);
233
234     private MPlist mplist_op (MPlistDelegate op, object val)
235     {
236       Type type = val.GetType ();
237
238       if (Object.ReferenceEquals (type, typeof (MSymbol)))
239         return op (MSymbol.symbol, val);
240       if (Object.ReferenceEquals (type, typeof (MText)))
241         return op (MSymbol.mtext, val);
242       if (Object.ReferenceEquals (type, typeof (MPlist)))
243         return op (MSymbol.plist, val);
244       if (Object.ReferenceEquals (type, typeof (int)))
245         return op (MSymbol.integer, val);
246       return op (MSymbol.t, val);
247     }
248
249     public MPlist Set (MSymbol key, object val)
250     {
251       if (IsEmpty)
252         Push (key, val);
253       else
254         Val = val;
255       return this;
256     }
257
258     public MPlist Set (object val)
259     {
260       return mplist_op (Set, val);
261     }
262
263     public MPlist Put (MSymbol key, object val)
264     {
265       return find (key).Set (key, val);
266     }
267
268     public MPlist Put (object val)
269     {
270       return mplist_op (Put, val);
271     }
272
273     public MPlist Push (MSymbol key, object val)
274     {
275       MPlist p = new MPlist (Key, Val);
276
277       p.next = this.next;
278       Key = key;
279       Val = val;
280       next = p;
281       return this;
282     }
283
284     public MPlist Push (object val)
285     {
286       return mplist_op (Push, val);
287     }
288
289     public object Pop (out MSymbol key)
290     {
291       key = Key;
292       if (IsEmpty)
293         return null;
294
295       object val = Val;
296
297       Key = next.Key;
298       Val = next.Val;
299       next = next.next;
300       return val;
301     }
302
303     public object Pop ()
304     {
305       MSymbol key;
306       return Pop (out key);
307     }
308
309     public MPlist Add (MSymbol key, object val)
310     {
311       MPlist p;
312
313       for (p = this; ! p.IsEmpty; p = p.next);
314       return p.Push (key, val);
315     }
316
317     public MPlist Clear ()
318     {
319       Key = MSymbol.nil;
320       Val = null;
321       next = null;
322       return this;
323     }
324
325     // Implement IEnumerable interface.
326     //   foreach (MPlist p in plist) { ... }
327
328     public virtual IEnumerator GetEnumerator ()
329     {
330       return new Enumerator (this);
331     }
332
333     private class Enumerator : IEnumerator
334     {
335       private MPlist plist;
336       private MPlist current;
337
338       internal Enumerator (MPlist plist)
339         {
340           this.plist = plist;
341         }
342
343       public object Current
344       {
345         get {
346           if (current == null || current.IsEmpty)
347             throw new InvalidOperationException ();
348           return current;
349         }
350       }
351
352       public void Reset ()
353       {
354         current = null;
355       }
356
357       public bool MoveNext ()
358       {
359         if (current == null)
360           current = plist;
361         else
362           current = current.next;
363         return (! current.IsEmpty);
364       }
365     }
366   }
367
368   public class MStreamReader : StreamReader
369   {
370     private static char[] escaped_char = new char[128];
371     private static int[] hexadecimal = new int[128];
372     private char comment_start;
373     private bool line_oriented;
374
375     public MStreamReader (Stream stream) : base (stream)
376       {
377         comment_start = ';';
378         line_oriented = false;
379       }
380
381     public MStreamReader (Stream stream, char comment_start,
382                           bool line_oriented) : base (stream)
383       {
384         this.comment_start = comment_start;
385         this.line_oriented = line_oriented;
386       }
387
388     static MStreamReader ()
389       {
390         for (int i = 0; i < 128; i++)
391           escaped_char[i] = (char) i;
392         escaped_char['0'] = (char) 0;
393         escaped_char['e'] = (char) 27;
394         escaped_char['a'] = '\a';
395         escaped_char['b'] = '\b';
396         escaped_char['f'] = '\f';
397         escaped_char['n'] = '\n';
398         escaped_char['r'] = '\r';
399         escaped_char['t'] = '\t';
400         escaped_char['v'] = '\v';
401         for (int i = 0; i < 128; i++)
402           hexadecimal[i] = -1;
403         for (int i = '0'; i <= '9'; i++)
404           hexadecimal[i] = i - '0';
405         for (int i = 'A'; i <= 'F'; i++)
406           hexadecimal[i] = hexadecimal[i + 'a' - 'A'] = i -'A' + 10;
407       }
408
409     private int ReadHexadecimal (int max)
410     {
411       int i = 0, c;
412
413       while ((c = Peek ()) >= 0 && c < 128 && (c = hexadecimal[c]) >= 0)
414         {
415           if (max >= 0 && (i * 16) + c >= max)
416             break;
417           Read ();
418           i = (i * 16) + c;
419         }
420       return i;
421     }
422
423     public bool ForwardLine ()
424     {
425       int c;
426       while ((c = Read ()) >=0 && c != '\n');
427       return (c == '\n');
428     }
429
430     public bool SkipSpace (out int c)
431     {
432       while ((c = Peek ()) == ' ' && c == '\t' && c == '\f')
433         Read ();
434       return (c >= 0);
435     }
436
437     public bool PeekChar (out int c)
438     {
439       while ((c = Peek ()) != -1)
440         {
441           if (c == comment_start)
442             ForwardLine ();
443           else if (c != ' ' && c != '\t' && c != '\n')
444             return true;
445           else if (c == '\n' && line_oriented)
446             return false;
447           else
448             Read ();
449         }
450       return false;
451     }
452
453     public bool ReadInteger (out int i)
454     {
455       int c = Peek ();
456
457       i = 0;
458       if (c < 0)
459         return false;
460       if (c == '0')
461         {
462           Read ();
463           c = Peek ();
464           if (c == 'x')
465             {
466               Read ();
467               i = ReadHexadecimal (-1);
468               return true;
469             }
470         }
471       while ((c = Peek ()) >= '0' && c <= '9')
472         i = (i * 10) + (Read () - '0');
473       return true;
474     }
475
476     public bool ReadChar (out int c)
477     {
478       c = Read ();
479       if (c < 0 || (line_oriented && c == '\n'))
480         return false;
481       if (c == '\\')
482         {
483           c = Read ();
484           if (c == '\n')
485             return ReadChar (out c);
486           if (c < 0)
487             c = '\\';
488           else if (c == 'x' || c == 'u' || c == 'U')
489             c = ReadHexadecimal (0x10FFFF);
490           else if (c < 128)
491             c = escaped_char[c];
492         }
493       return true;
494     }
495
496     private bool read_string (out string str, int prefix, bool for_symbol)
497     {
498       char[] buf = new char[256];
499       int c;
500       int i = 0;
501
502       str = null;
503       if (prefix >= 0)
504         buf[i++] = (char) prefix;
505       while ((c = Peek ()) >= 0
506              && c != '\"'
507              && (! for_symbol
508                  || (c != '(' && c != ')' && c != ' ' && c != '\t' && c != '\n')))
509         {
510           if (! ReadChar (out c))
511             break;
512           if (c < 0x10000)
513             {
514               buf[i++] = (char) c;
515             }
516           else
517             {
518               buf[i++] = (char) (0xD800 + ((c - 0x10000) >> 10));
519               buf[i++] = (char) (0xDC00 + ((c - 0x10000) & 0x3FF));
520             }
521           if (i >= 255)
522             {
523               if (str == null)
524                 str = new string (buf, 0, i);
525               else
526                 str += new string (buf, 0, i);
527               i = 0;
528             }
529         }
530       if (c == '\"' && ! for_symbol)
531         Read ();
532       if (i > 0)
533         {
534           if (str == null)
535             str = new string (buf, 0, i);
536           else
537             str += new string (buf, 0, i);
538         }
539       return (str != null);
540     }
541
542     public bool ReadString (out string str)
543     {
544       return read_string (out str, -1, false);
545     }
546
547     public bool ReadMText (out MText mt)
548     {
549       int c = Peek ();
550
551       if (c == '"')
552         {
553           string str;
554
555           Read ();
556           if (read_string (out str, -1, false))
557             mt = new MText (str);
558           else
559             mt = new MText ();
560           return true;
561         }
562       mt = new MText ();
563       if (c == '\\')
564         {
565           while ((c = Peek ()) == '\\')
566             {
567               Read ();
568               c = Peek ();
569               if (c != 'x')
570                 break;
571               Read ();
572               mt.Cat (ReadHexadecimal (0x10FFFF));
573             }
574           return true;
575         }
576       return false;
577     }
578
579     public bool ReadSymbol (out MSymbol sym, int prefix)
580     {
581       string str;
582
583       if (read_string (out str, prefix, true))
584         {
585           sym = MSymbol.Of (str);
586           return true;
587         }
588       sym = MSymbol.nil;
589       return false;
590     }
591
592     internal bool ReadElement (out MSymbol key, out object val)
593     {
594       int c;
595
596       if (! PeekChar (out c))
597         {
598           val = null;
599           key = MSymbol.nil;
600           return false;
601         }
602
603       if (c == '(')
604         {
605           Read ();
606           val = new MPlist (this);
607           key = MSymbol.plist;
608         }
609       else if (c == '"' || c == '\\')
610         {
611           MText mt;
612           ReadMText (out mt);
613           val = mt;
614           key = MSymbol.mtext;
615         }
616       else if (c >= '0' && c <= '9')
617         {
618           int i;
619           ReadInteger (out i);
620           val = i;
621           key = MSymbol.integer;
622         }
623       else if (c == '-')
624         {
625           Read ();
626           c = Peek ();
627           if (c >= '0' && c <= '9')
628             {
629               int i;
630               ReadInteger (out i);
631               val = - i;
632               key = MSymbol.integer;
633             }
634           else
635             {
636               MSymbol sym;
637
638               ReadSymbol (out sym, '-');
639               val = sym;
640               key = MSymbol.symbol;
641             }
642         }
643       else if (c == '?')
644         {
645           Read ();
646           if (ReadChar (out c))
647             {
648               val = c;
649               key = MSymbol.integer;
650             }
651           else
652             {
653               val = null;
654               key = MSymbol.nil;
655             }
656         }
657       else if (c == '#')
658         {
659           Read ();
660           if ((c = Peek ()) == 'x' || c == 'u')
661             {
662               Read ();
663               val = ReadHexadecimal (-1);
664               key = MSymbol.integer;
665             }
666           else
667             {
668               MSymbol sym;
669
670               ReadSymbol (out sym, '#');
671               val = sym;
672               key = MSymbol.symbol;
673             }
674         }
675       else if (c == ')')
676         {
677           Read ();
678           val = null;
679           key = MSymbol.nil;
680           return false;
681         }
682       else
683         {
684           MSymbol sym;
685
686           ReadSymbol (out sym, -1);
687           val = sym;
688           key = MSymbol.symbol;
689         }
690       return true;
691     }
692   }
693 }