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