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