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