importing messaging-2008 branch to trunk.
[mono.git] / mcs / mcs / cs-tokenizer.cs
1 //
2 // cs-tokenizer.cs: The Tokenizer for the C# compiler
3 //                  This also implements the preprocessor
4 //
5 // Author: Miguel de Icaza (miguel@gnu.org)
6 //         Marek Safar (marek.safar@seznam.cz)
7 //
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 //
10 // Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com)
11 // Copyright 2004-2008 Novell, Inc
12 //
13 //
14
15 using System;
16 using System.Text;
17 using System.Collections;
18 using System.IO;
19 using System.Globalization;
20 using System.Reflection;
21
22 namespace Mono.CSharp
23 {
24         /// <summary>
25         ///    Tokenizer for C# source code. 
26         /// </summary>
27
28         public class Tokenizer : yyParser.yyInput
29         {
30                 SeekableStreamReader reader;
31                 SourceFile ref_name;
32                 CompilationUnit file_name;
33                 bool hidden = false;
34                 int ref_line = 1;
35                 int line = 1;
36                 int col = 0;
37                 int previous_col;
38                 int current_token;
39                 bool handle_get_set = false;
40                 bool handle_remove_add = false;
41                 bool handle_where = false;
42                 bool handle_typeof = false;
43                 bool lambda_arguments_parsing;
44                 Location current_comment_location = Location.Null;
45                 ArrayList escaped_identifiers;
46                 int parsing_generic_less_than;
47                 
48                 //
49                 // Used mainly for parser optimizations. Some expressions for instance
50                 // can appear only in block (including initializer, base initializer)
51                 // scope only
52                 //
53                 public int parsing_block;
54                 internal int query_parsing;
55                 
56                 //
57                 // Set when parsing generic declaration (type or method header)
58                 //
59                 public bool parsing_generic_declaration;
60                 
61                 //
62                 // The value indicates that we have not reach any declaration or
63                 // namespace yet
64                 //
65                 public int parsing_declaration;
66
67                 //
68                 // The special character to inject on streams to trigger the EXPRESSION_PARSE
69                 // token to be returned.   It just happens to be a Unicode character that
70                 // would never be part of a program (can not be an identifier).
71                 //
72                 // This character is only tested just before the tokenizer is about to report
73                 // an error;   So on the regular operation mode, this addition will have no
74                 // impact on the tokenizer's performance.
75                 //
76                 
77                 public const int EvalStatementParserCharacter = 0x2190;   // Unicode Left Arrow
78                 public const int EvalCompilationUnitParserCharacter = 0x2191;  // Unicode Arrow
79                 public const int EvalUsingDeclarationsParserCharacter = 0x2192;  // Unicode Arrow
80                 
81                 //
82                 // XML documentation buffer. The save point is used to divide
83                 // comments on types and comments on members.
84                 //
85                 StringBuilder xml_comment_buffer;
86
87                 //
88                 // See comment on XmlCommentState enumeration.
89                 //
90                 XmlCommentState xml_doc_state = XmlCommentState.Allowed;
91
92                 //
93                 // Whether tokens have been seen on this line
94                 //
95                 bool tokens_seen = false;
96
97                 //
98                 // Whether a token has been seen on the file
99                 // This is needed because `define' is not allowed to be used
100                 // after a token has been seen.
101                 //
102                 bool any_token_seen = false;
103
104                 static readonly char[] simple_whitespaces = new char[] { ' ', '\t' };
105
106                 public bool PropertyParsing {
107                         get { return handle_get_set; }
108                         set { handle_get_set = value; }
109                 }
110
111                 public bool EventParsing {
112                         get { return handle_remove_add; }
113                         set { handle_remove_add = value; }
114                 }
115
116                 public bool ConstraintsParsing {
117                         get { return handle_where; }
118                         set { handle_where = value; }
119                 }
120
121                 public bool TypeOfParsing {
122                         get { return handle_typeof; }
123                         set { handle_typeof = value; }
124                 }
125                 
126                 public XmlCommentState doc_state {
127                         get { return xml_doc_state; }
128                         set {
129                                 if (value == XmlCommentState.Allowed) {
130                                         check_incorrect_doc_comment ();
131                                         reset_doc_comment ();
132                                 }
133                                 xml_doc_state = value;
134                         }
135                 }
136
137                 void AddEscapedIdentifier (LocatedToken lt)
138                 {
139                         if (escaped_identifiers == null)
140                                 escaped_identifiers = new ArrayList ();
141
142                         escaped_identifiers.Add (lt);
143                 }
144
145                 public bool IsEscapedIdentifier (Location loc)
146                 {
147                         if (escaped_identifiers != null) {
148                                 foreach (LocatedToken lt in escaped_identifiers)
149                                         if (lt.Location.Equals (loc))
150                                                 return true;
151                         }
152
153                         return false;
154                 }
155
156                 //
157                 // Class variables
158                 // 
159                 static CharArrayHashtable[] keywords;
160                 static Hashtable keyword_strings;
161                 static NumberStyles styles;
162                 static NumberFormatInfo csharp_format_info;
163                 
164                 //
165                 // Values for the associated token returned
166                 //
167                 internal int putback_char;      // Used by repl only
168                 Object val;
169
170                 //
171                 // Pre-processor
172                 //
173                 const int TAKING        = 1;
174                 const int ELSE_SEEN     = 4;
175                 const int PARENT_TAKING = 8;
176                 const int REGION        = 16;           
177
178                 //
179                 // pre-processor if stack state:
180                 //
181                 Stack ifstack;
182
183                 static System.Text.StringBuilder string_builder;
184
185                 const int max_id_size = 512;
186                 static char [] id_builder = new char [max_id_size];
187
188                 static CharArrayHashtable [] identifiers = new CharArrayHashtable [max_id_size + 1];
189
190                 const int max_number_size = 512;
191                 static char [] number_builder = new char [max_number_size];
192                 static int number_pos;
193                 
194                 //
195                 // Details about the error encoutered by the tokenizer
196                 //
197                 string error_details;
198                 
199                 public string error {
200                         get {
201                                 return error_details;
202                         }
203                 }
204                 
205                 public int Line {
206                         get {
207                                 return ref_line;
208                         }
209                 }
210
211                 //
212                 // This is used when the tokenizer needs to save
213                 // the current position as it needs to do some parsing
214                 // on its own to deamiguate a token in behalf of the
215                 // parser.
216                 //
217                 Stack position_stack = new Stack (2);
218                 class Position {
219                         public int position;
220                         public int line;
221                         public int ref_line;
222                         public int col;
223                         public bool hidden;
224                         public int putback_char;
225                         public int previous_col;
226                         public Stack ifstack;
227                         public int parsing_generic_less_than;
228                         public int current_token;
229
230                         public Position (Tokenizer t)
231                         {
232                                 position = t.reader.Position;
233                                 line = t.line;
234                                 ref_line = t.ref_line;
235                                 col = t.col;
236                                 hidden = t.hidden;
237                                 putback_char = t.putback_char;
238                                 previous_col = t.previous_col;
239                                 if (t.ifstack != null && t.ifstack.Count != 0)
240                                         ifstack = (Stack)t.ifstack.Clone ();
241                                 parsing_generic_less_than = t.parsing_generic_less_than;
242                                 current_token = t.current_token;
243                         }
244                 }
245                 
246                 public void PushPosition ()
247                 {
248                         position_stack.Push (new Position (this));
249                 }
250
251                 public void PopPosition ()
252                 {
253                         Position p = (Position) position_stack.Pop ();
254
255                         reader.Position = p.position;
256                         ref_line = p.ref_line;
257                         line = p.line;
258                         col = p.col;
259                         hidden = p.hidden;
260                         putback_char = p.putback_char;
261                         previous_col = p.previous_col;
262                         ifstack = p.ifstack;
263                         parsing_generic_less_than = p.parsing_generic_less_than;
264                         current_token = p.current_token;
265                 }
266
267                 // Do not reset the position, ignore it.
268                 public void DiscardPosition ()
269                 {
270                         position_stack.Pop ();
271                 }
272                 
273                 static void AddKeyword (string kw, int token)
274                 {
275                         keyword_strings.Add (kw, kw);
276                         if (keywords [kw.Length] == null) {
277                                 keywords [kw.Length] = new CharArrayHashtable (kw.Length);
278                         }
279                         keywords [kw.Length] [kw.ToCharArray ()] = token;
280                 }
281
282                 static void InitTokens ()
283                 {
284                         keyword_strings = new Hashtable ();
285                         keywords = new CharArrayHashtable [64];
286
287                         AddKeyword ("__arglist", Token.ARGLIST);
288                         AddKeyword ("abstract", Token.ABSTRACT);
289                         AddKeyword ("as", Token.AS);
290                         AddKeyword ("add", Token.ADD);
291                         AddKeyword ("base", Token.BASE);
292                         AddKeyword ("bool", Token.BOOL);
293                         AddKeyword ("break", Token.BREAK);
294                         AddKeyword ("byte", Token.BYTE);
295                         AddKeyword ("case", Token.CASE);
296                         AddKeyword ("catch", Token.CATCH);
297                         AddKeyword ("char", Token.CHAR);
298                         AddKeyword ("checked", Token.CHECKED);
299                         AddKeyword ("class", Token.CLASS);
300                         AddKeyword ("const", Token.CONST);
301                         AddKeyword ("continue", Token.CONTINUE);
302                         AddKeyword ("decimal", Token.DECIMAL);
303                         AddKeyword ("default", Token.DEFAULT);
304                         AddKeyword ("delegate", Token.DELEGATE);
305                         AddKeyword ("do", Token.DO);
306                         AddKeyword ("double", Token.DOUBLE);
307                         AddKeyword ("else", Token.ELSE);
308                         AddKeyword ("enum", Token.ENUM);
309                         AddKeyword ("event", Token.EVENT);
310                         AddKeyword ("explicit", Token.EXPLICIT);
311                         AddKeyword ("extern", Token.EXTERN);
312                         AddKeyword ("false", Token.FALSE);
313                         AddKeyword ("finally", Token.FINALLY);
314                         AddKeyword ("fixed", Token.FIXED);
315                         AddKeyword ("float", Token.FLOAT);
316                         AddKeyword ("for", Token.FOR);
317                         AddKeyword ("foreach", Token.FOREACH);
318                         AddKeyword ("goto", Token.GOTO);
319                         AddKeyword ("get", Token.GET);
320                         AddKeyword ("if", Token.IF);
321                         AddKeyword ("implicit", Token.IMPLICIT);
322                         AddKeyword ("in", Token.IN);
323                         AddKeyword ("int", Token.INT);
324                         AddKeyword ("interface", Token.INTERFACE);
325                         AddKeyword ("internal", Token.INTERNAL);
326                         AddKeyword ("is", Token.IS);
327                         AddKeyword ("lock", Token.LOCK);
328                         AddKeyword ("long", Token.LONG);
329                         AddKeyword ("namespace", Token.NAMESPACE);
330                         AddKeyword ("new", Token.NEW);
331                         AddKeyword ("null", Token.NULL);
332                         AddKeyword ("object", Token.OBJECT);
333                         AddKeyword ("operator", Token.OPERATOR);
334                         AddKeyword ("out", Token.OUT);
335                         AddKeyword ("override", Token.OVERRIDE);
336                         AddKeyword ("params", Token.PARAMS);
337                         AddKeyword ("private", Token.PRIVATE);
338                         AddKeyword ("protected", Token.PROTECTED);
339                         AddKeyword ("public", Token.PUBLIC);
340                         AddKeyword ("readonly", Token.READONLY);
341                         AddKeyword ("ref", Token.REF);
342                         AddKeyword ("remove", Token.REMOVE);
343                         AddKeyword ("return", Token.RETURN);
344                         AddKeyword ("sbyte", Token.SBYTE);
345                         AddKeyword ("sealed", Token.SEALED);
346                         AddKeyword ("set", Token.SET);
347                         AddKeyword ("short", Token.SHORT);
348                         AddKeyword ("sizeof", Token.SIZEOF);
349                         AddKeyword ("stackalloc", Token.STACKALLOC);
350                         AddKeyword ("static", Token.STATIC);
351                         AddKeyword ("string", Token.STRING);
352                         AddKeyword ("struct", Token.STRUCT);
353                         AddKeyword ("switch", Token.SWITCH);
354                         AddKeyword ("this", Token.THIS);
355                         AddKeyword ("throw", Token.THROW);
356                         AddKeyword ("true", Token.TRUE);
357                         AddKeyword ("try", Token.TRY);
358                         AddKeyword ("typeof", Token.TYPEOF);
359                         AddKeyword ("uint", Token.UINT);
360                         AddKeyword ("ulong", Token.ULONG);
361                         AddKeyword ("unchecked", Token.UNCHECKED);
362                         AddKeyword ("unsafe", Token.UNSAFE);
363                         AddKeyword ("ushort", Token.USHORT);
364                         AddKeyword ("using", Token.USING);
365                         AddKeyword ("virtual", Token.VIRTUAL);
366                         AddKeyword ("void", Token.VOID);
367                         AddKeyword ("volatile", Token.VOLATILE);
368                         AddKeyword ("while", Token.WHILE);
369                         AddKeyword ("partial", Token.PARTIAL);
370                         AddKeyword ("where", Token.WHERE);
371
372                         // LINQ keywords
373                         AddKeyword ("from", Token.FROM);
374                         AddKeyword ("join", Token.JOIN);
375                         AddKeyword ("on", Token.ON);
376                         AddKeyword ("equals", Token.EQUALS);
377                         AddKeyword ("select", Token.SELECT);
378                         AddKeyword ("group", Token.GROUP);
379                         AddKeyword ("by", Token.BY);
380                         AddKeyword ("let", Token.LET);
381                         AddKeyword ("orderby", Token.ORDERBY);
382                         AddKeyword ("ascending", Token.ASCENDING);
383                         AddKeyword ("descending", Token.DESCENDING);
384                         AddKeyword ("into", Token.INTO);
385                 }
386
387                 //
388                 // Class initializer
389                 // 
390                 static Tokenizer ()
391                 {
392                         Reset ();
393                 }
394
395                 public static void Reset ()
396                 {
397                         InitTokens ();
398                         csharp_format_info = NumberFormatInfo.InvariantInfo;
399                         styles = NumberStyles.Float;
400
401                         string_builder = new System.Text.StringBuilder ();
402                 }
403
404                 int GetKeyword (char[] id, int id_len)
405                 {
406                         /*
407                          * Keywords are stored in an array of hashtables grouped by their
408                          * length.
409                          */
410
411                         if ((id_len >= keywords.Length) || (keywords [id_len] == null))
412                                 return -1;
413                         object o = keywords [id_len] [id];
414
415                         if (o == null)
416                                 return -1;
417
418                         int next_token;
419                         int res = (int) o;
420                         switch (res) {
421                         case Token.GET:
422                         case Token.SET:
423                                 if (!handle_get_set)
424                                         res = -1;
425                                 break;
426                         case Token.REMOVE:
427                         case Token.ADD:
428                                 if (!handle_remove_add)
429                                         res = -1;
430                                 break;
431                         case Token.EXTERN:
432                                 if (parsing_declaration == 0)
433                                         res = Token.EXTERN_ALIAS;
434                                 break;
435                         case Token.DEFAULT:
436                                 if (peek_token () == Token.COLON) {
437                                         token ();
438                                         res = Token.DEFAULT_COLON;
439                                 }
440                                 break;
441                         case Token.WHERE:
442                                 if (!handle_where && query_parsing == 0)
443                                         res = -1;
444                                 break;
445                         case Token.FROM:
446                                 //
447                                 // A query expression is any expression that starts with `from identifier'
448                                 // followed by any token except ; , =
449                                 // 
450                                 if (query_parsing == 0) {
451                                         if (lambda_arguments_parsing) {
452                                                 res = -1;
453                                                 break;
454                                         }
455
456                                         PushPosition ();
457                                         // HACK: to disable generics micro-parser, because PushPosition does not
458                                         // store identifiers array
459                                         parsing_generic_less_than = 1;
460                                         switch (xtoken ()) {
461                                         case Token.IDENTIFIER:
462                                         case Token.INT:
463                                         case Token.BOOL:
464                                         case Token.BYTE:
465                                         case Token.CHAR:
466                                         case Token.DECIMAL:
467                                         case Token.FLOAT:
468                                         case Token.LONG:
469                                         case Token.OBJECT:
470                                         case Token.STRING:
471                                         case Token.UINT:
472                                         case Token.ULONG:
473                                                 next_token = xtoken ();
474                                                 if (next_token == Token.SEMICOLON || next_token == Token.COMMA || next_token == Token.EQUALS)
475                                                         goto default;
476                                                 
477                                                 ++query_parsing;
478                                                 if (RootContext.Version <= LanguageVersion.ISO_2)
479                                                         Report.FeatureIsNotAvailable (Location, "query expressions");
480                                                 break;
481                                         case Token.VOID:
482                                                 Expression.Error_VoidInvalidInTheContext (Location);
483                                                 break;
484                                         default:
485                                                 PopPosition ();
486                                                 // HACK: A token is not a keyword so we need to restore identifiers buffer
487                                                 // which has been overwritten before we grabbed the identifier
488                                                 id_builder [0] = 'f'; id_builder [1] = 'r'; id_builder [2] = 'o'; id_builder [3] = 'm';
489                                                 return -1;
490                                         }
491                                         PopPosition ();
492                                 }
493                                 break;
494                         case Token.JOIN:
495                         case Token.ON:
496                         case Token.EQUALS:
497                         case Token.SELECT:
498                         case Token.GROUP:
499                         case Token.BY:
500                         case Token.LET:
501                         case Token.ORDERBY:
502                         case Token.ASCENDING:
503                         case Token.DESCENDING:
504                         case Token.INTO:
505                                 if (query_parsing == 0)
506                                         res = -1;
507                                 break;
508                                 
509                         case Token.USING:
510                         case Token.NAMESPACE:
511                                 // TODO: some explanation needed
512                                 check_incorrect_doc_comment ();
513                                 break;
514                                 
515                         case Token.PARTIAL:
516                                 if (parsing_block > 0) {
517                                         res = -1;
518                                         break;
519                                 }
520
521                                 // Save current position and parse next token.
522                                 PushPosition ();
523
524                                 next_token = token ();
525                                 bool ok = (next_token == Token.CLASS) ||
526                                         (next_token == Token.STRUCT) ||
527                                         (next_token == Token.INTERFACE) ||
528                                         (next_token == Token.VOID);
529
530                                 PopPosition ();
531
532                                 if (ok) {
533                                         if (next_token == Token.VOID) {
534                                                 if (RootContext.Version == LanguageVersion.ISO_1 ||
535                                                     RootContext.Version == LanguageVersion.ISO_2)
536                                                         Report.FeatureIsNotAvailable (Location, "partial methods");
537                                         } else if (RootContext.Version == LanguageVersion.ISO_1)
538                                                 Report.FeatureIsNotAvailable (Location, "partial types");
539
540                                         return res;
541                                 }
542
543                                 if (next_token < Token.LAST_KEYWORD) {
544                                         Report.Error (267, Location,
545                                                 "The `partial' modifier can be used only immediately before `class', `struct', `interface', or `void' keyword");
546                                         return token ();
547                                 }                                       
548
549                                 res = -1;
550                                 break;
551                         }
552
553                         return res;
554                 }
555
556                 public Location Location {
557                         get {
558                                 return new Location (ref_line, hidden ? -1 : col);
559                         }
560                 }
561
562                 public Tokenizer (SeekableStreamReader input, CompilationUnit file)
563                 {
564                         this.ref_name = file;
565                         this.file_name = file;
566                         reader = input;
567                         
568                         putback_char = -1;
569
570                         xml_comment_buffer = new StringBuilder ();
571
572                         //
573                         // FIXME: This could be `Location.Push' but we have to
574                         // find out why the MS compiler allows this
575                         //
576                         Mono.CSharp.Location.Push (file, file);
577                 }
578
579                 static bool is_identifier_start_character (int c)
580                 {
581                         return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Char.IsLetter ((char)c);
582                 }
583
584                 static bool is_identifier_part_character (char c)
585                 {
586                         return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9') ||
587                                 Char.IsLetter (c) || Char.GetUnicodeCategory (c) == UnicodeCategory.ConnectorPunctuation;
588                 }
589
590                 public static bool IsKeyword (string s)
591                 {
592                         return keyword_strings [s] != null;
593                 }
594
595                 //
596                 // Open parens micro parser. Detects both lambda and cast ambiguity.
597                 //
598                 
599                 int TokenizeOpenParens ()
600                 {
601                         int ptoken;
602                         current_token = -1;
603
604                         int bracket_level = 0;
605                         bool is_type = false;
606                         bool can_be_type = false;
607                         
608                         while (true) {
609                                 ptoken = current_token;
610                                 token ();
611
612                                 switch (current_token) {
613                                 case Token.CLOSE_PARENS:
614                                         token ();
615                                         
616                                         //
617                                         // Expression inside parens is lambda, (int i) => 
618                                         //
619                                         if (current_token == Token.ARROW) {
620                                                 if (RootContext.Version <= LanguageVersion.ISO_2)
621                                                         Report.FeatureIsNotAvailable (Location, "lambda expressions");
622
623                                                 return Token.OPEN_PARENS_LAMBDA;
624                                         }
625
626                                         //
627                                         // Expression inside parens is single type, (int[])
628                                         //
629                                         if (is_type)
630                                                 return Token.OPEN_PARENS_CAST;
631
632                                         //
633                                         // Expression is possible cast, look at next token, (T)null
634                                         //
635                                         if (can_be_type) {
636                                                 switch (current_token) {
637                                                 case Token.OPEN_PARENS:
638                                                 case Token.BANG:
639                                                 case Token.TILDE:
640                                                 case Token.IDENTIFIER:
641                                                 case Token.LITERAL_INTEGER:
642                                                 case Token.LITERAL_FLOAT:
643                                                 case Token.LITERAL_DOUBLE:
644                                                 case Token.LITERAL_DECIMAL:
645                                                 case Token.LITERAL_CHARACTER:
646                                                 case Token.LITERAL_STRING:
647                                                 case Token.BASE:
648                                                 case Token.CHECKED:
649                                                 case Token.DELEGATE:
650                                                 case Token.FALSE:
651                                                 case Token.FIXED:
652                                                 case Token.NEW:
653                                                 case Token.NULL:
654                                                 case Token.SIZEOF:
655                                                 case Token.THIS:
656                                                 case Token.THROW:
657                                                 case Token.TRUE:
658                                                 case Token.TYPEOF:
659                                                 case Token.UNCHECKED:
660                                                 case Token.UNSAFE:
661                                                 case Token.DEFAULT:
662
663                                                 //
664                                                 // These can be part of a member access
665                                                 //
666                                                 case Token.INT:
667                                                 case Token.UINT:
668                                                 case Token.SHORT:
669                                                 case Token.USHORT:
670                                                 case Token.LONG:
671                                                 case Token.ULONG:
672                                                 case Token.DOUBLE:
673                                                 case Token.FLOAT:
674                                                 case Token.CHAR:
675                                                 case Token.BYTE:
676                                                 case Token.DECIMAL:
677                                                 case Token.BOOL:
678                                                         return Token.OPEN_PARENS_CAST;
679                                                 }
680                                         }
681                                         return Token.OPEN_PARENS;
682                                         
683                                 case Token.DOT:
684                                 case Token.DOUBLE_COLON:
685                                         if (ptoken != Token.IDENTIFIER && ptoken != Token.OP_GENERICS_GT)
686                                                 goto default;
687
688                                         continue;
689
690                                 case Token.IDENTIFIER:
691                                         switch (ptoken) {
692                                         case Token.DOT:
693                                         case Token.OP_GENERICS_LT:
694                                         case Token.COMMA:
695                                         case Token.DOUBLE_COLON:
696                                         case -1:
697                                                 if (bracket_level == 0)
698                                                         can_be_type = true;
699                                                 continue;
700                                         default:
701                                                 can_be_type = is_type = false;
702                                                 continue;
703                                         }
704
705                                 case Token.OBJECT:
706                                 case Token.STRING:
707                                 case Token.BOOL:
708                                 case Token.DECIMAL:
709                                 case Token.FLOAT:
710                                 case Token.DOUBLE:
711                                 case Token.SBYTE:
712                                 case Token.BYTE:
713                                 case Token.SHORT:
714                                 case Token.USHORT:
715                                 case Token.INT:
716                                 case Token.UINT:
717                                 case Token.LONG:
718                                 case Token.ULONG:
719                                 case Token.CHAR:
720                                 case Token.VOID:
721                                         if (bracket_level == 0)
722                                                 is_type = true;
723                                         continue;
724
725                                 case Token.COMMA:
726                                         if (bracket_level == 0) {
727                                                 bracket_level = 100;
728                                                 can_be_type = is_type = false;
729                                         }
730                                         continue;
731
732                                 case Token.OP_GENERICS_LT:
733                                 case Token.OPEN_BRACKET:
734                                         if (bracket_level++ == 0)
735                                                 is_type = true;
736                                         continue;
737
738                                 case Token.OP_GENERICS_GT:
739                                 case Token.CLOSE_BRACKET:
740                                         --bracket_level;
741                                         continue;
742
743                                 case Token.INTERR_NULLABLE:
744                                 case Token.STAR:
745                                         if (bracket_level == 0)
746                                                 is_type = true;
747                                         continue;
748
749                                 case Token.REF:
750                                 case Token.OUT:
751                                         can_be_type = is_type = false;
752                                         continue;
753
754                                 default:
755                                         return Token.OPEN_PARENS;
756                                 }
757                         }
758                 }
759
760                 public static bool IsValidIdentifier (string s)
761                 {
762                         if (s == null || s.Length == 0)
763                                 return false;
764
765                         if (!is_identifier_start_character (s [0]))
766                                 return false;
767                         
768                         for (int i = 1; i < s.Length; i ++)
769                                 if (! is_identifier_part_character (s [i]))
770                                         return false;
771                         
772                         return true;
773                 }
774
775                 bool parse_less_than ()
776                 {
777                 start:
778                         int the_token = token ();
779                         if (the_token == Token.OPEN_BRACKET) {
780                                 do {
781                                         the_token = token ();
782                                 } while (the_token != Token.CLOSE_BRACKET);
783                                 the_token = token ();
784                         }
785                         switch (the_token) {
786                         case Token.IDENTIFIER:
787                         case Token.OBJECT:
788                         case Token.STRING:
789                         case Token.BOOL:
790                         case Token.DECIMAL:
791                         case Token.FLOAT:
792                         case Token.DOUBLE:
793                         case Token.SBYTE:
794                         case Token.BYTE:
795                         case Token.SHORT:
796                         case Token.USHORT:
797                         case Token.INT:
798                         case Token.UINT:
799                         case Token.LONG:
800                         case Token.ULONG:
801                         case Token.CHAR:
802                         case Token.VOID:
803                                 break;
804
805                         case Token.OP_GENERICS_GT:
806                                 return true;
807
808                         default:
809                                 return false;
810                         }
811                 again:
812                         the_token = token ();
813
814                         if (the_token == Token.OP_GENERICS_GT)
815                                 return true;
816                         else if (the_token == Token.COMMA || the_token == Token.DOT || the_token == Token.DOUBLE_COLON)
817                                 goto start;
818                         else if (the_token == Token.INTERR_NULLABLE || the_token == Token.STAR)
819                                 goto again;
820                         else if (the_token == Token.OP_GENERICS_LT) {
821                                 if (!parse_less_than ())
822                                         return false;
823                                 goto again;
824                         } else if (the_token == Token.OPEN_BRACKET) {
825                         rank_specifiers:
826                                 the_token = token ();
827                                 if (the_token == Token.CLOSE_BRACKET)
828                                         goto again;
829                                 else if (the_token == Token.COMMA)
830                                         goto rank_specifiers;
831                                 return false;
832                         }
833
834                         return false;
835                 }
836
837                 bool parse_generic_dimension (out int dimension)
838                 {
839                         dimension = 1;
840
841                 again:
842                         int the_token = token ();
843                         if (the_token == Token.OP_GENERICS_GT)
844                                 return true;
845                         else if (the_token == Token.COMMA) {
846                                 dimension++;
847                                 goto again;
848                         }
849
850                         return false;
851                 }
852                 
853                 public int peek_token ()
854                 {
855                         int the_token;
856
857                         PushPosition ();
858                         the_token = token ();
859                         PopPosition ();
860                         
861                         return the_token;
862                 }
863                                         
864                 //
865                 // Tonizes `?' using custom disambiguous rules to return one
866                 // of following tokens: INTERR_NULLABLE, OP_COALESCING, INTERR
867                 //
868                 // Tricky expression look like:
869                 //
870                 // Foo ? a = x ? b : c;
871                 //
872                 int TokenizePossibleNullableType ()
873                 {
874                         if (parsing_block == 0)
875                                 return Token.INTERR_NULLABLE;
876
877                         int d = peek_char ();
878                         if (d == '?') {
879                                 get_char ();
880                                 return Token.OP_COALESCING;
881                         }
882
883                         switch (current_token) {
884                         case Token.CLOSE_PARENS:
885                         case Token.TRUE:
886                         case Token.FALSE:
887                         case Token.NULL:
888                         case Token.LITERAL_INTEGER:
889                         case Token.LITERAL_STRING:
890                                 return Token.INTERR;
891                         }
892
893                         if (d != ' ') {
894                                 if (d == ',' || d == ';' || d == '>')
895                                         return Token.INTERR_NULLABLE;
896                                 if (d == '*' || (d >= '0' && d <= '9'))
897                                         return Token.INTERR;
898                         }
899
900                         PushPosition ();
901                         int next_token;
902                         switch (xtoken ()) {
903                         case Token.LITERAL_INTEGER:
904                         case Token.LITERAL_STRING:
905                         case Token.LITERAL_CHARACTER:
906                         case Token.LITERAL_DECIMAL:
907                         case Token.LITERAL_DOUBLE:
908                         case Token.LITERAL_FLOAT:
909                         case Token.TRUE:
910                         case Token.FALSE:
911                         case Token.NULL:
912                         case Token.THIS:
913                                 next_token = Token.INTERR;
914                                 break;
915                                 
916                         case Token.SEMICOLON:
917                         case Token.COMMA:
918                         case Token.CLOSE_PARENS:
919                         case Token.OPEN_BRACKET:
920                         case Token.OP_GENERICS_GT:
921                                 next_token = Token.INTERR_NULLABLE;
922                                 break;
923                                 
924                         default:
925                                 next_token = -1;
926                                 break;
927                         }
928
929                         if (next_token == -1) {
930                                 switch (xtoken ()) {
931                                 case Token.COMMA:
932                                 case Token.SEMICOLON:
933                                 case Token.OPEN_BRACE:
934                                 case Token.CLOSE_PARENS:
935                                 case Token.IN:
936                                         next_token = Token.INTERR_NULLABLE;
937                                         break;
938                                         
939                                 case Token.COLON:
940                                         next_token = Token.INTERR;
941                                         break;                                                  
942                                         
943                                 default:
944                                         int ntoken;
945                                         int interrs = 1;
946                                         int colons = 0;
947                                         //
948                                         // All shorcuts failed, do it hard way
949                                         //
950                                         while ((ntoken = xtoken ()) != Token.EOF) {
951                                                 if (ntoken == Token.SEMICOLON)
952                                                         break;
953                                                 
954                                                 if (ntoken == Token.COLON) {
955                                                         if (++colons == interrs)
956                                                                 break;
957                                                         continue;
958                                                 }
959                                                 
960                                                 if (ntoken == Token.INTERR) {
961                                                         ++interrs;
962                                                         continue;
963                                                 }
964                                         }
965                                         
966                                         next_token = colons != interrs ? Token.INTERR_NULLABLE : Token.INTERR;
967                                         break;
968                                 }
969                         }
970                         
971                         PopPosition ();
972                         return next_token;
973                 }
974
975                 bool decimal_digits (int c)
976                 {
977                         int d;
978                         bool seen_digits = false;
979                         
980                         if (c != -1){
981                                 if (number_pos == max_number_size)
982                                         Error_NumericConstantTooLong ();
983                                 number_builder [number_pos++] = (char) c;
984                         }
985                         
986                         //
987                         // We use peek_char2, because decimal_digits needs to do a 
988                         // 2-character look-ahead (5.ToString for example).
989                         //
990                         while ((d = peek_char2 ()) != -1){
991                                 if (d >= '0' && d <= '9'){
992                                         if (number_pos == max_number_size)
993                                                 Error_NumericConstantTooLong ();
994                                         number_builder [number_pos++] = (char) d;
995                                         get_char ();
996                                         seen_digits = true;
997                                 } else
998                                         break;
999                         }
1000                         
1001                         return seen_digits;
1002                 }
1003
1004                 static bool is_hex (int e)
1005                 {
1006                         return (e >= '0' && e <= '9') || (e >= 'A' && e <= 'F') || (e >= 'a' && e <= 'f');
1007                 }
1008
1009                 static int real_type_suffix (int c)
1010                 {
1011                         int t;
1012
1013                         switch (c){
1014                         case 'F': case 'f':
1015                                 t =  Token.LITERAL_FLOAT;
1016                                 break;
1017                         case 'D': case 'd':
1018                                 t = Token.LITERAL_DOUBLE;
1019                                 break;
1020                         case 'M': case 'm':
1021                                  t= Token.LITERAL_DECIMAL;
1022                                 break;
1023                         default:
1024                                 return Token.NONE;
1025                         }
1026                         return t;
1027                 }
1028
1029                 int integer_type_suffix (ulong ul, int c)
1030                 {
1031                         bool is_unsigned = false;
1032                         bool is_long = false;
1033
1034                         if (c != -1){
1035                                 bool scanning = true;
1036                                 do {
1037                                         switch (c){
1038                                         case 'U': case 'u':
1039                                                 if (is_unsigned)
1040                                                         scanning = false;
1041                                                 is_unsigned = true;
1042                                                 get_char ();
1043                                                 break;
1044
1045                                         case 'l':
1046                                                 if (!is_unsigned){
1047                                                         //
1048                                                         // if we have not seen anything in between
1049                                                         // report this error
1050                                                         //
1051                                                         Report.Warning (78, 4, Location, "The 'l' suffix is easily confused with the digit '1' (use 'L' for clarity)");
1052                                                 }
1053                                                 //
1054                                                 // This goto statement causes the MS CLR 2.0 beta 1 csc to report an error, so
1055                                                 // work around that.
1056                                                 //
1057                                                 //goto case 'L';
1058                                                 if (is_long)
1059                                                         scanning = false;
1060                                                 is_long = true;
1061                                                 get_char ();
1062                                                 break;
1063
1064                                         case 'L': 
1065                                                 if (is_long)
1066                                                         scanning = false;
1067                                                 is_long = true;
1068                                                 get_char ();
1069                                                 break;
1070                                                 
1071                                         default:
1072                                                 scanning = false;
1073                                                 break;
1074                                         }
1075                                         c = peek_char ();
1076                                 } while (scanning);
1077                         }
1078
1079                         if (is_long && is_unsigned){
1080                                 val = ul;
1081                                 return Token.LITERAL_INTEGER;
1082                         } else if (is_unsigned){
1083                                 // uint if possible, or ulong else.
1084
1085                                 if ((ul & 0xffffffff00000000) == 0)
1086                                         val = (uint) ul;
1087                                 else
1088                                         val = ul;
1089                         } else if (is_long){
1090                                 // long if possible, ulong otherwise
1091                                 if ((ul & 0x8000000000000000) != 0)
1092                                         val = ul;
1093                                 else
1094                                         val = (long) ul;
1095                         } else {
1096                                 // int, uint, long or ulong in that order
1097                                 if ((ul & 0xffffffff00000000) == 0){
1098                                         uint ui = (uint) ul;
1099                                         
1100                                         if ((ui & 0x80000000) != 0)
1101                                                 val = ui;
1102                                         else
1103                                                 val = (int) ui;
1104                                 } else {
1105                                         if ((ul & 0x8000000000000000) != 0)
1106                                                 val = ul;
1107                                         else
1108                                                 val = (long) ul;
1109                                 }
1110                         }
1111                         return Token.LITERAL_INTEGER;
1112                 }
1113                                 
1114                 //
1115                 // given `c' as the next char in the input decide whether
1116                 // we need to convert to a special type, and then choose
1117                 // the best representation for the integer
1118                 //
1119                 int adjust_int (int c)
1120                 {
1121                         try {
1122                                 if (number_pos > 9){
1123                                         ulong ul = (uint) (number_builder [0] - '0');
1124
1125                                         for (int i = 1; i < number_pos; i++){
1126                                                 ul = checked ((ul * 10) + ((uint)(number_builder [i] - '0')));
1127                                         }
1128                                         return integer_type_suffix (ul, c);
1129                                 } else {
1130                                         uint ui = (uint) (number_builder [0] - '0');
1131
1132                                         for (int i = 1; i < number_pos; i++){
1133                                                 ui = checked ((ui * 10) + ((uint)(number_builder [i] - '0')));
1134                                         }
1135                                         return integer_type_suffix (ui, c);
1136                                 }
1137                         } catch (OverflowException) {
1138                                 error_details = "Integral constant is too large";
1139                                 Report.Error (1021, Location, error_details);
1140                                 val = 0ul;
1141                                 return Token.LITERAL_INTEGER;
1142                         }
1143                         catch (FormatException) {
1144                                 Report.Error (1013, Location, "Invalid number");
1145                                 val = 0ul;
1146                                 return Token.LITERAL_INTEGER;
1147                         }
1148                 }
1149                 
1150                 int adjust_real (int t)
1151                 {
1152                         string s = new String (number_builder, 0, number_pos);
1153                         const string error_details = "Floating-point constant is outside the range of type `{0}'";
1154
1155                         switch (t){
1156                         case Token.LITERAL_DECIMAL:
1157                                 try {
1158                                         val = System.Decimal.Parse (s, styles, csharp_format_info);
1159                                 } catch (OverflowException) {
1160                                         val = 0m;     
1161                                         Report.Error (594, Location, error_details, "decimal");
1162                                 }
1163                                 break;
1164                         case Token.LITERAL_FLOAT:
1165                                 try {
1166                                         val = float.Parse (s, styles, csharp_format_info);
1167                                 } catch (OverflowException) {
1168                                         val = 0.0f;     
1169                                         Report.Error (594, Location, error_details, "float");
1170                                 }
1171                                 break;
1172                                 
1173                         case Token.LITERAL_DOUBLE:
1174                         case Token.NONE:
1175                                 t = Token.LITERAL_DOUBLE;
1176                                 try {
1177                                         val = System.Double.Parse (s, styles, csharp_format_info);
1178                                 } catch (OverflowException) {
1179                                         val = 0.0;     
1180                                         Report.Error (594, Location, error_details, "double");
1181                                 }
1182                                 break;
1183                         }
1184                         return t;
1185                 }
1186
1187                 int handle_hex ()
1188                 {
1189                         int d;
1190                         ulong ul;
1191                         
1192                         get_char ();
1193                         while ((d = peek_char ()) != -1){
1194                                 if (is_hex (d)){
1195                                         number_builder [number_pos++] = (char) d;
1196                                         get_char ();
1197                                 } else
1198                                         break;
1199                         }
1200                         
1201                         string s = new String (number_builder, 0, number_pos);
1202                         try {
1203                                 if (number_pos <= 8)
1204                                         ul = System.UInt32.Parse (s, NumberStyles.HexNumber);
1205                                 else
1206                                         ul = System.UInt64.Parse (s, NumberStyles.HexNumber);
1207                         } catch (OverflowException){
1208                                 error_details = "Integral constant is too large";
1209                                 Report.Error (1021, Location, error_details);
1210                                 val = 0ul;
1211                                 return Token.LITERAL_INTEGER;
1212                         }
1213                         catch (FormatException) {
1214                                 Report.Error (1013, Location, "Invalid number");
1215                                 val = 0ul;
1216                                 return Token.LITERAL_INTEGER;
1217                         }
1218                         
1219                         return integer_type_suffix (ul, peek_char ());
1220                 }
1221
1222                 //
1223                 // Invoked if we know we have .digits or digits
1224                 //
1225                 int is_number (int c)
1226                 {
1227                         bool is_real = false;
1228                         int type;
1229
1230                         number_pos = 0;
1231
1232                         if (c >= '0' && c <= '9'){
1233                                 if (c == '0'){
1234                                         int peek = peek_char ();
1235
1236                                         if (peek == 'x' || peek == 'X')
1237                                                 return handle_hex ();
1238                                 }
1239                                 decimal_digits (c);
1240                                 c = get_char ();
1241                         }
1242
1243                         //
1244                         // We need to handle the case of
1245                         // "1.1" vs "1.string" (LITERAL_FLOAT vs NUMBER DOT IDENTIFIER)
1246                         //
1247                         if (c == '.'){
1248                                 if (decimal_digits ('.')){
1249                                         is_real = true;
1250                                         c = get_char ();
1251                                 } else {
1252                                         putback ('.');
1253                                         number_pos--;
1254                                         return adjust_int (-1);
1255                                 }
1256                         }
1257                         
1258                         if (c == 'e' || c == 'E'){
1259                                 is_real = true;
1260                                 if (number_pos == max_number_size)
1261                                         Error_NumericConstantTooLong ();
1262                                 number_builder [number_pos++] = 'e';
1263                                 c = get_char ();
1264                                 
1265                                 if (c == '+'){
1266                                         if (number_pos == max_number_size)
1267                                                 Error_NumericConstantTooLong ();
1268                                         number_builder [number_pos++] = '+';
1269                                         c = -1;
1270                                 } else if (c == '-') {
1271                                         if (number_pos == max_number_size)
1272                                                 Error_NumericConstantTooLong ();
1273                                         number_builder [number_pos++] = '-';
1274                                         c = -1;
1275                                 } else {
1276                                         if (number_pos == max_number_size)
1277                                                 Error_NumericConstantTooLong ();
1278                                         number_builder [number_pos++] = '+';
1279                                 }
1280                                         
1281                                 decimal_digits (c);
1282                                 c = get_char ();
1283                         }
1284
1285                         type = real_type_suffix (c);
1286                         if (type == Token.NONE && !is_real){
1287                                 putback (c);
1288                                 return adjust_int (c);
1289                         } else 
1290                                 is_real = true;
1291
1292                         if (type == Token.NONE){
1293                                 putback (c);
1294                         }
1295                         
1296                         if (is_real)
1297                                 return adjust_real (type);
1298
1299                         Console.WriteLine ("This should not be reached");
1300                         throw new Exception ("Is Number should never reach this point");
1301                 }
1302
1303                 //
1304                 // Accepts exactly count (4 or 8) hex, no more no less
1305                 //
1306                 int getHex (int count, out int surrogate, out bool error)
1307                 {
1308                         int i;
1309                         int total = 0;
1310                         int c;
1311                         int top = count != -1 ? count : 4;
1312                         
1313                         get_char ();
1314                         error = false;
1315                         surrogate = 0;
1316                         for (i = 0; i < top; i++){
1317                                 c = get_char ();
1318
1319                                 if (c >= '0' && c <= '9')
1320                                         c = (int) c - (int) '0';
1321                                 else if (c >= 'A' && c <= 'F')
1322                                         c = (int) c - (int) 'A' + 10;
1323                                 else if (c >= 'a' && c <= 'f')
1324                                         c = (int) c - (int) 'a' + 10;
1325                                 else {
1326                                         error = true;
1327                                         return 0;
1328                                 }
1329                                 
1330                                 total = (total * 16) + c;
1331                                 if (count == -1){
1332                                         int p = peek_char ();
1333                                         if (p == -1)
1334                                                 break;
1335                                         if (!is_hex ((char)p))
1336                                                 break;
1337                                 }
1338                         }
1339
1340                         if (top == 8) {
1341                                 if (total > 0x0010FFFF) {
1342                                         error = true;
1343                                         return 0;
1344                                 }
1345
1346                                 if (total >= 0x00010000) {
1347                                         surrogate = ((total - 0x00010000) % 0x0400 + 0xDC00);                                   
1348                                         total = ((total - 0x00010000) / 0x0400 + 0xD800);
1349                                 }
1350                         }
1351
1352                         return total;
1353                 }
1354
1355                 int escape (int c, out int surrogate)
1356                 {
1357                         bool error;
1358                         int d;
1359                         int v;
1360
1361                         d = peek_char ();
1362                         if (c != '\\') {
1363                                 surrogate = 0;
1364                                 return c;
1365                         }
1366                         
1367                         switch (d){
1368                         case 'a':
1369                                 v = '\a'; break;
1370                         case 'b':
1371                                 v = '\b'; break;
1372                         case 'n':
1373                                 v = '\n'; break;
1374                         case 't':
1375                                 v = '\t'; break;
1376                         case 'v':
1377                                 v = '\v'; break;
1378                         case 'r':
1379                                 v = '\r'; break;
1380                         case '\\':
1381                                 v = '\\'; break;
1382                         case 'f':
1383                                 v = '\f'; break;
1384                         case '0':
1385                                 v = 0; break;
1386                         case '"':
1387                                 v = '"'; break;
1388                         case '\'':
1389                                 v = '\''; break;
1390                         case 'x':
1391                                 v = getHex (-1, out surrogate, out error);
1392                                 if (error)
1393                                         goto default;
1394                                 return v;
1395                         case 'u':
1396                         case 'U':
1397                                 return EscapeUnicode (d, out surrogate);
1398                         default:
1399                                 surrogate = 0;
1400                                 Report.Error (1009, Location, "Unrecognized escape sequence `\\{0}'", ((char)d).ToString ());
1401                                 return d;
1402                         }
1403
1404                         get_char ();
1405                         surrogate = 0;
1406                         return v;
1407                 }
1408
1409                 int EscapeUnicode (int ch, out int surrogate)
1410                 {
1411                         bool error;
1412                         if (ch == 'U') {
1413                                 ch = getHex (8, out surrogate, out error);
1414                         } else {
1415                                 ch = getHex (4, out surrogate, out error);
1416                         }
1417
1418                         if (error)
1419                                 Report.Error (1009, Location, "Unrecognized escape sequence");
1420
1421                         return ch;
1422                 }
1423
1424                 int get_char ()
1425                 {
1426                         int x;
1427                         if (putback_char != -1) {
1428                                 x = putback_char;
1429                                 putback_char = -1;
1430                         } else
1431                                 x = reader.Read ();
1432                         if (x == '\n') {
1433                                 advance_line ();
1434                         } else {
1435                                 col++;
1436                         }
1437                         return x;
1438                 }
1439
1440                 void advance_line ()
1441                 {
1442                         line++;
1443                         ref_line++;
1444                         previous_col = col;
1445                         col = 0;
1446                 }
1447
1448                 int peek_char ()
1449                 {
1450                         if (putback_char == -1)
1451                                 putback_char = reader.Read ();
1452                         return putback_char;
1453                 }
1454
1455                 int peek_char2 ()
1456                 {
1457                         if (putback_char != -1)
1458                                 return putback_char;
1459                         return reader.Peek ();
1460                 }
1461                 
1462                 void putback (int c)
1463                 {
1464                         if (putback_char != -1){
1465                                 Console.WriteLine ("Col: " + col);
1466                                 Console.WriteLine ("Row: " + line);
1467                                 Console.WriteLine ("Name: " + ref_name.Name);
1468                                 Console.WriteLine ("Current [{0}] putting back [{1}]  ", putback_char, c);
1469                                 throw new Exception ("This should not happen putback on putback");
1470                         }
1471                         if (c == '\n' || col == 0) {
1472                                 // It won't happen though.
1473                                 line--;
1474                                 ref_line--;
1475                                 col = previous_col;
1476                         }
1477                         else
1478                                 col--;
1479                         putback_char = c;
1480                 }
1481
1482                 public bool advance ()
1483                 {
1484                         return peek_char () != -1;
1485                 }
1486
1487                 public Object Value {
1488                         get {
1489                                 return val;
1490                         }
1491                 }
1492
1493                 public Object value ()
1494                 {
1495                         return val;
1496                 }
1497
1498                 public int token ()
1499                 {
1500                         current_token = xtoken ();
1501                         return current_token;
1502                 }
1503
1504                 static StringBuilder static_cmd_arg = new System.Text.StringBuilder ();
1505
1506                 void get_cmd_arg (out string cmd, out string arg)
1507                 {
1508                         int c;
1509                         
1510                         tokens_seen = false;
1511                         arg = "";
1512
1513                         // skip over white space
1514                         do {
1515                                 c = get_char ();
1516                         } while (c == '\r' || c == ' ' || c == '\t');
1517
1518                         static_cmd_arg.Length = 0;
1519                         while (c != -1 && is_identifier_part_character ((char)c)) {
1520                                 static_cmd_arg.Append ((char)c);
1521                                 c = get_char ();
1522                                 if (c == '\\') {
1523                                         int peek = peek_char ();
1524                                         if (peek == 'U' || peek == 'u') {
1525                                                 int surrogate;
1526                                                 c = EscapeUnicode (c, out surrogate);
1527                                                 if (surrogate != 0) {
1528                                                         if (is_identifier_part_character ((char) c))
1529                                                                 static_cmd_arg.Append ((char) c);
1530                                                         c = surrogate;
1531                                                 }
1532                                         }
1533                                 }
1534                         }
1535
1536                         cmd = static_cmd_arg.ToString ();
1537
1538                         // skip over white space
1539                         while (c == '\r' || c == ' ' || c == '\t')
1540                                 c = get_char ();
1541
1542                         static_cmd_arg.Length = 0;
1543                         int has_identifier_argument = 0;
1544
1545                         while (c != -1 && c != '\n' && c != '\r') {
1546                                 if (c == '\\' && has_identifier_argument >= 0) {
1547                                         if (has_identifier_argument != 0 || (cmd == "define" || cmd == "if" || cmd == "elif" || cmd == "undef")) {
1548                                                 has_identifier_argument = 1;
1549
1550                                                 int peek = peek_char ();
1551                                                 if (peek == 'U' || peek == 'u') {
1552                                                         int surrogate;
1553                                                         c = EscapeUnicode (c, out surrogate);
1554                                                         if (surrogate != 0) {
1555                                                                 if (is_identifier_part_character ((char) c))
1556                                                                         static_cmd_arg.Append ((char) c);
1557                                                                 c = surrogate;
1558                                                         }
1559                                                 }
1560                                         } else {
1561                                                 has_identifier_argument = -1;
1562                                         }
1563                                 }
1564                                 static_cmd_arg.Append ((char) c);
1565                                 c = get_char ();
1566                         }
1567
1568                         if (static_cmd_arg.Length != 0)
1569                                 arg = static_cmd_arg.ToString ();
1570                 }
1571
1572                 //
1573                 // Handles the #line directive
1574                 //
1575                 bool PreProcessLine (string arg)
1576                 {
1577                         if (arg.Length == 0)
1578                                 return false;
1579
1580                         if (arg == "default"){
1581                                 ref_line = line;
1582                                 ref_name = file_name;
1583                                 hidden = false;
1584                                 Location.Push (file_name, ref_name);
1585                                 return true;
1586                         } else if (arg == "hidden"){
1587                                 hidden = true;
1588                                 return true;
1589                         }
1590                         
1591                         try {
1592                                 int pos;
1593
1594                                 if ((pos = arg.IndexOf (' ')) != -1 && pos != 0){
1595                                         ref_line = System.Int32.Parse (arg.Substring (0, pos));
1596                                         pos++;
1597                                         
1598                                         char [] quotes = { '\"' };
1599                                         
1600                                         string name = arg.Substring (pos). Trim (quotes);
1601                                         ref_name = Location.LookupFile (file_name, name);
1602                                         file_name.AddFile (ref_name);
1603                                         hidden = false;
1604                                         Location.Push (file_name, ref_name);
1605                                 } else {
1606                                         ref_line = System.Int32.Parse (arg);
1607                                         hidden = false;
1608                                 }
1609                         } catch {
1610                                 return false;
1611                         }
1612                         
1613                         return true;
1614                 }
1615
1616                 //
1617                 // Handles #define and #undef
1618                 //
1619                 void PreProcessDefinition (bool is_define, string ident, bool caller_is_taking)
1620                 {
1621                         if (ident.Length == 0 || ident == "true" || ident == "false"){
1622                                 Report.Error (1001, Location, "Missing identifier to pre-processor directive");
1623                                 return;
1624                         }
1625
1626                         if (ident.IndexOfAny (simple_whitespaces) != -1){
1627                                 Error_EndLineExpected ();
1628                                 return;
1629                         }
1630
1631                         if (!is_identifier_start_character (ident [0]))
1632                                 Report.Error (1001, Location, "Identifier expected: {0}", ident);
1633                         
1634                         foreach (char c in ident.Substring (1)){
1635                                 if (!is_identifier_part_character (c)){
1636                                         Report.Error (1001, Location, "Identifier expected: {0}",  ident);
1637                                         return;
1638                                 }
1639                         }
1640
1641                         if (!caller_is_taking)
1642                                 return;
1643
1644                         if (is_define) {
1645                                 //
1646                                 // #define ident
1647                                 //
1648                                 if (RootContext.IsConditionalDefined (ident))
1649                                         return;
1650
1651                                 file_name.AddDefine (ident);
1652                         } else {
1653                                 //
1654                                 // #undef ident
1655                                 //
1656                                 file_name.AddUndefine (ident);
1657                         }
1658                 }
1659
1660                 static byte read_hex (string arg, int pos, out bool error)
1661                 {
1662                         error = false;
1663
1664                         int total;
1665                         char c = arg [pos];
1666
1667                         if ((c >= '0') && (c <= '9'))
1668                                 total = (int) c - (int) '0';
1669                         else if ((c >= 'A') && (c <= 'F'))
1670                                 total = (int) c - (int) 'A' + 10;
1671                         else if ((c >= 'a') && (c <= 'f'))
1672                                 total = (int) c - (int) 'a' + 10;
1673                         else {
1674                                 error = true;
1675                                 return 0;
1676                         }
1677
1678                         total *= 16;
1679                         c = arg [pos+1];
1680
1681                         if ((c >= '0') && (c <= '9'))
1682                                 total += (int) c - (int) '0';
1683                         else if ((c >= 'A') && (c <= 'F'))
1684                                 total += (int) c - (int) 'A' + 10;
1685                         else if ((c >= 'a') && (c <= 'f'))
1686                                 total += (int) c - (int) 'a' + 10;
1687                         else {
1688                                 error = true;
1689                                 return 0;
1690                         }
1691
1692                         return (byte) total;
1693                 }
1694
1695                 /// <summary>
1696                 /// Handles #pragma checksum
1697                 /// </summary>
1698                 bool PreProcessPragmaChecksum (string arg)
1699                 {
1700                         if ((arg [0] != ' ') && (arg [0] != '\t'))
1701                                 return false;
1702
1703                         arg = arg.Trim (simple_whitespaces);
1704                         if ((arg.Length < 2) || (arg [0] != '"'))
1705                                 return false;
1706
1707                         StringBuilder file_sb = new StringBuilder ();
1708
1709                         int pos = 1;
1710                         char ch;
1711                         while ((ch = arg [pos++]) != '"') {
1712                                 if (pos >= arg.Length)
1713                                         return false;
1714
1715                                 if (ch == '\\') {
1716                                         if (pos+1 >= arg.Length)
1717                                                 return false;
1718                                         ch = arg [pos++];
1719                                 }
1720
1721                                 file_sb.Append (ch);
1722                         }
1723
1724                         if ((pos+2 >= arg.Length) || ((arg [pos] != ' ') && (arg [pos] != '\t')))
1725                                 return false;
1726
1727                         arg = arg.Substring (pos).Trim (simple_whitespaces);
1728                         if ((arg.Length < 42) || (arg [0] != '"') || (arg [1] != '{') ||
1729                             (arg [10] != '-') || (arg [15] != '-') || (arg [20] != '-') ||
1730                             (arg [25] != '-') || (arg [38] != '}') || (arg [39] != '"'))
1731                                 return false;
1732
1733                         bool error;
1734                         byte[] guid_bytes = new byte [16];
1735
1736                         for (int i = 0; i < 4; i++) {
1737                                 guid_bytes [i] = read_hex (arg, 2+2*i, out error);
1738                                 if (error)
1739                                         return false;
1740                         }
1741                         for (int i = 0; i < 2; i++) {
1742                                 guid_bytes [i+4] = read_hex (arg, 11+2*i, out error);
1743                                 if (error)
1744                                         return false;
1745                                 guid_bytes [i+6] = read_hex (arg, 16+2*i, out error);
1746                                 if (error)
1747                                         return false;
1748                                 guid_bytes [i+8] = read_hex (arg, 21+2*i, out error);
1749                                 if (error)
1750                                         return false;
1751                         }
1752
1753                         for (int i = 0; i < 6; i++) {
1754                                 guid_bytes [i+10] = read_hex (arg, 26+2*i, out error);
1755                                 if (error)
1756                                         return false;
1757                         }
1758
1759                         arg = arg.Substring (40).Trim (simple_whitespaces);
1760                         if ((arg.Length < 34) || (arg [0] != '"') || (arg [33] != '"'))
1761                                 return false;
1762
1763                         byte[] checksum_bytes = new byte [16];
1764                         for (int i = 0; i < 16; i++) {
1765                                 checksum_bytes [i] = read_hex (arg, 1+2*i, out error);
1766                                 if (error)
1767                                         return false;
1768                         }
1769
1770                         arg = arg.Substring (34).Trim (simple_whitespaces);
1771                         if (arg.Length > 0)
1772                                 return false;
1773
1774                         SourceFile file = Location.LookupFile (file_name, file_sb.ToString ());
1775                         file.SetChecksum (guid_bytes, checksum_bytes);
1776                         ref_name.AutoGenerated = true;
1777                         return true;
1778                 }
1779
1780                 /// <summary>
1781                 /// Handles #pragma directive
1782                 /// </summary>
1783                 void PreProcessPragma (string arg)
1784                 {
1785                         const string warning = "warning";
1786                         const string w_disable = "warning disable";
1787                         const string w_restore = "warning restore";
1788                         const string checksum = "checksum";
1789
1790                         if (arg == w_disable) {
1791                                 Report.RegisterWarningRegion (Location).WarningDisable (Location.Row);
1792                                 return;
1793                         }
1794
1795                         if (arg == w_restore) {
1796                                 Report.RegisterWarningRegion (Location).WarningEnable (Location.Row);
1797                                 return;
1798                         }
1799
1800                         if (arg.StartsWith (w_disable)) {
1801                                 int[] codes = ParseNumbers (arg.Substring (w_disable.Length));
1802                                 foreach (int code in codes) {
1803                                         if (code != 0)
1804                                                 Report.RegisterWarningRegion (Location).WarningDisable (Location, code);
1805                                 }
1806                                 return;
1807                         }
1808
1809                         if (arg.StartsWith (w_restore)) {
1810                                 int[] codes = ParseNumbers (arg.Substring (w_restore.Length));
1811                                 Hashtable w_table = Report.warning_ignore_table;
1812                                 foreach (int code in codes) {
1813                                         if (w_table != null && w_table.Contains (code))
1814                                                 Report.Warning (1635, 1, Location, String.Format ("Cannot restore warning `CS{0:0000}' because it was disabled globally", code));
1815                                         Report.RegisterWarningRegion (Location).WarningEnable (Location, code);
1816                                 }
1817                                 return;
1818                         }
1819
1820                         if (arg.StartsWith (warning)) {
1821                                 Report.Warning (1634, 1, Location, "Expected disable or restore");
1822                                 return;
1823                         }
1824
1825                         if (arg.StartsWith (checksum)) {
1826                                 if (!PreProcessPragmaChecksum (arg.Substring (checksum.Length)))
1827                                         Warning_InvalidPragmaChecksum ();
1828                                 return;
1829                         }
1830
1831                         Report.Warning (1633, 1, Location, "Unrecognized #pragma directive");
1832                 }
1833
1834                 int[] ParseNumbers (string text)
1835                 {
1836                         string[] string_array = text.Split (',');
1837                         int[] values = new int [string_array.Length];
1838                         int index = 0;
1839                         foreach (string string_code in string_array) {
1840                                 try {
1841                                         values[index++] = int.Parse (string_code, System.Globalization.CultureInfo.InvariantCulture);
1842                                 }
1843                                 catch (FormatException) {
1844                                         Report.Warning (1692, 1, Location, "Invalid number");
1845                                 }
1846                         }
1847                         return values;
1848                 }
1849
1850                 bool eval_val (string s)
1851                 {
1852                         if (s == "true")
1853                                 return true;
1854                         if (s == "false")
1855                                 return false;
1856
1857                         return file_name.IsConditionalDefined (s);
1858                 }
1859
1860                 bool pp_primary (ref string s)
1861                 {
1862                         s = s.Trim ();
1863                         int len = s.Length;
1864
1865                         if (len > 0){
1866                                 char c = s [0];
1867                                 
1868                                 if (c == '('){
1869                                         s = s.Substring (1);
1870                                         bool val = pp_expr (ref s, false);
1871                                         if (s.Length > 0 && s [0] == ')'){
1872                                                 s = s.Substring (1);
1873                                                 return val;
1874                                         }
1875                                         Error_InvalidDirective ();
1876                                         return false;
1877                                 }
1878                                 
1879                                 if (is_identifier_start_character (c)){
1880                                         int j = 1;
1881
1882                                         while (j < len){
1883                                                 c = s [j];
1884                                                 
1885                                                 if (is_identifier_part_character (c)){
1886                                                         j++;
1887                                                         continue;
1888                                                 }
1889                                                 bool v = eval_val (s.Substring (0, j));
1890                                                 s = s.Substring (j);
1891                                                 return v;
1892                                         }
1893                                         bool vv = eval_val (s);
1894                                         s = "";
1895                                         return vv;
1896                                 }
1897                         }
1898                         Error_InvalidDirective ();
1899                         return false;
1900                 }
1901                 
1902                 bool pp_unary (ref string s)
1903                 {
1904                         s = s.Trim ();
1905                         int len = s.Length;
1906
1907                         if (len > 0){
1908                                 if (s [0] == '!'){
1909                                         if (len > 1 && s [1] == '='){
1910                                                 Error_InvalidDirective ();
1911                                                 return false;
1912                                         }
1913                                         s = s.Substring (1);
1914                                         return ! pp_primary (ref s);
1915                                 } else
1916                                         return pp_primary (ref s);
1917                         } else {
1918                                 Error_InvalidDirective ();
1919                                 return false;
1920                         }
1921                 }
1922                 
1923                 bool pp_eq (ref string s)
1924                 {
1925                         bool va = pp_unary (ref s);
1926
1927                         s = s.Trim ();
1928                         int len = s.Length;
1929                         if (len > 0){
1930                                 if (s [0] == '='){
1931                                         if (len > 2 && s [1] == '='){
1932                                                 s = s.Substring (2);
1933                                                 return va == pp_unary (ref s);
1934                                         } else {
1935                                                 Error_InvalidDirective ();
1936                                                 return false;
1937                                         }
1938                                 } else if (s [0] == '!' && len > 1 && s [1] == '='){
1939                                         s = s.Substring (2);
1940
1941                                         return va != pp_unary (ref s);
1942
1943                                 } 
1944                         }
1945
1946                         return va;
1947                                 
1948                 }
1949                 
1950                 bool pp_and (ref string s)
1951                 {
1952                         bool va = pp_eq (ref s);
1953
1954                         s = s.Trim ();
1955                         int len = s.Length;
1956                         if (len > 0){
1957                                 if (s [0] == '&'){
1958                                         if (len > 2 && s [1] == '&'){
1959                                                 s = s.Substring (2);
1960                                                 return (va & pp_and (ref s));
1961                                         } else {
1962                                                 Error_InvalidDirective ();
1963                                                 return false;
1964                                         }
1965                                 } 
1966                         }
1967                         return va;
1968                 }
1969                 
1970                 //
1971                 // Evaluates an expression for `#if' or `#elif'
1972                 //
1973                 bool pp_expr (ref string s, bool isTerm)
1974                 {
1975                         bool va = pp_and (ref s);
1976                         s = s.Trim ();
1977                         int len = s.Length;
1978                         if (len > 0){
1979                                 char c = s [0];
1980                                 
1981                                 if (c == '|'){
1982                                         if (len > 2 && s [1] == '|'){
1983                                                 s = s.Substring (2);
1984                                                 return va | pp_expr (ref s, isTerm);
1985                                         } else {
1986                                                 Error_InvalidDirective ();
1987                                                 return false;
1988                                         }
1989                                 }
1990                                 if (isTerm) {
1991                                         Error_EndLineExpected ();
1992                                         return false;
1993                                 }
1994                         }
1995                         
1996                         return va;
1997                 }
1998
1999                 bool eval (string s)
2000                 {
2001                         bool v = pp_expr (ref s, true);
2002                         s = s.Trim ();
2003                         if (s.Length != 0){
2004                                 return false;
2005                         }
2006
2007                         return v;
2008                 }
2009
2010                 void Error_NumericConstantTooLong ()
2011                 {
2012                         Report.Error (1021, Location, "Numeric constant too long");                     
2013                 }
2014                 
2015                 void Error_InvalidDirective ()
2016                 {
2017                         Report.Error (1517, Location, "Invalid preprocessor directive");
2018                 }
2019
2020                 void Error_UnexpectedDirective (string extra)
2021                 {
2022                         Report.Error (
2023                                 1028, Location,
2024                                 "Unexpected processor directive ({0})", extra);
2025                 }
2026
2027                 void Error_TokensSeen ()
2028                 {
2029                         Report.Error (1032, Location,
2030                                 "Cannot define or undefine preprocessor symbols after first token in file");
2031                 }
2032
2033                 void Eror_WrongPreprocessorLocation ()
2034                 {
2035                         Report.Error (1040, Location,
2036                                 "Preprocessor directives must appear as the first non-whitespace character on a line");
2037                 }
2038
2039                 void Error_EndLineExpected ()
2040                 {
2041                         Report.Error (1025, Location, "Single-line comment or end-of-line expected");
2042                 }
2043                 
2044                 void Warning_InvalidPragmaChecksum ()
2045                 {
2046                         Report.Warning (1695, 1, Location,
2047                                         "Invalid #pragma checksum syntax; should be " +
2048                                         "#pragma checksum \"filename\" " +
2049                                         "\"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\" \"XXXX...\"");
2050                 }
2051                 //
2052                 // if true, then the code continues processing the code
2053                 // if false, the code stays in a loop until another directive is
2054                 // reached.
2055                 // When caller_is_taking is false we ignore all directives except the ones
2056                 // which can help us to identify where the #if block ends
2057                 bool handle_preprocessing_directive (bool caller_is_taking)
2058                 {
2059                         string cmd, arg;
2060                         bool region_directive = false;
2061
2062                         get_cmd_arg (out cmd, out arg);
2063
2064                         // Eat any trailing whitespaces and single-line comments
2065                         if (arg.IndexOf ("//") != -1)
2066                                 arg = arg.Substring (0, arg.IndexOf ("//"));
2067                         arg = arg.Trim (simple_whitespaces);
2068
2069                         //
2070                         // The first group of pre-processing instructions is always processed
2071                         //
2072                         switch (cmd){
2073                         case "region":
2074                                 region_directive = true;
2075                                 arg = "true";
2076                                 goto case "if";
2077
2078                         case "endregion":
2079                                 if (ifstack == null || ifstack.Count == 0){
2080                                         Error_UnexpectedDirective ("no #region for this #endregion");
2081                                         return true;
2082                                 }
2083                                 int pop = (int) ifstack.Pop ();
2084                                         
2085                                 if ((pop & REGION) == 0)
2086                                         Report.Error (1027, Location, "Expected `#endif' directive");
2087                                         
2088                                 return caller_is_taking;
2089                                 
2090                         case "if":
2091                                 if (ifstack == null)
2092                                         ifstack = new Stack (2);
2093
2094                                 int flags = region_directive ? REGION : 0;
2095                                 if (ifstack.Count == 0){
2096                                         flags |= PARENT_TAKING;
2097                                 } else {
2098                                         int state = (int) ifstack.Peek ();
2099                                         if ((state & TAKING) != 0) {
2100                                                 flags |= PARENT_TAKING;
2101                                         }
2102                                 }
2103
2104                                 if (caller_is_taking && eval (arg)) {
2105                                         ifstack.Push (flags | TAKING);
2106                                         return true;
2107                                 }
2108                                 ifstack.Push (flags);
2109                                 return false;
2110                                 
2111                         case "endif":
2112                                 if (ifstack == null || ifstack.Count == 0){
2113                                         Error_UnexpectedDirective ("no #if for this #endif");
2114                                         return true;
2115                                 } else {
2116                                         pop = (int) ifstack.Pop ();
2117                                         
2118                                         if ((pop & REGION) != 0)
2119                                                 Report.Error (1038, Location, "#endregion directive expected");
2120                                         
2121                                         if (arg.Length != 0) {
2122                                                 Error_EndLineExpected ();
2123                                         }
2124                                         
2125                                         if (ifstack.Count == 0)
2126                                                 return true;
2127
2128                                         int state = (int) ifstack.Peek ();
2129                                         return (state & TAKING) != 0;
2130                                 }
2131
2132                         case "elif":
2133                                 if (ifstack == null || ifstack.Count == 0){
2134                                         Error_UnexpectedDirective ("no #if for this #elif");
2135                                         return true;
2136                                 } else {
2137                                         int state = (int) ifstack.Pop ();
2138
2139                                         if ((state & REGION) != 0) {
2140                                                 Report.Error (1038, Location, "#endregion directive expected");
2141                                                 return true;
2142                                         }
2143
2144                                         if ((state & ELSE_SEEN) != 0){
2145                                                 Error_UnexpectedDirective ("#elif not valid after #else");
2146                                                 return true;
2147                                         }
2148
2149                                         if ((state & TAKING) != 0) {
2150                                                 ifstack.Push (0);
2151                                                 return false;
2152                                         }
2153
2154                                         if (eval (arg) && ((state & PARENT_TAKING) != 0)){
2155                                                 ifstack.Push (state | TAKING);
2156                                                 return true;
2157                                         }
2158
2159                                         ifstack.Push (state);
2160                                         return false;
2161                                 }
2162
2163                         case "else":
2164                                 if (ifstack == null || ifstack.Count == 0){
2165                                         Error_UnexpectedDirective ("no #if for this #else");
2166                                         return true;
2167                                 } else {
2168                                         int state = (int) ifstack.Peek ();
2169
2170                                         if ((state & REGION) != 0) {
2171                                                 Report.Error (1038, Location, "#endregion directive expected");
2172                                                 return true;
2173                                         }
2174
2175                                         if ((state & ELSE_SEEN) != 0){
2176                                                 Error_UnexpectedDirective ("#else within #else");
2177                                                 return true;
2178                                         }
2179
2180                                         ifstack.Pop ();
2181
2182                                         if (arg.Length != 0) {
2183                                                 Error_EndLineExpected ();
2184                                                 return true;
2185                                         }
2186
2187                                         bool ret = false;
2188                                         if ((state & PARENT_TAKING) != 0) {
2189                                                 ret = (state & TAKING) == 0;
2190                                         
2191                                                 if (ret)
2192                                                         state |= TAKING;
2193                                                 else
2194                                                         state &= ~TAKING;
2195                                         }
2196         
2197                                         ifstack.Push (state | ELSE_SEEN);
2198                                         
2199                                         return ret;
2200                                 }
2201                         case "define":
2202                                 if (any_token_seen){
2203                                         Error_TokensSeen ();
2204                                         return caller_is_taking;
2205                                 }
2206                                 PreProcessDefinition (true, arg, caller_is_taking);
2207                                 return caller_is_taking;
2208
2209                         case "undef":
2210                                 if (any_token_seen){
2211                                         Error_TokensSeen ();
2212                                         return caller_is_taking;
2213                                 }
2214                                 PreProcessDefinition (false, arg, caller_is_taking);
2215                                 return caller_is_taking;
2216                         }
2217
2218                         //
2219                         // These are only processed if we are in a `taking' block
2220                         //
2221                         if (!caller_is_taking)
2222                                 return false;
2223                                         
2224                         switch (cmd){
2225                         case "error":
2226                                 Report.Error (1029, Location, "#error: '{0}'", arg);
2227                                 return true;
2228
2229                         case "warning":
2230                                 Report.Warning (1030, 1, Location, "#warning: `{0}'", arg);
2231                                 return true;
2232
2233                         case "pragma":
2234                                 if (RootContext.Version == LanguageVersion.ISO_1) {
2235                                         Report.FeatureIsNotAvailable (Location, "#pragma");
2236                                         return true;
2237                                 }
2238
2239                                 PreProcessPragma (arg);
2240                                 return true;
2241
2242                         case "line":
2243                                 if (!PreProcessLine (arg))
2244                                         Report.Error (
2245                                                 1576, Location,
2246                                                 "The line number specified for #line directive is missing or invalid");
2247                                 return caller_is_taking;
2248                         }
2249
2250                         Report.Error (1024, Location, "Wrong preprocessor directive");
2251                         return true;
2252
2253                 }
2254
2255                 private int consume_string (bool quoted)
2256                 {
2257                         int c;
2258                         string_builder.Length = 0;
2259
2260                         //
2261                         // No need to parse full string when parsing lambda arguments
2262                         //
2263                         if (lambda_arguments_parsing)
2264                                 return Token.LITERAL_STRING;                    
2265                         
2266                         while ((c = get_char ()) != -1){
2267                                 if (c == '"'){
2268                                         if (quoted && peek_char () == '"'){
2269                                                 string_builder.Append ((char) c);
2270                                                 get_char ();
2271                                                 continue;
2272                                         } else {
2273                                                 val = string_builder.ToString ();
2274                                                 return Token.LITERAL_STRING;
2275                                         }
2276                                 }
2277
2278                                 if (c == '\n'){
2279                                         if (!quoted)
2280                                                 Report.Error (1010, Location, "Newline in constant");
2281                                 }
2282
2283                                 if (!quoted){
2284                                         int surrogate;
2285                                         c = escape (c, out surrogate);
2286                                         if (c == -1)
2287                                                 return Token.ERROR;
2288                                         if (surrogate != 0) {
2289                                                 string_builder.Append ((char) c);
2290                                                 c = surrogate;
2291                                         }
2292                                 }
2293                                 string_builder.Append ((char) c);
2294                         }
2295
2296                         Report.Error (1039, Location, "Unterminated string literal");
2297                         return Token.EOF;
2298                 }
2299
2300                 private int consume_identifier (int s)
2301                 {
2302                         int res = consume_identifier (s, false);
2303
2304                         if (doc_state == XmlCommentState.Allowed)
2305                                 doc_state = XmlCommentState.NotAllowed;
2306
2307                         return res;
2308                 }
2309
2310                 private int consume_identifier (int c, bool quoted) 
2311                 {
2312                         int pos = 0;
2313
2314                         if (c == '\\') {
2315                                 int surrogate;
2316                                 c = escape (c, out surrogate);
2317                                 if (surrogate != 0) {
2318                                         id_builder [pos++] = (char) c;
2319                                         c = surrogate;
2320                                 }
2321                         }
2322
2323                         id_builder [pos++] = (char) c;
2324                         Location loc = Location;
2325
2326                         while ((c = get_char ()) != -1) {
2327                         loop:
2328                                 if (is_identifier_part_character ((char) c)){
2329                                         if (pos == max_id_size){
2330                                                 Report.Error (645, loc, "Identifier too long (limit is 512 chars)");
2331                                                 return Token.ERROR;
2332                                         }
2333                                         
2334                                         id_builder [pos++] = (char) c;
2335                                 } else if (c == '\\') {
2336                                         int surrogate;
2337                                         c = escape (c, out surrogate);
2338                                         if (surrogate != 0) {
2339                                                 if (is_identifier_part_character ((char) c))
2340                                                         id_builder [pos++] = (char) c;
2341                                                 c = surrogate;
2342                                         }
2343                                         goto loop;
2344                                 } else {
2345                                         putback (c);
2346                                         break;
2347                                 }
2348                         }
2349
2350                         //
2351                         // Optimization: avoids doing the keyword lookup
2352                         // on uppercase letters
2353                         //
2354                         if (id_builder [0] >= '_' && !quoted) {
2355                                 int keyword = GetKeyword (id_builder, pos);
2356                                 if (keyword != -1) {
2357                                         // TODO: No need to store location for keyword, required location cleanup
2358                                         val = loc;
2359                                         return keyword;
2360                                 }
2361                         }
2362
2363                         //
2364                         // Keep identifiers in an array of hashtables to avoid needless
2365                         // allocations
2366                         //
2367                         CharArrayHashtable identifiers_group = identifiers [pos];
2368                         if (identifiers_group != null) {
2369                                 val = identifiers_group [id_builder];
2370                                 if (val != null) {
2371                                         val = new LocatedToken (loc, (string) val);
2372                                         if (quoted)
2373                                                 AddEscapedIdentifier ((LocatedToken) val);
2374                                         return Token.IDENTIFIER;
2375                                 }
2376                         } else {
2377                                 identifiers_group = new CharArrayHashtable (pos);
2378                                 identifiers [pos] = identifiers_group;
2379                         }
2380
2381                         char [] chars = new char [pos];
2382                         Array.Copy (id_builder, chars, pos);
2383
2384                         val = new String (id_builder, 0, pos);
2385                         identifiers_group.Add (chars, val);
2386
2387                         if (RootContext.Version == LanguageVersion.ISO_1) {
2388                                 for (int i = 1; i < chars.Length; i += 3) {
2389                                         if (chars [i] == '_' && (chars [i - 1] == '_' || chars [i + 1] == '_')) {
2390                                                 Report.Error (1638, loc,
2391                                                         "`{0}': Any identifier with double underscores cannot be used when ISO language version mode is specified", val.ToString ());
2392                                         }
2393                                 }
2394                         }
2395
2396                         val = new LocatedToken (loc, (string) val);
2397                         if (quoted)
2398                                 AddEscapedIdentifier ((LocatedToken) val);
2399                         return Token.IDENTIFIER;
2400                 }
2401                 
2402                 public int xtoken ()
2403                 {
2404                         int d, c;
2405
2406                         // Whether we have seen comments on the current line
2407                         bool comments_seen = false;
2408                         while ((c = get_char ()) != -1) {
2409                                 switch (c) {
2410                                 case '\t':
2411                                         col = ((col + 8) / 8) * 8;
2412                                         continue;
2413
2414                                 case ' ':
2415                                 case '\f':
2416                                 case '\v':
2417                                 case 0xa0:
2418                                 case 0:
2419                                         continue;
2420
2421                                 case '\r':
2422                                         if (peek_char () != '\n')
2423                                                 advance_line ();
2424                                         else
2425                                                 get_char ();
2426
2427                                         any_token_seen |= tokens_seen;
2428                                         tokens_seen = false;
2429                                         comments_seen = false;
2430                                         continue;
2431
2432                                 case '\\':
2433                                         tokens_seen = true;
2434                                         return consume_identifier (c);
2435
2436                                 case '{':
2437                                         val = Location;
2438                                         return Token.OPEN_BRACE;
2439                                 case '}':
2440                                         val = Location;
2441                                         return Token.CLOSE_BRACE;
2442                                 case '[':
2443                                         // To block doccomment inside attribute declaration.
2444                                         if (doc_state == XmlCommentState.Allowed)
2445                                                 doc_state = XmlCommentState.NotAllowed;
2446                                         return Token.OPEN_BRACKET;
2447                                 case ']':
2448                                         return Token.CLOSE_BRACKET;
2449                                 case '(':
2450                                         val = Location;
2451                                         //
2452                                         // An expression versions of parens can appear in block context only
2453                                         //
2454                                         if (parsing_block != 0 && !lambda_arguments_parsing) {
2455                                                 
2456                                                 //
2457                                                 // Optmize most common case where we know that parens
2458                                                 // is not special
2459                                                 //
2460                                                 switch (current_token) {
2461                                                 case Token.IDENTIFIER:
2462                                                 case Token.IF:
2463                                                 case Token.FOR:
2464                                                 case Token.FOREACH:
2465                                                 case Token.TYPEOF:
2466                                                 case Token.WHILE:
2467                                                 case Token.USING:
2468                                                 case Token.DEFAULT:
2469                                                 case Token.DELEGATE:
2470                                                 case Token.OP_GENERICS_GT:
2471                                                         return Token.OPEN_PARENS;
2472                                                 }
2473
2474                                                 lambda_arguments_parsing = true;
2475                                                 PushPosition ();
2476                                                 d = TokenizeOpenParens ();
2477                                                 PopPosition ();
2478                                                 lambda_arguments_parsing = false;
2479                                                 return d;
2480                                         }
2481
2482                                         return Token.OPEN_PARENS;
2483                                 case ')':
2484                                         return Token.CLOSE_PARENS;
2485                                 case ',':
2486                                         return Token.COMMA;
2487                                 case ';':
2488                                         return Token.SEMICOLON;
2489                                 case '~':
2490                                         return Token.TILDE;
2491                                 case '?':
2492                                         return TokenizePossibleNullableType ();
2493                                 case '<':
2494                                         if (parsing_generic_less_than++ > 0)
2495                                                 return Token.OP_GENERICS_LT;
2496
2497                                         return TokenizeLessThan ();
2498
2499                                 case '>':
2500                                         d = peek_char ();
2501
2502                                         if (d == '='){
2503                                                 get_char ();
2504                                                 return Token.OP_GE;
2505                                         }
2506
2507                                         if (parsing_generic_less_than > 1 || (parsing_generic_less_than == 1 && d != '>')) {
2508                                                 parsing_generic_less_than--;
2509                                                 return Token.OP_GENERICS_GT;
2510                                         }
2511
2512                                         if (d == '>') {
2513                                                 get_char ();
2514                                                 d = peek_char ();
2515
2516                                                 if (d == '=') {
2517                                                         get_char ();
2518                                                         return Token.OP_SHIFT_RIGHT_ASSIGN;
2519                                                 }
2520                                                 return Token.OP_SHIFT_RIGHT;
2521                                         }
2522
2523                                         return Token.OP_GT;
2524                                 
2525                                 case '+':
2526                                         d = peek_char ();
2527                                         if (d == '+') {
2528                                                 d = Token.OP_INC;
2529                                         } else if (d == '=') {
2530                                                 d = Token.OP_ADD_ASSIGN;
2531                                         } else {
2532                                                 return Token.PLUS;
2533                                         }
2534                                         get_char ();
2535                                         return d;
2536
2537                                 case '-':
2538                                         d = peek_char ();
2539                                         if (d == '-') {
2540                                                 d = Token.OP_DEC;
2541                                         } else if (d == '=')
2542                                                 d = Token.OP_SUB_ASSIGN;
2543                                         else if (d == '>')
2544                                                 d = Token.OP_PTR;
2545                                         else {
2546                                                 return Token.MINUS;
2547                                         }
2548                                         get_char ();
2549                                         return d;
2550
2551                                 case '!':
2552                                         if (peek_char () == '='){
2553                                                 get_char ();
2554                                                 return Token.OP_NE;
2555                                         }
2556                                         return Token.BANG;
2557
2558                                 case '=':
2559                                         d = peek_char ();
2560                                         if (d == '='){
2561                                                 get_char ();
2562                                                 return Token.OP_EQ;
2563                                         }
2564                                         if (d == '>'){
2565                                                 get_char ();
2566                                                 return Token.ARROW;
2567                                         }
2568
2569                                         return Token.ASSIGN;
2570
2571                                 case '&':
2572                                         d = peek_char ();
2573                                         if (d == '&'){
2574                                                 get_char ();
2575                                                 return Token.OP_AND;
2576                                         }
2577                                         if (d == '='){
2578                                                 get_char ();
2579                                                 return Token.OP_AND_ASSIGN;
2580                                         }
2581                                         return Token.BITWISE_AND;
2582
2583                                 case '|':
2584                                         d = peek_char ();
2585                                         if (d == '|'){
2586                                                 get_char ();
2587                                                 return Token.OP_OR;
2588                                         }
2589                                         if (d == '='){
2590                                                 get_char ();
2591                                                 return Token.OP_OR_ASSIGN;
2592                                         }
2593                                         return Token.BITWISE_OR;
2594
2595                                 case '*':
2596                                         if (peek_char () == '='){
2597                                                 get_char ();
2598                                                 return Token.OP_MULT_ASSIGN;
2599                                         }
2600                                         val = Location;
2601                                         return Token.STAR;
2602
2603                                 case '/':
2604                                         d = peek_char ();
2605                                         if (d == '='){
2606                                                 get_char ();
2607                                                 return Token.OP_DIV_ASSIGN;
2608                                         }
2609
2610                                         // Handle double-slash comments.
2611                                         if (d == '/'){
2612                                                 get_char ();
2613                                                 if (RootContext.Documentation != null && peek_char () == '/') {
2614                                                         get_char ();
2615                                                         // Don't allow ////.
2616                                                         if ((d = peek_char ()) != '/') {
2617                                                                 update_comment_location ();
2618                                                                 if (doc_state == XmlCommentState.Allowed)
2619                                                                         handle_one_line_xml_comment ();
2620                                                                 else if (doc_state == XmlCommentState.NotAllowed)
2621                                                                         warn_incorrect_doc_comment ();
2622                                                         }
2623                                                 }
2624                                                 while ((d = get_char ()) != -1 && (d != '\n') && d != '\r');
2625
2626                                                 any_token_seen |= tokens_seen;
2627                                                 tokens_seen = false;
2628                                                 comments_seen = false;
2629                                                 continue;
2630                                         } else if (d == '*'){
2631                                                 get_char ();
2632                                                 bool docAppend = false;
2633                                                 if (RootContext.Documentation != null && peek_char () == '*') {
2634                                                         get_char ();
2635                                                         update_comment_location ();
2636                                                         // But when it is /**/, just do nothing.
2637                                                         if (peek_char () == '/') {
2638                                                                 get_char ();
2639                                                                 continue;
2640                                                         }
2641                                                         if (doc_state == XmlCommentState.Allowed)
2642                                                                 docAppend = true;
2643                                                         else if (doc_state == XmlCommentState.NotAllowed)
2644                                                                 warn_incorrect_doc_comment ();
2645                                                 }
2646
2647                                                 int current_comment_start = 0;
2648                                                 if (docAppend) {
2649                                                         current_comment_start = xml_comment_buffer.Length;
2650                                                         xml_comment_buffer.Append (Environment.NewLine);
2651                                                 }
2652
2653                                                 while ((d = get_char ()) != -1){
2654                                                         if (d == '*' && peek_char () == '/'){
2655                                                                 get_char ();
2656                                                                 comments_seen = true;
2657                                                                 break;
2658                                                         }
2659                                                         if (docAppend)
2660                                                                 xml_comment_buffer.Append ((char) d);
2661                                                         
2662                                                         if (d == '\n'){
2663                                                                 any_token_seen |= tokens_seen;
2664                                                                 tokens_seen = false;
2665                                                                 // 
2666                                                                 // Reset 'comments_seen' just to be consistent.
2667                                                                 // It doesn't matter either way, here.
2668                                                                 //
2669                                                                 comments_seen = false;
2670                                                         }
2671                                                 }
2672                                                 if (!comments_seen)
2673                                                         Report.Error (1035, Location, "End-of-file found, '*/' expected");
2674
2675                                                 if (docAppend)
2676                                                         update_formatted_doc_comment (current_comment_start);
2677                                                 continue;
2678                                         }
2679                                         return Token.DIV;
2680
2681                                 case '%':
2682                                         if (peek_char () == '='){
2683                                                 get_char ();
2684                                                 return Token.OP_MOD_ASSIGN;
2685                                         }
2686                                         return Token.PERCENT;
2687
2688                                 case '^':
2689                                         if (peek_char () == '='){
2690                                                 get_char ();
2691                                                 return Token.OP_XOR_ASSIGN;
2692                                         }
2693                                         return Token.CARRET;
2694
2695                                 case ':':
2696                                         if (peek_char () == ':') {
2697                                                 get_char ();
2698                                                 return Token.DOUBLE_COLON;
2699                                         }
2700                                         return Token.COLON;
2701
2702                                 case '0': case '1': case '2': case '3': case '4':
2703                                 case '5': case '6': case '7': case '8': case '9':
2704                                         tokens_seen = true;
2705                                         return is_number (c);
2706
2707                                 case '\n': // white space
2708                                         any_token_seen |= tokens_seen;
2709                                         tokens_seen = false;
2710                                         comments_seen = false;
2711                                         continue;
2712
2713                                 case '.':
2714                                         tokens_seen = true;
2715                                         d = peek_char ();
2716                                         if (d >= '0' && d <= '9')
2717                                                 return is_number (c);
2718                                         return Token.DOT;
2719                                 
2720                                 case '#':
2721                                         if (tokens_seen || comments_seen) {
2722                                                 Eror_WrongPreprocessorLocation ();
2723                                                 return Token.ERROR;
2724                                         }
2725                                         
2726                                         if (handle_preprocessing_directive (true))
2727                                                 continue;
2728
2729                                         bool directive_expected = false;
2730                                         while ((c = get_char ()) != -1) {
2731                                                 if (col == 1) {
2732                                                         directive_expected = true;
2733                                                 } else if (!directive_expected) {
2734                                                         // TODO: Implement comment support for disabled code and uncomment this code
2735 //                                                      if (c == '#') {
2736 //                                                              Eror_WrongPreprocessorLocation ();
2737 //                                                              return Token.ERROR;
2738 //                                                      }
2739                                                         continue;
2740                                                 }
2741
2742                                                 if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f' || c == '\v' )
2743                                                         continue;
2744
2745                                                 if (c == '#') {
2746                                                         if (handle_preprocessing_directive (false))
2747                                                                 break;
2748                                                 }
2749                                                 directive_expected = false;
2750                                         }
2751
2752                                         if (c != -1) {
2753                                                 tokens_seen = false;
2754                                                 continue;
2755                                         }
2756
2757                                         return Token.EOF;
2758                                 
2759                                 case '"':
2760                                         return consume_string (false);
2761
2762                                 case '\'':
2763                                         return TokenizeBackslash ();
2764                                 
2765                                 case '@':
2766                                         c = get_char ();
2767                                         if (c == '"') {
2768                                                 tokens_seen = true;
2769                                                 return consume_string (true);
2770                                         }
2771
2772                                         if (is_identifier_start_character (c)){
2773                                                 return consume_identifier (c, true);
2774                                         }
2775
2776                                         Report.Error (1646, Location, "Keyword, identifier, or string expected after verbatim specifier: @");
2777                                         return Token.ERROR;
2778
2779                                 case EvalStatementParserCharacter:
2780                                         return Token.EVAL_STATEMENT_PARSER;
2781                                 case EvalCompilationUnitParserCharacter:
2782                                         return Token.EVAL_COMPILATION_UNIT_PARSER;
2783                                 case EvalUsingDeclarationsParserCharacter:
2784                                         return Token.EVAL_USING_DECLARATIONS_UNIT_PARSER;
2785                                 }
2786
2787                                 if (is_identifier_start_character (c)) {
2788                                         tokens_seen = true;
2789                                         return consume_identifier (c);
2790                                 }
2791
2792                                 error_details = ((char)c).ToString ();
2793                                 return Token.ERROR;
2794                         }
2795
2796                         return Token.EOF;
2797                 }
2798
2799                 int TokenizeBackslash ()
2800                 {
2801                         int c = get_char ();
2802                         tokens_seen = true;
2803                         if (c == '\'') {
2804                                 error_details = "Empty character literal";
2805                                 Report.Error (1011, Location, error_details);
2806                                 return Token.ERROR;
2807                         }
2808                         if (c == '\r' || c == '\n') {
2809                                 Report.Error (1010, Location, "Newline in constant");
2810                                 return Token.ERROR;
2811                         }
2812
2813                         int d;
2814                         c = escape (c, out d);
2815                         if (c == -1)
2816                                 return Token.ERROR;
2817                         if (d != 0)
2818                                 throw new NotImplementedException ();
2819
2820                         val = (char) c;
2821                         c = get_char ();
2822
2823                         if (c != '\'') {
2824                                 Report.Error (1012, Location, "Too many characters in character literal");
2825
2826                                 // Try to recover, read until newline or next "'"
2827                                 while ((c = get_char ()) != -1) {
2828                                         if (c == '\n' || c == '\'')
2829                                                 break;
2830                                 }
2831                                 return Token.ERROR;
2832                         }
2833
2834                         return Token.LITERAL_CHARACTER;
2835                 }
2836
2837                 int TokenizeLessThan ()
2838                 {
2839                         int d;
2840                         if (handle_typeof) {
2841                                 PushPosition ();
2842                                 if (parse_generic_dimension (out d)) {
2843                                         val = d;
2844                                         DiscardPosition ();
2845                                         return Token.GENERIC_DIMENSION;
2846                                 }
2847                                 PopPosition ();
2848                         }
2849
2850                         // Save current position and parse next token.
2851                         PushPosition ();
2852                         if (parse_less_than ()) {
2853                                 if (parsing_generic_declaration && token () != Token.DOT) {
2854                                         d = Token.OP_GENERICS_LT_DECL;
2855                                 } else {
2856                                         d = Token.OP_GENERICS_LT;
2857                                 }
2858                                 PopPosition ();
2859                                 return d;
2860                         }
2861
2862                         PopPosition ();
2863                         parsing_generic_less_than = 0;
2864
2865                         d = peek_char ();
2866                         if (d == '<') {
2867                                 get_char ();
2868                                 d = peek_char ();
2869
2870                                 if (d == '=') {
2871                                         get_char ();
2872                                         return Token.OP_SHIFT_LEFT_ASSIGN;
2873                                 }
2874                                 return Token.OP_SHIFT_LEFT;
2875                         }
2876
2877                         if (d == '=') {
2878                                 get_char ();
2879                                 return Token.OP_LE;
2880                         }
2881                         return Token.OP_LT;
2882                 }
2883
2884                 //
2885                 // Handles one line xml comment
2886                 //
2887                 private void handle_one_line_xml_comment ()
2888                 {
2889                         int c;
2890                         while ((c = peek_char ()) == ' ')
2891                                 get_char (); // skip heading whitespaces.
2892                         while ((c = peek_char ()) != -1 && c != '\n' && c != '\r') {
2893                                 xml_comment_buffer.Append ((char) get_char ());
2894                         }
2895                         if (c == '\r' || c == '\n')
2896                                 xml_comment_buffer.Append (Environment.NewLine);
2897                 }
2898
2899                 //
2900                 // Remove heading "*" in Javadoc-like xml documentation.
2901                 //
2902                 private void update_formatted_doc_comment (int current_comment_start)
2903                 {
2904                         int length = xml_comment_buffer.Length - current_comment_start;
2905                         string [] lines = xml_comment_buffer.ToString (
2906                                 current_comment_start,
2907                                 length).Replace ("\r", "").Split ('\n');
2908                         
2909                         // The first line starts with /**, thus it is not target
2910                         // for the format check.
2911                         for (int i = 1; i < lines.Length; i++) {
2912                                 string s = lines [i];
2913                                 int idx = s.IndexOf ('*');
2914                                 string head = null;
2915                                 if (idx < 0) {
2916                                         if (i < lines.Length - 1)
2917                                                 return;
2918                                         head = s;
2919                                 } else
2920                                         head = s.Substring (0, idx);
2921                                 foreach (char c in head)
2922                                         if (c != ' ')
2923                                                 return;
2924                                 lines [i] = s.Substring (idx + 1);
2925                         }
2926                         xml_comment_buffer.Remove (current_comment_start, length);
2927                         xml_comment_buffer.Insert (current_comment_start, String.Join (Environment.NewLine, lines));
2928                 }
2929
2930                 //
2931                 // Updates current comment location.
2932                 //
2933                 private void update_comment_location ()
2934                 {
2935                         if (current_comment_location.IsNull) {
2936                                 // "-2" is for heading "//" or "/*"
2937                                 current_comment_location =
2938                                         new Location (ref_line, hidden ? -1 : col - 2);
2939                         }
2940                 }
2941
2942                 //
2943                 // Checks if there was incorrect doc comments and raise
2944                 // warnings.
2945                 //
2946                 public void check_incorrect_doc_comment ()
2947                 {
2948                         if (xml_comment_buffer.Length > 0)
2949                                 warn_incorrect_doc_comment ();
2950                 }
2951
2952                 //
2953                 // Raises a warning when tokenizer found incorrect doccomment
2954                 // markup.
2955                 //
2956                 private void warn_incorrect_doc_comment ()
2957                 {
2958                         if (doc_state != XmlCommentState.Error) {
2959                                 doc_state = XmlCommentState.Error;
2960                                 // in csc, it is 'XML comment is not placed on 
2961                                 // a valid language element'. But that does not
2962                                 // make sense.
2963                                 Report.Warning (1587, 2, Location, "XML comment is not placed on a valid language element");
2964                         }
2965                 }
2966
2967                 //
2968                 // Consumes the saved xml comment lines (if any)
2969                 // as for current target member or type.
2970                 //
2971                 public string consume_doc_comment ()
2972                 {
2973                         if (xml_comment_buffer.Length > 0) {
2974                                 string ret = xml_comment_buffer.ToString ();
2975                                 reset_doc_comment ();
2976                                 return ret;
2977                         }
2978                         return null;
2979                 }
2980
2981                 void reset_doc_comment ()
2982                 {
2983                         xml_comment_buffer.Length = 0;
2984                         current_comment_location = Location.Null;
2985                 }
2986
2987                 public void cleanup ()
2988                 {
2989                         if (ifstack != null && ifstack.Count >= 1) {
2990                                 int state = (int) ifstack.Pop ();
2991                                 if ((state & REGION) != 0)
2992                                         Report.Error (1038, Location, "#endregion directive expected");
2993                                 else 
2994                                         Report.Error (1027, Location, "Expected `#endif' directive");
2995                         }
2996                 }
2997         }
2998
2999         //
3000         // Indicates whether it accepts XML documentation or not.
3001         //
3002         public enum XmlCommentState {
3003                 // comment is allowed in this state.
3004                 Allowed,
3005                 // comment is not allowed in this state.
3006                 NotAllowed,
3007                 // once comments appeared when it is NotAllowed, then the
3008                 // state is changed to it, until the state is changed to
3009                 // .Allowed.
3010                 Error
3011         }
3012 }
3013