b25564cc9babb41ae8edafc2f0168d0b13e17e40
[mono.git] / mcs / mcs / cs-tokenizer.cs
1 //\r
2 // cs-tokenizer.cs: The Tokenizer for the C# compiler\r
3 //                  This also implements the preprocessor\r
4 //\r
5 // Author: Miguel de Icaza (miguel@gnu.org)\r
6 //\r
7 // Licensed under the terms of the GNU GPL\r
8 //\r
9 // (C) 2001 Ximian, Inc (http://www.ximian.com)\r
10 //\r
11 \r
12 /*\r
13  * TODO:\r
14  *   Make sure we accept the proper Unicode ranges, per the spec.\r
15  *   Report error 1032\r
16 */\r
17 \r
18 using System;\r
19 using System.Text;\r
20 using System.Collections;\r
21 using System.IO;\r
22 using System.Globalization;\r
23 \r
24 namespace Mono.CSharp\r
25 {\r
26         /// <summary>\r
27         ///    Tokenizer for C# source code. \r
28         /// </summary>\r
29 \r
30         public class Tokenizer : yyParser.yyInput\r
31         {\r
32                 StreamReader reader;\r
33                 public string ref_name;\r
34                 public int ref_line = 1;\r
35                 public int line = 1;\r
36                 public int col = 1;\r
37                 public int current_token;\r
38                 bool handle_get_set = false;\r
39                 bool handle_remove_add = false;\r
40                 \r
41                 //\r
42                 // Returns a verbose representation of the current location\r
43                 //\r
44                 public string location {\r
45                         get {\r
46                                 string det;\r
47 \r
48                                 if (current_token == Token.ERROR)\r
49                                         det = "detail: " + error_details;\r
50                                 else\r
51                                         det = "";\r
52                                 \r
53                                 // return "Line:     "+line+" Col: "+col + "\n" +\r
54                                 //       "VirtLine: "+ref_line +\r
55                                 //       " Token: "+current_token + " " + det;\r
56 \r
57                                 return ref_name + " " + "(" + line + "," + col + "), Token:" + current_token + " " + det;\r
58                         }\r
59                 }\r
60 \r
61                 public bool PropertyParsing {\r
62                         get {\r
63                                 return handle_get_set;\r
64                         }\r
65 \r
66                         set {\r
67                                 handle_get_set = value;\r
68                         }\r
69                 }\r
70 \r
71                 public bool EventParsing {\r
72                         get {\r
73                                 return handle_remove_add;\r
74                         }\r
75 \r
76                         set {\r
77                                 handle_remove_add = value;\r
78                         }\r
79                 }\r
80                 \r
81                 //\r
82                 // Class variables\r
83                 // \r
84                 static Hashtable keywords;\r
85                 static NumberStyles styles;\r
86                 static NumberFormatInfo csharp_format_info;\r
87                 \r
88                 //\r
89                 // Values for the associated token returned\r
90                 //\r
91                 System.Text.StringBuilder number;\r
92                 int putback_char;\r
93                 Object val;\r
94 \r
95                 //\r
96                 // Pre-processor\r
97                 //\r
98                 Hashtable defines;\r
99 \r
100                 const int TAKING        = 1;\r
101                 const int TAKEN_BEFORE  = 2;\r
102                 const int ELSE_SEEN     = 4;\r
103                 const int PARENT_TAKING = 8;\r
104                 \r
105                 //\r
106                 // pre-processor if stack state:\r
107                 //\r
108                 Stack ifstack;\r
109                 \r
110                 //\r
111                 // Details about the error encoutered by the tokenizer\r
112                 //\r
113                 string error_details;\r
114                 \r
115                 public string error {\r
116                         get {\r
117                                 return error_details;\r
118                         }\r
119                 }\r
120                 \r
121                 public int Line {\r
122                         get {\r
123                                 return line;\r
124                         }\r
125                 }\r
126 \r
127                 public int Col {\r
128                         get {\r
129                                 return col;\r
130                         }\r
131                 }\r
132                 \r
133                 static void InitTokens ()\r
134                 {\r
135                         keywords = new Hashtable ();\r
136 \r
137                         keywords.Add ("abstract", Token.ABSTRACT);\r
138                         keywords.Add ("as", Token.AS);\r
139                         keywords.Add ("add", Token.ADD);\r
140                         keywords.Add ("base", Token.BASE);\r
141                         keywords.Add ("bool", Token.BOOL);\r
142                         keywords.Add ("break", Token.BREAK);\r
143                         keywords.Add ("byte", Token.BYTE);\r
144                         keywords.Add ("case", Token.CASE);\r
145                         keywords.Add ("catch", Token.CATCH);\r
146                         keywords.Add ("char", Token.CHAR);\r
147                         keywords.Add ("checked", Token.CHECKED);\r
148                         keywords.Add ("class", Token.CLASS);\r
149                         keywords.Add ("const", Token.CONST);\r
150                         keywords.Add ("continue", Token.CONTINUE);\r
151                         keywords.Add ("decimal", Token.DECIMAL);\r
152                         keywords.Add ("default", Token.DEFAULT);\r
153                         keywords.Add ("delegate", Token.DELEGATE);\r
154                         keywords.Add ("do", Token.DO);\r
155                         keywords.Add ("double", Token.DOUBLE);\r
156                         keywords.Add ("else", Token.ELSE);\r
157                         keywords.Add ("enum", Token.ENUM);\r
158                         keywords.Add ("event", Token.EVENT);\r
159                         keywords.Add ("explicit", Token.EXPLICIT);\r
160                         keywords.Add ("extern", Token.EXTERN);\r
161                         keywords.Add ("false", Token.FALSE);\r
162                         keywords.Add ("finally", Token.FINALLY);\r
163                         keywords.Add ("fixed", Token.FIXED);\r
164                         keywords.Add ("float", Token.FLOAT);\r
165                         keywords.Add ("for", Token.FOR);\r
166                         keywords.Add ("foreach", Token.FOREACH);\r
167                         keywords.Add ("goto", Token.GOTO);\r
168                         keywords.Add ("get", Token.GET);\r
169                         keywords.Add ("if", Token.IF);\r
170                         keywords.Add ("implicit", Token.IMPLICIT);\r
171                         keywords.Add ("in", Token.IN);\r
172                         keywords.Add ("int", Token.INT);\r
173                         keywords.Add ("interface", Token.INTERFACE);\r
174                         keywords.Add ("internal", Token.INTERNAL);\r
175                         keywords.Add ("is", Token.IS);\r
176                         keywords.Add ("lock", Token.LOCK);\r
177                         keywords.Add ("long", Token.LONG);\r
178                         keywords.Add ("namespace", Token.NAMESPACE);\r
179                         keywords.Add ("new", Token.NEW);\r
180                         keywords.Add ("null", Token.NULL);\r
181                         keywords.Add ("object", Token.OBJECT);\r
182                         keywords.Add ("operator", Token.OPERATOR);\r
183                         keywords.Add ("out", Token.OUT);\r
184                         keywords.Add ("override", Token.OVERRIDE);\r
185                         keywords.Add ("params", Token.PARAMS);\r
186                         keywords.Add ("private", Token.PRIVATE);\r
187                         keywords.Add ("protected", Token.PROTECTED);\r
188                         keywords.Add ("public", Token.PUBLIC);\r
189                         keywords.Add ("readonly", Token.READONLY);\r
190                         keywords.Add ("ref", Token.REF);\r
191                         keywords.Add ("remove", Token.REMOVE);\r
192                         keywords.Add ("return", Token.RETURN);\r
193                         keywords.Add ("sbyte", Token.SBYTE);\r
194                         keywords.Add ("sealed", Token.SEALED);\r
195                         keywords.Add ("set", Token.SET);\r
196                         keywords.Add ("short", Token.SHORT);\r
197                         keywords.Add ("sizeof", Token.SIZEOF);\r
198                         keywords.Add ("static", Token.STATIC);\r
199                         keywords.Add ("string", Token.STRING);\r
200                         keywords.Add ("struct", Token.STRUCT);\r
201                         keywords.Add ("switch", Token.SWITCH);\r
202                         keywords.Add ("this", Token.THIS);\r
203                         keywords.Add ("throw", Token.THROW);\r
204                         keywords.Add ("true", Token.TRUE);\r
205                         keywords.Add ("try", Token.TRY);\r
206                         keywords.Add ("typeof", Token.TYPEOF);\r
207                         keywords.Add ("uint", Token.UINT);\r
208                         keywords.Add ("ulong", Token.ULONG);\r
209                         keywords.Add ("unchecked", Token.UNCHECKED);\r
210                         keywords.Add ("unsafe", Token.UNSAFE);\r
211                         keywords.Add ("ushort", Token.USHORT);\r
212                         keywords.Add ("using", Token.USING);\r
213                         keywords.Add ("virtual", Token.VIRTUAL);\r
214                         keywords.Add ("void", Token.VOID);\r
215                         keywords.Add ("while", Token.WHILE);\r
216                 }\r
217 \r
218                 //\r
219                 // Class initializer\r
220                 // \r
221                 static Tokenizer ()\r
222                 {\r
223                         InitTokens ();\r
224                         csharp_format_info = new NumberFormatInfo ();\r
225                         csharp_format_info.CurrencyDecimalSeparator = ".";\r
226                         styles = NumberStyles.AllowExponent | NumberStyles.AllowDecimalPoint;\r
227                 }\r
228 \r
229                 bool is_keyword (string name)\r
230                 {\r
231                         bool res;\r
232                         \r
233                         res = keywords.Contains (name);\r
234                         if (handle_get_set == false && (name == "get" || name == "set"))\r
235                                 return false;\r
236                         if (handle_remove_add == false && (name == "remove" || name == "add"))\r
237                                 return false;\r
238                         return res;\r
239                 }\r
240 \r
241                 int GetKeyword (string name)\r
242                 {\r
243                         return (int) (keywords [name]);\r
244                 }\r
245 \r
246                 public Location Location {\r
247                         get {\r
248                                 return new Location (ref_line);\r
249                         }\r
250                 }\r
251                 \r
252                 public Tokenizer (System.IO.Stream input, string fname, ArrayList defs)\r
253                 {\r
254                         this.ref_name = fname;\r
255                         reader = new System.IO.StreamReader (input);\r
256                         putback_char = -1;\r
257 \r
258                         if (defs != null){\r
259                                 defines = new Hashtable ();\r
260                                 foreach (string def in defs)\r
261                                         defines [def] = true;\r
262                         }\r
263 \r
264                         //\r
265                         // FIXME: This could be `Location.Push' but we have to\r
266                         // find out why the MS compiler allows this\r
267                         //\r
268                         Mono.CSharp.Location.Push (fname);\r
269                 }\r
270 \r
271                 bool is_identifier_start_character (char c)\r
272                 {\r
273                         return Char.IsLetter (c) || c == '_' ;\r
274                 }\r
275 \r
276                 bool is_identifier_part_character (char c)\r
277                 {\r
278                         return (Char.IsLetter (c) || Char.IsDigit (c) || c == '_');\r
279                 }\r
280 \r
281                 int is_punct (char c, ref bool doread)\r
282                 {\r
283                         int idx = "{}[](),:;~+-*/%&|^!=<>?".IndexOf (c);\r
284                         int d;\r
285                         int t;\r
286 \r
287                         doread = false;\r
288 \r
289                         switch (c){\r
290                         case '{':\r
291                                 return Token.OPEN_BRACE;\r
292                         case '}':\r
293                                 return Token.CLOSE_BRACE;\r
294                         case '[':\r
295                                 return Token.OPEN_BRACKET;\r
296                         case ']':\r
297                                 return Token.CLOSE_BRACKET;\r
298                         case '(':\r
299                                 return Token.OPEN_PARENS;\r
300                         case ')':\r
301                                 return Token.CLOSE_PARENS;\r
302                         case ',':\r
303                                 return Token.COMMA;\r
304                         case ':':\r
305                                 return Token.COLON;\r
306                         case ';':\r
307                                 return Token.SEMICOLON;\r
308                         case '~':\r
309                                 return Token.TILDE;\r
310                         case '?':\r
311                                 return Token.INTERR;\r
312                         }\r
313 \r
314                         d = peekChar ();\r
315                         if (c == '+'){\r
316                                 \r
317                                 if (d == '+')\r
318                                         t = Token.OP_INC;\r
319                                 else if (d == '=')\r
320                                         t = Token.OP_ADD_ASSIGN;\r
321                                 else\r
322                                         return Token.PLUS;\r
323                                 doread = true;\r
324                                 return t;\r
325                         }\r
326                         if (c == '-'){\r
327                                 if (d == '-')\r
328                                         t = Token.OP_DEC;\r
329                                 else if (d == '=')\r
330                                         t = Token.OP_SUB_ASSIGN;\r
331                                 else if (d == '>')\r
332                                         return Token.OP_PTR;\r
333                                 else\r
334                                         return Token.MINUS;\r
335                                 doread = true;\r
336                                 return t;\r
337                         }\r
338 \r
339                         if (c == '!'){\r
340                                 if (d == '='){\r
341                                         doread = true;\r
342                                         return Token.OP_NE;\r
343                                 }\r
344                                 return Token.BANG;\r
345                         }\r
346 \r
347                         if (c == '='){\r
348                                 if (d == '='){\r
349                                         doread = true;\r
350                                         return Token.OP_EQ;\r
351                                 }\r
352                                 return Token.ASSIGN;\r
353                         }\r
354 \r
355                         if (c == '&'){\r
356                                 if (d == '&'){\r
357                                         doread = true;\r
358                                         return Token.OP_AND;\r
359                                 } else if (d == '='){\r
360                                         doread = true;\r
361                                         return Token.OP_AND_ASSIGN;\r
362                                 }\r
363                                 return Token.BITWISE_AND;\r
364                         }\r
365 \r
366                         if (c == '|'){\r
367                                 if (d == '|'){\r
368                                         doread = true;\r
369                                         return Token.OP_OR;\r
370                                 } else if (d == '='){\r
371                                         doread = true;\r
372                                         return Token.OP_OR_ASSIGN;\r
373                                 }\r
374                                 return Token.BITWISE_OR;\r
375                         }\r
376 \r
377                         if (c == '*'){\r
378                                 if (d == '='){\r
379                                         doread = true;\r
380                                         return Token.OP_MULT_ASSIGN;\r
381                                 }\r
382                                 return Token.STAR;\r
383                         }\r
384 \r
385                         if (c == '/'){\r
386                                 if (d == '='){\r
387                                         doread = true;\r
388                                         return Token.OP_DIV_ASSIGN;\r
389                                 }\r
390                                 return Token.DIV;\r
391                         }\r
392 \r
393                         if (c == '%'){\r
394                                 if (d == '='){\r
395                                         doread = true;\r
396                                         return Token.OP_MOD_ASSIGN;\r
397                                 }\r
398                                 return Token.PERCENT;\r
399                         }\r
400 \r
401                         if (c == '^'){\r
402                                 if (d == '='){\r
403                                         doread = true;\r
404                                         return Token.OP_XOR_ASSIGN;\r
405                                 }\r
406                                 return Token.CARRET;\r
407                         }\r
408 \r
409                         if (c == '<'){\r
410                                 if (d == '<'){\r
411                                         getChar ();\r
412                                         d = peekChar ();\r
413 \r
414                                         if (d == '='){\r
415                                                 doread = true;\r
416                                                 return Token.OP_SHIFT_LEFT_ASSIGN;\r
417                                         }\r
418                                         return Token.OP_SHIFT_LEFT;\r
419                                 } else if (d == '='){\r
420                                         doread = true;\r
421                                         return Token.OP_LE;\r
422                                 }\r
423                                 return Token.OP_LT;\r
424                         }\r
425 \r
426                         if (c == '>'){\r
427                                 if (d == '>'){\r
428                                         getChar ();\r
429                                         d = peekChar ();\r
430 \r
431                                         if (d == '='){\r
432                                                 doread = true;\r
433                                                 return Token.OP_SHIFT_RIGHT_ASSIGN;\r
434                                         }\r
435                                         return Token.OP_SHIFT_RIGHT;\r
436                                 } else if (d == '='){\r
437                                         doread = true;\r
438                                         return Token.OP_GE;\r
439                                 }\r
440                                 return Token.OP_GT;\r
441                         }\r
442                         return Token.ERROR;\r
443                 }\r
444 \r
445                 bool decimal_digits (int c)\r
446                 {\r
447                         int d;\r
448                         bool seen_digits = false;\r
449                         \r
450                         if (c != -1)\r
451                                 number.Append ((char) c);\r
452                         \r
453                         while ((d = peekChar ()) != -1){\r
454                                 if (Char.IsDigit ((char)d)){\r
455                                         number.Append ((char) d);\r
456                                         getChar ();\r
457                                         seen_digits = true;\r
458                                 } else\r
459                                         break;\r
460                         }\r
461                         return seen_digits;\r
462                 }\r
463 \r
464                 void hex_digits (int c)\r
465                 {\r
466                         int d;\r
467 \r
468                         if (c != -1)\r
469                                 number.Append ((char) c);\r
470                         while ((d = peekChar ()) != -1){\r
471                                 char e = Char.ToUpper ((char) d);\r
472                                 \r
473                                 if (Char.IsDigit (e) || (e >= 'A' && e <= 'F')){\r
474                                         number.Append ((char) e);\r
475                                         getChar ();\r
476                                 } else\r
477                                         break;\r
478                         }\r
479                 }\r
480                 \r
481                 int real_type_suffix (int c)\r
482                 {\r
483                         int t;\r
484 \r
485                         switch (c){\r
486                         case 'F': case 'f':\r
487                                 t =  Token.LITERAL_FLOAT;\r
488                                 break;\r
489                         case 'D': case 'd':\r
490                                 t = Token.LITERAL_DOUBLE;\r
491                                 break;\r
492                         case 'M': case 'm':\r
493                                  t= Token.LITERAL_DECIMAL;\r
494                                 break;\r
495                         default:\r
496                                 return Token.NONE;\r
497                         }\r
498                         getChar ();\r
499                         return t;\r
500                 }\r
501 \r
502                 int integer_type_suffix (ulong ul, int c)\r
503                 {\r
504                         bool is_unsigned = false;\r
505                         bool is_long = false;\r
506 \r
507                         if (c != -1){\r
508                                 bool scanning = true;\r
509                                 do {\r
510                                         switch (c){\r
511                                         case 'U': case 'u':\r
512                                                 if (is_unsigned)\r
513                                                         scanning = false;\r
514                                                 is_unsigned = true;\r
515                                                 getChar ();\r
516                                                 break;\r
517 \r
518                                         case 'l':\r
519                                                 if (!is_unsigned){\r
520                                                         //\r
521                                                         // if we have not seen anything in between\r
522                                                         // report this error\r
523                                                         //\r
524                                                         Report.Warning (\r
525                                                                 78, Location,\r
526                                                         "the 'l' suffix is easily confused with digit `1'," +\r
527                                                         " use 'L' for clarity");\r
528                                                 }\r
529                                                 goto case 'L';\r
530                                                 \r
531                                         case 'L': \r
532                                                 if (is_long)\r
533                                                         scanning = false;\r
534                                                 is_long = true;\r
535                                                 getChar ();\r
536                                                 break;\r
537                                                 \r
538                                         default:\r
539                                                 scanning = false;\r
540                                                 break;\r
541                                         }\r
542                                         c = peekChar ();\r
543                                 } while (scanning);\r
544                         }\r
545 \r
546                         if (is_long && is_unsigned){\r
547                                 val = ul;\r
548                                 return Token.LITERAL_INTEGER;\r
549                         } else if (is_unsigned){\r
550                                 // uint if possible, or ulong else.\r
551 \r
552                                 if ((ul & 0xffffffff00000000) == 0)\r
553                                         val = (uint) ul;\r
554                                 else\r
555                                         val = ul;\r
556                         } else if (is_long){\r
557                                 // long if possible, ulong otherwise\r
558                                 if ((ul & 0x8000000000000000) != 0)\r
559                                         val = ul;\r
560                                 else\r
561                                         val = (long) ul;\r
562                         } else {\r
563                                 // int, uint, long or ulong in that order\r
564                                 if ((ul & 0xffffffff00000000) == 0){\r
565                                         uint ui = (uint) ul;\r
566                                         \r
567                                         if ((ui & 0x80000000) != 0)\r
568                                                 val = ui;\r
569                                         else\r
570                                                 val = (int) ui;\r
571                                 } else {\r
572                                         if ((ul & 0x8000000000000000) != 0)\r
573                                                 val = ul;\r
574                                         else\r
575                                                 val = (long) ul;\r
576                                 }\r
577                         }\r
578                         return Token.LITERAL_INTEGER;\r
579                 }\r
580                                 \r
581                 //\r
582                 // given `c' as the next char in the input decide whether\r
583                 // we need to convert to a special type, and then choose\r
584                 // the best representation for the integer\r
585                 //\r
586                 int adjust_int (int c)\r
587                 {\r
588                         ulong ul = System.UInt64.Parse (number.ToString ());\r
589                         return integer_type_suffix (ul, c);\r
590                 }\r
591 \r
592                 int adjust_real (int t)\r
593                 {\r
594                         string s = number.ToString ();\r
595 \r
596                         switch (t){\r
597                         case Token.LITERAL_DECIMAL:\r
598                                 val = new System.Decimal ();\r
599                                 val = System.Decimal.Parse (\r
600                                         s, styles, csharp_format_info);\r
601                                 break;\r
602                         case Token.LITERAL_DOUBLE:\r
603                                 val = new System.Double ();\r
604                                 val = System.Double.Parse (\r
605                                         s, styles, csharp_format_info);\r
606                                 break;\r
607                         case Token.LITERAL_FLOAT:\r
608                                 val = new System.Double ();\r
609                                 val = (float) System.Double.Parse (\r
610                                         s, styles, csharp_format_info);\r
611                                 break;\r
612 \r
613                         case Token.NONE:\r
614                                 val = new System.Double ();\r
615                                 val = System.Double.Parse (\r
616                                         s, styles, csharp_format_info);\r
617                                 t = Token.LITERAL_DOUBLE;\r
618                                 break;\r
619                         }\r
620                         return t;\r
621                 }\r
622 \r
623                 //\r
624                 // Invoked if we know we have .digits or digits\r
625                 //\r
626                 int is_number (int c)\r
627                 {\r
628                         bool is_real = false;\r
629                         number = new System.Text.StringBuilder ();\r
630                         int type;\r
631 \r
632                         number.Length = 0;\r
633 \r
634                         if (Char.IsDigit ((char)c)){\r
635                                 if (c == '0' && peekChar () == 'x' || peekChar () == 'X'){\r
636                                         ulong ul;\r
637                                         getChar ();\r
638                                         hex_digits (-1);\r
639 \r
640                                         string s = number.ToString ();\r
641 \r
642                                         ul = System.UInt64.Parse (s, NumberStyles.HexNumber);\r
643                                         return integer_type_suffix (ul, peekChar ());\r
644                                 }\r
645                                 decimal_digits (c);\r
646                                 c = getChar ();\r
647                         }\r
648 \r
649                         //\r
650                         // We need to handle the case of\r
651                         // "1.1" vs "1.string" (LITERAL_FLOAT vs NUMBER DOT IDENTIFIER)\r
652                         //\r
653                         if (c == '.'){\r
654                                 if (decimal_digits ('.')){\r
655                                         is_real = true;\r
656                                         c = peekChar ();\r
657                                 } else {\r
658                                         putback ('.');\r
659                                         number.Length -= 1;\r
660                                         return adjust_int (-1);\r
661                                 }\r
662                         }\r
663                         \r
664                         if (c == 'e' || c == 'E'){\r
665                                 is_real = true;\r
666                                 number.Append ("e");\r
667                                 getChar ();\r
668                                 \r
669                                 c = peekChar ();\r
670                                 if (c == '+'){\r
671                                         number.Append ((char) c);\r
672                                         getChar ();\r
673                                         c = peekChar ();\r
674                                 } else if (c == '-'){\r
675                                         number.Append ((char) c);\r
676                                         getChar ();\r
677                                         c = peekChar ();\r
678                                 }\r
679                                 decimal_digits (-1);\r
680                                 c = peekChar ();\r
681                         }\r
682 \r
683                         type = real_type_suffix (c);\r
684                         if (type == Token.NONE && !is_real){\r
685                                 putback (c);\r
686                                 return adjust_int (c);\r
687                         } else\r
688                                 is_real = true;\r
689 \r
690                         if (is_real)\r
691                                 return adjust_real (type);\r
692 \r
693                         Console.WriteLine ("This should not be reached");\r
694                         throw new Exception ("Is Number should never reach this point");\r
695                 }\r
696                         \r
697                 int escape (int c)\r
698                 {\r
699                         int d;\r
700                         int v;\r
701 \r
702                         d = peekChar ();\r
703                         if (c != '\\')\r
704                                 return c;\r
705                         \r
706                         switch (d){\r
707                         case 'a':\r
708                                 v = '\a'; break;\r
709                         case 'b':\r
710                                 v = '\b'; break;\r
711                         case 'n':\r
712                                 v = '\n'; break;\r
713                         case 't':\r
714                                 v = '\t'; break;\r
715                         case 'v':\r
716                                 v = '\v'; break;\r
717                         case 'r':\r
718                                 v = 'c'; break;\r
719                         case '\\':\r
720                                 v = '\\'; break;\r
721                         case 'f':\r
722                                 v = '\f'; break;\r
723                         case '0':\r
724                                 v = 0; break;\r
725                         case '"':\r
726                                 v = '"'; break;\r
727                         case '\'':\r
728                                 v = '\''; break;\r
729                         default:\r
730                                 error_details = "cs1009: Unrecognized escape sequence " + (char)d;\r
731                                 return -1;\r
732                         }\r
733                         getChar ();\r
734                         return v;\r
735                 }\r
736 \r
737                 int getChar ()\r
738                 {\r
739                         if (putback_char != -1){\r
740                                 int x = putback_char;\r
741                                 putback_char = -1;\r
742 \r
743                                 return x;\r
744                         }\r
745                         return reader.Read ();\r
746                 }\r
747 \r
748                 int peekChar ()\r
749                 {\r
750                         if (putback_char != -1)\r
751                                 return putback_char;\r
752                         return reader.Peek ();\r
753                 }\r
754 \r
755                 void putback (int c)\r
756                 {\r
757                         if (putback_char != -1)\r
758                                 throw new Exception ("This should not happen putback on putback");\r
759                         putback_char = c;\r
760                 }\r
761 \r
762                 public bool advance ()\r
763                 {\r
764                         return peekChar () != -1;\r
765                 }\r
766 \r
767                 public Object Value {\r
768                         get {\r
769                                 return val;\r
770                         }\r
771                 }\r
772 \r
773                 public Object value ()\r
774                 {\r
775                         return val;\r
776                 }\r
777                 \r
778                 public int token ()\r
779                 {\r
780                         current_token = xtoken ();\r
781                         return current_token;\r
782                 }\r
783 \r
784                 static StringBuilder static_cmd_arg = new System.Text.StringBuilder ();\r
785                 \r
786                 void get_cmd_arg (out string cmd, out string arg)\r
787                 {\r
788                         int c;\r
789                         \r
790                         arg = "";\r
791                         static_cmd_arg.Length = 0;\r
792                                 \r
793                         while ((c = getChar ()) != -1 && (c != '\n') && (c != ' '))\r
794                                 static_cmd_arg.Append ((char) c);\r
795 \r
796                         cmd = static_cmd_arg.ToString ();\r
797 \r
798                         if (c == '\n'){\r
799                                 line++;\r
800                                 ref_line++;\r
801                                 return;\r
802                         }\r
803 \r
804                         // skip over white space\r
805                         while ((c = getChar ()) != -1 && (c != '\n') && (c == ' '))\r
806                                 ;\r
807 \r
808                         if (c == '\n'){\r
809                                 line++;\r
810                                 ref_line++;\r
811                                 return;\r
812                         }\r
813                         \r
814                         static_cmd_arg.Length = 0;\r
815                         static_cmd_arg.Append ((char) c);\r
816                         \r
817                         while ((c = getChar ()) != -1 && (c != '\n'))\r
818                                 static_cmd_arg.Append ((char) c);\r
819 \r
820                         if (c == '\n'){\r
821                                 line++;\r
822                                 ref_line++;\r
823                         }\r
824                         arg = static_cmd_arg.ToString ().Trim ();\r
825                 }\r
826 \r
827                 //\r
828                 // Handles the #line directive\r
829                 //\r
830                 bool PreProcessLine (string arg)\r
831                 {\r
832                         if (arg == "")\r
833                                 return false;\r
834 \r
835                         if (arg == "default"){\r
836                                 line = ref_line = line;\r
837                                 return false;\r
838                         }\r
839                         \r
840                         try {\r
841                                 int pos;\r
842 \r
843                                 if ((pos = arg.IndexOf (' ')) != -1 && pos != 0){\r
844                                         ref_line = System.Int32.Parse (arg.Substring (0, pos));\r
845                                         pos++;\r
846                                         \r
847                                         char [] quotes = { '\"' };\r
848                                         \r
849                                         ref_name = arg.Substring (pos);\r
850                                         ref_name.TrimStart (quotes);\r
851                                         ref_name.TrimEnd (quotes);\r
852                                 } else {\r
853                                         ref_line = System.Int32.Parse (arg);\r
854                                 }\r
855                         } catch {\r
856                                 return false;\r
857                         }\r
858                         \r
859                         return true;\r
860                 }\r
861 \r
862                 //\r
863                 // Handles #define and #undef\r
864                 //\r
865                 void PreProcessDefinition (bool is_define, string arg)\r
866                 {\r
867                         if (arg == "" || arg == "true" || arg == "false"){\r
868                                 Report.Error(1001, Location, "Missing identifer to pre-processor directive");\r
869                                 return;\r
870                         }\r
871 \r
872                         if (is_define){\r
873                                 if (defines == null)\r
874                                         defines = new Hashtable ();\r
875                                 defines [arg] = 1;\r
876                         } else {\r
877                                 if (defines == null)\r
878                                         return;\r
879                                 if (defines.Contains (arg))\r
880                                         defines.Remove (arg);\r
881                         }\r
882                 }\r
883 \r
884                 bool eval_val (string s)\r
885                 {\r
886                         if (s == "true")\r
887                                 return true;\r
888                         if (s == "false")\r
889                                 return false;\r
890                         \r
891                         if (defines == null)\r
892                                 return false;\r
893                         if (defines.Contains (s))\r
894                                 return true;\r
895 \r
896                         return false;\r
897                 }\r
898                 \r
899                 //\r
900                 // Evaluates an expression for `#if' or `#elif'\r
901                 //\r
902                 bool eval (string s)\r
903                 {\r
904                         return eval_val (s);\r
905                 }\r
906 \r
907                 void report1028 (string extra)\r
908                 {\r
909                         Report.Error (\r
910                                 1028, Location,\r
911                                 "Unexpected processor directive (" + extra + ")");\r
912                 }\r
913                 \r
914                 //\r
915                 // if true, then the code continues processing the code\r
916                 // if false, the code stays in a loop until another directive is\r
917                 // reached.\r
918                 //\r
919                 bool handle_preprocessing_directive ()\r
920                 {\r
921                         char [] blank = { ' ', '\t' };\r
922                         string cmd, arg;\r
923                         \r
924                         get_cmd_arg (out cmd, out arg);\r
925 \r
926                         switch (cmd){\r
927                         case "line":\r
928                                 if (!PreProcessLine (arg))\r
929                                         Report.Error (\r
930                                                 1576, Location,\r
931                                                 "Argument to #line directive is missing or invalid");\r
932                                 return true;\r
933 \r
934                         case "define":\r
935                                 PreProcessDefinition (true, arg);\r
936                                 return true;\r
937 \r
938                         case "undef":\r
939                                 PreProcessDefinition (false, arg);\r
940                                 return true;\r
941 \r
942                         case "error":\r
943                                 Report.Error (1029, Location, "#error: '" + arg + "'");\r
944                                 return true;\r
945 \r
946                         case "warning":\r
947                                 Report.Warning (1030, Location, "#warning: '" + arg + "'");\r
948                                 return true;\r
949 \r
950                         case "region":\r
951                                 arg = "true";\r
952                                 goto case "if";\r
953 \r
954                         case "endregion":\r
955                                 goto case "endif";\r
956                                 \r
957                         case "if":\r
958                                 if (arg == ""){\r
959                                         Report.Error (1517, Location, "Invalid pre-processor directive");\r
960                                         return true;\r
961                                 }\r
962                                 bool taking = false;\r
963                                 if (ifstack == null)\r
964                                         ifstack = new Stack ();\r
965 \r
966                                 if (ifstack.Count == 0){\r
967                                         taking = true;\r
968                                 } else {\r
969                                         int state = (int) ifstack.Peek ();\r
970                                         if ((state & TAKING) != 0)\r
971                                                 taking = true;\r
972                                 }\r
973                                         \r
974                                 if (eval (arg) && taking){\r
975                                         ifstack.Push (TAKING | TAKEN_BEFORE | PARENT_TAKING);\r
976                                         return true;\r
977                                 } else {\r
978                                         ifstack.Push (taking ? PARENT_TAKING : 0);\r
979                                         return false;\r
980                                 }\r
981                                 \r
982                         case "endif":\r
983                                 if (ifstack == null || ifstack.Count == 0){\r
984                                         report1028 ("no #if for this #endif");\r
985                                         return true;\r
986                                 } else {\r
987                                         ifstack.Pop ();\r
988                                         if (ifstack.Count == 0)\r
989                                                 return true;\r
990                                         else {\r
991                                                 int state = (int) ifstack.Peek ();\r
992 \r
993                                                 if ((state & TAKING) != 0)\r
994                                                         return true;\r
995                                                 else\r
996                                                         return false;\r
997                                         }\r
998                                 }\r
999 \r
1000                         case "elif":\r
1001                                 if (ifstack == null || ifstack.Count == 0){\r
1002                                         report1028 ("no #if for this #elif");\r
1003                                         return true;\r
1004                                 } else {\r
1005                                         int state = (int) ifstack.Peek ();\r
1006 \r
1007                                         if ((state & ELSE_SEEN) != 0){\r
1008                                                 report1028 ("#elif not valid after #else");\r
1009                                                 return true;\r
1010                                         }\r
1011 \r
1012                                         if ((state & (TAKEN_BEFORE | TAKING)) != 0)\r
1013                                                 return false;\r
1014 \r
1015                                         if (eval (arg) && ((state & PARENT_TAKING) != 0)){\r
1016                                                 state = (int) ifstack.Pop ();\r
1017                                                 ifstack.Push (state | TAKING | TAKEN_BEFORE);\r
1018                                                 return true;\r
1019                                         } else \r
1020                                                 return false;\r
1021                                 }\r
1022 \r
1023                         case "else":\r
1024                                 if (ifstack == null || ifstack.Count == 0){\r
1025                                         Report.Error (\r
1026                                                 1028, Location,\r
1027                                                 "Unexpected processor directive (no #if for this #else)");\r
1028                                         return true;\r
1029                                 } else {\r
1030                                         int state = (int) ifstack.Peek ();\r
1031 \r
1032                                         if ((state & ELSE_SEEN) != 0){\r
1033                                                 report1028 ("#else within #else");\r
1034                                                 return true;\r
1035                                         }\r
1036 \r
1037                                         ifstack.Pop ();\r
1038                                         ifstack.Push (state | ELSE_SEEN);\r
1039 \r
1040                                         if ((state & TAKEN_BEFORE) == 0){\r
1041                                                 if ((state & PARENT_TAKING) != 0)\r
1042                                                         return true;\r
1043                                                 else\r
1044                                                         return false;\r
1045                                         }\r
1046                                         return false;\r
1047                                 }\r
1048                         }\r
1049                         \r
1050                         Report.Error (1024, "Preprocessor directive expected");\r
1051                         return true;\r
1052                 }\r
1053                 \r
1054                 public int xtoken ()\r
1055                 {\r
1056                         int t;\r
1057                         bool allow_keyword_as_ident = false;\r
1058                         bool doread = false;\r
1059                         int c;\r
1060 \r
1061                         val = null;\r
1062                         // optimization: eliminate col and implement #directive semantic correctly.\r
1063                         for (;(c = getChar ()) != -1; col++) {\r
1064                                 if (is_identifier_start_character ((char) c)){\r
1065                                         System.Text.StringBuilder id = new System.Text.StringBuilder ();\r
1066                                         string ids;\r
1067                                         \r
1068                                         id.Append ((char) c);\r
1069                                         \r
1070                                         while ((c = peekChar ()) != -1) {\r
1071                                                 if (is_identifier_part_character ((char) c)){\r
1072                                                         id.Append ((char)getChar ());\r
1073                                                         col++;\r
1074                                                 } else \r
1075                                                         break;\r
1076                                         }\r
1077                                         \r
1078                                         ids = id.ToString ();\r
1079 \r
1080                                         if (!is_keyword (ids) || allow_keyword_as_ident) {\r
1081                                                 val = ids;\r
1082                                                 return Token.IDENTIFIER;\r
1083                                         }\r
1084 \r
1085                                         // true, false and null are in the hash anyway.\r
1086                                         return GetKeyword (ids);\r
1087 \r
1088                                 }\r
1089 \r
1090                                 if (c == '.'){\r
1091                                         if (Char.IsDigit ((char) peekChar ()))\r
1092                                                 return is_number (c);\r
1093                                         return Token.DOT;\r
1094                                 }\r
1095                                 \r
1096                                 if (Char.IsDigit ((char) c))\r
1097                                         return is_number (c);\r
1098 \r
1099                                 // Handle double-slash comments.\r
1100                                 if (c == '/'){\r
1101                                         int d = peekChar ();\r
1102                                 \r
1103                                         if (d == '/'){\r
1104                                                 getChar ();\r
1105                                                 while ((d = getChar ()) != -1 && (d != '\n'))\r
1106                                                         col++;\r
1107                                                 line++;\r
1108                                                 ref_line++;\r
1109                                                 col = 0;\r
1110                                                 continue;\r
1111                                         } else if (d == '*'){\r
1112                                                 getChar ();\r
1113 \r
1114                                                 while ((d = getChar ()) != -1){\r
1115                                                         if (d == '*' && peekChar () == '/'){\r
1116                                                                 getChar ();\r
1117                                                                 col++;\r
1118                                                                 break;\r
1119                                                         }\r
1120                                                         if (d == '\n'){\r
1121                                                                 line++;\r
1122                                                                 ref_line++;\r
1123                                                                 col = 0;\r
1124                                                         }\r
1125                                                 }\r
1126                                                 continue;\r
1127                                         }\r
1128                                 }\r
1129 \r
1130                                 /* For now, ignore pre-processor commands */\r
1131                                 // FIXME: In C# the '#' is not limited to appear\r
1132                                 // on the first column.\r
1133                                 if (col <= 1 && c == '#'){\r
1134                                 start_again:\r
1135                                         \r
1136                                         bool cont = handle_preprocessing_directive ();\r
1137 \r
1138                                         if (cont){\r
1139                                                 col = 0;\r
1140                                                 continue;\r
1141                                         }\r
1142                                         col = 1;\r
1143 \r
1144                                         for (;(c = getChar ()) != -1; col++){\r
1145                                                 if (c == '\n'){\r
1146                                                         col = 0;\r
1147                                                         line++;\r
1148                                                         ref_line++;\r
1149                                                 } else if (col == 1 && c == '#'){\r
1150                                                         goto start_again;\r
1151                                                 }\r
1152                                         }\r
1153                                         if (c == -1)\r
1154                                                 Report.Error (1027, Location, "#endif expected");\r
1155                                         continue;\r
1156                                 }\r
1157                                 \r
1158                                 if ((t = is_punct ((char)c, ref doread)) != Token.ERROR){\r
1159                                         if (doread){\r
1160                                                 getChar ();\r
1161                                                 col++;\r
1162                                         }\r
1163                                         return t;\r
1164                                 }\r
1165                                 \r
1166                                 if (c == '"'){\r
1167                                         System.Text.StringBuilder s = new System.Text.StringBuilder ();\r
1168 \r
1169                                         while ((c = getChar ()) != -1){\r
1170                                                 if (c == '"'){\r
1171                                                         val = s.ToString ();\r
1172                                                         return Token.LITERAL_STRING;\r
1173                                                 }\r
1174 \r
1175                                                 c = escape (c);\r
1176                                                 if (c == -1)\r
1177                                                         return Token.ERROR;\r
1178                                                 s.Append ((char) c);\r
1179                                         }\r
1180                                 }\r
1181 \r
1182                                 if (c == '\''){\r
1183                                         c = getChar ();\r
1184                                         if (c == '\''){\r
1185                                                 error_details = "CS1011: Empty character literal";\r
1186                                                 return Token.ERROR;\r
1187                                         }\r
1188                                         c = escape (c);\r
1189                                         if (c == -1)\r
1190                                                 return Token.ERROR;\r
1191                                         val = new System.Char ();\r
1192                                         val = (char) c;\r
1193                                         c = getChar ();\r
1194                                         if (c != '\''){\r
1195                                                 error_details = "CS1012: Too many characters in character literal";\r
1196                                                 // Try to recover, read until newline or next "'"\r
1197                                                 while ((c = getChar ()) != -1){\r
1198                                                         if (c == '\n' || c == '\'')\r
1199                                                                 break;\r
1200                                                         \r
1201                                                 }\r
1202                                                 return Token.ERROR;\r
1203                                         }\r
1204                                         return Token.LITERAL_CHARACTER;\r
1205                                 }\r
1206                                 \r
1207                                 // white space\r
1208                                 if (c == '\n'){\r
1209                                         line++;\r
1210                                         ref_line++;\r
1211                                         col = 0;\r
1212                                         continue;\r
1213                                 }\r
1214                                 if (c == ' ' || c == '\t' || c == '\f' || c == '\v' || c == '\r'){\r
1215                                         if (c == '\t')\r
1216                                                 col = (((col + 8) / 8) * 8) - 1;\r
1217                                         \r
1218                                         continue;\r
1219                                 }\r
1220 \r
1221                                 if (c == '@'){\r
1222                                         allow_keyword_as_ident = true;\r
1223                                         continue;\r
1224                                 }\r
1225 \r
1226                                 error_details = ((char)c).ToString ();\r
1227                                 \r
1228                                 return Token.ERROR;\r
1229                         }\r
1230 \r
1231                         if (ifstack != null && ifstack.Count > 1)\r
1232                                 Report.Error (1027, Location, "#endif expected");\r
1233                         return Token.EOF;\r
1234                 }\r
1235         }\r
1236 }\r
1237 \r