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