7cb933321c0d1d1e3960459b1f900ee788424e4d
[mono.git] / mcs / mcs / cs-tokenizer.cs
1 //
2 // cs-tokenizer.cs: The Tokenizer for the C# compiler
3 //                  This also implements the preprocessor
4 //
5 // Author: Miguel de Icaza (miguel@gnu.org)
6 //
7 // Licensed under the terms of the GNU GPL
8 //
9 // (C) 2001, 2002 Ximian, Inc (http://www.ximian.com)
10 // (C) 2004 Novell, Inc
11 //
12
13 /*
14  * TODO:
15  *   Make sure we accept the proper Unicode ranges, per the spec.
16  *   Report error 1032
17 */
18
19 using System;
20 using System.Text;
21 using System.Collections;
22 using System.IO;
23 using System.Globalization;
24 using System.Reflection;
25
26 namespace Mono.CSharp
27 {
28         /// <summary>
29         ///    Tokenizer for C# source code. 
30         /// </summary>
31
32         public class Tokenizer : yyParser.yyInput
33         {
34                 SeekableStreamReader reader;
35                 SourceFile ref_name;
36                 SourceFile file_name;
37                 int ref_line = 1;
38                 int line = 1;
39                 int col = 0;
40                 int previous_col;
41                 int current_token;
42                 bool handle_get_set = false;
43                 bool handle_remove_add = false;
44                 bool handle_assembly = false;
45                 Location current_location;
46                 Location current_comment_location = Location.Null;
47                 ArrayList escapedIdentifiers = new ArrayList ();
48
49                 //
50                 // XML documentation buffer. The save point is used to divide
51                 // comments on types and comments on members.
52                 //
53                 StringBuilder xml_comment_buffer;
54
55                 //
56                 // See comment on XmlCommentState enumeration.
57                 //
58                 XmlCommentState xmlDocState = XmlCommentState.Allowed;
59
60                 //
61                 // Whether tokens have been seen on this line
62                 //
63                 bool tokens_seen = false;
64
65                 //
66                 // Whether a token has been seen on the file
67                 // This is needed because `define' is not allowed to be used
68                 // after a token has been seen.
69                 //
70                 bool any_token_seen = false;
71
72                 static Hashtable tokenValues;
73
74                 private static Hashtable TokenValueName
75                 {
76                         get {
77                                 if (tokenValues == null)
78                                         tokenValues = GetTokenValueNameHash ();
79
80                                 return tokenValues;
81                         }
82                 }
83
84                 private static Hashtable GetTokenValueNameHash ()
85                 {
86                         Type t = typeof (Token);
87                         FieldInfo [] fields = t.GetFields ();
88                         Hashtable hash = new Hashtable ();
89                         foreach (FieldInfo field in fields) {
90                                 if (field.IsLiteral && field.IsStatic && field.FieldType == typeof (int))
91                                         hash.Add (field.GetValue (null), field.Name);
92                         }
93                         return hash;
94                 }
95                 
96                 //
97                 // Returns a verbose representation of the current location
98                 //
99                 public string location {
100                         get {
101                                 string det;
102
103                                 if (current_token == Token.ERROR)
104                                         det = "detail: " + error_details;
105                                 else
106                                         det = "";
107                                 
108                                 // return "Line:     "+line+" Col: "+col + "\n" +
109                                 //       "VirtLine: "+ref_line +
110                                 //       " Token: "+current_token + " " + det;
111                                 string current_token_name = TokenValueName [current_token] as string;
112                                 if (current_token_name == null)
113                                         current_token_name = current_token.ToString ();
114
115                                 return String.Format ("{0} ({1},{2}), Token: {3} {4}", ref_name.Name,
116                                                                                        ref_line,
117                                                                                        col,
118                                                                                        current_token_name,
119                                                                                        det);
120                         }
121                 }
122
123                 public bool PropertyParsing {
124                         get {
125                                 return handle_get_set;
126                         }
127
128                         set {
129                                 handle_get_set = value;
130                         }
131                 }
132
133                 public bool AssemblyTargetParsing {
134                         get {
135                                 return handle_assembly;
136                         }
137
138                         set {
139                                 handle_assembly = value;
140                         }
141                 }
142
143                 public bool EventParsing {
144                         get {
145                                 return handle_remove_add;
146                         }
147
148                         set {
149                                 handle_remove_add = value;
150                         }
151                 }
152
153                 public XmlCommentState doc_state {
154                         get { return xmlDocState; }
155                         set {
156                                 if (value == XmlCommentState.Allowed) {
157                                         check_incorrect_doc_comment ();
158                                         reset_doc_comment ();
159                                 }
160                                 xmlDocState = value;
161                         }
162                 }
163
164                 public bool IsEscapedIdentifier (Location loc)
165                 {
166                         foreach (LocatedToken lt in escapedIdentifiers)
167                                 if (lt.Location.Equals (loc))
168                                         return true;
169                         return false;
170                 }
171
172                 
173                 //
174                 // Class variables
175                 // 
176                 static CharArrayHashtable[] keywords;
177                 static Hashtable keywordStrings = new Hashtable ();
178                 static NumberStyles styles;
179                 static NumberFormatInfo csharp_format_info;
180                 
181                 //
182                 // Values for the associated token returned
183                 //
184                 int putback_char;
185                 Object val;
186
187                 //
188                 // Pre-processor
189                 //
190                 Hashtable defines;
191
192                 const int TAKING        = 1;
193                 const int TAKEN_BEFORE  = 2;
194                 const int ELSE_SEEN     = 4;
195                 const int PARENT_TAKING = 8;
196                 const int REGION        = 16;           
197
198                 //
199                 // pre-processor if stack state:
200                 //
201                 Stack ifstack;
202
203                 static System.Text.StringBuilder string_builder;
204
205                 const int max_id_size = 512;
206                 static char [] id_builder = new char [max_id_size];
207
208                 static CharArrayHashtable [] identifiers = new CharArrayHashtable [max_id_size + 1];
209
210                 const int max_number_size = 512;
211                 static char [] number_builder = new char [max_number_size];
212                 static int number_pos;
213                 
214                 //
215                 // Details about the error encoutered by the tokenizer
216                 //
217                 string error_details;
218                 
219                 public string error {
220                         get {
221                                 return error_details;
222                         }
223                 }
224                 
225                 public int Line {
226                         get {
227                                 return ref_line;
228                         }
229                 }
230
231                 public int Col {
232                         get {
233                                 return col;
234                         }
235                 }
236
237                 static void AddKeyword (string kw, int token) {
238                         keywordStrings.Add (kw, kw);
239                         if (keywords [kw.Length] == null) {
240                                 keywords [kw.Length] = new CharArrayHashtable (kw.Length);
241                         }
242                         keywords [kw.Length] [kw.ToCharArray ()] = token;
243                 }
244
245                 static void InitTokens ()
246                 {
247                         keywords = new CharArrayHashtable [64];
248
249                         AddKeyword ("__arglist", Token.ARGLIST);
250                         AddKeyword ("abstract", Token.ABSTRACT);
251                         AddKeyword ("as", Token.AS);
252                         AddKeyword ("add", Token.ADD);
253                         AddKeyword ("assembly", Token.ASSEMBLY);
254                         AddKeyword ("base", Token.BASE);
255                         AddKeyword ("bool", Token.BOOL);
256                         AddKeyword ("break", Token.BREAK);
257                         AddKeyword ("byte", Token.BYTE);
258                         AddKeyword ("case", Token.CASE);
259                         AddKeyword ("catch", Token.CATCH);
260                         AddKeyword ("char", Token.CHAR);
261                         AddKeyword ("checked", Token.CHECKED);
262                         AddKeyword ("class", Token.CLASS);
263                         AddKeyword ("const", Token.CONST);
264                         AddKeyword ("continue", Token.CONTINUE);
265                         AddKeyword ("decimal", Token.DECIMAL);
266                         AddKeyword ("default", Token.DEFAULT);
267                         AddKeyword ("delegate", Token.DELEGATE);
268                         AddKeyword ("do", Token.DO);
269                         AddKeyword ("double", Token.DOUBLE);
270                         AddKeyword ("else", Token.ELSE);
271                         AddKeyword ("enum", Token.ENUM);
272                         AddKeyword ("event", Token.EVENT);
273                         AddKeyword ("explicit", Token.EXPLICIT);
274                         AddKeyword ("extern", Token.EXTERN);
275                         AddKeyword ("false", Token.FALSE);
276                         AddKeyword ("finally", Token.FINALLY);
277                         AddKeyword ("fixed", Token.FIXED);
278                         AddKeyword ("float", Token.FLOAT);
279                         AddKeyword ("for", Token.FOR);
280                         AddKeyword ("foreach", Token.FOREACH);
281                         AddKeyword ("goto", Token.GOTO);
282                         AddKeyword ("get", Token.GET);
283                         AddKeyword ("if", Token.IF);
284                         AddKeyword ("implicit", Token.IMPLICIT);
285                         AddKeyword ("in", Token.IN);
286                         AddKeyword ("int", Token.INT);
287                         AddKeyword ("interface", Token.INTERFACE);
288                         AddKeyword ("internal", Token.INTERNAL);
289                         AddKeyword ("is", Token.IS);
290                         AddKeyword ("lock", Token.LOCK);
291                         AddKeyword ("long", Token.LONG);
292                         AddKeyword ("namespace", Token.NAMESPACE);
293                         AddKeyword ("new", Token.NEW);
294                         AddKeyword ("null", Token.NULL);
295                         AddKeyword ("object", Token.OBJECT);
296                         AddKeyword ("operator", Token.OPERATOR);
297                         AddKeyword ("out", Token.OUT);
298                         AddKeyword ("override", Token.OVERRIDE);
299                         AddKeyword ("params", Token.PARAMS);
300                         AddKeyword ("private", Token.PRIVATE);
301                         AddKeyword ("protected", Token.PROTECTED);
302                         AddKeyword ("public", Token.PUBLIC);
303                         AddKeyword ("readonly", Token.READONLY);
304                         AddKeyword ("ref", Token.REF);
305                         AddKeyword ("remove", Token.REMOVE);
306                         AddKeyword ("return", Token.RETURN);
307                         AddKeyword ("sbyte", Token.SBYTE);
308                         AddKeyword ("sealed", Token.SEALED);
309                         AddKeyword ("set", Token.SET);
310                         AddKeyword ("short", Token.SHORT);
311                         AddKeyword ("sizeof", Token.SIZEOF);
312                         AddKeyword ("stackalloc", Token.STACKALLOC);
313                         AddKeyword ("static", Token.STATIC);
314                         AddKeyword ("string", Token.STRING);
315                         AddKeyword ("struct", Token.STRUCT);
316                         AddKeyword ("switch", Token.SWITCH);
317                         AddKeyword ("this", Token.THIS);
318                         AddKeyword ("throw", Token.THROW);
319                         AddKeyword ("true", Token.TRUE);
320                         AddKeyword ("try", Token.TRY);
321                         AddKeyword ("typeof", Token.TYPEOF);
322                         AddKeyword ("uint", Token.UINT);
323                         AddKeyword ("ulong", Token.ULONG);
324                         AddKeyword ("unchecked", Token.UNCHECKED);
325                         AddKeyword ("unsafe", Token.UNSAFE);
326                         AddKeyword ("ushort", Token.USHORT);
327                         AddKeyword ("using", Token.USING);
328                         AddKeyword ("virtual", Token.VIRTUAL);
329                         AddKeyword ("void", Token.VOID);
330                         AddKeyword ("volatile", Token.VOLATILE);
331                         AddKeyword ("while", Token.WHILE);
332                         AddKeyword ("partial", Token.PARTIAL);
333                 }
334
335                 //
336                 // Class initializer
337                 // 
338                 static Tokenizer ()
339                 {
340                         InitTokens ();
341                         csharp_format_info = NumberFormatInfo.InvariantInfo;
342                         styles = NumberStyles.Float;
343                         
344                         string_builder = new System.Text.StringBuilder ();
345                 }
346
347                 int GetKeyword (char[] id, int id_len)
348                 {
349                         /*
350                          * Keywords are stored in an array of hashtables grouped by their
351                          * length.
352                          */
353
354                         if ((id_len >= keywords.Length) || (keywords [id_len] == null))
355                                 return -1;
356                         object o = keywords [id_len] [id];
357
358                         if (o == null)
359                                 return -1;
360                         
361                         int res = (int) o;
362
363                         if (handle_get_set == false && (res == Token.GET || res == Token.SET))
364                                 return -1;
365                         if (handle_remove_add == false && (res == Token.REMOVE || res == Token.ADD))
366                                 return -1;
367                         if (handle_assembly == false && res == Token.ASSEMBLY)
368                                 return -1;
369
370                         return res;
371                         
372                 }
373
374                 public Location Location {
375                         get { return current_location; }
376                 }
377
378                 void define (string def)
379                 {
380                         if (!RootContext.AllDefines.Contains (def)){
381                                 RootContext.AllDefines [def] = true;
382                         }
383                         if (defines.Contains (def))
384                                 return;
385                         defines [def] = true;
386                 }
387                 
388                 public Tokenizer (SeekableStreamReader input, SourceFile file, ArrayList defs)
389                 {
390                         this.ref_name = file;
391                         this.file_name = file;
392                         reader = input;
393                         
394                         putback_char = -1;
395
396                         if (defs != null){
397                                 defines = new Hashtable ();
398                                 foreach (string def in defs)
399                                         define (def);
400                         }
401
402                         xml_comment_buffer = new StringBuilder ();
403
404                         //
405                         // FIXME: This could be `Location.Push' but we have to
406                         // find out why the MS compiler allows this
407                         //
408                         Mono.CSharp.Location.Push (file, 0);
409                 }
410
411                 static bool is_identifier_start_character (char c)
412                 {
413                         return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || Char.IsLetter (c);
414                 }
415
416                 static bool is_identifier_part_character (char c)
417                 {
418                         return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9') || Char.IsLetter (c);
419                 }
420
421                 public static bool IsKeyword (string s)
422                 {
423                         return keywordStrings [s] != null;
424                 }
425
426                 public static bool IsValidIdentifier (string s)
427                 {
428                         if (s == null || s.Length == 0)
429                                 return false;
430
431                         if (!is_identifier_start_character (s [0]))
432                                 return false;
433                         
434                         for (int i = 1; i < s.Length; i ++)
435                                 if (! is_identifier_part_character (s [i]))
436                                         return false;
437                         
438                         return true;
439                 }
440
441                 int is_punct (char c, ref bool doread)
442                 {
443                         int d;
444                         int t;
445
446                         doread = false;
447
448                         switch (c){
449                         case '{':
450                                 val = Location;
451                                 return Token.OPEN_BRACE;
452                         case '}':
453                                 val = Location;
454                                 return Token.CLOSE_BRACE;
455                         case '[':
456                                 // To block doccomment inside attribute declaration.
457                                 if (doc_state == XmlCommentState.Allowed)
458                                         doc_state = XmlCommentState.NotAllowed;
459                                 return Token.OPEN_BRACKET;
460                         case ']':
461                                 return Token.CLOSE_BRACKET;
462                         case '(':
463                                 return Token.OPEN_PARENS;
464                         case ')': {
465                                 if (deambiguate_close_parens == 0)
466                                         return Token.CLOSE_PARENS;
467
468                                 --deambiguate_close_parens;
469
470                                 // Save current position and parse next token.
471                                 int old = reader.Position;
472                                 int old_ref_line = ref_line;
473                                 int old_col = col;
474
475                                 // disable preprocessing directives when peeking
476                                 process_directives = false;
477                                 int new_token = token ();
478                                 process_directives = true;
479                                 reader.Position = old;
480                                 ref_line = old_ref_line;
481                                 col = old_col;
482                                 putback_char = -1;
483
484                                 if (new_token == Token.OPEN_PARENS)
485                                         return Token.CLOSE_PARENS_OPEN_PARENS;
486                                 else if (new_token == Token.MINUS)
487                                         return Token.CLOSE_PARENS_MINUS;
488                                 else if (IsCastToken (new_token))
489                                         return Token.CLOSE_PARENS_CAST;
490                                 else
491                                         return Token.CLOSE_PARENS_NO_CAST;
492                         }
493
494                         case ',':
495                                 return Token.COMMA;
496                         case ';':
497                                 val = Location;
498                                 return Token.SEMICOLON;
499                         case '~':
500                                 val = Location;
501                                 return Token.TILDE;
502                         case '?':
503                                 return Token.INTERR;
504                         }
505
506                         d = peekChar ();
507                         if (c == '+'){
508                                 
509                                 if (d == '+') {
510                                         val = Location;
511                                         t = Token.OP_INC;
512                                 }
513                                 else if (d == '=')
514                                         t = Token.OP_ADD_ASSIGN;
515                                 else {
516                                         val = Location;
517                                         return Token.PLUS;
518                                 }
519                                 doread = true;
520                                 return t;
521                         }
522                         if (c == '-'){
523                                 if (d == '-') {
524                                         val = Location;
525                                         t = Token.OP_DEC;
526                                 }
527                                 else if (d == '=')
528                                         t = Token.OP_SUB_ASSIGN;
529                                 else if (d == '>')
530                                         t = Token.OP_PTR;
531                                 else {
532                                         val = Location;
533                                         return Token.MINUS;
534                                 }
535                                 doread = true;
536                                 return t;
537                         }
538
539                         if (c == '!'){
540                                 if (d == '='){
541                                         doread = true;
542                                         return Token.OP_NE;
543                                 }
544                                 val = Location;
545                                 return Token.BANG;
546                         }
547
548                         if (c == '='){
549                                 if (d == '='){
550                                         doread = true;
551                                         return Token.OP_EQ;
552                                 }
553                                 return Token.ASSIGN;
554                         }
555
556                         if (c == '&'){
557                                 if (d == '&'){
558                                         doread = true;
559                                         return Token.OP_AND;
560                                 } else if (d == '='){
561                                         doread = true;
562                                         return Token.OP_AND_ASSIGN;
563                                 }
564                                 val = Location;
565                                 return Token.BITWISE_AND;
566                         }
567
568                         if (c == '|'){
569                                 if (d == '|'){
570                                         doread = true;
571                                         return Token.OP_OR;
572                                 } else if (d == '='){
573                                         doread = true;
574                                         return Token.OP_OR_ASSIGN;
575                                 }
576                                 return Token.BITWISE_OR;
577                         }
578
579                         if (c == '*'){
580                                 if (d == '='){
581                                         doread = true;
582                                         return Token.OP_MULT_ASSIGN;
583                                 }
584                                 val = Location;
585                                 return Token.STAR;
586                         }
587
588                         if (c == '/'){
589                                 if (d == '='){
590                                         doread = true;
591                                         return Token.OP_DIV_ASSIGN;
592                                 }
593                                 return Token.DIV;
594                         }
595
596                         if (c == '%'){
597                                 if (d == '='){
598                                         doread = true;
599                                         return Token.OP_MOD_ASSIGN;
600                                 }
601                                 return Token.PERCENT;
602                         }
603
604                         if (c == '^'){
605                                 if (d == '='){
606                                         doread = true;
607                                         return Token.OP_XOR_ASSIGN;
608                                 }
609                                 return Token.CARRET;
610                         }
611
612                         if (c == '<'){
613                                 if (d == '<'){
614                                         getChar ();
615                                         d = peekChar ();
616
617                                         if (d == '='){
618                                                 doread = true;
619                                                 return Token.OP_SHIFT_LEFT_ASSIGN;
620                                         }
621                                         return Token.OP_SHIFT_LEFT;
622                                 } else if (d == '='){
623                                         doread = true;
624                                         return Token.OP_LE;
625                                 }
626                                 return Token.OP_LT;
627                         }
628
629                         if (c == '>'){
630                                 if (d == '>'){
631                                         getChar ();
632                                         d = peekChar ();
633
634                                         if (d == '='){
635                                                 doread = true;
636                                                 return Token.OP_SHIFT_RIGHT_ASSIGN;
637                                         }
638                                         return Token.OP_SHIFT_RIGHT;
639                                 } else if (d == '='){
640                                         doread = true;
641                                         return Token.OP_GE;
642                                 }
643                                 return Token.OP_GT;
644                         }
645                         if (c == ':'){
646                                 if (d == ':'){
647                                         doread = true;
648                                         return Token.DOUBLE_COLON;
649                                 }
650                                 val = Location;
651                                 return Token.COLON;
652                         }
653
654                         return Token.ERROR;
655                 }
656
657                 int deambiguate_close_parens = 0;
658
659                 public void Deambiguate_CloseParens ()
660                 {
661                         putback (')');
662                         deambiguate_close_parens++;
663                 }
664
665                 void Error_NumericConstantTooLong ()
666                 {
667                         Report.Error (1021, Location, "Numeric constant too long");                     
668                 }
669                 
670                 bool decimal_digits (int c)
671                 {
672                         int d;
673                         bool seen_digits = false;
674                         
675                         if (c != -1){
676                                 if (number_pos == max_number_size)
677                                         Error_NumericConstantTooLong ();
678                                 number_builder [number_pos++] = (char) c;
679                         }
680                         
681                         //
682                         // We use peekChar2, because decimal_digits needs to do a 
683                         // 2-character look-ahead (5.ToString for example).
684                         //
685                         while ((d = peekChar2 ()) != -1){
686                                 if (d >= '0' && d <= '9'){
687                                         if (number_pos == max_number_size)
688                                                 Error_NumericConstantTooLong ();
689                                         number_builder [number_pos++] = (char) d;
690                                         getChar ();
691                                         seen_digits = true;
692                                 } else
693                                         break;
694                         }
695                         
696                         return seen_digits;
697                 }
698
699                 bool is_hex (int e)
700                 {
701                         return (e >= '0' && e <= '9') || (e >= 'A' && e <= 'F') || (e >= 'a' && e <= 'f');
702                 }
703                                 
704                 int real_type_suffix (int c)
705                 {
706                         int t;
707
708                         switch (c){
709                         case 'F': case 'f':
710                                 t =  Token.LITERAL_FLOAT;
711                                 break;
712                         case 'D': case 'd':
713                                 t = Token.LITERAL_DOUBLE;
714                                 break;
715                         case 'M': case 'm':
716                                  t= Token.LITERAL_DECIMAL;
717                                 break;
718                         default:
719                                 return Token.NONE;
720                         }
721                         return t;
722                 }
723
724                 int integer_type_suffix (ulong ul, int c)
725                 {
726                         bool is_unsigned = false;
727                         bool is_long = false;
728
729                         if (c != -1){
730                                 bool scanning = true;
731                                 do {
732                                         switch (c){
733                                         case 'U': case 'u':
734                                                 if (is_unsigned)
735                                                         scanning = false;
736                                                 is_unsigned = true;
737                                                 getChar ();
738                                                 break;
739
740                                         case 'l':
741                                                 if (!is_unsigned && (RootContext.WarningLevel >= 4)){
742                                                         //
743                                                         // if we have not seen anything in between
744                                                         // report this error
745                                                         //
746                                                         Report.Warning (78, Location, "The 'l' suffix is easily confused with the digit '1' (use 'L' for clarity)");
747                                                 }
748                                                 //
749                                                 // This goto statement causes the MS CLR 2.0 beta 1 csc to report an error, so
750                                                 // work around that.
751                                                 //
752                                                 //goto case 'L';
753                                                 if (is_long)
754                                                         scanning = false;
755                                                 is_long = true;
756                                                 getChar ();
757                                                 break;
758
759                                         case 'L': 
760                                                 if (is_long)
761                                                         scanning = false;
762                                                 is_long = true;
763                                                 getChar ();
764                                                 break;
765                                                 
766                                         default:
767                                                 scanning = false;
768                                                 break;
769                                         }
770                                         c = peekChar ();
771                                 } while (scanning);
772                         }
773
774                         if (is_long && is_unsigned){
775                                 val = ul;
776                                 return Token.LITERAL_INTEGER;
777                         } else if (is_unsigned){
778                                 // uint if possible, or ulong else.
779
780                                 if ((ul & 0xffffffff00000000) == 0)
781                                         val = (uint) ul;
782                                 else
783                                         val = ul;
784                         } else if (is_long){
785                                 // long if possible, ulong otherwise
786                                 if ((ul & 0x8000000000000000) != 0)
787                                         val = ul;
788                                 else
789                                         val = (long) ul;
790                         } else {
791                                 // int, uint, long or ulong in that order
792                                 if ((ul & 0xffffffff00000000) == 0){
793                                         uint ui = (uint) ul;
794                                         
795                                         if ((ui & 0x80000000) != 0)
796                                                 val = ui;
797                                         else
798                                                 val = (int) ui;
799                                 } else {
800                                         if ((ul & 0x8000000000000000) != 0)
801                                                 val = ul;
802                                         else
803                                                 val = (long) ul;
804                                 }
805                         }
806                         return Token.LITERAL_INTEGER;
807                 }
808                                 
809                 //
810                 // given `c' as the next char in the input decide whether
811                 // we need to convert to a special type, and then choose
812                 // the best representation for the integer
813                 //
814                 int adjust_int (int c)
815                 {
816                         try {
817                                 if (number_pos > 9){
818                                         ulong ul = (uint) (number_builder [0] - '0');
819
820                                         for (int i = 1; i < number_pos; i++){
821                                                 ul = checked ((ul * 10) + ((uint)(number_builder [i] - '0')));
822                                         }
823                                         return integer_type_suffix (ul, c);
824                                 } else {
825                                         uint ui = (uint) (number_builder [0] - '0');
826
827                                         for (int i = 1; i < number_pos; i++){
828                                                 ui = checked ((ui * 10) + ((uint)(number_builder [i] - '0')));
829                                         }
830                                         return integer_type_suffix (ui, c);
831                                 }
832                         } catch (OverflowException) {
833                                 error_details = "Integral constant is too large";
834                                 Report.Error (1021, Location, error_details);
835                                 val = 0ul;
836                                 return Token.LITERAL_INTEGER;
837                         }
838                 }
839                 
840                 int adjust_real (int t)
841                 {
842                         string s = new String (number_builder, 0, number_pos);
843                         const string error_details = "Floating-point constant is outside the range of type `{0}'";
844
845                         switch (t){
846                         case Token.LITERAL_DECIMAL:
847                                 try {
848                                         val = System.Decimal.Parse (s, styles, csharp_format_info);
849                                 } catch (OverflowException) {
850                                         val = 0m;     
851                                         Report.Error (594, Location, error_details, "decimal");
852                                 }
853                                 break;
854                         case Token.LITERAL_FLOAT:
855                                 try {
856                                         val = (float) System.Double.Parse (s, styles, csharp_format_info);
857                                 } catch (OverflowException) {
858                                         val = 0.0f;     
859                                         Report.Error (594, Location, error_details, "float");
860                                 }
861                                 break;
862                                 
863                         case Token.LITERAL_DOUBLE:
864                         case Token.NONE:
865                                 t = Token.LITERAL_DOUBLE;
866                                 try {
867                                         val = System.Double.Parse (s, styles, csharp_format_info);
868                                 } catch (OverflowException) {
869                                         val = 0.0;     
870                                         Report.Error (594, Location, error_details, "double");
871                                 }
872                                 break;
873                         }
874                         return t;
875                 }
876
877                 int handle_hex ()
878                 {
879                         int d;
880                         ulong ul;
881                         
882                         getChar ();
883                         while ((d = peekChar ()) != -1){
884                                 if (is_hex (d)){
885                                         number_builder [number_pos++] = (char) d;
886                                         getChar ();
887                                 } else
888                                         break;
889                         }
890                         
891                         string s = new String (number_builder, 0, number_pos);
892                         try {
893                                 if (number_pos <= 8)
894                                         ul = System.UInt32.Parse (s, NumberStyles.HexNumber);
895                                 else
896                                         ul = System.UInt64.Parse (s, NumberStyles.HexNumber);
897                         } catch (OverflowException){
898                                 error_details = "Integral constant is too large";
899                                 Report.Error (1021, Location, error_details);
900                                 val = 0ul;
901                                 return Token.LITERAL_INTEGER;
902                         }
903                         catch (FormatException) {
904                                 Report.Error (1013, Location, "Invalid number");
905                                 val = 0ul;
906                                 return Token.LITERAL_INTEGER;
907                         }
908                         
909                         return integer_type_suffix (ul, peekChar ());
910                 }
911
912                 //
913                 // Invoked if we know we have .digits or digits
914                 //
915                 int is_number (int c)
916                 {
917                         bool is_real = false;
918                         int type;
919
920                         number_pos = 0;
921
922                         if (c >= '0' && c <= '9'){
923                                 if (c == '0'){
924                                         int peek = peekChar ();
925
926                                         if (peek == 'x' || peek == 'X')
927                                                 return handle_hex ();
928                                 }
929                                 decimal_digits (c);
930                                 c = getChar ();
931                         }
932
933                         //
934                         // We need to handle the case of
935                         // "1.1" vs "1.string" (LITERAL_FLOAT vs NUMBER DOT IDENTIFIER)
936                         //
937                         if (c == '.'){
938                                 if (decimal_digits ('.')){
939                                         is_real = true;
940                                         c = getChar ();
941                                 } else {
942                                         putback ('.');
943                                         number_pos--;
944                                         return adjust_int (-1);
945                                 }
946                         }
947                         
948                         if (c == 'e' || c == 'E'){
949                                 is_real = true;
950                                 if (number_pos == max_number_size)
951                                         Error_NumericConstantTooLong ();
952                                 number_builder [number_pos++] = 'e';
953                                 c = getChar ();
954                                 
955                                 if (c == '+'){
956                                         if (number_pos == max_number_size)
957                                                 Error_NumericConstantTooLong ();
958                                         number_builder [number_pos++] = '+';
959                                         c = -1;
960                                 } else if (c == '-') {
961                                         if (number_pos == max_number_size)
962                                                 Error_NumericConstantTooLong ();
963                                         number_builder [number_pos++] = '-';
964                                         c = -1;
965                                 } else {
966                                         if (number_pos == max_number_size)
967                                                 Error_NumericConstantTooLong ();
968                                         number_builder [number_pos++] = '+';
969                                 }
970                                         
971                                 decimal_digits (c);
972                                 c = getChar ();
973                         }
974
975                         type = real_type_suffix (c);
976                         if (type == Token.NONE && !is_real){
977                                 putback (c);
978                                 return adjust_int (c);
979                         } else 
980                                 is_real = true;
981
982                         if (type == Token.NONE){
983                                 putback (c);
984                         }
985                         
986                         if (is_real)
987                                 return adjust_real (type);
988
989                         Console.WriteLine ("This should not be reached");
990                         throw new Exception ("Is Number should never reach this point");
991                 }
992
993                 //
994                 // Accepts exactly count (4 or 8) hex, no more no less
995                 //
996                 int getHex (int count, out bool error)
997                 {
998                         int i;
999                         int total = 0;
1000                         int c;
1001                         int top = count != -1 ? count : 4;
1002                         
1003                         getChar ();
1004                         error = false;
1005                         for (i = 0; i < top; i++){
1006                                 c = getChar ();
1007                                 
1008                                 if (c >= '0' && c <= '9')
1009                                         c = (int) c - (int) '0';
1010                                 else if (c >= 'A' && c <= 'F')
1011                                         c = (int) c - (int) 'A' + 10;
1012                                 else if (c >= 'a' && c <= 'f')
1013                                         c = (int) c - (int) 'a' + 10;
1014                                 else {
1015                                         error = true;
1016                                         return 0;
1017                                 }
1018                                 
1019                                 total = (total * 16) + c;
1020                                 if (count == -1){
1021                                         int p = peekChar ();
1022                                         if (p == -1)
1023                                                 break;
1024                                         if (!is_hex ((char)p))
1025                                                 break;
1026                                 }
1027                         }
1028                         return total;
1029                 }
1030
1031                 int escape (int c)
1032                 {
1033                         bool error;
1034                         int d;
1035                         int v;
1036
1037                         d = peekChar ();
1038                         if (c != '\\')
1039                                 return c;
1040                         
1041                         switch (d){
1042                         case 'a':
1043                                 v = '\a'; break;
1044                         case 'b':
1045                                 v = '\b'; break;
1046                         case 'n':
1047                                 v = '\n'; break;
1048                         case 't':
1049                                 v = '\t'; break;
1050                         case 'v':
1051                                 v = '\v'; break;
1052                         case 'r':
1053                                 v = '\r'; break;
1054                         case '\\':
1055                                 v = '\\'; break;
1056                         case 'f':
1057                                 v = '\f'; break;
1058                         case '0':
1059                                 v = 0; break;
1060                         case '"':
1061                                 v = '"'; break;
1062                         case '\'':
1063                                 v = '\''; break;
1064                         case 'x':
1065                                 v = getHex (-1, out error);
1066                                 if (error)
1067                                         goto default;
1068                                 return v;
1069                         case 'u':
1070                                 v = getHex (4, out error);
1071                                 if (error)
1072                                         goto default;
1073                                 return v;
1074                         case 'U':
1075                                 v = getHex (8, out error);
1076                                 if (error)
1077                                         goto default;
1078                                 return v;
1079                         default:
1080                                 Report.Error (1009, Location, "Unrecognized escape sequence `\\{0}'", (char)d);
1081                                 return d;
1082                         }
1083                         getChar ();
1084                         return v;
1085                 }
1086
1087                 int getChar ()
1088                 {
1089                         int x;
1090                         if (putback_char != -1) {
1091                                 x = putback_char;
1092                                 putback_char = -1;
1093                         }
1094                         else
1095                                 x = reader.Read ();
1096                         if (x == '\n') {
1097                                 line++;
1098                                 ref_line++;
1099                                 previous_col = col;
1100                                 col = 0;
1101                         }
1102                         else
1103                                 col++;
1104                         return x;
1105                 }
1106
1107                 int peekChar ()
1108                 {
1109                         if (putback_char != -1)
1110                                 return putback_char;
1111                         putback_char = reader.Read ();
1112                         return putback_char;
1113                 }
1114
1115                 int peekChar2 ()
1116                 {
1117                         if (putback_char != -1)
1118                                 return putback_char;
1119                         return reader.Peek ();
1120                 }
1121                 
1122                 void putback (int c)
1123                 {
1124                         if (putback_char != -1){
1125                                 Console.WriteLine ("Col: " + col);
1126                                 Console.WriteLine ("Row: " + line);
1127                                 Console.WriteLine ("Name: " + ref_name.Name);
1128                                 Console.WriteLine ("Current [{0}] putting back [{1}]  ", putback_char, c);
1129                                 throw new Exception ("This should not happen putback on putback");
1130                         }
1131                         if (c == '\n' || col == 0) {
1132                                 // It won't happen though.
1133                                 line--;
1134                                 ref_line--;
1135                                 col = previous_col;
1136                         }
1137                         else
1138                                 col--;
1139                         putback_char = c;
1140                 }
1141
1142                 public bool advance ()
1143                 {
1144                         return peekChar () != -1;
1145                 }
1146
1147                 public Object Value {
1148                         get {
1149                                 return val;
1150                         }
1151                 }
1152
1153                 public Object value ()
1154                 {
1155                         return val;
1156                 }
1157
1158                 bool IsCastToken (int token)
1159                 {
1160                         switch (token) {
1161                         case Token.BANG:
1162                         case Token.TILDE:
1163                         case Token.IDENTIFIER:
1164                         case Token.LITERAL_INTEGER:
1165                         case Token.LITERAL_FLOAT:
1166                         case Token.LITERAL_DOUBLE:
1167                         case Token.LITERAL_DECIMAL:
1168                         case Token.LITERAL_CHARACTER:
1169                         case Token.LITERAL_STRING:
1170                         case Token.BASE:
1171                         case Token.CHECKED:
1172                         case Token.FALSE:
1173                         case Token.FIXED:
1174                         case Token.NEW:
1175                         case Token.NULL:
1176                         case Token.SIZEOF:
1177                         case Token.THIS:
1178                         case Token.THROW:
1179                         case Token.TRUE:
1180                         case Token.TYPEOF:
1181                         case Token.UNCHECKED:
1182                         case Token.UNSAFE:
1183
1184                                 //
1185                                 // These can be part of a member access
1186                                 //
1187                         case Token.INT:
1188                         case Token.UINT:
1189                         case Token.SHORT:
1190                         case Token.USHORT:
1191                         case Token.LONG:
1192                         case Token.ULONG:
1193                         case Token.DOUBLE:
1194                         case Token.FLOAT:
1195                         case Token.CHAR:
1196                                 return true;
1197
1198                         default:
1199                                 return false;
1200                         }
1201                 }
1202
1203                 public int token ()
1204                 {
1205                         current_token = xtoken ();
1206                         return current_token;
1207                 }
1208
1209                 static StringBuilder static_cmd_arg = new System.Text.StringBuilder ();
1210                 
1211                 void get_cmd_arg (out string cmd, out string arg)
1212                 {
1213                         int c;
1214                         
1215                         tokens_seen = false;
1216                         arg = "";
1217                         static_cmd_arg.Length = 0;
1218
1219                         // skip over white space
1220                         while ((c = getChar ()) != -1 && (c != '\n') && ((c == '\r') || (c == ' ') || (c == '\t')))
1221                                 ;
1222
1223
1224                         while ((c != -1) && (c != '\n') && (c != ' ') && (c != '\t') && (c != '\r')){
1225                                 if (is_identifier_part_character ((char) c)){
1226                                         static_cmd_arg.Append ((char) c);
1227                                         c = getChar ();
1228                                 } else {
1229                                         putback (c);
1230                                         break;
1231                                 }
1232                         }
1233
1234                         cmd = static_cmd_arg.ToString ();
1235
1236                         if (c == '\n'){
1237                                 return;
1238                         }
1239
1240                         // skip over white space
1241                         while ((c = getChar ()) != -1 && (c != '\n') && ((c == '\r') || (c == ' ') || (c == '\t')))
1242                                 ;
1243
1244                         if (c == '\n'){
1245                                 return;
1246                         } else if (c == '\r'){
1247                                 return;
1248                         } else if (c == -1){
1249                                 arg = "";
1250                                 return;
1251                         }
1252                         
1253                         static_cmd_arg.Length = 0;
1254                         static_cmd_arg.Append ((char) c);
1255                         
1256                         while ((c = getChar ()) != -1 && (c != '\n') && (c != '\r')){
1257                                 static_cmd_arg.Append ((char) c);
1258                         }
1259
1260                         arg = static_cmd_arg.ToString ().Trim ();
1261                 }
1262
1263                 //
1264                 // Handles the #line directive
1265                 //
1266                 bool PreProcessLine (string arg)
1267                 {
1268                         if (arg == "")
1269                                 return false;
1270
1271                         if (arg == "default"){
1272                                 ref_line = line;
1273                                 ref_name = file_name;
1274                                 Location.Push (ref_name, line);
1275                                 return true;
1276                         } else if (arg == "hidden"){
1277                                 //
1278                                 // We ignore #line hidden
1279                                 //
1280                                 return true;
1281                         }
1282
1283                         try {
1284                                 int pos;
1285
1286                                 if ((pos = arg.IndexOf (' ')) != -1 && pos != 0){
1287                                         ref_line = System.Int32.Parse (arg.Substring (0, pos));
1288                                         pos++;
1289                                         
1290                                         char [] quotes = { '\"' };
1291                                         
1292                                         string name = arg.Substring (pos). Trim (quotes);
1293                                         ref_name = Location.LookupFile (name);
1294                                         file_name.HasLineDirective = true;
1295                                         ref_name.HasLineDirective = true;
1296                                         Location.Push (ref_name, ref_line);
1297                                 } else {
1298                                         ref_line = System.Int32.Parse (arg);
1299                                 }
1300                         } catch {
1301                                 return false;
1302                         }
1303                         
1304                         return true;
1305                 }
1306
1307                 //
1308                 // Handles #define and #undef
1309                 //
1310                 void PreProcessDefinition (bool is_define, string arg)
1311                 {
1312                         if (arg == "" || arg == "true" || arg == "false"){
1313                                 Report.Error (1001, Location, "Missing identifer to pre-processor directive");
1314                                 return;
1315                         }
1316
1317                         char[] whitespace = { ' ', '\t' };
1318                         if (arg.IndexOfAny (whitespace) != -1){
1319                                 Report.Error (1025, Location, "Single-line comment or end-of-line expected");
1320                                 return;
1321                         }
1322
1323                         if (!is_identifier_start_character (arg [0]))
1324                                 Report.Error (1001, Location, "Identifier expected: " + arg);
1325                         
1326                         foreach (char c in arg.Substring (1)){
1327                                 if (!is_identifier_part_character (c)){
1328                                         Report.Error (1001, Location, "Identifier expected: " + arg);
1329                                         return;
1330                                 }
1331                         }
1332
1333                         if (is_define){
1334                                 if (defines == null)
1335                                         defines = new Hashtable ();
1336                                 define (arg);
1337                         } else {
1338                                 if (defines == null)
1339                                         return;
1340                                 if (defines.Contains (arg))
1341                                         defines.Remove (arg);
1342                         }
1343                 }
1344
1345                 /// <summary>
1346                 /// Handles #pragma directive
1347                 /// </summary>
1348                 void PreProcessPragma (string arg)
1349                 {
1350                         const string warning = "warning";
1351                         const string w_disable = "warning disable";
1352                         const string w_restore = "warning restore";
1353
1354                         if (arg == w_disable) {
1355                                 Report.RegisterWarningRegion (Location).WarningDisable (line);
1356                                 return;
1357                         }
1358
1359                         if (arg == w_restore) {
1360                                 Report.RegisterWarningRegion (Location).WarningEnable (line);
1361                                 return;
1362                         }
1363
1364                         if (arg.StartsWith (w_disable)) {
1365                                 int[] codes = ParseNumbers (arg.Substring (w_disable.Length));
1366                                 foreach (int code in codes) {
1367                                         if (code != 0)
1368                                                 Report.RegisterWarningRegion (Location).WarningDisable (Location, code);
1369                                 }
1370                                 return;
1371                         }
1372
1373                         if (arg.StartsWith (w_restore)) {
1374                                 int[] codes = ParseNumbers (arg.Substring (w_restore.Length));
1375                                 Hashtable w_table = Report.warning_ignore_table;
1376                                 foreach (int code in codes) {
1377                                         if (w_table != null && w_table.Contains (code))
1378                                                 Report.Warning (1635, 1, Location, "Cannot restore warning 'CS{0:0000}' because it was disabled globally", code);
1379                                         Report.RegisterWarningRegion (Location).WarningEnable (Location, code);
1380                                 }
1381                                 return;
1382                         }
1383
1384                         if (arg.StartsWith (warning)) {
1385                                 Report.Warning (1634, 1, Location, "Expected disable or restore");
1386                                 return;
1387                         }
1388
1389                         Report.Warning (1633, 1, Location, "Unrecognized #pragma directive");
1390                 }
1391
1392                 int[] ParseNumbers (string text)
1393                 {
1394                         string[] string_array = text.Split (',');
1395                         int[] values = new int [string_array.Length];
1396                         int index = 0;
1397                         foreach (string string_code in string_array) {
1398                                 try {
1399                                         values[index++] = int.Parse (string_code, System.Globalization.CultureInfo.InvariantCulture);
1400                                 }
1401                                 catch (FormatException) {
1402                                         Report.Warning (1692, Location, "Invalid number");
1403                                 }
1404                         }
1405                         return values;
1406                 }
1407
1408                 bool eval_val (string s)
1409                 {
1410                         if (s == "true")
1411                                 return true;
1412                         if (s == "false")
1413                                 return false;
1414                         
1415                         if (defines == null)
1416                                 return false;
1417                         if (defines.Contains (s))
1418                                 return true;
1419
1420                         return false;
1421                 }
1422
1423                 bool pp_primary (ref string s)
1424                 {
1425                         s = s.Trim ();
1426                         int len = s.Length;
1427
1428                         if (len > 0){
1429                                 char c = s [0];
1430                                 
1431                                 if (c == '('){
1432                                         s = s.Substring (1);
1433                                         bool val = pp_expr (ref s);
1434                                         if (s.Length > 0 && s [0] == ')'){
1435                                                 s = s.Substring (1);
1436                                                 return val;
1437                                         }
1438                                         Error_InvalidDirective ();
1439                                         return false;
1440                                 }
1441                                 
1442                                 if (is_identifier_start_character (c)){
1443                                         int j = 1;
1444
1445                                         while (j < len){
1446                                                 c = s [j];
1447                                                 
1448                                                 if (is_identifier_part_character (c)){
1449                                                         j++;
1450                                                         continue;
1451                                                 }
1452                                                 bool v = eval_val (s.Substring (0, j));
1453                                                 s = s.Substring (j);
1454                                                 return v;
1455                                         }
1456                                         bool vv = eval_val (s);
1457                                         s = "";
1458                                         return vv;
1459                                 }
1460                         }
1461                         Error_InvalidDirective ();
1462                         return false;
1463                 }
1464                 
1465                 bool pp_unary (ref string s)
1466                 {
1467                         s = s.Trim ();
1468                         int len = s.Length;
1469
1470                         if (len > 0){
1471                                 if (s [0] == '!'){
1472                                         if (len > 1 && s [1] == '='){
1473                                                 Error_InvalidDirective ();
1474                                                 return false;
1475                                         }
1476                                         s = s.Substring (1);
1477                                         return ! pp_primary (ref s);
1478                                 } else
1479                                         return pp_primary (ref s);
1480                         } else {
1481                                 Error_InvalidDirective ();
1482                                 return false;
1483                         }
1484                 }
1485                 
1486                 bool pp_eq (ref string s)
1487                 {
1488                         bool va = pp_unary (ref s);
1489
1490                         s = s.Trim ();
1491                         int len = s.Length;
1492                         if (len > 0){
1493                                 if (s [0] == '='){
1494                                         if (len > 2 && s [1] == '='){
1495                                                 s = s.Substring (2);
1496                                                 return va == pp_unary (ref s);
1497                                         } else {
1498                                                 Error_InvalidDirective ();
1499                                                 return false;
1500                                         }
1501                                 } else if (s [0] == '!' && len > 1 && s [1] == '='){
1502                                         s = s.Substring (2);
1503
1504                                         return va != pp_unary (ref s);
1505
1506                                 } 
1507                         }
1508
1509                         return va;
1510                                 
1511                 }
1512                 
1513                 bool pp_and (ref string s)
1514                 {
1515                         bool va = pp_eq (ref s);
1516
1517                         s = s.Trim ();
1518                         int len = s.Length;
1519                         if (len > 0){
1520                                 if (s [0] == '&'){
1521                                         if (len > 2 && s [1] == '&'){
1522                                                 s = s.Substring (2);
1523                                                 return (va & pp_and (ref s));
1524                                         } else {
1525                                                 Error_InvalidDirective ();
1526                                                 return false;
1527                                         }
1528                                 } 
1529                         }
1530                         return va;
1531                 }
1532                 
1533                 //
1534                 // Evaluates an expression for `#if' or `#elif'
1535                 //
1536                 bool pp_expr (ref string s)
1537                 {
1538                         bool va = pp_and (ref s);
1539                         s = s.Trim ();
1540                         int len = s.Length;
1541                         if (len > 0){
1542                                 char c = s [0];
1543                                 
1544                                 if (c == '|'){
1545                                         if (len > 2 && s [1] == '|'){
1546                                                 s = s.Substring (2);
1547                                                 return va | pp_expr (ref s);
1548                                         } else {
1549                                                 Error_InvalidDirective ();
1550                                                 return false;
1551                                         }
1552                                 } 
1553                         }
1554                         
1555                         return va;
1556                 }
1557
1558                 bool eval (string s)
1559                 {
1560                         bool v = pp_expr (ref s);
1561                         s = s.Trim ();
1562                         if (s.Length != 0){
1563                                 Error_InvalidDirective ();
1564                                 return false;
1565                         }
1566
1567                         return v;
1568                 }
1569                 
1570                 void Error_InvalidDirective ()
1571                 {
1572                         Report.Error (1517, Location, "Invalid preprocessor directive");
1573                 }
1574
1575                 void Error_UnexpectedDirective (string extra)
1576                 {
1577                         Report.Error (
1578                                 1028, Location,
1579                                 "Unexpected processor directive (" + extra + ")");
1580                 }
1581
1582                 void Error_TokensSeen ()
1583                 {
1584                         Report.Error (1032, Location,
1585                                 "Cannot define or undefine preprocessor symbols after first token in file");
1586                 }
1587                 
1588                 //
1589                 // Set to false to stop handling preprocesser directives
1590                 // 
1591                 bool process_directives = true;
1592
1593                 //
1594                 // if true, then the code continues processing the code
1595                 // if false, the code stays in a loop until another directive is
1596                 // reached.
1597                 //
1598                 bool handle_preprocessing_directive (bool caller_is_taking)
1599                 {
1600                         string cmd, arg;
1601                         bool region_directive = false;
1602
1603                         get_cmd_arg (out cmd, out arg);
1604
1605                         // Eat any trailing whitespaces and single-line comments
1606                         if (arg.IndexOf ("//") != -1)
1607                                 arg = arg.Substring (0, arg.IndexOf ("//"));
1608                         arg = arg.TrimEnd (' ', '\t');
1609
1610                         //
1611                         // The first group of pre-processing instructions is always processed
1612                         //
1613                         switch (cmd){
1614                         case "pragma":
1615                                 if (RootContext.Version == LanguageVersion.ISO_1) {
1616                                         Report.FeatureIsNotStandardized (Location, "#pragma");
1617                                         return caller_is_taking;
1618                                 }
1619
1620                                 PreProcessPragma (arg);
1621                                 return caller_is_taking;
1622
1623                         case "line":
1624                                 if (!PreProcessLine (arg))
1625                                         Report.Error (
1626                                                 1576, Location,
1627                                                 "The line number specified for #line directive is missing or invalid");
1628                                 return caller_is_taking;
1629
1630                         case "region":
1631                                 region_directive = true;
1632                                 arg = "true";
1633                                 goto case "if";
1634
1635                         case "endregion":
1636                                 region_directive = true;
1637                                 goto case "endif";
1638                                 
1639                         case "if":
1640                                 if (arg == ""){
1641                                         Error_InvalidDirective ();
1642                                         return true;
1643                                 }
1644                                 bool taking = false;
1645                                 if (ifstack == null)
1646                                         ifstack = new Stack ();
1647
1648                                 if (ifstack.Count == 0){
1649                                         taking = true;
1650                                 } else {
1651                                         int state = (int) ifstack.Peek ();
1652                                         if ((state & TAKING) != 0)
1653                                                 taking = true;
1654                                 }
1655
1656                                 if (eval (arg) && taking){
1657                                         int push = TAKING | TAKEN_BEFORE | PARENT_TAKING;
1658                                         if (region_directive)
1659                                                 push |= REGION;
1660                                         ifstack.Push (push);
1661                                         return true;
1662                                 } else {
1663                                         int push = (taking ? PARENT_TAKING : 0);
1664                                         if (region_directive)
1665                                                 push |= REGION;
1666                                         ifstack.Push (push);
1667                                         return false;
1668                                 }
1669                                 
1670                         case "endif":
1671                                 if (ifstack == null || ifstack.Count == 0){
1672                                         Error_UnexpectedDirective ("no #if for this #endif");
1673                                         return true;
1674                                 } else {
1675                                         int pop = (int) ifstack.Pop ();
1676                                         
1677                                         if (region_directive && ((pop & REGION) == 0))
1678                                                 Report.Error (1027, Location, "Expected `#endif' directive");
1679                                         else if (!region_directive && ((pop & REGION) != 0))
1680                                                 Report.Error (1038, Location, "#endregion directive expected");
1681                                         
1682                                         if (!region_directive && arg.Length != 0) {
1683                                                 Report.Error (1025, Location, "Single-line comment or end-of-line expected");
1684                                         }
1685                                         
1686                                         if (ifstack.Count == 0)
1687                                                 return true;
1688                                         else {
1689                                                 int state = (int) ifstack.Peek ();
1690
1691                                                 if ((state & TAKING) != 0)
1692                                                         return true;
1693                                                 else
1694                                                         return false;
1695                                         }
1696                                 }
1697
1698                         case "elif":
1699                                 if (ifstack == null || ifstack.Count == 0){
1700                                         Error_UnexpectedDirective ("no #if for this #elif");
1701                                         return true;
1702                                 } else {
1703                                         int state = (int) ifstack.Peek ();
1704
1705                                         if ((state & REGION) != 0) {
1706                                                 Report.Error (1038, Location, "#endregion directive expected");
1707                                                 return true;
1708                                         }
1709
1710                                         if ((state & ELSE_SEEN) != 0){
1711                                                 Error_UnexpectedDirective ("#elif not valid after #else");
1712                                                 return true;
1713                                         }
1714
1715                                         if ((state & (TAKEN_BEFORE | TAKING)) != 0)
1716                                                 return false;
1717
1718                                         if (eval (arg) && ((state & PARENT_TAKING) != 0)){
1719                                                 state = (int) ifstack.Pop ();
1720                                                 ifstack.Push (state | TAKING | TAKEN_BEFORE);
1721                                                 return true;
1722                                         } else 
1723                                                 return false;
1724                                 }
1725
1726                         case "else":
1727                                 if (ifstack == null || ifstack.Count == 0){
1728                                         Error_UnexpectedDirective ("no #if for this #else");
1729                                         return true;
1730                                 } else {
1731                                         int state = (int) ifstack.Peek ();
1732
1733                                         if ((state & REGION) != 0) {
1734                                                 Report.Error (1038, Location, "#endregion directive expected");
1735                                                 return true;
1736                                         }
1737
1738                                         if ((state & ELSE_SEEN) != 0){
1739                                                 Error_UnexpectedDirective ("#else within #else");
1740                                                 return true;
1741                                         }
1742
1743                                         ifstack.Pop ();
1744
1745                                         bool ret;
1746                                         if ((state & TAKEN_BEFORE) == 0){
1747                                                 ret = ((state & PARENT_TAKING) != 0);
1748                                         } else
1749                                                 ret = false;
1750                                         
1751                                         if (ret)
1752                                                 state |= TAKING;
1753                                         else
1754                                                 state &= ~TAKING;
1755                                         
1756                                         ifstack.Push (state | ELSE_SEEN);
1757                                         
1758                                         return ret;
1759                                 }
1760                         }
1761
1762                         //
1763                         // These are only processed if we are in a `taking' block
1764                         //
1765                         if (!caller_is_taking)
1766                                 return false;
1767                                         
1768                         switch (cmd){
1769                         case "define":
1770                                 if (any_token_seen){
1771                                         Error_TokensSeen ();
1772                                         return true;
1773                                 }
1774                                 PreProcessDefinition (true, arg);
1775                                 return true;
1776
1777                         case "undef":
1778                                 if (any_token_seen){
1779                                         Error_TokensSeen ();
1780                                         return true;
1781                                 }
1782                                 PreProcessDefinition (false, arg);
1783                                 return true;
1784
1785                         case "error":
1786                                 Report.Error (1029, Location, "#error: '" + arg + "'");
1787                                 return true;
1788
1789                         case "warning":
1790                                 Report.Warning (1030, Location, "#warning: `{0}'", arg);
1791                                 return true;
1792                         }
1793
1794                         Report.Error (1024, Location, "Wrong preprocessor directive");
1795                         return true;
1796
1797                 }
1798
1799                 private int consume_string (bool quoted)
1800                 {
1801                         int c;
1802                         string_builder.Length = 0;
1803                                                                 
1804                         while ((c = getChar ()) != -1){
1805                                 if (c == '"'){
1806                                         if (quoted && peekChar () == '"'){
1807                                                 string_builder.Append ((char) c);
1808                                                 getChar ();
1809                                                 continue;
1810                                         } else {
1811                                                 val = string_builder.ToString ();
1812                                                 return Token.LITERAL_STRING;
1813                                         }
1814                                 }
1815
1816                                 if (c == '\n'){
1817                                         if (!quoted)
1818                                                 Report.Error (1010, Location, "Newline in constant");
1819                                 }
1820
1821                                 if (!quoted){
1822                                         c = escape (c);
1823                                         if (c == -1)
1824                                                 return Token.ERROR;
1825                                 }
1826                                 string_builder.Append ((char) c);
1827                         }
1828
1829                         Report.Error (1039, Location, "Unterminated string literal");
1830                         return Token.EOF;
1831                 }
1832
1833                 private int consume_identifier (int s)
1834                 {
1835                         int res = consume_identifier (s, false);
1836
1837                         if (doc_state == XmlCommentState.Allowed)
1838                                 doc_state = XmlCommentState.NotAllowed;
1839                         switch (res) {
1840                         case Token.USING:
1841                         case Token.NAMESPACE:
1842                                 check_incorrect_doc_comment ();
1843                                 break;
1844                         }
1845
1846                         if (res == Token.PARTIAL) {
1847                                 // Save current position and parse next token.
1848                                 int old = reader.Position;
1849                                 int old_putback = putback_char;
1850                                 int old_ref_line = ref_line;
1851                                 int old_col = col;
1852
1853                                 putback_char = -1;
1854
1855                                 int next_token = token ();
1856                                 bool ok = (next_token == Token.CLASS) ||
1857                                         (next_token == Token.STRUCT) ||
1858                                         (next_token == Token.INTERFACE) ||
1859                                         (next_token == Token.ENUM); // "partial" is a keyword in 'partial enum', even though it's not valid
1860
1861                                 reader.Position = old;
1862                                 ref_line = old_ref_line;
1863                                 col = old_col;
1864                                 putback_char = old_putback;
1865
1866                                 if (ok)
1867                                         return res;
1868                                 else {
1869                                         val = new LocatedToken (Location, "partial");
1870                                         return Token.IDENTIFIER;
1871                                 }
1872                         }
1873
1874                         return res;
1875                 }
1876
1877                 private int consume_identifier (int s, bool quoted) 
1878                 {
1879                         int pos = 1;
1880                         int c = -1;
1881                         
1882                         id_builder [0] = (char) s;
1883
1884                         current_location = new Location (ref_line, Col);
1885
1886                         while ((c = getChar ()) != -1) {
1887                                 if (is_identifier_part_character ((char) c)){
1888                                         if (pos == max_id_size){
1889                                                 Report.Error (645, Location, "Identifier too long (limit is 512 chars)");
1890                                                 return Token.ERROR;
1891                                         }
1892                                         
1893                                         id_builder [pos++] = (char) c;
1894 //                                      putback_char = -1;
1895                                 } else {
1896 //                                      putback_char = c;
1897                                         putback (c);
1898                                         break;
1899                                 }
1900                         }
1901
1902                         //
1903                         // Optimization: avoids doing the keyword lookup
1904                         // on uppercase letters and _
1905                         //
1906                         if (!quoted && (s >= 'a' || s == '_')){
1907                                 int keyword = GetKeyword (id_builder, pos);
1908                                 if (keyword != -1) {
1909                                         val = Location;
1910                                         return keyword;
1911                                 }
1912                         }
1913
1914                         //
1915                         // Keep identifiers in an array of hashtables to avoid needless
1916                         // allocations
1917                         //
1918
1919                         if (identifiers [pos] != null) {
1920                                 val = identifiers [pos][id_builder];
1921                                 if (val != null) {
1922                                         val = new LocatedToken (Location, (string) val);
1923                                         if (quoted)
1924                                                 escapedIdentifiers.Add (val);
1925                                         return Token.IDENTIFIER;
1926                                 }
1927                         }
1928                         else
1929                                 identifiers [pos] = new CharArrayHashtable (pos);
1930
1931                         val = new String (id_builder, 0, pos);
1932                         if (RootContext.Version == LanguageVersion.ISO_1 && id_builder [0] == '_' && id_builder [1] == '_') {
1933                                 Report.Error (1638, Location, 
1934                                         "`{0}': Any identifier with double underscores cannot be used when ISO language version mode is specified", val);
1935                         }
1936
1937                         char [] chars = new char [pos];
1938                         Array.Copy (id_builder, chars, pos);
1939
1940                         identifiers [pos] [chars] = val;
1941
1942                         val = new LocatedToken (Location, (string) val);
1943                         if (quoted)
1944                                 escapedIdentifiers.Add (val);
1945                         return Token.IDENTIFIER;
1946                 }
1947                 
1948                 public int xtoken ()
1949                 {
1950                         int t;
1951                         bool doread = false;
1952                         int c;
1953
1954                         // Whether we have seen comments on the current line
1955                         bool comments_seen = false;
1956                         
1957                         val = null;
1958                         // optimization: eliminate col and implement #directive semantic correctly.
1959                         for (;(c = getChar ()) != -1;) {
1960                                 if (c == ' ')
1961                                         continue;
1962                                 
1963                                 if (c == '\t') {
1964                                         continue;
1965                                 }
1966                                 
1967                                 if (c == ' ' || c == '\f' || c == '\v' || c == 0xa0)
1968                                         continue;
1969
1970                                 if (c == '\r') {
1971                                         if (peekChar () == '\n')
1972                                                 getChar ();
1973
1974                                         any_token_seen |= tokens_seen;
1975                                         tokens_seen = false;
1976                                         comments_seen = false;
1977                                         continue;
1978                                 }
1979
1980                                 // Handle double-slash comments.
1981                                 if (c == '/'){
1982                                         int d = peekChar ();
1983                                 
1984                                         if (d == '/'){
1985                                                 getChar ();
1986                                                 if (RootContext.Documentation != null && peekChar () == '/') {
1987                                                         getChar ();
1988                                                         // Don't allow ////.
1989                                                         if ((d = peekChar ()) != '/') {
1990                                                                 update_comment_location ();
1991                                                                 if (doc_state == XmlCommentState.Allowed)
1992                                                                         handle_one_line_xml_comment ();
1993                                                                 else if (doc_state == XmlCommentState.NotAllowed)
1994                                                                         warn_incorrect_doc_comment ();
1995                                                         }
1996                                                 }
1997                                                 while ((d = getChar ()) != -1 && (d != '\n') && d != '\r')
1998                                                 if (d == '\n'){
1999                                                 }
2000                                                 any_token_seen |= tokens_seen;
2001                                                 tokens_seen = false;
2002                                                 comments_seen = false;
2003                                                 continue;
2004                                         } else if (d == '*'){
2005                                                 getChar ();
2006                                                 bool docAppend = false;
2007                                                 if (RootContext.Documentation != null && peekChar () == '*') {
2008                                                         getChar ();
2009                                                         update_comment_location ();
2010                                                         // But when it is /**/, just do nothing.
2011                                                         if (peekChar () == '/') {
2012                                                                 getChar ();
2013                                                                 continue;
2014                                                         }
2015                                                         if (doc_state == XmlCommentState.Allowed)
2016                                                                 docAppend = true;
2017                                                         else if (doc_state == XmlCommentState.NotAllowed)
2018                                                                 warn_incorrect_doc_comment ();
2019                                                 }
2020
2021                                                 int current_comment_start = 0;
2022                                                 if (docAppend) {
2023                                                         current_comment_start = xml_comment_buffer.Length;
2024                                                         xml_comment_buffer.Append (Environment.NewLine);
2025                                                 }
2026
2027                                                 Location start_location = Location;
2028
2029                                                 while ((d = getChar ()) != -1){
2030                                                         if (d == '*' && peekChar () == '/'){
2031                                                                 getChar ();
2032                                                                 comments_seen = true;
2033                                                                 break;
2034                                                         }
2035                                                         if (docAppend)
2036                                                                 xml_comment_buffer.Append ((char) d);
2037                                                         
2038                                                         if (d == '\n'){
2039                                                                 any_token_seen |= tokens_seen;
2040                                                                 tokens_seen = false;
2041                                                                 // 
2042                                                                 // Reset 'comments_seen' just to be consistent.
2043                                                                 // It doesn't matter either way, here.
2044                                                                 //
2045                                                                 comments_seen = false;
2046                                                         }
2047                                                 }
2048                                                 if (!comments_seen)
2049                                                         Report.Error (1035, start_location, "End-of-file found, '*/' expected");
2050
2051                                                 if (docAppend)
2052                                                         update_formatted_doc_comment (current_comment_start);
2053                                                 continue;
2054                                         }
2055                                         goto is_punct_label;
2056                                 }
2057
2058                                 
2059                                 if (is_identifier_start_character ((char)c)){
2060                                         tokens_seen = true;
2061                                         return consume_identifier (c);
2062                                 }
2063
2064                         is_punct_label:
2065                                 current_location = new Location (ref_line, Col);
2066                                 if ((t = is_punct ((char)c, ref doread)) != Token.ERROR){
2067                                         tokens_seen = true;
2068                                         if (doread){
2069                                                 getChar ();
2070                                         }
2071                                         return t;
2072                                 }
2073
2074                                 // white space
2075                                 if (c == '\n'){
2076                                         any_token_seen |= tokens_seen;
2077                                         tokens_seen = false;
2078                                         comments_seen = false;
2079                                         continue;
2080                                 }
2081
2082                                 if (c >= '0' && c <= '9'){
2083                                         tokens_seen = true;
2084                                         return is_number (c);
2085                                 }
2086
2087                                 if (c == '.'){
2088                                         tokens_seen = true;
2089                                         int peek = peekChar ();
2090                                         if (peek >= '0' && peek <= '9')
2091                                                 return is_number (c);
2092                                         return Token.DOT;
2093                                 }
2094                                 
2095                                 /* For now, ignore pre-processor commands */
2096                                 // FIXME: In C# the '#' is not limited to appear
2097                                 // on the first column.
2098                                 if (c == '#') {
2099                                         // return NONE if we're not processing directives (during token peeks)
2100                                         if (!process_directives)
2101                                                 return Token.NONE;
2102
2103                                         bool cont = true;
2104                                         if (tokens_seen || comments_seen) {
2105                                                error_details = "Preprocessor directives must appear as the first" +
2106                                                " non-whitespace character on a line.";
2107
2108                                                Report.Error (1040, Location, error_details);
2109
2110                                                return Token.ERROR;
2111                                        }
2112                                         
2113                                 start_again:
2114                                         
2115                                         cont = handle_preprocessing_directive (cont);
2116
2117                                         if (cont){
2118                                                 continue;
2119                                         }
2120
2121                                         bool skipping = false;
2122                                         for (;(c = getChar ()) != -1;){
2123                                                 if (c == '\n'){
2124                                                         skipping = false;
2125                                                 } else if (c == ' ' || c == '\t' || c == '\v' || c == '\r' || c == 0xa0)
2126                                                         continue;
2127                                                 else if (c != '#')
2128                                                         skipping = true;
2129                                                 if (c == '#' && !skipping)
2130                                                         goto start_again;
2131                                         }
2132                                         any_token_seen |= tokens_seen;
2133                                         tokens_seen = false;
2134                                         if (c == -1)
2135                                                 Report.Error (1027, Location, "Expected `#endif' directive");
2136                                         continue;
2137                                 }
2138                                 
2139                                 if (c == '"') 
2140                                         return consume_string (false);
2141
2142                                 if (c == '\''){
2143                                         c = getChar ();
2144                                         tokens_seen = true;
2145                                         if (c == '\''){
2146                                                 error_details = "Empty character literal";
2147                                                 Report.Error (1011, Location, error_details);
2148                                                 return Token.ERROR;
2149                                         }
2150                                         c = escape (c);
2151                                         if (c == -1)
2152                                                 return Token.ERROR;
2153                                         val = new System.Char ();
2154                                         val = (char) c;
2155                                         c = getChar ();
2156
2157                                         if (c != '\''){
2158                                                 error_details = "Too many characters in character literal";
2159                                                 Report.Error (1012, Location, error_details);
2160
2161                                                 // Try to recover, read until newline or next "'"
2162                                                 while ((c = getChar ()) != -1){
2163                                                         if (c == '\n'){
2164                                                                 break;
2165                                                         }
2166                                                         else if (c == '\'')
2167                                                                 break;
2168                                                 }
2169                                                 return Token.ERROR;
2170                                         }
2171                                         return Token.LITERAL_CHARACTER;
2172                                 }
2173                                 
2174                                 if (c == '@') {
2175                                         c = getChar ();
2176                                         if (c == '"') {
2177                                                 tokens_seen = true;
2178                                                 return consume_string (true);
2179                                         } else if (is_identifier_start_character ((char) c)){
2180                                                 return consume_identifier (c, true);
2181                                         } else {
2182                                                 Report.Error (1646, Location, "Keyword, identifier, or string expected after verbatim specifier: @");
2183                                         }
2184                                 }
2185
2186                                 error_details = ((char)c).ToString ();
2187                                 
2188                                 return Token.ERROR;
2189                         }
2190
2191                         return Token.EOF;
2192                 }
2193
2194                 //
2195                 // Handles one line xml comment
2196                 //
2197                 private void handle_one_line_xml_comment ()
2198                 {
2199                         int c;
2200                         while ((c = peekChar ()) == ' ')
2201                                 getChar (); // skip heading whitespaces.
2202                         while ((c = peekChar ()) != -1 && c != '\n' && c != '\r') {
2203                                 xml_comment_buffer.Append ((char) getChar ());
2204                         }
2205                         if (c == '\r' || c == '\n')
2206                                 xml_comment_buffer.Append (Environment.NewLine);
2207                 }
2208
2209                 //
2210                 // Remove heading "*" in Javadoc-like xml documentation.
2211                 //
2212                 private void update_formatted_doc_comment (int current_comment_start)
2213                 {
2214                         int length = xml_comment_buffer.Length - current_comment_start;
2215                         string [] lines = xml_comment_buffer.ToString (
2216                                 current_comment_start,
2217                                 length).Replace ("\r", "").Split ('\n');
2218                         
2219                         // The first line starts with /**, thus it is not target
2220                         // for the format check.
2221                         for (int i = 1; i < lines.Length; i++) {
2222                                 string s = lines [i];
2223                                 int idx = s.IndexOf ('*');
2224                                 string head = null;
2225                                 if (idx < 0) {
2226                                         if (i < lines.Length - 1)
2227                                                 return;
2228                                         head = s;
2229                                 } else
2230                                         head = s.Substring (0, idx);
2231                                 foreach (char c in head)
2232                                         if (c != ' ')
2233                                                 return;
2234                                 lines [i] = s.Substring (idx + 1);
2235                         }
2236                         xml_comment_buffer.Remove (current_comment_start, length);
2237                         xml_comment_buffer.Insert (current_comment_start, String.Join (Environment.NewLine, lines));
2238                 }
2239
2240                 //
2241                 // Updates current comment location.
2242                 //
2243                 private void update_comment_location ()
2244                 {
2245                         if (current_comment_location.IsNull) {
2246                                 // "-2" is for heading "//" or "/*"
2247                                 current_comment_location =
2248                                         new Location (ref_line, col - 2);
2249                         }
2250                 }
2251
2252                 //
2253                 // Checks if there was incorrect doc comments and raise
2254                 // warnings.
2255                 //
2256                 public void check_incorrect_doc_comment ()
2257                 {
2258                         if (xml_comment_buffer.Length > 0)
2259                                 warn_incorrect_doc_comment ();
2260                 }
2261
2262                 //
2263                 // Raises a warning when tokenizer found incorrect doccomment
2264                 // markup.
2265                 //
2266                 private void warn_incorrect_doc_comment ()
2267                 {
2268                         if (doc_state != XmlCommentState.Error) {
2269                                 doc_state = XmlCommentState.Error;
2270                                 // in csc, it is 'XML comment is not placed on 
2271                                 // a valid language element'. But that does not
2272                                 // make sense.
2273                                 Report.Warning (1587, 2, Location, "XML comment is placed on an invalid language element.");
2274                         }
2275                 }
2276
2277                 //
2278                 // Consumes the saved xml comment lines (if any)
2279                 // as for current target member or type.
2280                 //
2281                 public string consume_doc_comment ()
2282                 {
2283                         if (xml_comment_buffer.Length > 0) {
2284                                 string ret = xml_comment_buffer.ToString ();
2285                                 reset_doc_comment ();
2286                                 return ret;
2287                         }
2288                         return null;
2289                 }
2290
2291                 void reset_doc_comment ()
2292                 {
2293                         xml_comment_buffer.Length = 0;
2294                         current_comment_location = Location.Null;
2295                 }
2296
2297                 public void cleanup ()
2298                 {
2299                         if (ifstack != null && ifstack.Count >= 1) {
2300                                 int state = (int) ifstack.Pop ();
2301                                 if ((state & REGION) != 0)
2302                                         Report.Error (1038, Location, "#endregion directive expected");
2303                                 else 
2304                                         Report.Error (1027, "Expected `#endif' directive");
2305                         }
2306                                 
2307                 }
2308         }
2309
2310         //
2311         // Indicates whether it accepts XML documentation or not.
2312         //
2313         public enum XmlCommentState {
2314                 // comment is allowed in this state.
2315                 Allowed,
2316                 // comment is not allowed in this state.
2317                 NotAllowed,
2318                 // once comments appeared when it is NotAllowed, then the
2319                 // state is changed to it, until the state is changed to
2320                 // .Allowed.
2321                 Error
2322         }
2323 }