*** 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 (MStreamReader reader)
22       {
23         MSymbol key;
24         object val;
25         bool result = reader.ReadElement (out key, out val);
26
27         Key = key;
28         Val = val;
29         if (result)
30           next = new MPlist (reader);
31       }
32
33     private MPlist (MSymbol key, object val)
34       {
35         Key = key;
36         Val = val;
37       }
38
39     public bool IsEmpty { get { return next == null; } }
40     public MPlist Next { get { return next; } }
41
42     internal bool IsSymbol { get { return Key == MSymbol.symbol; } }
43     internal bool IsMText { get { return Key == MSymbol.mtext; } }
44     internal bool IsPlist { get { return Key == MSymbol.plist; } }
45     internal bool IsInteger { get { return Key == MSymbol.integer; } }
46
47     internal MSymbol Symbol { get { return (MSymbol) Val; } }
48     internal MText Text { get { return (MText) Val; } }
49     internal MPlist Plist { get { return (MPlist) Val; } }
50     internal int Integer { get { return (int) Val; } }
51
52     public int Count
53     {
54       get
55         {
56           int i = 0;
57
58           for (MPlist p = this; p.next != null; i++, p = p.next);
59           return i;
60         }
61     }
62
63     public MPlist Clone ()
64     {
65       MPlist plist = new MPlist (), pl = plist;
66
67       for (MPlist p = this; p.next != null; p = p.next)
68         pl = pl.Add (p.Key, p.Val);
69       return plist;
70     }
71
72     public override string ToString ()
73     {
74         string str = "(";
75
76         for (MPlist p = this; ! p.IsEmpty; p = p.next)
77           {
78             if (p != this)
79               str += " ";
80             if (p.Key != MSymbol.symbol
81                 && p.Key != MSymbol.integer
82                 && p.Key != MSymbol.plist
83                 && p.Key != MSymbol.mtext)
84               str += p.Key + ":";
85             str += p.Val;
86           }
87         return str + ")";
88     }
89
90     private MPlist find (MSymbol key)
91     {
92       MPlist p;
93
94       for (p = this; ! p.IsEmpty; p = p.next)
95         if (p.Key == key)
96           break;
97       return p;
98     }
99
100     public MPlist Find (MSymbol key)
101     {
102       MPlist p = find (key);
103
104       return (p.IsEmpty ? null : p);
105     }
106
107     public object Get (MSymbol key)
108     {
109       return find (key).Val;
110     }
111
112     private delegate MPlist MPlistDelegate (MSymbol key, object val);
113
114     private MPlist mplist_op (MPlistDelegate op, object val)
115     {
116       Type type = val.GetType ();
117
118       if (Object.ReferenceEquals (type, typeof (MSymbol)))
119         return op (MSymbol.symbol, val);
120       if (Object.ReferenceEquals (type, typeof (MText)))
121         return op (MSymbol.mtext, val);
122       if (Object.ReferenceEquals (type, typeof (MPlist)))
123         return op (MSymbol.plist, val);
124       if (Object.ReferenceEquals (type, typeof (int)))
125         return op (MSymbol.integer, val);
126       return op (MSymbol.t, val);
127     }
128
129     public MPlist Set (MSymbol key, object val)
130     {
131       if (IsEmpty)
132         Push (key, val);
133       else
134         Val = val;
135       return this;
136     }
137
138     public MPlist Set (object val)
139     {
140       return mplist_op (Set, val);
141     }
142
143     public MPlist Put (MSymbol key, object val)
144     {
145       return find (key).Set (key, val);
146     }
147
148     public MPlist Put (object val)
149     {
150       return mplist_op (Put, val);
151     }
152
153     public MPlist Push (MSymbol key, object val)
154     {
155       MPlist p = new MPlist (Key, Val);
156
157       p.next = this.next;
158       Key = key;
159       Val = val;
160       next = p;
161       return this;
162     }
163
164     public MPlist Push (object val)
165     {
166       return mplist_op (Push, val);
167     }
168
169     public object Pop (out MSymbol key)
170     {
171       key = Key;
172       if (IsEmpty)
173         return null;
174
175       object val = Val;
176
177       Key = next.Key;
178       Val = next.Val;
179       next = next.next;
180       return val;
181     }
182
183     public object Pop ()
184     {
185       MSymbol key;
186       return Pop (out key);
187     }
188
189     public MPlist Add (MSymbol key, object val)
190     {
191       MPlist p;
192
193       for (p = this; ! p.IsEmpty; p = p.next);
194       return p.Push (key, val);
195     }
196
197     public MPlist Clear ()
198     {
199       Key = MSymbol.nil;
200       Val = null;
201       next = null;
202       return this;
203     }
204
205     // Implement IEnumerable interface.
206     //   foreach (MPlist p in plist) { ... }
207
208     public virtual IEnumerator GetEnumerator ()
209     {
210       return new Enumerator (this);
211     }
212
213     private class Enumerator : IEnumerator
214     {
215       private MPlist plist;
216       private MPlist current;
217
218       internal Enumerator (MPlist plist)
219         {
220           this.plist = plist;
221         }
222
223       public object Current
224       {
225         get {
226           if (current == null || current.IsEmpty)
227             throw new InvalidOperationException ();
228           return current;
229         }
230       }
231
232       public void Reset ()
233       {
234         current = null;
235       }
236
237       public bool MoveNext ()
238       {
239         if (current == null)
240           current = plist;
241         else
242           current = current.next;
243         return (! current.IsEmpty);
244       }
245     }
246   }
247
248   public class MStreamReader : StreamReader
249   {
250     private static char[] escaped_char = new char[128];
251     private static int[] hexadecimal = new int[128];
252
253     public MStreamReader (Stream stream) : base (stream)
254       {
255       }
256
257     static MStreamReader ()
258       {
259         for (int i = 0; i < 128; i++)
260           escaped_char[i] = (char) i;
261         escaped_char['e'] = (char) 27;
262         escaped_char['b'] = '\b';
263         escaped_char['f'] = '\f';
264         escaped_char['n'] = '\n';
265         escaped_char['r'] = '\r';
266         escaped_char['t'] = '\t';
267         escaped_char['\\'] = '\\';
268         for (int i = 0; i < 128; i++)
269           hexadecimal[i] = -1;
270         for (int i = '0'; i <= '9'; i++)
271           hexadecimal[i] = i - '0';
272         for (int i = 'A'; i <= 'F'; i++)
273           hexadecimal[i] = hexadecimal[i + 'a' - 'A'] = i -'A' + 10;
274       }
275
276     internal int PeekChar ()
277     {
278       bool comment = false;
279       int c;
280
281       while ((c = Peek ()) != -1)
282         {
283           if (comment)
284             {
285               if ((c = Read ()) == '\n')
286                 comment = false;
287             }
288           else
289             {
290               if (c == ';')
291                 comment = true;
292               else if (c != ' ' && c != '\t' && c != '\n')
293                 return c;
294               Read ();
295             }
296         }
297       return c;
298     }
299
300     internal int ReadHexadecimal ()
301     {
302       int i = 0, c;
303
304       while ((c = Peek ()) >= 0 && c < 128 && (c = hexadecimal[c]) >= 0)
305         {
306           Read ();
307           i = (i * 16) + c;
308         }
309       return i;
310     }
311
312     internal int ReadInteger ()
313     {
314       int i = 0, c;
315
316       while ((c = Peek ()) >= '0' && c <= '9')
317         i = (i * 10) + (Read () - '0');
318       return i;
319     }
320
321     internal int ReadChar ()
322     {
323       int c = Read ();
324
325       if (c == '\\')
326         {
327           c = Read ();
328           if (c == -1)
329             return -1;
330           if (c == 'x' || c == 'u')
331             return ReadHexadecimal ();
332           if (c < 128)
333             c = escaped_char[c];
334         }
335       return c;
336     }
337
338     internal MText ReadMtext ()
339     {
340       MText mt = new MText ();
341       int c;
342
343       while ((c = Peek ()) != -1 && c != '"')
344         {
345           if (c == '\\')
346             {
347               c = ReadChar ();
348               if (Peek () == '\n')
349                 {
350                   ReadChar ();
351                   continue;
352                 }
353               if (c == -1)
354                 {
355                   mt.Cat ('\\');
356                   break;
357                 }
358               mt.Cat (c);
359             }
360           else
361             mt.Cat (Read ());
362         }
363       if (c == '"')
364         Read ();
365       return mt;
366     }
367
368     internal string ReadSymbolName ()
369     {
370       int c = Peek ();
371
372       if (c == -1 || c == '(' || c == ' ' || c == '\n' || c == '"')
373         return "";
374       Read ();
375       if (c == '\\')
376         {
377           c = Read ();
378           if (c == -1)
379             c = '\\';
380         }
381       return (char) c + ReadSymbolName ();
382     }
383
384     internal bool ReadElement (out MSymbol key, out object val)
385     {
386       int c = PeekChar ();
387
388       if (c == '(')
389         {
390           Read ();
391           val = new MPlist (this);
392           key = MSymbol.plist;
393         }
394       else if (c == '"')
395         {
396           Read ();
397           val = ReadMtext ();
398           key = MSymbol.mtext;
399         }
400       else if (c >= '0' && c <= '9')
401         {
402           int i = ReadInteger ();
403
404           val = i;
405           key = MSymbol.integer;
406         }
407       else if (c == '-')
408         {
409           Read ();
410           c = Peek ();
411           if (c >= '0' && c <= '9')
412             {
413               int i = ReadInteger ();
414               val = - i;
415               key = MSymbol.integer;
416             }
417           else
418             {
419               string str = ReadSymbolName ();
420
421               val = new MSymbol ("-" + str);
422               key = MSymbol.symbol;
423             }
424         }
425       else if (c == '?')
426         {
427           Read ();
428           val = ReadChar ();
429           key = MSymbol.integer;
430         }
431       else if (c == '#')
432         {
433           Read ();
434           if ((c = Peek ()) == 'x' || c == 'u')
435             {
436               Read ();
437               val = ReadHexadecimal ();
438               key = MSymbol.integer;
439             }
440           else
441             {
442               string str = ReadSymbolName ();
443
444               val = new MSymbol ("#" + (char) c + str);
445               key = MSymbol.symbol;
446             }
447         }
448       else if (c == -1 || c == ')')
449         {
450           if (c == ')')
451             Read ();
452           val = null;
453           key = MSymbol.nil;
454           return false;
455         }
456       else
457         {
458           val = new MSymbol (ReadSymbolName ());
459           key = MSymbol.symbol;
460         }
461       return true;
462     }
463   }
464 }