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