2005-11-24 Chris Toshok <toshok@ximian.com>
[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, 4, 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                         catch (FormatException) {
1060                                 Report.Error (1013, Location, "Invalid number");
1061                                 val = 0ul;
1062                                 return Token.LITERAL_INTEGER;
1063                         }
1064                         
1065                         return integer_type_suffix (ul, peekChar ());
1066                 }
1067
1068                 //
1069                 // Invoked if we know we have .digits or digits
1070                 //
1071                 int is_number (int c)
1072                 {
1073                         bool is_real = false;
1074                         int type;
1075
1076                         number_pos = 0;
1077
1078                         if (c >= '0' && c <= '9'){
1079                                 if (c == '0'){
1080                                         int peek = peekChar ();
1081
1082                                         if (peek == 'x' || peek == 'X')
1083                                                 return handle_hex ();
1084                                 }
1085                                 decimal_digits (c);
1086                                 c = getChar ();
1087                         }
1088
1089                         //
1090                         // We need to handle the case of
1091                         // "1.1" vs "1.string" (LITERAL_FLOAT vs NUMBER DOT IDENTIFIER)
1092                         //
1093                         if (c == '.'){
1094                                 if (decimal_digits ('.')){
1095                                         is_real = true;
1096                                         c = getChar ();
1097                                 } else {
1098                                         putback ('.');
1099                                         number_pos--;
1100                                         return adjust_int (-1);
1101                                 }
1102                         }
1103                         
1104                         if (c == 'e' || c == 'E'){
1105                                 is_real = true;
1106                                 if (number_pos == max_number_size)
1107                                         Error_NumericConstantTooLong ();
1108                                 number_builder [number_pos++] = 'e';
1109                                 c = getChar ();
1110                                 
1111                                 if (c == '+'){
1112                                         if (number_pos == max_number_size)
1113                                                 Error_NumericConstantTooLong ();
1114                                         number_builder [number_pos++] = '+';
1115                                         c = -1;
1116                                 } else if (c == '-') {
1117                                         if (number_pos == max_number_size)
1118                                                 Error_NumericConstantTooLong ();
1119                                         number_builder [number_pos++] = '-';
1120                                         c = -1;
1121                                 } else {
1122                                         if (number_pos == max_number_size)
1123                                                 Error_NumericConstantTooLong ();
1124                                         number_builder [number_pos++] = '+';
1125                                 }
1126                                         
1127                                 decimal_digits (c);
1128                                 c = getChar ();
1129                         }
1130
1131                         type = real_type_suffix (c);
1132                         if (type == Token.NONE && !is_real){
1133                                 putback (c);
1134                                 return adjust_int (c);
1135                         } else 
1136                                 is_real = true;
1137
1138                         if (type == Token.NONE){
1139                                 putback (c);
1140                         }
1141                         
1142                         if (is_real)
1143                                 return adjust_real (type);
1144
1145                         Console.WriteLine ("This should not be reached");
1146                         throw new Exception ("Is Number should never reach this point");
1147                 }
1148
1149                 //
1150                 // Accepts exactly count (4 or 8) hex, no more no less
1151                 //
1152                 int getHex (int count, out bool error)
1153                 {
1154                         int i;
1155                         int total = 0;
1156                         int c;
1157                         int top = count != -1 ? count : 4;
1158                         
1159                         getChar ();
1160                         error = false;
1161                         for (i = 0; i < top; i++){
1162                                 c = getChar ();
1163                                 
1164                                 if (c >= '0' && c <= '9')
1165                                         c = (int) c - (int) '0';
1166                                 else if (c >= 'A' && c <= 'F')
1167                                         c = (int) c - (int) 'A' + 10;
1168                                 else if (c >= 'a' && c <= 'f')
1169                                         c = (int) c - (int) 'a' + 10;
1170                                 else {
1171                                         error = true;
1172                                         return 0;
1173                                 }
1174                                 
1175                                 total = (total * 16) + c;
1176                                 if (count == -1){
1177                                         int p = peekChar ();
1178                                         if (p == -1)
1179                                                 break;
1180                                         if (!is_hex ((char)p))
1181                                                 break;
1182                                 }
1183                         }
1184                         return total;
1185                 }
1186
1187                 int escape (int c)
1188                 {
1189                         bool error;
1190                         int d;
1191                         int v;
1192
1193                         d = peekChar ();
1194                         if (c != '\\')
1195                                 return c;
1196                         
1197                         switch (d){
1198                         case 'a':
1199                                 v = '\a'; break;
1200                         case 'b':
1201                                 v = '\b'; break;
1202                         case 'n':
1203                                 v = '\n'; break;
1204                         case 't':
1205                                 v = '\t'; break;
1206                         case 'v':
1207                                 v = '\v'; break;
1208                         case 'r':
1209                                 v = '\r'; break;
1210                         case '\\':
1211                                 v = '\\'; break;
1212                         case 'f':
1213                                 v = '\f'; break;
1214                         case '0':
1215                                 v = 0; break;
1216                         case '"':
1217                                 v = '"'; break;
1218                         case '\'':
1219                                 v = '\''; break;
1220                         case 'x':
1221                                 v = getHex (-1, out error);
1222                                 if (error)
1223                                         goto default;
1224                                 return v;
1225                         case 'u':
1226                                 v = getHex (4, out error);
1227                                 if (error)
1228                                         goto default;
1229                                 return v;
1230                         case 'U':
1231                                 v = getHex (8, out error);
1232                                 if (error)
1233                                         goto default;
1234                                 return v;
1235                         default:
1236                                 Report.Error (1009, Location, "Unrecognized escape sequence `\\{0}'", ((char)d).ToString ());
1237                                 return d;
1238                         }
1239                         getChar ();
1240                         return v;
1241                 }
1242
1243                 int getChar ()
1244                 {
1245                         int x;
1246                         if (putback_char != -1) {
1247                                 x = putback_char;
1248                                 putback_char = -1;
1249                         }
1250                         else
1251                                 x = reader.Read ();
1252                         if (x == '\n') {
1253                                 line++;
1254                                 ref_line++;
1255                                 previous_col = col;
1256                                 col = 0;
1257                         }
1258                         else
1259                                 col++;
1260                         return x;
1261                 }
1262
1263                 int peekChar ()
1264                 {
1265                         if (putback_char != -1)
1266                                 return putback_char;
1267                         putback_char = reader.Read ();
1268                         return putback_char;
1269                 }
1270
1271                 int peekChar2 ()
1272                 {
1273                         if (putback_char != -1)
1274                                 return putback_char;
1275                         return reader.Peek ();
1276                 }
1277                 
1278                 void putback (int c)
1279                 {
1280                         if (putback_char != -1){
1281                                 Console.WriteLine ("Col: " + col);
1282                                 Console.WriteLine ("Row: " + line);
1283                                 Console.WriteLine ("Name: " + ref_name.Name);
1284                                 Console.WriteLine ("Current [{0}] putting back [{1}]  ", putback_char, c);
1285                                 throw new Exception ("This should not happen putback on putback");
1286                         }
1287                         if (c == '\n' || col == 0) {
1288                                 // It won't happen though.
1289                                 line--;
1290                                 ref_line--;
1291                                 col = previous_col;
1292                         }
1293                         else
1294                                 col--;
1295                         putback_char = c;
1296                 }
1297
1298                 public bool advance ()
1299                 {
1300                         return peekChar () != -1;
1301                 }
1302
1303                 public Object Value {
1304                         get {
1305                                 return val;
1306                         }
1307                 }
1308
1309                 public Object value ()
1310                 {
1311                         return val;
1312                 }
1313
1314                 bool IsCastToken (int token)
1315                 {
1316                         switch (token) {
1317                         case Token.BANG:
1318                         case Token.TILDE:
1319                         case Token.IDENTIFIER:
1320                         case Token.LITERAL_INTEGER:
1321                         case Token.LITERAL_FLOAT:
1322                         case Token.LITERAL_DOUBLE:
1323                         case Token.LITERAL_DECIMAL:
1324                         case Token.LITERAL_CHARACTER:
1325                         case Token.LITERAL_STRING:
1326                         case Token.BASE:
1327                         case Token.CHECKED:
1328                         case Token.FALSE:
1329                         case Token.FIXED:
1330                         case Token.NEW:
1331                         case Token.NULL:
1332                         case Token.SIZEOF:
1333                         case Token.THIS:
1334                         case Token.THROW:
1335                         case Token.TRUE:
1336                         case Token.TYPEOF:
1337                         case Token.UNCHECKED:
1338                         case Token.UNSAFE:
1339
1340                                 //
1341                                 // These can be part of a member access
1342                                 //
1343                         case Token.INT:
1344                         case Token.UINT:
1345                         case Token.SHORT:
1346                         case Token.USHORT:
1347                         case Token.LONG:
1348                         case Token.ULONG:
1349                         case Token.DOUBLE:
1350                         case Token.FLOAT:
1351                         case Token.CHAR:
1352                                 return true;
1353
1354                         default:
1355                                 return false;
1356                         }
1357                 }
1358
1359                 public int token ()
1360                 {
1361                         current_token = xtoken ();
1362
1363                         if (current_token != Token.DEFAULT)
1364                                 return current_token;
1365
1366                         int c = consume_whitespace ();
1367                         if (c == -1)
1368                                 current_token = Token.ERROR;
1369                         else if (c == '(')
1370                                 current_token = Token.DEFAULT_OPEN_PARENS;
1371                         else
1372                                 putback (c);
1373
1374                         return current_token;
1375                 }
1376
1377                 static StringBuilder static_cmd_arg = new System.Text.StringBuilder ();
1378                 
1379                 void get_cmd_arg (out string cmd, out string arg)
1380                 {
1381                         int c;
1382                         
1383                         tokens_seen = false;
1384                         arg = "";
1385                         static_cmd_arg.Length = 0;
1386
1387                         // skip over white space
1388                         while ((c = getChar ()) != -1 && (c != '\n') && ((c == '\r') || (c == ' ') || (c == '\t')))
1389                                 ;
1390                                 
1391                         while ((c != -1) && (c != '\n') && (c != ' ') && (c != '\t') && (c != '\r')){
1392                                 if (is_identifier_part_character ((char) c)){
1393                                         static_cmd_arg.Append ((char) c);
1394                                         c = getChar ();
1395                                 } else {
1396                                         putback (c);
1397                                         break;
1398                                 }
1399                         }
1400
1401                         cmd = static_cmd_arg.ToString ();
1402
1403                         if (c == '\n'){
1404                                 return;
1405                         }
1406
1407                         // skip over white space
1408                         while ((c = getChar ()) != -1 && (c != '\n') && ((c == '\r') || (c == ' ') || (c == '\t')))
1409                                 ;
1410
1411                         if (c == '\n'){
1412                                 return;
1413                         } else if (c == '\r'){
1414                                 return;
1415                         } else if (c == -1){
1416                                 arg = "";
1417                                 return;
1418                         }
1419                         
1420                         static_cmd_arg.Length = 0;
1421                         static_cmd_arg.Append ((char) c);
1422                         
1423                         while ((c = getChar ()) != -1 && (c != '\n') && (c != '\r')){
1424                                 static_cmd_arg.Append ((char) c);
1425                         }
1426
1427                         arg = static_cmd_arg.ToString ().Trim ();
1428                 }
1429
1430                 //
1431                 // Handles the #line directive
1432                 //
1433                 bool PreProcessLine (string arg)
1434                 {
1435                         if (arg == "")
1436                                 return false;
1437
1438                         if (arg == "default"){
1439                                 ref_line = line;
1440                                 ref_name = file_name;
1441                                 Location.Push (ref_name, line);
1442                                 return true;
1443                         } else if (arg == "hidden"){
1444                                 //
1445                                 // We ignore #line hidden
1446                                 //
1447                                 return true;
1448                         }
1449                         
1450                         try {
1451                                 int pos;
1452
1453                                 if ((pos = arg.IndexOf (' ')) != -1 && pos != 0){
1454                                         ref_line = System.Int32.Parse (arg.Substring (0, pos));
1455                                         pos++;
1456                                         
1457                                         char [] quotes = { '\"' };
1458                                         
1459                                         string name = arg.Substring (pos). Trim (quotes);
1460                                         ref_name = Location.LookupFile (name);
1461                                         file_name.HasLineDirective = true;
1462                                         ref_name.HasLineDirective = true;
1463                                         Location.Push (ref_name, ref_line);
1464                                 } else {
1465                                         ref_line = System.Int32.Parse (arg);
1466                                 }
1467                         } catch {
1468                                 return false;
1469                         }
1470                         
1471                         return true;
1472                 }
1473
1474                 //
1475                 // Handles #define and #undef
1476                 //
1477                 void PreProcessDefinition (bool is_define, string arg)
1478                 {
1479                         if (arg == "" || arg == "true" || arg == "false"){
1480                                 Report.Error (1001, Location, "Missing identifer to pre-processor directive");
1481                                 return;
1482                         }
1483
1484                         char[] whitespace = { ' ', '\t' };
1485                         if (arg.IndexOfAny (whitespace) != -1){
1486                                 Report.Error (1025, Location, "Single-line comment or end-of-line expected");
1487                                 return;
1488                         }
1489
1490                         if (!is_identifier_start_character (arg [0]))
1491                                 Report.Error (1001, Location, "Identifier expected: " + arg);
1492                         
1493                         foreach (char c in arg.Substring (1)){
1494                                 if (!is_identifier_part_character (c)){
1495                                         Report.Error (1001, Location, "Identifier expected: " + arg);
1496                                         return;
1497                                 }
1498                         }
1499
1500                         if (is_define){
1501                                 if (defines == null)
1502                                         defines = new Hashtable ();
1503                                 define (arg);
1504                         } else {
1505                                 if (defines == null)
1506                                         return;
1507                                 if (defines.Contains (arg))
1508                                         defines.Remove (arg);
1509                         }
1510                 }
1511
1512                 /// <summary>
1513                 /// Handles #pragma directive
1514                 /// </summary>
1515                 void PreProcessPragma (string arg)
1516                 {
1517                         const string warning = "warning";
1518                         const string w_disable = "warning disable";
1519                         const string w_restore = "warning restore";
1520
1521                         if (arg == w_disable) {
1522                                 Report.RegisterWarningRegion (Location).WarningDisable (line);
1523                                 return;
1524                         }
1525
1526                         if (arg == w_restore) {
1527                                 Report.RegisterWarningRegion (Location).WarningEnable (line);
1528                                 return;
1529                         }
1530
1531                         if (arg.StartsWith (w_disable)) {
1532                                 int[] codes = ParseNumbers (arg.Substring (w_disable.Length));
1533                                 foreach (int code in codes) {
1534                                         if (code != 0)
1535                                                 Report.RegisterWarningRegion (Location).WarningDisable (Location, code);
1536                                 }
1537                                 return;
1538                         }
1539
1540                         if (arg.StartsWith (w_restore)) {
1541                                 int[] codes = ParseNumbers (arg.Substring (w_restore.Length));
1542                                 Hashtable w_table = Report.warning_ignore_table;
1543                                 foreach (int code in codes) {
1544                                         if (w_table != null && w_table.Contains (code))
1545                                                 Report.Warning (1635, 1, Location, String.Format ("Cannot restore warning `CS{0:0000}' because it was disabled globally", code));
1546                                         Report.RegisterWarningRegion (Location).WarningEnable (Location, code);
1547                                 }
1548                                 return;
1549                         }
1550
1551                         if (arg.StartsWith (warning)) {
1552                                 Report.Warning (1634, 1, Location, "Expected disable or restore");
1553                                 return;
1554                         }
1555
1556                         Report.Warning (1633, 1, Location, "Unrecognized #pragma directive");
1557                 }
1558
1559                 int[] ParseNumbers (string text)
1560                 {
1561                         string[] string_array = text.Split (',');
1562                         int[] values = new int [string_array.Length];
1563                         int index = 0;
1564                         foreach (string string_code in string_array) {
1565                                 try {
1566                                         values[index++] = int.Parse (string_code, System.Globalization.CultureInfo.InvariantCulture);
1567                                 }
1568                                 catch (FormatException) {
1569                                         Report.Warning (1692, 1, Location, "Invalid number");
1570                                 }
1571                         }
1572                         return values;
1573                 }
1574
1575                 bool eval_val (string s)
1576                 {
1577                         if (s == "true")
1578                                 return true;
1579                         if (s == "false")
1580                                 return false;
1581                         
1582                         if (defines == null)
1583                                 return false;
1584                         if (defines.Contains (s))
1585                                 return true;
1586
1587                         return false;
1588                 }
1589
1590                 bool pp_primary (ref string s)
1591                 {
1592                         s = s.Trim ();
1593                         int len = s.Length;
1594
1595                         if (len > 0){
1596                                 char c = s [0];
1597                                 
1598                                 if (c == '('){
1599                                         s = s.Substring (1);
1600                                         bool val = pp_expr (ref s);
1601                                         if (s.Length > 0 && s [0] == ')'){
1602                                                 s = s.Substring (1);
1603                                                 return val;
1604                                         }
1605                                         Error_InvalidDirective ();
1606                                         return false;
1607                                 }
1608                                 
1609                                 if (is_identifier_start_character (c)){
1610                                         int j = 1;
1611
1612                                         while (j < len){
1613                                                 c = s [j];
1614                                                 
1615                                                 if (is_identifier_part_character (c)){
1616                                                         j++;
1617                                                         continue;
1618                                                 }
1619                                                 bool v = eval_val (s.Substring (0, j));
1620                                                 s = s.Substring (j);
1621                                                 return v;
1622                                         }
1623                                         bool vv = eval_val (s);
1624                                         s = "";
1625                                         return vv;
1626                                 }
1627                         }
1628                         Error_InvalidDirective ();
1629                         return false;
1630                 }
1631                 
1632                 bool pp_unary (ref string s)
1633                 {
1634                         s = s.Trim ();
1635                         int len = s.Length;
1636
1637                         if (len > 0){
1638                                 if (s [0] == '!'){
1639                                         if (len > 1 && s [1] == '='){
1640                                                 Error_InvalidDirective ();
1641                                                 return false;
1642                                         }
1643                                         s = s.Substring (1);
1644                                         return ! pp_primary (ref s);
1645                                 } else
1646                                         return pp_primary (ref s);
1647                         } else {
1648                                 Error_InvalidDirective ();
1649                                 return false;
1650                         }
1651                 }
1652                 
1653                 bool pp_eq (ref string s)
1654                 {
1655                         bool va = pp_unary (ref s);
1656
1657                         s = s.Trim ();
1658                         int len = s.Length;
1659                         if (len > 0){
1660                                 if (s [0] == '='){
1661                                         if (len > 2 && s [1] == '='){
1662                                                 s = s.Substring (2);
1663                                                 return va == pp_unary (ref s);
1664                                         } else {
1665                                                 Error_InvalidDirective ();
1666                                                 return false;
1667                                         }
1668                                 } else if (s [0] == '!' && len > 1 && s [1] == '='){
1669                                         s = s.Substring (2);
1670
1671                                         return va != pp_unary (ref s);
1672
1673                                 } 
1674                         }
1675
1676                         return va;
1677                                 
1678                 }
1679                 
1680                 bool pp_and (ref string s)
1681                 {
1682                         bool va = pp_eq (ref s);
1683
1684                         s = s.Trim ();
1685                         int len = s.Length;
1686                         if (len > 0){
1687                                 if (s [0] == '&'){
1688                                         if (len > 2 && s [1] == '&'){
1689                                                 s = s.Substring (2);
1690                                                 return (va & pp_and (ref s));
1691                                         } else {
1692                                                 Error_InvalidDirective ();
1693                                                 return false;
1694                                         }
1695                                 } 
1696                         }
1697                         return va;
1698                 }
1699                 
1700                 //
1701                 // Evaluates an expression for `#if' or `#elif'
1702                 //
1703                 bool pp_expr (ref string s)
1704                 {
1705                         bool va = pp_and (ref s);
1706                         s = s.Trim ();
1707                         int len = s.Length;
1708                         if (len > 0){
1709                                 char c = s [0];
1710                                 
1711                                 if (c == '|'){
1712                                         if (len > 2 && s [1] == '|'){
1713                                                 s = s.Substring (2);
1714                                                 return va | pp_expr (ref s);
1715                                         } else {
1716                                                 Error_InvalidDirective ();
1717                                                 return false;
1718                                         }
1719                                 } 
1720                         }
1721                         
1722                         return va;
1723                 }
1724
1725                 bool eval (string s)
1726                 {
1727                         bool v = pp_expr (ref s);
1728                         s = s.Trim ();
1729                         if (s.Length != 0){
1730                                 Error_InvalidDirective ();
1731                                 return false;
1732                         }
1733
1734                         return v;
1735                 }
1736                 
1737                 void Error_InvalidDirective ()
1738                 {
1739                         Report.Error (1517, Location, "Invalid preprocessor directive");
1740                 }
1741
1742                 void Error_UnexpectedDirective (string extra)
1743                 {
1744                         Report.Error (
1745                                 1028, Location,
1746                                 "Unexpected processor directive (" + extra + ")");
1747                 }
1748
1749                 void Error_TokensSeen ()
1750                 {
1751                         Report.Error (1032, Location,
1752                                 "Cannot define or undefine preprocessor symbols after first token in file");
1753                 }
1754                 
1755                 //
1756                 // if true, then the code continues processing the code
1757                 // if false, the code stays in a loop until another directive is
1758                 // reached.
1759                 //
1760                 bool handle_preprocessing_directive (bool caller_is_taking)
1761                 {
1762                         string cmd, arg;
1763                         bool region_directive = false;
1764
1765                         current_location = new Location (ref_line, Col);
1766
1767                         get_cmd_arg (out cmd, out arg);
1768
1769                         // Eat any trailing whitespaces and single-line comments
1770                         if (arg.IndexOf ("//") != -1)
1771                                 arg = arg.Substring (0, arg.IndexOf ("//"));
1772                         arg = arg.TrimEnd (' ', '\t');
1773
1774                         //
1775                         // The first group of pre-processing instructions is always processed
1776                         //
1777                         switch (cmd){
1778                         case "pragma":
1779                                 if (RootContext.Version == LanguageVersion.ISO_1) {
1780                                         Report.FeatureIsNotStandardized (Location, "#pragma");
1781                                         return caller_is_taking;
1782                                 }
1783
1784                                 PreProcessPragma (arg);
1785                                 return caller_is_taking;
1786                                 
1787                         case "line":
1788                                 if (!PreProcessLine (arg))
1789                                         Report.Error (
1790                                                 1576, Location,
1791                                                 "The line number specified for #line directive is missing or invalid");
1792                                 return caller_is_taking;
1793
1794                         case "region":
1795                                 region_directive = true;
1796                                 arg = "true";
1797                                 goto case "if";
1798
1799                         case "endregion":
1800                                 region_directive = true;
1801                                 goto case "endif";
1802                                 
1803                         case "if":
1804                                 if (arg == ""){
1805                                         Error_InvalidDirective ();
1806                                         return true;
1807                                 }
1808                                 bool taking = false;
1809                                 if (ifstack == null)
1810                                         ifstack = new Stack ();
1811
1812                                 if (ifstack.Count == 0){
1813                                         taking = true;
1814                                 } else {
1815                                         int state = (int) ifstack.Peek ();
1816                                         if ((state & TAKING) != 0)
1817                                                 taking = true;
1818                                 }
1819
1820                                 if (eval (arg) && taking){
1821                                         int push = TAKING | TAKEN_BEFORE | PARENT_TAKING;
1822                                         if (region_directive)
1823                                                 push |= REGION;
1824                                         ifstack.Push (push);
1825                                         return true;
1826                                 } else {
1827                                         int push = (taking ? PARENT_TAKING : 0);
1828                                         if (region_directive)
1829                                                 push |= REGION;
1830                                         ifstack.Push (push);
1831                                         return false;
1832                                 }
1833                                 
1834                         case "endif":
1835                                 if (ifstack == null || ifstack.Count == 0){
1836                                         Error_UnexpectedDirective ("no #if for this #endif");
1837                                         return true;
1838                                 } else {
1839                                         int pop = (int) ifstack.Pop ();
1840                                         
1841                                         if (region_directive && ((pop & REGION) == 0))
1842                                                 Report.Error (1027, Location, "Expected `#endif' directive");
1843                                         else if (!region_directive && ((pop & REGION) != 0))
1844                                                 Report.Error (1038, Location, "#endregion directive expected");
1845                                         
1846                                         if (!region_directive && arg.Length != 0) {
1847                                                 Report.Error (1025, Location, "Single-line comment or end-of-line expected");
1848                                         }
1849                                         
1850                                         if (ifstack.Count == 0)
1851                                                 return true;
1852                                         else {
1853                                                 int state = (int) ifstack.Peek ();
1854
1855                                                 if ((state & TAKING) != 0)
1856                                                         return true;
1857                                                 else
1858                                                         return false;
1859                                         }
1860                                 }
1861
1862                         case "elif":
1863                                 if (ifstack == null || ifstack.Count == 0){
1864                                         Error_UnexpectedDirective ("no #if for this #elif");
1865                                         return true;
1866                                 } else {
1867                                         int state = (int) ifstack.Peek ();
1868
1869                                         if ((state & REGION) != 0) {
1870                                                 Report.Error (1038, Location, "#endregion directive expected");
1871                                                 return true;
1872                                         }
1873
1874                                         if ((state & ELSE_SEEN) != 0){
1875                                                 Error_UnexpectedDirective ("#elif not valid after #else");
1876                                                 return true;
1877                                         }
1878
1879                                         if ((state & (TAKEN_BEFORE | TAKING)) != 0)
1880                                                 return false;
1881
1882                                         if (eval (arg) && ((state & PARENT_TAKING) != 0)){
1883                                                 state = (int) ifstack.Pop ();
1884                                                 ifstack.Push (state | TAKING | TAKEN_BEFORE);
1885                                                 return true;
1886                                         } else 
1887                                                 return false;
1888                                 }
1889
1890                         case "else":
1891                                 if (ifstack == null || ifstack.Count == 0){
1892                                         Error_UnexpectedDirective ("no #if for this #else");
1893                                         return true;
1894                                 } else {
1895                                         int state = (int) ifstack.Peek ();
1896
1897                                         if ((state & REGION) != 0) {
1898                                                 Report.Error (1038, Location, "#endregion directive expected");
1899                                                 return true;
1900                                         }
1901
1902                                         if ((state & ELSE_SEEN) != 0){
1903                                                 Error_UnexpectedDirective ("#else within #else");
1904                                                 return true;
1905                                         }
1906
1907                                         ifstack.Pop ();
1908
1909                                         bool ret;
1910                                         if ((state & TAKEN_BEFORE) == 0){
1911                                                 ret = ((state & PARENT_TAKING) != 0);
1912                                         } else
1913                                                 ret = false;
1914                                         
1915                                         if (ret)
1916                                                 state |= TAKING;
1917                                         else
1918                                                 state &= ~TAKING;
1919                                         
1920                                         ifstack.Push (state | ELSE_SEEN);
1921                                         
1922                                         return ret;
1923                                 }
1924                         }
1925
1926                         //
1927                         // These are only processed if we are in a `taking' block
1928                         //
1929                         if (!caller_is_taking)
1930                                 return false;
1931                                         
1932                         switch (cmd){
1933                         case "define":
1934                                 if (any_token_seen){
1935                                         Error_TokensSeen ();
1936                                         return true;
1937                                 }
1938                                 PreProcessDefinition (true, arg);
1939                                 return true;
1940
1941                         case "undef":
1942                                 if (any_token_seen){
1943                                         Error_TokensSeen ();
1944                                         return true;
1945                                 }
1946                                 PreProcessDefinition (false, arg);
1947                                 return true;
1948
1949                         case "error":
1950                                 Report.Error (1029, Location, "#error: '" + arg + "'");
1951                                 return true;
1952
1953                         case "warning":
1954                                 Report.Warning (1030, 1, Location, "#warning: `{0}'", arg);
1955                                 return true;
1956                         }
1957
1958                         Report.Error (1024, Location, "Wrong preprocessor directive");
1959                         return true;
1960
1961                 }
1962
1963                 private int consume_string (bool quoted) 
1964                 {
1965                         int c;
1966                         string_builder.Length = 0;
1967                                                                 
1968                         while ((c = getChar ()) != -1){
1969                                 if (c == '"'){
1970                                         if (quoted && peekChar () == '"'){
1971                                                 string_builder.Append ((char) c);
1972                                                 getChar ();
1973                                                 continue;
1974                                         } else {
1975                                                 val = string_builder.ToString ();
1976                                                 return Token.LITERAL_STRING;
1977                                         }
1978                                 }
1979
1980                                 if (c == '\n'){
1981                                         if (!quoted)
1982                                                 Report.Error (1010, Location, "Newline in constant");
1983                                 }
1984
1985                                 if (!quoted){
1986                                         c = escape (c);
1987                                         if (c == -1)
1988                                                 return Token.ERROR;
1989                                 }
1990                                 string_builder.Append ((char) c);
1991                         }
1992
1993                         Report.Error (1039, Location, "Unterminated string literal");
1994                         return Token.EOF;
1995                 }
1996
1997                 private int consume_identifier (int s)
1998                 {
1999                         int res = consume_identifier (s, false);
2000
2001                         if (doc_state == XmlCommentState.Allowed)
2002                                 doc_state = XmlCommentState.NotAllowed;
2003                         switch (res) {
2004                         case Token.USING:
2005                         case Token.NAMESPACE:
2006                                 check_incorrect_doc_comment ();
2007                                 break;
2008                         }
2009
2010                         if (res == Token.PARTIAL) {
2011                                 // Save current position and parse next token.
2012                                 int old = reader.Position;
2013                                 int old_putback = putback_char;
2014                                 int old_ref_line = ref_line;
2015                                 int old_col = col;
2016
2017                                 putback_char = -1;
2018
2019                                 int next_token = token ();
2020                                 bool ok = (next_token == Token.CLASS) ||
2021                                         (next_token == Token.STRUCT) ||
2022                                         (next_token == Token.INTERFACE) ||
2023                                         (next_token == Token.ENUM); // "partial" is a keyword in 'partial enum', even though it's not valid
2024
2025                                 reader.Position = old;
2026                                 ref_line = old_ref_line;
2027                                 col = old_col;
2028                                 putback_char = old_putback;
2029
2030                                 if (ok)
2031                                         return res;
2032                                 else {
2033                                         val = new LocatedToken (Location, "partial");
2034                                         return Token.IDENTIFIER;
2035                                 }
2036                         }
2037
2038                         return res;
2039                 }
2040
2041                 private int consume_identifier (int s, bool quoted) 
2042                 {
2043                         int pos = 1;
2044                         int c = -1;
2045                         
2046                         id_builder [0] = (char) s;
2047                                         
2048                         current_location = new Location (ref_line, Col);
2049
2050                         while ((c = getChar ()) != -1) {
2051                                 if (is_identifier_part_character ((char) c)){
2052                                         if (pos == max_id_size){
2053                                                 Report.Error (645, Location, "Identifier too long (limit is 512 chars)");
2054                                                 return Token.ERROR;
2055                                         }
2056                                         
2057                                         id_builder [pos++] = (char) c;
2058 //                                      putback_char = -1;
2059                                 } else {
2060 //                                      putback_char = c;
2061                                         putback (c);
2062                                         break;
2063                                 }
2064                         }
2065
2066                         //
2067                         // Optimization: avoids doing the keyword lookup
2068                         // on uppercase letters and _
2069                         //
2070                         if (!quoted && (s >= 'a' || s == '_')){
2071                                 int keyword = GetKeyword (id_builder, pos);
2072                                 if (keyword != -1) {
2073                                         val = Location;
2074                                 return keyword;
2075                                 }
2076                         }
2077
2078                         //
2079                         // Keep identifiers in an array of hashtables to avoid needless
2080                         // allocations
2081                         //
2082
2083                         if (identifiers [pos] != null) {
2084                                 val = identifiers [pos][id_builder];
2085                                 if (val != null) {
2086                                         val = new LocatedToken (Location, (string) val);
2087                                         if (quoted)
2088                                                 escapedIdentifiers.Add (val);
2089                                         return Token.IDENTIFIER;
2090                                 }
2091                         }
2092                         else
2093                                 identifiers [pos] = new CharArrayHashtable (pos);
2094
2095                         val = new String (id_builder, 0, pos);
2096                         if (RootContext.Version == LanguageVersion.ISO_1) {
2097                                 for (int i = 1; i < id_builder.Length; i += 3) {
2098                                         if (id_builder [i] == '_' && (id_builder [i - 1] == '_' || id_builder [i + 1] == '_')) {
2099                                                 Report.Error (1638, Location, 
2100                                                         "`{0}': Any identifier with double underscores cannot be used when ISO language version mode is specified", val.ToString ());
2101                                                 break;
2102                                         }
2103                                 }
2104                         }
2105
2106                         char [] chars = new char [pos];
2107                         Array.Copy (id_builder, chars, pos);
2108
2109                         identifiers [pos] [chars] = val;
2110
2111                         val = new LocatedToken (Location, (string) val);
2112                         if (quoted)
2113                                 escapedIdentifiers.Add (val);
2114                         return Token.IDENTIFIER;
2115                 }
2116
2117                 int consume_whitespace ()
2118                 {
2119                         int c;
2120
2121                         // Whether we have seen comments on the current line
2122                         bool comments_seen = false;
2123                         
2124                         val = null;
2125                         // optimization: eliminate col and implement #directive semantic correctly.
2126                         for (;(c = getChar ()) != -1;) {
2127                                 if (c == ' ')
2128                                         continue;
2129                                 
2130                                 if (c == '\t') {
2131                                         continue;
2132                                 }
2133                                 
2134                                 if (c == ' ' || c == '\f' || c == '\v' || c == 0xa0)
2135                                         continue;
2136
2137                                 if (c == '\r') {
2138                                         if (peekChar () == '\n')
2139                                                 getChar ();
2140
2141                                         any_token_seen |= tokens_seen;
2142                                         tokens_seen = false;
2143                                         comments_seen = false;
2144                                         continue;
2145                                 }
2146
2147                                 // Handle double-slash comments.
2148                                 if (c == '/'){
2149                                         int d = peekChar ();
2150                                 
2151                                         if (d == '/'){
2152                                                 getChar ();
2153                                                 if (RootContext.Documentation != null && peekChar () == '/') {
2154                                                         getChar ();
2155                                                         // Don't allow ////.
2156                                                         if ((d = peekChar ()) != '/') {
2157                                                                 update_comment_location ();
2158                                                                 if (doc_state == XmlCommentState.Allowed)
2159                                                                         handle_one_line_xml_comment ();
2160                                                                 else if (doc_state == XmlCommentState.NotAllowed)
2161                                                                         warn_incorrect_doc_comment ();
2162                                                         }
2163                                                 }
2164                                                 while ((d = getChar ()) != -1 && (d != '\n') && d != '\r')
2165                                                 if (d == '\n'){
2166                                                 }
2167                                                 any_token_seen |= tokens_seen;
2168                                                 tokens_seen = false;
2169                                                 comments_seen = false;
2170                                                 continue;
2171                                         } else if (d == '*'){
2172                                                 getChar ();
2173                                                 bool docAppend = false;
2174                                                 if (RootContext.Documentation != null && peekChar () == '*') {
2175                                                         getChar ();
2176                                                         update_comment_location ();
2177                                                         // But when it is /**/, just do nothing.
2178                                                         if (peekChar () == '/') {
2179                                                                 getChar ();
2180                                                                 continue;
2181                                                         }
2182                                                         if (doc_state == XmlCommentState.Allowed)
2183                                                                 docAppend = true;
2184                                                         else if (doc_state == XmlCommentState.NotAllowed)
2185                                                                 warn_incorrect_doc_comment ();
2186                                                 }
2187
2188                                                 int current_comment_start = 0;
2189                                                 if (docAppend) {
2190                                                         current_comment_start = xml_comment_buffer.Length;
2191                                                         xml_comment_buffer.Append (Environment.NewLine);
2192                                                 }
2193
2194                                                 Location start_location = Location;
2195
2196                                                 while ((d = getChar ()) != -1){
2197                                                         if (d == '*' && peekChar () == '/'){
2198                                                                 getChar ();
2199                                                                 comments_seen = true;
2200                                                                 break;
2201                                                         }
2202                                                         if (docAppend)
2203                                                                 xml_comment_buffer.Append ((char) d);
2204                                                         
2205                                                         if (d == '\n'){
2206                                                                 any_token_seen |= tokens_seen;
2207                                                                 tokens_seen = false;
2208                                                                 // 
2209                                                                 // Reset 'comments_seen' just to be consistent.
2210                                                                 // It doesn't matter either way, here.
2211                                                                 //
2212                                                                 comments_seen = false;
2213                                                         }
2214                                                 }
2215                                                 if (!comments_seen)
2216                                                         Report.Error (1035, start_location, "End-of-file found, '*/' expected");
2217
2218                                                 if (docAppend)
2219                                                         update_formatted_doc_comment (current_comment_start);
2220                                                 continue;
2221                                         }
2222                                         goto is_punct_label;
2223                                 }
2224
2225                         is_punct_label:
2226                                 // white space
2227                                 if (c == '\n'){
2228                                         any_token_seen |= tokens_seen;
2229                                         tokens_seen = false;
2230                                         comments_seen = false;
2231                                         continue;
2232                                 }
2233
2234                                 /* For now, ignore pre-processor commands */
2235                                 // FIXME: In C# the '#' is not limited to appear
2236                                 // on the first column.
2237                                 if (c == '#') {
2238                                         bool cont = true;
2239                                         
2240                                         if (tokens_seen || comments_seen) {
2241                                                error_details = "Preprocessor directives must appear as the first non-whitespace " +
2242                                                        "character on a line.";
2243
2244                                                Report.Error (1040, Location, error_details);
2245
2246                                                return Token.ERROR;
2247                                        }
2248                                         
2249                                 start_again:
2250                                         
2251                                         cont = handle_preprocessing_directive (cont);
2252
2253                                         if (cont){
2254                                                 continue;
2255                                         }
2256
2257                                         bool skipping = false;
2258                                         for (;(c = getChar ()) != -1;){
2259                                                 if (c == '\n'){
2260                                                         skipping = false;
2261                                                 } else if (c == ' ' || c == '\t' || c == '\v' || c == '\r' || c == 0xa0)
2262                                                         continue;
2263                                                 else if (c != '#')
2264                                                         skipping = true;
2265                                                 if (c == '#' && !skipping)
2266                                                         goto start_again;
2267                                         }
2268                                         any_token_seen |= tokens_seen;
2269                                         tokens_seen = false;
2270                                         if (c == -1)
2271                                                 Report.Error (1027, Location, "Expected `#endif' directive");
2272                                         continue;
2273                                 }
2274
2275                                 return c;
2276                         }
2277
2278                         return -1;
2279                 }
2280                 
2281                 public int xtoken ()
2282                 {
2283                         int t;
2284                         bool doread = false;
2285                         int c;
2286
2287                         val = null;
2288                         // optimization: eliminate col and implement #directive semantic correctly.
2289
2290                         c = consume_whitespace ();
2291                         if (c == -1)
2292                                 return Token.EOF;
2293
2294                         if (is_identifier_start_character ((char)c)){
2295                                 tokens_seen = true;
2296                                         return consume_identifier (c);
2297                         }
2298
2299                         current_location = new Location (ref_line, Col);
2300                         if ((t = is_punct ((char)c, ref doread)) != Token.ERROR){
2301                                 tokens_seen = true;
2302                                 if (doread){
2303                                         getChar ();
2304                                         col++;
2305                                 }
2306                                 return t;
2307                         }
2308
2309                         if (c >= '0' && c <= '9'){
2310                                 tokens_seen = true;
2311                                 return is_number (c);
2312                         }
2313
2314                         if (c == '.'){
2315                                 tokens_seen = true;
2316                                 int peek = peekChar ();
2317                                 if (peek >= '0' && peek <= '9')
2318                                         return is_number (c);
2319                                 return Token.DOT;
2320                         }
2321
2322                         if (c == '"') 
2323                                 return consume_string (false);
2324
2325                         if (c == '\''){
2326                                 c = getChar ();
2327                                 tokens_seen = true;
2328                                 if (c == '\''){
2329                                         error_details = "Empty character literal";
2330                                         Report.Error (1011, Location, error_details);
2331                                         return Token.ERROR;
2332                                 }
2333                                 if (c == '\r' || c == '\n') {
2334                                         Report.Error (1010, Location, "Newline in constant");
2335                                         return Token.ERROR;
2336                                 }
2337                                 c = escape (c);
2338                                 if (c == -1)
2339                                         return Token.ERROR;
2340                                 val = new System.Char ();
2341                                 val = (char) c;
2342                                 c = getChar ();
2343
2344                                 if (c != '\''){
2345                                         error_details = "Too many characters in character literal";
2346                                         Report.Error (1012, Location, error_details);
2347
2348                                         // Try to recover, read until newline or next "'"
2349                                         while ((c = getChar ()) != -1){
2350                                                 if (c == '\n'){
2351                                                         break;
2352                                                 }
2353                                                 else if (c == '\'')
2354                                                         break;
2355                                         }
2356                                         return Token.ERROR;
2357                                 }
2358                                 return Token.LITERAL_CHARACTER;
2359                         }
2360                                 
2361                         if (c == '@') {
2362                                 c = getChar ();
2363                                 if (c == '"') {
2364                                         tokens_seen = true;
2365                                         return consume_string (true);
2366                                 } else if (is_identifier_start_character ((char) c)){
2367                                         return consume_identifier (c, true);
2368                                 } else {
2369                                         Report.Error (1646, Location, "Keyword, identifier, or string expected after verbatim specifier: @");
2370                                 }
2371                         }
2372
2373                         if (c == '#') {
2374                                 error_details = "Preprocessor directives must appear as the first non-whitespace " +
2375                                         "character on a line.";
2376
2377                                 Report.Error (1040, Location, error_details);
2378
2379                                 return Token.ERROR;
2380                         }
2381
2382                         error_details = ((char)c).ToString ();
2383
2384                         return Token.ERROR;
2385                 }
2386
2387                 //
2388                 // Handles one line xml comment
2389                 //
2390                 private void handle_one_line_xml_comment ()
2391                 {
2392                         int c;
2393                         while ((c = peekChar ()) == ' ')
2394                                 getChar (); // skip heading whitespaces.
2395                         while ((c = peekChar ()) != -1 && c != '\n' && c != '\r') {
2396                                 xml_comment_buffer.Append ((char) getChar ());
2397                         }
2398                         if (c == '\r' || c == '\n')
2399                                 xml_comment_buffer.Append (Environment.NewLine);
2400                 }
2401
2402                 //
2403                 // Remove heading "*" in Javadoc-like xml documentation.
2404                 //
2405                 private void update_formatted_doc_comment (int current_comment_start)
2406                 {
2407                         int length = xml_comment_buffer.Length - current_comment_start;
2408                         string [] lines = xml_comment_buffer.ToString (
2409                                 current_comment_start,
2410                                 length).Replace ("\r", "").Split ('\n');
2411                         
2412                         // The first line starts with /**, thus it is not target
2413                         // for the format check.
2414                         for (int i = 1; i < lines.Length; i++) {
2415                                 string s = lines [i];
2416                                 int idx = s.IndexOf ('*');
2417                                 string head = null;
2418                                 if (idx < 0) {
2419                                         if (i < lines.Length - 1)
2420                                                 return;
2421                                         head = s;
2422                                 } else
2423                                         head = s.Substring (0, idx);
2424                                 foreach (char c in head)
2425                                         if (c != ' ')
2426                                                 return;
2427                                 lines [i] = s.Substring (idx + 1);
2428                         }
2429                         xml_comment_buffer.Remove (current_comment_start, length);
2430                         xml_comment_buffer.Insert (current_comment_start, String.Join (Environment.NewLine, lines));
2431                 }
2432
2433                 //
2434                 // Updates current comment location.
2435                 //
2436                 private void update_comment_location ()
2437                 {
2438                         if (current_comment_location.IsNull) {
2439                                 // "-2" is for heading "//" or "/*"
2440                                 current_comment_location =
2441                                         new Location (ref_line, col - 2);
2442                         }
2443                 }
2444
2445                 //
2446                 // Checks if there was incorrect doc comments and raise
2447                 // warnings.
2448                 //
2449                 public void check_incorrect_doc_comment ()
2450                 {
2451                         if (xml_comment_buffer.Length > 0)
2452                                 warn_incorrect_doc_comment ();
2453                 }
2454
2455                 //
2456                 // Raises a warning when tokenizer found incorrect doccomment
2457                 // markup.
2458                 //
2459                 private void warn_incorrect_doc_comment ()
2460                 {
2461                         if (doc_state != XmlCommentState.Error) {
2462                                 doc_state = XmlCommentState.Error;
2463                                 // in csc, it is 'XML comment is not placed on 
2464                                 // a valid language element'. But that does not
2465                                 // make sense.
2466                                 Report.Warning (1587, 2, Location, "XML comment is not placed on a valid language element");
2467                         }
2468                 }
2469
2470                 //
2471                 // Consumes the saved xml comment lines (if any)
2472                 // as for current target member or type.
2473                 //
2474                 public string consume_doc_comment ()
2475                 {
2476                         if (xml_comment_buffer.Length > 0) {
2477                                 string ret = xml_comment_buffer.ToString ();
2478                                 reset_doc_comment ();
2479                                 return ret;
2480                         }
2481                         return null;
2482                 }
2483
2484                 void reset_doc_comment ()
2485                 {
2486                         xml_comment_buffer.Length = 0;
2487                         current_comment_location = Location.Null;
2488                 }
2489
2490                 public void cleanup ()
2491                 {
2492                         if (ifstack != null && ifstack.Count >= 1) {
2493                                 int state = (int) ifstack.Pop ();
2494                                 if ((state & REGION) != 0)
2495                                         Report.Error (1038, Location, "#endregion directive expected");
2496                                 else 
2497                                         Report.Error (1027, "Expected `#endif' directive");
2498                         }
2499                                 
2500                 }
2501         }
2502
2503         //
2504         // Indicates whether it accepts XML documentation or not.
2505         //
2506         public enum XmlCommentState {
2507                 // comment is allowed in this state.
2508                 Allowed,
2509                 // comment is not allowed in this state.
2510                 NotAllowed,
2511                 // once comments appeared when it is NotAllowed, then the
2512                 // state is changed to it, until the state is changed to
2513                 // .Allowed.
2514                 Error
2515         }
2516 }
2517