Merge pull request #555 from jack-pappas/sigaltstack-patch
[mono.git] / mcs / class / System.ServiceModel.Web / System.Runtime.Serialization.Json / JavaScriptReader.cs
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.Globalization;
5 using System.IO;
6 using System.Linq;
7 using System.Text;
8
9 namespace System.Runtime.Serialization.Json
10 {
11         internal class JavaScriptReader
12         {
13                 TextReader r;
14                 int line = 1, column = 0;
15 //              bool raise_on_number_error; // FIXME: use it
16
17                 public JavaScriptReader (TextReader reader, bool raiseOnNumberError)
18                 {
19                         if (reader == null)
20                                 throw new ArgumentNullException ("reader");
21                         this.r = reader;
22 //                      raise_on_number_error = raiseOnNumberError;
23                 }
24
25                 public object Read ()
26                 {
27                         object v = ReadCore ();
28                         SkipSpaces ();
29                         if (r.Read () >= 0)
30                                 throw JsonError (String.Format ("extra characters in JSON input"));
31                         return v;
32                 }
33
34                 object ReadCore ()
35                 {
36                         SkipSpaces ();
37                         int c = PeekChar ();
38                         if (c < 0)
39                                 throw JsonError ("Incomplete JSON input");
40                         switch (c) {
41                         case '[':
42                                 ReadChar ();
43                                 var list = new List<object> ();
44                                 SkipSpaces ();
45                                 if (PeekChar () == ']') {
46                                         ReadChar ();
47                                         return list;
48                                 }
49                                 while (true) {
50                                         list.Add (ReadCore ());
51                                         SkipSpaces ();
52                                         c = PeekChar ();
53                                         if (c != ',')
54                                                 break;
55                                         ReadChar ();
56                                         continue;
57                                 }
58                                 if (ReadChar () != ']')
59                                         throw JsonError ("JSON array must end with ']'");
60                                 return list.ToArray ();
61                         case '{':
62                                 ReadChar ();
63                                 var obj = new Dictionary<string,object> ();
64                                 SkipSpaces ();
65                                 if (PeekChar () == '}') {
66                                         ReadChar ();
67                                         return obj;
68                                 }
69                                 while (true) {
70                                         SkipSpaces ();
71                                         if (PeekChar () == '}')
72                                                 break;
73                                         string name = ReadStringLiteral ();
74                                         SkipSpaces ();
75                                         Expect (':');
76                                         SkipSpaces ();
77                                         obj [name] = ReadCore (); // it does not reject duplicate names.
78                                         SkipSpaces ();
79                                         c = ReadChar ();
80                                         if (c == ',')
81                                                 continue;
82                                         if (c == '}')
83                                                 break;
84                                 }
85 #if MONOTOUCH
86                                 int idx = 0;
87                                 KeyValuePair<string, object> [] ret = new KeyValuePair<string, object>[obj.Count];
88                                 foreach (KeyValuePair <string, object> kvp in obj)
89                                         ret [idx++] = kvp;
90
91                                 return ret;
92 #else
93                                 return obj.ToArray ();
94 #endif
95                         case 't':
96                                 Expect ("true");
97                                 return true;
98                         case 'f':
99                                 Expect ("false");
100                                 return false;
101                         case 'n':
102                                 Expect ("null");
103                                 // FIXME: what should we return?
104                                 return (string) null;
105                         case '"':
106                                 return ReadStringLiteral ();
107                         default:
108                                 if ('0' <= c && c <= '9' || c == '-')
109                                         return ReadNumericLiteral ();
110                                 else
111                                         throw JsonError (String.Format ("Unexpected character '{0}'", (char) c));
112                         }
113                 }
114
115                 int peek;
116                 bool has_peek;
117                 bool prev_lf;
118
119                 int PeekChar ()
120                 {
121                         if (!has_peek) {
122                                 peek = r.Read ();
123                                 has_peek = true;
124                         }
125                         return peek;
126                 }
127
128                 int ReadChar ()
129                 {
130                         int v = has_peek ? peek : r.Read ();
131
132                         has_peek = false;
133
134                         if (prev_lf) {
135                                 line++;
136                                 column = 0;
137                                 prev_lf = false;
138                         }
139
140                         if (v == '\n')
141                                 prev_lf = true;
142                         column++;
143
144                         return v;
145                 }
146
147                 void SkipSpaces ()
148                 {
149                         while (true) {
150                                 switch (PeekChar ()) {
151                                 case ' ': case '\t': case '\r': case '\n':
152                                         ReadChar ();
153                                         continue;
154                                 default:
155                                         return;
156                                 }
157                         }
158                 }
159
160                 // It could return either int, long or decimal, depending on the parsed value.
161                 object ReadNumericLiteral ()
162                 {
163                         bool negative = false;
164                         if (PeekChar () == '-') {
165                                 negative = true;
166                                 ReadChar ();
167                                 if (PeekChar () < 0)
168                                         throw JsonError ("Invalid JSON numeric literal; extra negation");
169                         }
170
171                         int c;
172                         decimal val = 0;
173                         int x = 0;
174                         bool zeroStart = PeekChar () == '0';
175                         for (; ; x++) {
176                                 c = PeekChar ();
177                                 if (c < '0' || '9' < c)
178                                         break;
179                                 val = val * 10 + (c - '0');
180                                 ReadChar ();
181                                 if (zeroStart && x == 1 && c == '0')
182                                         throw JsonError ("leading multiple zeros are not allowed");
183                         }
184
185                         // fraction
186
187                         bool hasFrac = false;
188                         decimal frac = 0;
189                         int fdigits = 0;
190                         if (PeekChar () == '.') {
191                                 hasFrac = true;
192                                 ReadChar ();
193                                 if (PeekChar () < 0)
194                                         throw JsonError ("Invalid JSON numeric literal; extra dot");
195                                 decimal d = 10;
196                                 while (true) {
197                                         c = PeekChar ();
198                                         if (c < '0' || '9' < c)
199                                                 break;
200                                         ReadChar ();
201                                         frac += (c - '0') / d;
202                                         d *= 10;
203                                         fdigits++;
204                                 }
205                                 if (fdigits == 0)
206                                         throw JsonError ("Invalid JSON numeric literal; extra dot");
207                         }
208                         frac = Decimal.Round (frac, fdigits);
209
210                         c = PeekChar ();
211                         if (c != 'e' && c != 'E') {
212                                 if (!hasFrac) {
213                                         if (negative && int.MinValue <= -val ||
214                                             !negative && val <= int.MaxValue)
215                                                 return (int) (negative ? -val : val);
216                                         if (negative && long.MinValue <= -val ||
217                                             !negative && val <= long.MaxValue)
218                                                 return (long) (negative ? -val : val);
219                                 }
220                                 var v = val + frac;
221                                 return negative ? -v : v;
222                         }
223
224                         // exponent
225
226                         ReadChar ();
227
228                         int exp = 0;
229                         if (PeekChar () < 0)
230                                 throw new ArgumentException ("Invalid JSON numeric literal; incomplete exponent");
231                         
232                         bool negexp = false;
233                         c = PeekChar ();
234                         if (c == '-') {
235                                 ReadChar ();
236                                 negexp = true;
237                         }
238                         else if (c == '+')
239                                 ReadChar ();
240
241                         if (PeekChar () < 0)
242                                 throw JsonError ("Invalid JSON numeric literal; incomplete exponent");
243                         while (true) {
244                                 c = PeekChar ();
245                                 if (c < '0' || '9' < c)
246                                         break;
247                                 exp = exp * 10 + (c - '0');
248                                 ReadChar ();
249                         }
250                         // it is messy to handle exponent, so I just use Decimal.Parse() with assured JSON format.
251                         if (negexp)
252                                 return new Decimal ((double) (val + frac) / Math.Pow (10, exp));
253                         int [] bits = Decimal.GetBits (val + frac);
254                         return new Decimal (bits [0], bits [1], bits [2], negative, (byte) exp);
255                 }
256
257                 StringBuilder vb = new StringBuilder ();
258
259                 string ReadStringLiteral ()
260                 {
261                         if (PeekChar () != '"')
262                                 throw JsonError ("Invalid JSON string literal format");
263
264                         ReadChar ();
265                         vb.Length = 0;
266                         while (true) {
267                                 int c = ReadChar ();
268                                 if (c < 0)
269                                         throw JsonError ("JSON string is not closed");
270                                 if (c == '"')
271                                         return vb.ToString ();
272                                 else if (c != '\\') {
273                                         vb.Append ((char) c);
274                                         continue;
275                                 }
276
277                                 // escaped expression
278                                 c = ReadChar ();
279                                 if (c < 0)
280                                         throw JsonError ("Invalid JSON string literal; incomplete escape sequence");
281                                 switch (c) {
282                                 case '"':
283                                 case '\\':
284                                 case '/':
285                                         vb.Append ((char) c);
286                                         break;
287                                 case 'b':
288                                         vb.Append ('\x8');
289                                         break;
290                                 case 'f':
291                                         vb.Append ('\f');
292                                         break;
293                                 case 'n':
294                                         vb.Append ('\n');
295                                         break;
296                                 case 'r':
297                                         vb.Append ('\r');
298                                         break;
299                                 case 't':
300                                         vb.Append ('\t');
301                                         break;
302                                 case 'u':
303                                         ushort cp = 0;
304                                         for (int i = 0; i < 4; i++) {
305                                                 cp <<= 4;
306                                                 if ((c = ReadChar ()) < 0)
307                                                         throw JsonError ("Incomplete unicode character escape literal");
308                                                 if ('0' <= c && c <= '9')
309                                                         cp += (ushort) (c - '0');
310                                                 if ('A' <= c && c <= 'F')
311                                                         cp += (ushort) (c - 'A' + 10);
312                                                 if ('a' <= c && c <= 'f')
313                                                         cp += (ushort) (c - 'a' + 10);
314                                         }
315                                         vb.Append ((char) cp);
316                                         break;
317                                 default:
318                                         throw JsonError ("Invalid JSON string literal; unexpected escape character");
319                                 }
320                         }
321                 }
322
323                 void Expect (char expected)
324                 {
325                         int c;
326                         if ((c = ReadChar ()) != expected)
327                                 throw JsonError (String.Format ("Expected '{0}', got '{1}'", expected, (char) c));
328                 }
329
330                 void Expect (string expected)
331                 {
332                         for (int i = 0; i < expected.Length; i++)
333                                 if (ReadChar () != expected [i])
334                                         throw JsonError (String.Format ("Expected '{0}', differed at {1}", expected, i));
335                 }
336
337                 Exception JsonError (string msg)
338                 {
339                         return new ArgumentException (String.Format ("{0}. At line {1}, column {2}", msg, line, column));
340                 }
341         }
342 }