Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2817-BinPackingSpeedup/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Parser/mcs/cs-tokenizer.cs

Last change on this file was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 95.0 KB
Line 
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@gmail.com)
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// Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
13//
14using System;
15using System.Text;
16using System.Collections.Generic;
17using System.Globalization;
18using System.Diagnostics;
19using System.Collections;
20
21namespace Mono.CSharp
22{
23  //
24  // This class has to be used by parser only, it reuses token
25  // details once a file is parsed
26  //
27  public class LocatedToken
28  {
29    public int row, column;
30    public string value;
31    public SourceFile file;
32
33    public LocatedToken ()
34    {
35    }
36
37    public LocatedToken (string value, Location loc)
38    {
39      this.value = value;
40      file = loc.SourceFile;
41      row = loc.Row;
42      column = loc.Column;
43    }
44
45    public override string ToString ()
46    {
47      return string.Format ("Token '{0}' at {1},{2}", Value, row, column);
48    }
49
50    public Location Location
51    {
52      get { return new Location (file, row, column); }
53    }
54
55    public string Value
56    {
57      get { return value; }
58    }
59  }
60
61  /// <summary>
62  ///    Tokenizer for C# source code.
63  /// </summary>
64  public class Tokenizer : yyParser.yyInput
65  {
66    class KeywordEntry<T>
67    {
68      public readonly T Token;
69      public KeywordEntry<T> Next;
70      public readonly char[] Value;
71
72      public KeywordEntry (string value,T token)
73      {
74        this.Value = value.ToCharArray ();
75        this.Token = token;
76      }
77    }
78
79    sealed class IdentifiersComparer : IEqualityComparer<char[]>
80    {
81      readonly int length;
82
83      public IdentifiersComparer (int length)
84      {
85        this.length = length;
86      }
87
88      public bool Equals (char[] x, char[] y)
89      {
90        for (int i = 0; i < length; ++i)
91          if (x [i] != y [i])
92            return false;
93
94        return true;
95      }
96
97      public int GetHashCode (char[] obj)
98      {
99        int h = 0;
100        for (int i = 0; i < length; ++i)
101          h = (h << 5) - h + obj [i];
102
103        return h;
104      }
105    }
106
107    public class LocatedTokenBuffer
108    {
109      readonly LocatedToken[] buffer;
110      public int pos;
111
112      public LocatedTokenBuffer ()
113      {
114        buffer = new LocatedToken[0];
115      }
116
117      public LocatedTokenBuffer (LocatedToken[] buffer)
118      {
119        this.buffer = buffer ?? new LocatedToken[0];
120      }
121
122      public LocatedToken Create (SourceFile file, int row, int column)
123      {
124        return Create (null, file, row, column);
125      }
126
127      public LocatedToken Create (string value, SourceFile file, int row, int column)
128      {
129        //
130        // TODO: I am not very happy about the logic but it's the best
131        // what I could come up with for now.
132        // Ideally we should be using just tiny buffer (256 elements) which
133        // is enough to hold all details for currect stack and recycle elements
134        // poped from the stack but there is a trick needed to recycle
135        // them properly.
136        //
137        LocatedToken entry;
138        if (pos >= buffer.Length) {
139          entry = new LocatedToken ();
140        } else {
141          entry = buffer[pos];
142          if (entry == null) {
143            entry = new LocatedToken ();
144            buffer[pos] = entry;
145          }
146
147          ++pos;
148        }
149        entry.value = value;
150        entry.file = file;
151        entry.row = row;
152        entry.column = column;
153        return entry;
154      }
155
156      //
157      // Used for token not required by expression evaluator
158      //
159      [Conditional ("FULL_AST")]
160      public void CreateOptional (SourceFile file, int row, int col, ref object token)
161      {
162        token = Create (file, row, col);
163      }
164    }
165
166    public enum PreprocessorDirective
167    {
168      Invalid = 0,
169
170      Region = 1,
171      Endregion = 2,
172      If = 3 | RequiresArgument,
173      Endif = 4,
174      Elif = 5 | RequiresArgument,
175      Else = 6,
176      Define = 7 | RequiresArgument,
177      Undef = 8 | RequiresArgument,
178      Error = 9,
179      Warning = 10,
180      Pragma = 11 | CustomArgumentsParsing,
181      Line = 12 | CustomArgumentsParsing,
182
183      CustomArgumentsParsing = 1 << 10,
184      RequiresArgument = 1 << 11
185    }
186
187    readonly SeekableStreamReader reader;
188    readonly CompilationSourceFile source_file;
189    public CompilationSourceFile SourceFile { get { return source_file; } }
190    readonly CompilerContext context;
191    readonly Report Report;
192
193
194    SourceFile current_source;
195    Location hidden_block_start;
196    int ref_line = 1;
197    int line = 1;
198    int col = 0;
199    int previous_col;
200    int current_token;
201    readonly int tab_size;
202    bool handle_get_set = false;
203    bool handle_remove_add = false;
204    bool handle_where;
205    bool lambda_arguments_parsing;
206    List<Location> escaped_identifiers;
207    int parsing_generic_less_than;
208    readonly bool doc_processing;
209    readonly LocatedTokenBuffer ltb;
210   
211    //
212    // Used mainly for parser optimizations. Some expressions for instance
213    // can appear only in block (including initializer, base initializer)
214    // scope only
215    //
216    public int parsing_block;
217    internal bool query_parsing;
218   
219    //
220    // When parsing type only, useful for ambiguous nullable types
221    //
222    public int parsing_type;
223   
224    //
225    // Set when parsing generic declaration (type or method header)
226    //
227    public bool parsing_generic_declaration;
228    public bool parsing_generic_declaration_doc;
229   
230    //
231    // The value indicates that we have not reach any declaration or
232    // namespace yet
233    //
234    public int parsing_declaration;
235    public bool parsing_attribute_section;
236
237    public bool parsing_modifiers;
238
239    //
240    // The special characters to inject on streams to run the unit parser
241    // in the special expression mode. Using private characters from
242    // Plane Sixteen (U+100000 to U+10FFFD)
243    //
244    // This character is only tested just before the tokenizer is about to report
245    // an error;   So on the regular operation mode, this addition will have no
246    // impact on the tokenizer's performance.
247    //
248   
249    public const int EvalStatementParserCharacter = 0x100000;
250    public const int EvalCompilationUnitParserCharacter = 0x100001;
251    public const int EvalUsingDeclarationsParserCharacter = 0x100002;
252    public const int DocumentationXref = 0x100003;
253
254    const int UnicodeLS = 0x2028;
255    const int UnicodePS = 0x2029;
256   
257    //
258    // XML documentation buffer. The save point is used to divide
259    // comments on types and comments on members.
260    //
261    StringBuilder xml_comment_buffer;
262
263    //
264    // See comment on XmlCommentState enumeration.
265    //
266    XmlCommentState xml_doc_state = XmlCommentState.Allowed;
267
268    //
269    // Whether tokens have been seen on this line
270    //
271    bool tokens_seen = false;
272
273    //
274    // Set to true once the GENERATE_COMPLETION token has bee
275    // returned.   This helps produce one GENERATE_COMPLETION,
276    // as many COMPLETE_COMPLETION as necessary to complete the
277    // AST tree and one final EOF.
278    //
279    bool generated;
280   
281    //
282    // Whether a token has been seen on the file
283    // This is needed because `define' is not allowed to be used
284    // after a token has been seen.
285    //
286    bool any_token_seen;
287
288    //
289    // Class variables
290    //
291    static readonly KeywordEntry<int>[][] keywords;
292    static readonly KeywordEntry<PreprocessorDirective>[][] keywords_preprocessor;
293    static readonly HashSet<string> keyword_strings;
294    static readonly NumberStyles styles;
295    static readonly NumberFormatInfo csharp_format_info;
296
297    // Pragma arguments
298    static readonly char[] pragma_warning = "warning".ToCharArray ();
299    static readonly char[] pragma_warning_disable = "disable".ToCharArray ();
300    static readonly char[] pragma_warning_restore = "restore".ToCharArray ();
301    static readonly char[] pragma_checksum = "checksum".ToCharArray ();
302    static readonly char[] line_hidden = "hidden".ToCharArray ();
303    static readonly char[] line_default = "default".ToCharArray ();
304
305    static readonly char[] simple_whitespaces = new char[] { ' ', '\t' };
306    bool startsLine = true;
307    internal SpecialsBag sbag;
308
309    public bool PropertyParsing {
310      get { return handle_get_set; }
311      set { handle_get_set = value; }
312    }
313
314    public bool EventParsing {
315      get { return handle_remove_add; }
316      set { handle_remove_add = value; }
317    }
318
319    public bool ConstraintsParsing {
320      get { return handle_where; }
321      set { handle_where = value; }
322    }
323 
324    public XmlCommentState doc_state {
325      get { return xml_doc_state; }
326      set {
327        if (value == XmlCommentState.Allowed) {
328          check_incorrect_doc_comment ();
329          reset_doc_comment ();
330        }
331        xml_doc_state = value;
332      }
333    }
334
335    //
336    // This is used to trigger completion generation on the parser
337    public bool CompleteOnEOF;
338
339    void AddEscapedIdentifier (Location loc)
340    {
341      if (escaped_identifiers == null)
342        escaped_identifiers = new List<Location> ();
343
344      escaped_identifiers.Add (loc);
345    }
346
347    public bool IsEscapedIdentifier (ATypeNameExpression name)
348    {
349      return escaped_identifiers != null && escaped_identifiers.Contains (name.Location);
350    }
351
352    //
353    // Values for the associated token returned
354    //
355    internal int putback_char;  // Used by repl only
356    object val;
357
358    //
359    // Pre-processor
360    //
361    const int TAKING        = 1;
362    const int ELSE_SEEN = 4;
363    const int PARENT_TAKING = 8;
364    const int REGION = 16;   
365
366    //
367    // pre-processor if stack state:
368    //
369    Stack<int> ifstack;
370
371    public const int MaxIdentifierLength = 512;
372    public const int MaxNumberLength = 512;
373
374    readonly char[] id_builder;
375    readonly Dictionary<char[], string>[] identifiers;
376    readonly char[] number_builder;
377    int number_pos;
378
379    char[] value_builder = new char[64];
380
381    public int Line {
382      get {
383        return ref_line;
384      }
385      set {
386        ref_line = value;
387      }
388    }
389
390    public int Column {
391      get {
392        return col;
393      }
394      set {
395        col = value;
396      }
397    }
398
399    //
400    // This is used when the tokenizer needs to save
401    // the current position as it needs to do some parsing
402    // on its own to deamiguate a token in behalf of the
403    // parser.
404    //
405    Stack<Position> position_stack = new Stack<Position> (2);
406
407    class Position
408    {
409      public int position;
410      public int line;
411      public int ref_line;
412      public int col;
413      public Location hidden;
414      public int putback_char;
415      public int previous_col;
416      public Stack<int> ifstack;
417      public int parsing_generic_less_than;
418      public int current_token;
419      public object val;
420
421      public Position (Tokenizer t)
422      {
423        position = t.reader.Position;
424        line = t.line;
425        ref_line = t.ref_line;
426        col = t.col;
427        hidden = t.hidden_block_start;
428        putback_char = t.putback_char;
429        previous_col = t.previous_col;
430        if (t.ifstack != null && t.ifstack.Count != 0) {
431          // There is no simple way to clone Stack<T> all
432          // methods reverse the order
433          var clone = t.ifstack.ToArray ();
434          Array.Reverse (clone);
435          ifstack = new Stack<int> (clone);
436        }
437        parsing_generic_less_than = t.parsing_generic_less_than;
438        current_token = t.current_token;
439        val = t.val;
440      }
441    }
442
443    public Tokenizer (SeekableStreamReader input, CompilationSourceFile file, ParserSession session, Report report)
444    {
445      this.source_file = file;
446      this.context = file.Compiler;
447      this.current_source = file.SourceFile;
448      this.identifiers = session.Identifiers;
449      this.id_builder = session.IDBuilder;
450      this.number_builder = session.NumberBuilder;
451      this.ltb = new LocatedTokenBuffer (session.LocatedTokens);
452      this.Report = report;
453
454      reader = input;
455
456      putback_char = -1;
457
458      xml_comment_buffer = new StringBuilder ();
459      doc_processing = context.Settings.DocumentationFile != null;
460
461      tab_size = context.Settings.TabSize;
462    }
463   
464    public void PushPosition ()
465    {
466      position_stack.Push (new Position (this));
467    }
468
469    public void PopPosition ()
470    {
471      Position p = position_stack.Pop ();
472
473      reader.Position = p.position;
474      ref_line = p.ref_line;
475      line = p.line;
476      col = p.col;
477      hidden_block_start = p.hidden;
478      putback_char = p.putback_char;
479      previous_col = p.previous_col;
480      ifstack = p.ifstack;
481      parsing_generic_less_than = p.parsing_generic_less_than;
482      current_token = p.current_token;
483      val = p.val;
484    }
485
486    // Do not reset the position, ignore it.
487    public void DiscardPosition ()
488    {
489      position_stack.Pop ();
490  }
491   
492    static void AddKeyword (string kw, int token)
493    {
494      keyword_strings.Add (kw);
495
496      AddKeyword (keywords, kw, token);
497}
498
499    static void AddPreprocessorKeyword (string kw, PreprocessorDirective directive)
500    {
501      AddKeyword (keywords_preprocessor, kw, directive);
502    }
503
504    static void AddKeyword<T> (KeywordEntry<T>[][] keywords, string kw, T token)
505    {
506      int length = kw.Length;
507      if (keywords[length] == null) {
508        keywords[length] = new KeywordEntry<T>['z' - '_' + 1];
509      }
510
511      int char_index = kw[0] - '_';
512      var kwe = keywords[length][char_index];
513      if (kwe == null) {
514        keywords[length][char_index] = new KeywordEntry<T> (kw, token);
515        return;
516      }
517
518      while (kwe.Next != null) {
519        kwe = kwe.Next;
520      }
521
522      kwe.Next = new KeywordEntry<T> (kw, token);
523    }
524
525    //
526    // Class initializer
527    //
528    static Tokenizer ()
529    {
530      keyword_strings = new HashSet<string> ();
531
532      // 11 is the length of the longest keyword for now
533      keywords = new KeywordEntry<int>[11][];
534
535      AddKeyword ("__arglist", Token.ARGLIST);
536      AddKeyword ("__makeref", Token.MAKEREF);
537      AddKeyword ("__reftype", Token.REFTYPE);
538      AddKeyword ("__refvalue", Token.REFVALUE);
539      AddKeyword ("abstract", Token.ABSTRACT);
540      AddKeyword ("as", Token.AS);
541      AddKeyword ("add", Token.ADD);
542      AddKeyword ("base", Token.BASE);
543      AddKeyword ("bool", Token.BOOL);
544      AddKeyword ("break", Token.BREAK);
545      AddKeyword ("byte", Token.BYTE);
546      AddKeyword ("case", Token.CASE);
547      AddKeyword ("catch", Token.CATCH);
548      AddKeyword ("char", Token.CHAR);
549      AddKeyword ("checked", Token.CHECKED);
550      AddKeyword ("class", Token.CLASS);
551      AddKeyword ("const", Token.CONST);
552      AddKeyword ("continue", Token.CONTINUE);
553      AddKeyword ("decimal", Token.DECIMAL);
554      AddKeyword ("default", Token.DEFAULT);
555      AddKeyword ("delegate", Token.DELEGATE);
556      AddKeyword ("do", Token.DO);
557      AddKeyword ("double", Token.DOUBLE);
558      AddKeyword ("else", Token.ELSE);
559      AddKeyword ("enum", Token.ENUM);
560      AddKeyword ("event", Token.EVENT);
561      AddKeyword ("explicit", Token.EXPLICIT);
562      AddKeyword ("extern", Token.EXTERN);
563      AddKeyword ("false", Token.FALSE);
564      AddKeyword ("finally", Token.FINALLY);
565      AddKeyword ("fixed", Token.FIXED);
566      AddKeyword ("float", Token.FLOAT);
567      AddKeyword ("for", Token.FOR);
568      AddKeyword ("foreach", Token.FOREACH);
569      AddKeyword ("goto", Token.GOTO);
570      AddKeyword ("get", Token.GET);
571      AddKeyword ("if", Token.IF);
572      AddKeyword ("implicit", Token.IMPLICIT);
573      AddKeyword ("in", Token.IN);
574      AddKeyword ("int", Token.INT);
575      AddKeyword ("interface", Token.INTERFACE);
576      AddKeyword ("internal", Token.INTERNAL);
577      AddKeyword ("is", Token.IS);
578      AddKeyword ("lock", Token.LOCK);
579      AddKeyword ("long", Token.LONG);
580      AddKeyword ("namespace", Token.NAMESPACE);
581      AddKeyword ("new", Token.NEW);
582      AddKeyword ("null", Token.NULL);
583      AddKeyword ("object", Token.OBJECT);
584      AddKeyword ("operator", Token.OPERATOR);
585      AddKeyword ("out", Token.OUT);
586      AddKeyword ("override", Token.OVERRIDE);
587      AddKeyword ("params", Token.PARAMS);
588      AddKeyword ("private", Token.PRIVATE);
589      AddKeyword ("protected", Token.PROTECTED);
590      AddKeyword ("public", Token.PUBLIC);
591      AddKeyword ("readonly", Token.READONLY);
592      AddKeyword ("ref", Token.REF);
593      AddKeyword ("remove", Token.REMOVE);
594      AddKeyword ("return", Token.RETURN);
595      AddKeyword ("sbyte", Token.SBYTE);
596      AddKeyword ("sealed", Token.SEALED);
597      AddKeyword ("set", Token.SET);
598      AddKeyword ("short", Token.SHORT);
599      AddKeyword ("sizeof", Token.SIZEOF);
600      AddKeyword ("stackalloc", Token.STACKALLOC);
601      AddKeyword ("static", Token.STATIC);
602      AddKeyword ("string", Token.STRING);
603      AddKeyword ("struct", Token.STRUCT);
604      AddKeyword ("switch", Token.SWITCH);
605      AddKeyword ("this", Token.THIS);
606      AddKeyword ("throw", Token.THROW);
607      AddKeyword ("true", Token.TRUE);
608      AddKeyword ("try", Token.TRY);
609      AddKeyword ("typeof", Token.TYPEOF);
610      AddKeyword ("uint", Token.UINT);
611      AddKeyword ("ulong", Token.ULONG);
612      AddKeyword ("unchecked", Token.UNCHECKED);
613      AddKeyword ("unsafe", Token.UNSAFE);
614      AddKeyword ("ushort", Token.USHORT);
615      AddKeyword ("using", Token.USING);
616      AddKeyword ("virtual", Token.VIRTUAL);
617      AddKeyword ("void", Token.VOID);
618      AddKeyword ("volatile", Token.VOLATILE);
619      AddKeyword ("while", Token.WHILE);
620      AddKeyword ("partial", Token.PARTIAL);
621      AddKeyword ("where", Token.WHERE);
622
623      // LINQ keywords
624      AddKeyword ("from", Token.FROM);
625      AddKeyword ("join", Token.JOIN);
626      AddKeyword ("on", Token.ON);
627      AddKeyword ("equals", Token.EQUALS);
628      AddKeyword ("select", Token.SELECT);
629      AddKeyword ("group", Token.GROUP);
630      AddKeyword ("by", Token.BY);
631      AddKeyword ("let", Token.LET);
632      AddKeyword ("orderby", Token.ORDERBY);
633      AddKeyword ("ascending", Token.ASCENDING);
634      AddKeyword ("descending", Token.DESCENDING);
635      AddKeyword ("into", Token.INTO);
636
637      // Contextual async keywords
638      AddKeyword ("async", Token.ASYNC);
639      AddKeyword ("await", Token.AWAIT);
640
641      keywords_preprocessor = new KeywordEntry<PreprocessorDirective>[10][];
642
643      AddPreprocessorKeyword ("region", PreprocessorDirective.Region);
644      AddPreprocessorKeyword ("endregion", PreprocessorDirective.Endregion);
645      AddPreprocessorKeyword ("if", PreprocessorDirective.If);
646      AddPreprocessorKeyword ("endif", PreprocessorDirective.Endif);
647      AddPreprocessorKeyword ("elif", PreprocessorDirective.Elif);
648      AddPreprocessorKeyword ("else", PreprocessorDirective.Else);
649      AddPreprocessorKeyword ("define", PreprocessorDirective.Define);
650      AddPreprocessorKeyword ("undef", PreprocessorDirective.Undef);
651      AddPreprocessorKeyword ("error", PreprocessorDirective.Error);
652      AddPreprocessorKeyword ("warning", PreprocessorDirective.Warning);
653      AddPreprocessorKeyword ("pragma", PreprocessorDirective.Pragma);
654      AddPreprocessorKeyword ("line", PreprocessorDirective.Line);
655
656      csharp_format_info = NumberFormatInfo.InvariantInfo;
657      styles = NumberStyles.Float;
658    }
659
660    int GetKeyword (char[] id, int id_len)
661    {
662      //
663      // Keywords are stored in an array of arrays grouped by their
664      // length and then by the first character
665      //
666      if (id_len >= keywords.Length || keywords [id_len] == null)
667        return -1;
668
669      int first_index = id [0] - '_';
670      if (first_index > 'z' - '_')
671        return -1;
672
673      var kwe = keywords [id_len] [first_index];
674      if (kwe == null)
675        return -1;
676
677      int res;
678      do {
679        res = kwe.Token;
680        for (int i = 1; i < id_len; ++i) {
681          if (id [i] != kwe.Value [i]) {
682            res = 0;
683            kwe = kwe.Next;
684            break;
685          }
686        }
687      } while (res == 0 && kwe != null);
688
689      if (res == 0)
690        return -1;
691
692      int next_token;
693      switch (res) {
694      case Token.GET:
695      case Token.SET:
696        if (!handle_get_set)
697          res = -1;
698        break;
699      case Token.REMOVE:
700      case Token.ADD:
701        if (!handle_remove_add)
702          res = -1;
703        break;
704      case Token.EXTERN:
705        if (parsing_declaration == 0)
706          res = Token.EXTERN_ALIAS;
707        break;
708      case Token.DEFAULT:
709        if (peek_token () == Token.COLON) {
710          token ();
711          res = Token.DEFAULT_COLON;
712        }
713        break;
714      case Token.WHERE:
715        if (!(handle_where && current_token != Token.COLON) && !query_parsing)
716          res = -1;
717        break;
718      case Token.FROM:
719        //
720        // A query expression is any expression that starts with `from identifier'
721        // followed by any token except ; , =
722        //
723        if (!query_parsing) {
724          if (lambda_arguments_parsing || parsing_block == 0) {
725            res = -1;
726            break;
727          }
728
729          PushPosition ();
730          // HACK: to disable generics micro-parser, because PushPosition does not
731          // store identifiers array
732          parsing_generic_less_than = 1;
733          switch (xtoken ()) {
734          case Token.IDENTIFIER:
735          case Token.INT:
736          case Token.BOOL:
737          case Token.BYTE:
738          case Token.CHAR:
739          case Token.DECIMAL:
740          case Token.DOUBLE:
741          case Token.FLOAT:
742          case Token.LONG:
743          case Token.OBJECT:
744          case Token.STRING:
745          case Token.UINT:
746          case Token.ULONG:
747            next_token = xtoken ();
748            if (next_token == Token.SEMICOLON || next_token == Token.COMMA || next_token == Token.EQUALS || next_token == Token.ASSIGN)
749              goto default;
750           
751            res = Token.FROM_FIRST;
752            query_parsing = true;
753            if (context.Settings.Version <= LanguageVersion.ISO_2)
754              Report.FeatureIsNotAvailable (context, Location, "query expressions");
755            break;
756          case Token.VOID:
757            Expression.Error_VoidInvalidInTheContext (Location, Report);
758            break;
759          default:
760            PopPosition ();
761            // HACK: A token is not a keyword so we need to restore identifiers buffer
762            // which has been overwritten before we grabbed the identifier
763            id_builder [0] = 'f'; id_builder [1] = 'r'; id_builder [2] = 'o'; id_builder [3] = 'm';
764            return -1;
765          }
766          PopPosition ();
767        }
768        break;
769      case Token.JOIN:
770      case Token.ON:
771      case Token.EQUALS:
772      case Token.SELECT:
773      case Token.GROUP:
774      case Token.BY:
775      case Token.LET:
776      case Token.ORDERBY:
777      case Token.ASCENDING:
778      case Token.DESCENDING:
779      case Token.INTO:
780        if (!query_parsing)
781          res = -1;
782        break;
783       
784      case Token.USING:
785      case Token.NAMESPACE:
786        // TODO: some explanation needed
787        check_incorrect_doc_comment ();
788        parsing_modifiers = false;
789        break;
790       
791      case Token.PARTIAL:
792        if (parsing_block > 0) {
793          res = -1;
794          break;
795        }
796
797        // Save current position and parse next token.
798        PushPosition ();
799
800        next_token = token ();
801        bool ok = (next_token == Token.CLASS) ||
802          (next_token == Token.STRUCT) ||
803          (next_token == Token.INTERFACE) ||
804          (next_token == Token.VOID);
805
806        PopPosition ();
807
808        if (ok) {
809          if (next_token == Token.VOID) {
810            if (context.Settings.Version <= LanguageVersion.ISO_2)
811              Report.FeatureIsNotAvailable (context, Location, "partial methods");
812          } else if (context.Settings.Version == LanguageVersion.ISO_1)
813            Report.FeatureIsNotAvailable (context, Location, "partial types");
814
815          return res;
816        }
817
818        if (next_token < Token.LAST_KEYWORD) {
819          Report.Error (267, Location,
820            "The `partial' modifier can be used only immediately before `class', `struct', `interface', or `void' keyword");
821          return token ();
822        }
823
824        // HACK: A token is not a keyword so we need to restore identifiers buffer
825        // which has been overwritten before we grabbed the identifier
826        id_builder[0] = 'p';
827        id_builder[1] = 'a';
828        id_builder[2] = 'r';
829        id_builder[3] = 't';
830        id_builder[4] = 'i';
831        id_builder[5] = 'a';
832        id_builder[6] = 'l';
833        res = -1;
834        break;
835
836      case Token.ASYNC:
837        if (parsing_modifiers) {
838          //
839          // Skip attributes section or constructor called async
840          //
841          if (parsing_attribute_section || peek_token () == Token.OPEN_PARENS) {
842            res = -1;
843          } else {
844            // async is keyword
845          }
846        } else if (parsing_block > 0) {
847          switch (peek_token ()) {
848          case Token.DELEGATE:
849          case Token.OPEN_PARENS_LAMBDA:
850            // async is keyword
851            break;
852          case Token.IDENTIFIER:
853            PushPosition ();
854            xtoken ();
855            if (xtoken () != Token.ARROW) {
856              PopPosition ();
857              goto default;
858            }
859
860            PopPosition ();
861            break;
862          default:
863            // peek_token could overwrite id_buffer
864            id_builder [0] = 'a'; id_builder [1] = 's'; id_builder [2] = 'y'; id_builder [3] = 'n'; id_builder [4] = 'c';
865            res = -1;
866            break;
867          }
868        } else {
869          res = -1;
870        }
871
872        if (res == Token.ASYNC && context.Settings.Version <= LanguageVersion.V_4) {
873          Report.FeatureIsNotAvailable (context, Location, "asynchronous functions");
874        }
875       
876        break;
877
878      case Token.AWAIT:
879        if (parsing_block == 0)
880          res = -1;
881
882        break;
883      }
884
885      return res;
886    }
887
888    static PreprocessorDirective GetPreprocessorDirective (char[] id, int id_len)
889    {
890      //
891      // Keywords are stored in an array of arrays grouped by their
892      // length and then by the first character
893      //
894      if (id_len >= keywords_preprocessor.Length || keywords_preprocessor[id_len] == null)
895        return PreprocessorDirective.Invalid;
896
897      int first_index = id[0] - '_';
898      if (first_index > 'z' - '_')
899        return PreprocessorDirective.Invalid;
900
901      var kwe = keywords_preprocessor[id_len][first_index];
902      if (kwe == null)
903        return PreprocessorDirective.Invalid;
904
905      PreprocessorDirective res = PreprocessorDirective.Invalid;
906      do {
907        res = kwe.Token;
908        for (int i = 1; i < id_len; ++i) {
909          if (id[i] != kwe.Value[i]) {
910            res = 0;
911            kwe = kwe.Next;
912            break;
913          }
914        }
915      } while (res == PreprocessorDirective.Invalid && kwe != null);
916
917      return res;
918    }
919
920    public Location Location {
921      get {
922        return new Location (current_source, ref_line, col);
923      }
924    }
925
926    static bool is_identifier_start_character (int c)
927    {
928      return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Char.IsLetter ((char)c);
929    }
930
931    static bool is_identifier_part_character (char c)
932    {
933      if (c >= 'a' && c <= 'z')
934        return true;
935
936      if (c >= 'A' && c <= 'Z')
937        return true;
938
939      if (c == '_' || (c >= '0' && c <= '9'))
940        return true;
941
942      if (c < 0x80)
943        return false;
944
945      return is_identifier_part_character_slow_part (c);
946    }
947
948    static bool is_identifier_part_character_slow_part (char c)
949    {
950      if (Char.IsLetter (c))
951        return true;
952
953      switch (Char.GetUnicodeCategory (c)) {
954        case UnicodeCategory.ConnectorPunctuation:
955
956        // combining-character: A Unicode character of classes Mn or Mc
957        case UnicodeCategory.NonSpacingMark:
958        case UnicodeCategory.SpacingCombiningMark:
959
960        // decimal-digit-character: A Unicode character of the class Nd
961        case UnicodeCategory.DecimalDigitNumber:
962        return true;
963      }
964
965      return false;
966    }
967
968    public static bool IsKeyword (string s)
969    {
970      return keyword_strings.Contains (s);
971    }
972
973    //
974    // Open parens micro parser. Detects both lambda and cast ambiguity.
975    // 
976    int TokenizeOpenParens ()
977    {
978      int ptoken;
979      current_token = -1;
980
981      int bracket_level = 0;
982      bool is_type = false;
983      bool can_be_type = false;
984     
985      while (true) {
986        ptoken = current_token;
987        token ();
988
989        switch (current_token) {
990        case Token.CLOSE_PARENS:
991          token ();
992         
993          //
994          // Expression inside parens is lambda, (int i) =>
995          //
996          if (current_token == Token.ARROW)
997            return Token.OPEN_PARENS_LAMBDA;
998
999          //
1000          // Expression inside parens is single type, (int[])
1001          //
1002          if (is_type) {
1003            if (current_token == Token.SEMICOLON)
1004              return Token.OPEN_PARENS;
1005
1006            return Token.OPEN_PARENS_CAST;
1007          }
1008
1009          //
1010          // Expression is possible cast, look at next token, (T)null
1011          //
1012          if (can_be_type) {
1013            switch (current_token) {
1014            case Token.OPEN_PARENS:
1015            case Token.BANG:
1016            case Token.TILDE:
1017            case Token.IDENTIFIER:
1018            case Token.LITERAL:
1019            case Token.BASE:
1020            case Token.CHECKED:
1021            case Token.DELEGATE:
1022            case Token.FALSE:
1023            case Token.FIXED:
1024            case Token.NEW:
1025            case Token.NULL:
1026            case Token.SIZEOF:
1027            case Token.THIS:
1028            case Token.THROW:
1029            case Token.TRUE:
1030            case Token.TYPEOF:
1031            case Token.UNCHECKED:
1032            case Token.UNSAFE:
1033            case Token.DEFAULT:
1034            case Token.AWAIT:
1035
1036            //
1037            // These can be part of a member access
1038            //
1039            case Token.INT:
1040            case Token.UINT:
1041            case Token.SHORT:
1042            case Token.USHORT:
1043            case Token.LONG:
1044            case Token.ULONG:
1045            case Token.DOUBLE:
1046            case Token.FLOAT:
1047            case Token.CHAR:
1048            case Token.BYTE:
1049            case Token.DECIMAL:
1050            case Token.BOOL:
1051              return Token.OPEN_PARENS_CAST;
1052            }
1053          }
1054          return Token.OPEN_PARENS;
1055         
1056        case Token.DOT:
1057        case Token.DOUBLE_COLON:
1058          if (ptoken != Token.IDENTIFIER && ptoken != Token.OP_GENERICS_GT)
1059            goto default;
1060
1061          continue;
1062
1063        case Token.IDENTIFIER:
1064        case Token.AWAIT:
1065          switch (ptoken) {
1066          case Token.DOT:
1067            if (bracket_level == 0) {
1068              is_type = false;
1069              can_be_type = true;
1070            }
1071
1072            continue;
1073          case Token.OP_GENERICS_LT:
1074          case Token.COMMA:
1075          case Token.DOUBLE_COLON:
1076          case -1:
1077            if (bracket_level == 0)
1078              can_be_type = true;
1079            continue;
1080          default:
1081            can_be_type = is_type = false;
1082            continue;
1083          }
1084
1085        case Token.OBJECT:
1086        case Token.STRING:
1087        case Token.BOOL:
1088        case Token.DECIMAL:
1089        case Token.FLOAT:
1090        case Token.DOUBLE:
1091        case Token.SBYTE:
1092        case Token.BYTE:
1093        case Token.SHORT:
1094        case Token.USHORT:
1095        case Token.INT:
1096        case Token.UINT:
1097        case Token.LONG:
1098        case Token.ULONG:
1099        case Token.CHAR:
1100        case Token.VOID:
1101          if (bracket_level == 0)
1102            is_type = true;
1103          continue;
1104
1105        case Token.COMMA:
1106          if (bracket_level == 0) {
1107            bracket_level = 100;
1108            can_be_type = is_type = false;
1109          }
1110          continue;
1111
1112        case Token.OP_GENERICS_LT:
1113        case Token.OPEN_BRACKET:
1114          if (bracket_level++ == 0)
1115            is_type = true;
1116          continue;
1117
1118        case Token.OP_GENERICS_GT:
1119        case Token.CLOSE_BRACKET:
1120          --bracket_level;
1121          continue;
1122
1123        case Token.INTERR_NULLABLE:
1124        case Token.STAR:
1125          if (bracket_level == 0)
1126            is_type = true;
1127          continue;
1128
1129        case Token.REF:
1130        case Token.OUT:
1131          can_be_type = is_type = false;
1132          continue;
1133
1134        default:
1135          return Token.OPEN_PARENS;
1136        }
1137      }
1138    }
1139
1140    public static bool IsValidIdentifier (string s)
1141    {
1142      if (s == null || s.Length == 0)
1143        return false;
1144
1145      if (!is_identifier_start_character (s [0]))
1146        return false;
1147     
1148      for (int i = 1; i < s.Length; i ++)
1149        if (! is_identifier_part_character (s [i]))
1150          return false;
1151     
1152      return true;
1153    }
1154
1155    Stack<List<Location>> genericDimensionLocations = new Stack<List<Location>> ();
1156
1157    public List<Location> GenericDimensionLocations {
1158      get {
1159        if (genericDimensionLocations.Count == 0)
1160          return null;
1161        return genericDimensionLocations.Pop ();
1162      }
1163    }
1164
1165    bool parse_less_than (ref int genericDimension)
1166    {
1167      genericDimensionLocations.Push (new List<Location> ());
1168      genericDimensionLocations.Peek ().Add (Location);
1169      start:
1170      int the_token = token ();
1171      if (the_token == Token.OPEN_BRACKET) {
1172        while (true) {
1173          the_token = token ();
1174          if (the_token == Token.EOF)
1175            return true;
1176
1177          if (the_token == Token.CLOSE_BRACKET)
1178            break;
1179        }
1180        the_token = token ();
1181      } else if (the_token == Token.IN || the_token == Token.OUT) {
1182        the_token = token ();
1183      }
1184      switch (the_token) {
1185      case Token.IDENTIFIER:
1186      case Token.OBJECT:
1187      case Token.STRING:
1188      case Token.BOOL:
1189      case Token.DECIMAL:
1190      case Token.FLOAT:
1191      case Token.DOUBLE:
1192      case Token.SBYTE:
1193      case Token.BYTE:
1194      case Token.SHORT:
1195      case Token.USHORT:
1196      case Token.INT:
1197      case Token.UINT:
1198      case Token.LONG:
1199      case Token.ULONG:
1200      case Token.CHAR:
1201      case Token.VOID:
1202        break;
1203      case Token.OP_GENERICS_GT:
1204        genericDimension = 1;
1205        genericDimensionLocations.Peek ().Add (Location);
1206        return true;
1207      case Token.IN:
1208      case Token.OUT:
1209        genericDimensionLocations.Pop ();
1210        return true;
1211      case Token.COMMA:
1212        do {
1213          ++genericDimension;
1214          if (genericDimensionLocations.Count > 0)
1215            genericDimensionLocations.Peek ().Add (Location);
1216          the_token = token ();
1217        } while (the_token == Token.COMMA);
1218
1219        if (the_token == Token.OP_GENERICS_GT) {
1220          ++genericDimension;
1221          if (genericDimensionLocations.Count > 0)
1222            genericDimensionLocations.Peek ().Add (Location);
1223          return true;
1224        }
1225
1226        genericDimensionLocations.Pop ();
1227        return false;
1228      default:
1229        genericDimensionLocations.Pop ();
1230        return false;
1231      }
1232      again:
1233      the_token = token ();
1234      if (the_token == Token.OP_GENERICS_GT) {
1235        genericDimensionLocations.Peek ().Add (Location);
1236        return true;
1237      }
1238      else if (the_token == Token.COMMA || the_token == Token.DOT || the_token == Token.DOUBLE_COLON)
1239        goto start;
1240      else if (the_token == Token.INTERR_NULLABLE || the_token == Token.STAR)
1241        goto again;
1242      else if (the_token == Token.OP_GENERICS_LT) {
1243        if (!parse_less_than (ref genericDimension)) {
1244          genericDimensionLocations.Pop ();
1245          return false;
1246        }
1247        goto again;
1248      } else if (the_token == Token.OPEN_BRACKET) {
1249        rank_specifiers:
1250        the_token = token ();
1251        if (the_token == Token.CLOSE_BRACKET)
1252          goto again;
1253        else if (the_token == Token.COMMA)
1254          goto rank_specifiers;
1255        genericDimensionLocations.Pop ();
1256        return false;
1257      }
1258
1259      genericDimensionLocations.Pop ();
1260      return false;
1261    }
1262
1263
1264    public int peek_token ()
1265    {
1266      int the_token;
1267
1268      PushPosition ();
1269      sbag.Suppress = true;
1270      the_token = token ();
1271      sbag.Suppress = false;
1272      PopPosition ();
1273     
1274      return the_token;
1275    }
1276         
1277    //
1278    // Tonizes `?' using custom disambiguous rules to return one
1279    // of following tokens: INTERR_NULLABLE, OP_COALESCING, INTERR
1280    //
1281    // Tricky expression looks like:
1282    //
1283    // Foo ? a = x ? b : c;
1284    //
1285    int TokenizePossibleNullableType ()
1286    {
1287      if (parsing_block == 0 || parsing_type > 0)
1288        return Token.INTERR_NULLABLE;
1289
1290      int d = peek_char ();
1291      if (d == '?') {
1292        get_char ();
1293        return Token.OP_COALESCING;
1294      }
1295
1296      if (d == '.') {
1297        return Token.INTERR_OPERATOR;
1298      }
1299
1300      if (d != ' ') {
1301        if (d == ',' || d == ';' || d == '>')
1302          return Token.INTERR_NULLABLE;
1303        if (d == '*' || (d >= '0' && d <= '9'))
1304          return Token.INTERR;
1305      }
1306
1307      PushPosition ();
1308      current_token = Token.NONE;
1309      int next_token;
1310      int parens = 0;
1311      int generics = 0;
1312
1313      var nt = xtoken ();
1314      switch (nt) {
1315      case Token.DOT:
1316      case Token.OPEN_BRACKET_EXPR:
1317        next_token = Token.INTERR_OPERATOR;
1318        break;
1319      case Token.LITERAL:
1320      case Token.TRUE:
1321      case Token.FALSE:
1322      case Token.NULL:
1323      case Token.THIS:
1324      case Token.NEW:
1325        next_token = Token.INTERR;
1326        break;
1327       
1328      case Token.SEMICOLON:
1329      case Token.COMMA:
1330      case Token.CLOSE_PARENS:
1331      case Token.OPEN_BRACKET:
1332      case Token.OP_GENERICS_GT:
1333      case Token.INTERR:
1334      case Token.OP_COALESCING:
1335      case Token.COLON:
1336        next_token = Token.INTERR_NULLABLE;
1337        break;
1338
1339      case Token.OPEN_PARENS:
1340      case Token.OPEN_PARENS_CAST:
1341      case Token.OPEN_PARENS_LAMBDA:
1342        next_token = -1;
1343        ++parens;
1344        break;
1345
1346      case Token.OP_GENERICS_LT:
1347      case Token.OP_GENERICS_LT_DECL:
1348      case Token.GENERIC_DIMENSION:
1349        next_token = -1;
1350        ++generics;
1351        break;
1352
1353      default:
1354        next_token = -1;
1355        break;
1356      }
1357
1358      if (next_token == -1) {
1359        switch (xtoken ()) {
1360        case Token.COMMA:
1361        case Token.SEMICOLON:
1362        case Token.OPEN_BRACE:
1363        case Token.IN:
1364          next_token = Token.INTERR_NULLABLE;
1365          break;
1366         
1367        case Token.COLON:
1368          next_token = Token.INTERR;
1369          break;
1370
1371        case Token.OPEN_PARENS:
1372        case Token.OPEN_PARENS_CAST:
1373        case Token.OPEN_PARENS_LAMBDA:
1374          ++parens;
1375          goto default;
1376
1377        case Token.CLOSE_PARENS:
1378          --parens;
1379          goto default;
1380
1381        case Token.OP_GENERICS_LT:
1382        case Token.OP_GENERICS_LT_DECL:
1383        case Token.GENERIC_DIMENSION:
1384          ++generics;
1385          goto default;
1386
1387        default:
1388          int ntoken;
1389          int interrs = 1;
1390          int colons = 0;
1391          int braces = 0;
1392          int brackets = 0;
1393          //
1394          // All shorcuts failed, do it hard way
1395          //
1396          while ((ntoken = xtoken ()) != Token.EOF) {
1397            switch (ntoken) {
1398            case Token.OPEN_BRACE:
1399              ++braces;
1400              continue;
1401            case Token.OPEN_PARENS:
1402            case Token.OPEN_PARENS_CAST:
1403            case Token.OPEN_PARENS_LAMBDA:
1404              ++parens;
1405              continue;
1406            case Token.CLOSE_BRACE:
1407              --braces;
1408              continue;
1409            case Token.OP_GENERICS_LT:
1410            case Token.OP_GENERICS_LT_DECL:
1411            case Token.GENERIC_DIMENSION:
1412              ++generics;
1413              continue;
1414            case Token.OPEN_BRACKET:
1415            case Token.OPEN_BRACKET_EXPR:
1416              ++brackets;
1417              continue;
1418            case Token.CLOSE_BRACKET:
1419              --brackets;
1420              continue;
1421            case Token.CLOSE_PARENS:
1422              if (parens > 0) {
1423                --parens;
1424                continue;
1425              }
1426
1427              PopPosition ();
1428              return Token.INTERR_NULLABLE;
1429
1430            case Token.OP_GENERICS_GT:
1431              if (generics > 0) {
1432                --generics;
1433                continue;
1434              }
1435
1436              PopPosition ();
1437              return Token.INTERR_NULLABLE;
1438            }
1439
1440            if (braces != 0)
1441              continue;
1442
1443            if (ntoken == Token.SEMICOLON)
1444              break;
1445
1446            if (parens != 0)
1447              continue;
1448
1449            if (ntoken == Token.COMMA) {
1450              if (generics != 0 || brackets != 0)
1451                continue;
1452
1453              PopPosition ();
1454              return Token.INTERR_NULLABLE;
1455            }
1456           
1457            if (ntoken == Token.COLON) {
1458              if (++colons == interrs)
1459                break;
1460              continue;
1461            }
1462           
1463            if (ntoken == Token.INTERR) {
1464              ++interrs;
1465              continue;
1466            }
1467          }
1468         
1469          next_token = colons != interrs && braces == 0 ? Token.INTERR_NULLABLE : Token.INTERR;
1470          break;
1471        }
1472      }
1473     
1474      PopPosition ();
1475      return next_token;
1476    }
1477
1478    bool decimal_digits (int c)
1479    {
1480      int d;
1481      bool seen_digits = false;
1482     
1483      if (c != -1){
1484        if (number_pos == MaxNumberLength)
1485          Error_NumericConstantTooLong ();
1486        number_builder [number_pos++] = (char) c;
1487      }
1488     
1489      //
1490      // We use peek_char2, because decimal_digits needs to do a
1491      // 2-character look-ahead (5.ToString for example).
1492      //
1493      while ((d = peek_char2 ()) != -1){
1494        if (d >= '0' && d <= '9'){
1495          if (number_pos == MaxNumberLength)
1496            Error_NumericConstantTooLong ();
1497          number_builder [number_pos++] = (char) d;
1498          get_char ();
1499          seen_digits = true;
1500        } else
1501          break;
1502      }
1503     
1504      return seen_digits;
1505    }
1506
1507    static bool is_hex (int e)
1508    {
1509      return (e >= '0' && e <= '9') || (e >= 'A' && e <= 'F') || (e >= 'a' && e <= 'f');
1510    }
1511
1512    static TypeCode real_type_suffix (int c)
1513    {
1514      switch (c){
1515      case 'F': case 'f':
1516        return TypeCode.Single;
1517      case 'D': case 'd':
1518        return TypeCode.Double;
1519      case 'M': case 'm':
1520        return TypeCode.Decimal;
1521      default:
1522        return TypeCode.Empty;
1523      }
1524    }
1525
1526    ILiteralConstant integer_type_suffix (ulong ul, int c, Location loc)
1527    {
1528      bool is_unsigned = false;
1529      bool is_long = false;
1530
1531      if (c != -1){
1532        bool scanning = true;
1533        do {
1534          switch (c){
1535          case 'U': case 'u':
1536            if (is_unsigned)
1537              scanning = false;
1538            is_unsigned = true;
1539            get_char ();
1540            break;
1541
1542          case 'l':
1543            if (!is_unsigned){
1544              //
1545              // if we have not seen anything in between
1546              // report this error
1547              //
1548              Report.Warning (78, 4, Location, "The `l' suffix is easily confused with the digit `1' (use `L' for clarity)");
1549            }
1550
1551            goto case 'L';
1552
1553          case 'L':
1554            if (is_long)
1555              scanning = false;
1556            is_long = true;
1557            get_char ();
1558            break;
1559           
1560          default:
1561            scanning = false;
1562            break;
1563          }
1564          c = peek_char ();
1565        } while (scanning);
1566      }
1567
1568      if (is_long && is_unsigned){
1569        return new ULongLiteral (context.BuiltinTypes, ul, loc);
1570      }
1571     
1572      if (is_unsigned){
1573        // uint if possible, or ulong else.
1574
1575        if ((ul & 0xffffffff00000000) == 0)
1576          return new UIntLiteral (context.BuiltinTypes, (uint) ul, loc);
1577        else
1578          return new ULongLiteral (context.BuiltinTypes, ul, loc);
1579      } else if (is_long){
1580        // long if possible, ulong otherwise
1581        if ((ul & 0x8000000000000000) != 0)
1582          return new ULongLiteral (context.BuiltinTypes, ul, loc);
1583        else
1584          return new LongLiteral (context.BuiltinTypes, (long) ul, loc);
1585      } else {
1586        // int, uint, long or ulong in that order
1587        if ((ul & 0xffffffff00000000) == 0){
1588          uint ui = (uint) ul;
1589         
1590          if ((ui & 0x80000000) != 0)
1591            return new UIntLiteral (context.BuiltinTypes, ui, loc);
1592          else
1593            return new IntLiteral (context.BuiltinTypes, (int) ui, loc);
1594        } else {
1595          if ((ul & 0x8000000000000000) != 0)
1596            return new ULongLiteral (context.BuiltinTypes, ul, loc);
1597          else
1598            return new LongLiteral (context.BuiltinTypes, (long) ul, loc);
1599        }
1600      }
1601    }
1602       
1603    //
1604    // given `c' as the next char in the input decide whether
1605    // we need to convert to a special type, and then choose
1606    // the best representation for the integer
1607    //
1608    ILiteralConstant adjust_int (int c, Location loc)
1609    {
1610      try {
1611        if (number_pos > 9){
1612          ulong ul = (uint) (number_builder [0] - '0');
1613
1614          for (int i = 1; i < number_pos; i++){
1615            ul = checked ((ul * 10) + ((uint)(number_builder [i] - '0')));
1616          }
1617
1618          return integer_type_suffix (ul, c, loc);
1619        } else {
1620          uint ui = (uint) (number_builder [0] - '0');
1621
1622          for (int i = 1; i < number_pos; i++){
1623            ui = checked ((ui * 10) + ((uint)(number_builder [i] - '0')));
1624          }
1625
1626          return integer_type_suffix (ui, c, loc);
1627        }
1628      } catch (OverflowException) {
1629        Error_NumericConstantTooLong ();
1630        return new IntLiteral (context.BuiltinTypes, 0, loc);
1631      }
1632      catch (FormatException) {
1633        Report.Error (1013, Location, "Invalid number");
1634        return new IntLiteral (context.BuiltinTypes, 0, loc);
1635      }
1636    }
1637   
1638    ILiteralConstant adjust_real (TypeCode t, Location loc)
1639    {
1640      string s = new string (number_builder, 0, number_pos);
1641      const string error_details = "Floating-point constant is outside the range of type `{0}'";
1642
1643      switch (t){
1644      case TypeCode.Decimal:
1645        try {
1646          return new DecimalLiteral (context.BuiltinTypes, decimal.Parse (s, styles, csharp_format_info), loc);
1647        } catch (OverflowException) {
1648          Report.Error (594, Location, error_details, "decimal");
1649          return new DecimalLiteral (context.BuiltinTypes, 0, loc);
1650        }
1651      case TypeCode.Single:
1652        try {
1653          return new FloatLiteral (context.BuiltinTypes, float.Parse (s, styles, csharp_format_info), loc);
1654        } catch (OverflowException) {
1655          Report.Error (594, Location, error_details, "float");
1656          return new FloatLiteral (context.BuiltinTypes, 0, loc);
1657        }
1658      default:
1659        try {
1660          return new DoubleLiteral (context.BuiltinTypes, double.Parse (s, styles, csharp_format_info), loc);
1661        } catch (OverflowException) {
1662          Report.Error (594, loc, error_details, "double");
1663          return new DoubleLiteral (context.BuiltinTypes, 0, loc);
1664        }
1665      }
1666    }
1667
1668    ILiteralConstant handle_hex (Location loc)
1669    {
1670      int d;
1671      ulong ul;
1672     
1673      get_char ();
1674      while ((d = peek_char ()) != -1){
1675        if (is_hex (d)){
1676          number_builder [number_pos++] = (char) d;
1677          get_char ();
1678        } else
1679          break;
1680      }
1681     
1682      string s = new String (number_builder, 0, number_pos);
1683
1684      try {
1685        if (number_pos <= 8)
1686          ul = System.UInt32.Parse (s, NumberStyles.HexNumber);
1687        else
1688          ul = System.UInt64.Parse (s, NumberStyles.HexNumber);
1689
1690        return integer_type_suffix (ul, peek_char (), loc);
1691      } catch (OverflowException){
1692        Error_NumericConstantTooLong ();
1693        return new IntLiteral (context.BuiltinTypes, 0, loc);
1694      }
1695      catch (FormatException) {
1696        Report.Error (1013, Location, "Invalid number");
1697        return new IntLiteral (context.BuiltinTypes, 0, loc);
1698      }
1699    }
1700
1701    //
1702    // Invoked if we know we have .digits or digits
1703    //
1704    int is_number (int c, bool dotLead)
1705    {
1706      ILiteralConstant res;
1707
1708#if FULL_AST
1709      int read_start = reader.Position - 1;
1710      if (dotLead) {
1711        //
1712        // Caller did peek_char
1713        //
1714        --read_start;
1715      }
1716#endif
1717      number_pos = 0;
1718      var loc = Location;
1719      bool hasLeadingDot = c == '.';
1720
1721      if (!dotLead){
1722        if (c == '0'){
1723          int peek = peek_char ();
1724
1725          if (peek == 'x' || peek == 'X') {
1726            val = res = handle_hex (loc);
1727#if FULL_AST
1728            res.ParsedValue = reader.ReadChars (read_start, reader.Position - 1);
1729#endif
1730
1731            return Token.LITERAL;
1732          }
1733        }
1734        decimal_digits (c);
1735        c = peek_char ();
1736      }
1737
1738      //
1739      // We need to handle the case of
1740      // "1.1" vs "1.string" (LITERAL_FLOAT vs NUMBER DOT IDENTIFIER)
1741      //
1742      bool is_real = false;
1743      if (c == '.'){
1744        if (!dotLead)
1745          get_char ();
1746
1747        if (decimal_digits ('.')){
1748          is_real = true;
1749          c = peek_char ();
1750        } else {
1751          putback ('.');
1752          number_pos--;
1753          val = res = adjust_int (-1, loc);
1754#if FULL_AST
1755          res.ParsedValue = reader.ReadChars (read_start, reader.Position - 1);
1756#endif
1757          return Token.LITERAL;
1758        }
1759      }
1760     
1761      if (c == 'e' || c == 'E'){
1762        is_real = true;
1763        get_char ();
1764        if (number_pos == MaxNumberLength)
1765          Error_NumericConstantTooLong ();
1766        number_builder [number_pos++] = (char) c;
1767        c = get_char ();
1768       
1769        if (c == '+'){
1770          if (number_pos == MaxNumberLength)
1771            Error_NumericConstantTooLong ();
1772          number_builder [number_pos++] = '+';
1773          c = -1;
1774        } else if (c == '-') {
1775          if (number_pos == MaxNumberLength)
1776            Error_NumericConstantTooLong ();
1777          number_builder [number_pos++] = '-';
1778          c = -1;
1779        } else {
1780          if (number_pos == MaxNumberLength)
1781            Error_NumericConstantTooLong ();
1782          number_builder [number_pos++] = '+';
1783        }
1784         
1785        decimal_digits (c);
1786        c = peek_char ();
1787      }
1788
1789      var type = real_type_suffix (c);
1790      if (type == TypeCode.Empty && !is_real) {
1791        res = adjust_int (c, loc);
1792      } else {
1793        is_real = true;
1794
1795        if (type != TypeCode.Empty) {
1796          get_char ();
1797        }
1798
1799        res = adjust_real (type, loc);
1800      }
1801
1802      val = res;
1803#if FULL_AST
1804      var chars = reader.ReadChars (read_start, reader.Position - (type == TypeCode.Empty && c > 0 ? 1 : 0));
1805      if (chars[chars.Length - 1] == '\r')
1806        Array.Resize (ref chars, chars.Length - 1);
1807      res.ParsedValue = chars;
1808#endif
1809
1810      return Token.LITERAL;
1811    }
1812
1813    //
1814    // Accepts exactly count (4 or 8) hex, no more no less
1815    //
1816    int getHex (int count, out int surrogate, out bool error)
1817    {
1818      int i;
1819      int total = 0;
1820      int c;
1821      int top = count != -1 ? count : 4;
1822     
1823      get_char ();
1824      error = false;
1825      surrogate = 0;
1826      for (i = 0; i < top; i++){
1827        c = get_char ();
1828
1829        if (c >= '0' && c <= '9')
1830          c = (int) c - (int) '0';
1831        else if (c >= 'A' && c <= 'F')
1832          c = (int) c - (int) 'A' + 10;
1833        else if (c >= 'a' && c <= 'f')
1834          c = (int) c - (int) 'a' + 10;
1835        else {
1836          error = true;
1837          return 0;
1838        }
1839       
1840        total = (total * 16) + c;
1841        if (count == -1){
1842          int p = peek_char ();
1843          if (p == -1)
1844            break;
1845          if (!is_hex ((char)p))
1846            break;
1847        }
1848      }
1849
1850      if (top == 8) {
1851        if (total > 0x0010FFFF) {
1852          error = true;
1853          return 0;
1854        }
1855
1856        if (total >= 0x00010000) {
1857          surrogate = ((total - 0x00010000) % 0x0400 + 0xDC00);         
1858          total = ((total - 0x00010000) / 0x0400 + 0xD800);
1859        }
1860      }
1861
1862      return total;
1863    }
1864
1865    int escape (int c, out int surrogate)
1866    {
1867      bool error;
1868      int d;
1869      int v;
1870
1871      d = peek_char ();
1872      if (c != '\\') {
1873        surrogate = 0;
1874        return c;
1875      }
1876     
1877      switch (d){
1878      case 'a':
1879        v = '\a'; break;
1880      case 'b':
1881        v = '\b'; break;
1882      case 'n':
1883        v = '\n'; break;
1884      case 't':
1885        v = '\t'; break;
1886      case 'v':
1887        v = '\v'; break;
1888      case 'r':
1889        v = '\r'; break;
1890      case '\\':
1891        v = '\\'; break;
1892      case 'f':
1893        v = '\f'; break;
1894      case '0':
1895        v = 0; break;
1896      case '"':
1897        v = '"'; break;
1898      case '\'':
1899        v = '\''; break;
1900      case 'x':
1901        v = getHex (-1, out surrogate, out error);
1902        if (error)
1903          goto default;
1904        return v;
1905      case 'u':
1906      case 'U':
1907        return EscapeUnicode (d, out surrogate);
1908      default:
1909        surrogate = 0;
1910        Report.Error (1009, Location, "Unrecognized escape sequence `\\{0}'", ((char)d).ToString ());
1911        return d;
1912      }
1913
1914      get_char ();
1915      surrogate = 0;
1916      return v;
1917    }
1918
1919    int EscapeUnicode (int ch, out int surrogate)
1920    {
1921      bool error;
1922      if (ch == 'U') {
1923        ch = getHex (8, out surrogate, out error);
1924      } else {
1925        ch = getHex (4, out surrogate, out error);
1926      }
1927
1928      if (error)
1929        Report.Error (1009, Location, "Unrecognized escape sequence");
1930
1931      return ch;
1932    }
1933
1934    int get_char ()
1935    {
1936      int x;
1937      if (putback_char != -1) {
1938        x = putback_char;
1939        putback_char = -1;
1940      } else {
1941        x = reader.Read ();
1942      }
1943     
1944      if (x <= 13) {
1945        if (x == '\r') {
1946          if (peek_char () == '\n') {
1947            putback_char = -1;
1948            advance_line (SpecialsBag.NewLine.Windows);
1949          } else {
1950            advance_line (SpecialsBag.NewLine.Unix);
1951          }
1952
1953          x = '\n';
1954        } else if (x == '\n') {
1955          advance_line (SpecialsBag.NewLine.Unix);
1956        } else {
1957          col++;
1958        }
1959      } else if (x >= UnicodeLS && x <= UnicodePS) {
1960        advance_line (SpecialsBag.NewLine.Unix);
1961      } else {
1962        col++;
1963      }
1964
1965      return x;
1966    }
1967
1968    bool recordNewLine = true;
1969    void advance_line (SpecialsBag.NewLine newLine)
1970    {
1971      if (recordNewLine)
1972        sbag.AddNewLine (line, col, newLine);
1973      line++;
1974      ref_line++;
1975      previous_col = col;
1976      col = 0;
1977      startsLine = true;
1978    }
1979
1980    int peek_char ()
1981    {
1982      if (putback_char == -1)
1983        putback_char = reader.Read ();
1984      return putback_char;
1985    }
1986
1987    int peek_char2 ()
1988    {
1989      if (putback_char != -1)
1990        return putback_char;
1991      return reader.Peek ();
1992    }
1993   
1994    public void putback (int c)
1995    {
1996      if (putback_char != -1) {
1997        throw new InternalErrorException (string.Format ("Secondary putback [{0}] putting back [{1}] is not allowed", (char)putback_char, (char) c), Location);
1998      }
1999
2000      if (c == '\n' || col == 0 || (c >= UnicodeLS && c <= UnicodePS)) {
2001        // It won't happen though.
2002        line--;
2003        ref_line--;
2004        col = previous_col;
2005      }
2006      else
2007        col--;
2008      putback_char = c;
2009    }
2010
2011    public bool advance ()
2012    {
2013      return peek_char () != -1 || CompleteOnEOF;
2014    }
2015
2016    public Object Value {
2017      get {
2018        return val;
2019      }
2020    }
2021
2022    public Object value ()
2023    {
2024      return val;
2025    }
2026
2027    public int token ()
2028    {
2029      current_token = xtoken ();
2030      return current_token;
2031    }
2032
2033    int TokenizePreprocessorIdentifier (out int c)
2034    {
2035      int startCol, endLine, endCol;
2036      return TokenizePreprocessorIdentifier (out c, out startCol, out endLine, out endCol);
2037    }
2038
2039    int TokenizePreprocessorIdentifier (out int c, out int startCol, out int endLine, out int endCol)
2040    {
2041      // skip over white space
2042      do {
2043        endLine = line;
2044        endCol = col;
2045        c = get_char ();
2046      } while (c == ' ' || c == '\t');
2047      startCol = col;
2048      int pos = 0;
2049      while (c != -1 && c >= 'a' && c <= 'z') {
2050        id_builder[pos++] = (char) c;
2051        endCol = col + 1;
2052        c = get_char ();
2053        if (c == '\\') {
2054          int peek = peek_char ();
2055          if (peek == 'U' || peek == 'u') {
2056            int surrogate;
2057            c = EscapeUnicode (c, out surrogate);
2058            if (surrogate != 0) {
2059              if (is_identifier_part_character ((char) c)) {
2060                id_builder[pos++] = (char) c;
2061              }
2062              c = surrogate;
2063            }
2064          }
2065        }
2066      }
2067
2068      return pos;
2069    }
2070
2071    PreprocessorDirective get_cmd_arg (out string arg)
2072    {
2073      int c;
2074      int startLine = line, startCol = col;
2075
2076      tokens_seen = false;
2077      arg = "";
2078     
2079      int startCol2, endLine, endCol;
2080      var cmd = GetPreprocessorDirective (id_builder, TokenizePreprocessorIdentifier (out c, out startCol2, out endLine, out endCol));
2081     
2082      if ((cmd & PreprocessorDirective.CustomArgumentsParsing) != 0) {
2083        if (position_stack.Count == 0)
2084          sbag.AddPreProcessorDirective (startLine, startCol, line, col, cmd, null);
2085        return cmd;
2086      }
2087     
2088      // skip over white space
2089      while (c == ' ' || c == '\t') {
2090        c = get_char ();
2091      }
2092      int has_identifier_argument = (int)(cmd & PreprocessorDirective.RequiresArgument);
2093      int pos = 0;
2094      while (c != -1 && c != '\n' && c != UnicodeLS && c != UnicodePS) {
2095        if (c == '\\' && has_identifier_argument >= 0) {
2096          if (has_identifier_argument != 0) {
2097            has_identifier_argument = 1;
2098
2099            int peek = peek_char ();
2100            if (peek == 'U' || peek == 'u') {
2101              int surrogate;
2102              c = EscapeUnicode (c, out surrogate);
2103              if (surrogate != 0) {
2104                if (is_identifier_part_character ((char)c)) {
2105                  if (pos == value_builder.Length)
2106                    Array.Resize (ref value_builder, pos * 2);
2107
2108                  value_builder [pos++] = (char)c;
2109                }
2110                c = surrogate;
2111              }
2112            }
2113          } else {
2114            has_identifier_argument = -1;
2115          }
2116        } else if (c == '/' && peek_char () == '/') {
2117          //
2118          // Eat single-line comments
2119          //
2120          get_char ();
2121          ReadToEndOfLine ();
2122          break;
2123        }
2124       
2125        endLine = line;
2126        endCol = col + 1;
2127       
2128        if (pos == value_builder.Length)
2129          Array.Resize (ref value_builder, pos * 2);
2130
2131        value_builder[pos++] = (char) c;
2132        c = get_char ();
2133      }
2134
2135      if (pos != 0) {
2136        if (pos > MaxIdentifierLength)
2137          arg = new string (value_builder, 0, pos);
2138        else
2139          arg = InternIdentifier (value_builder, pos);
2140
2141        // Eat any trailing whitespaces
2142        arg = arg.Trim (simple_whitespaces);
2143      }
2144      if (position_stack.Count == 0)
2145        sbag.AddPreProcessorDirective (startLine, startCol, endLine, endCol, cmd, arg);
2146
2147      return cmd;
2148    }
2149
2150    //
2151    // Handles the #line directive
2152    //
2153    bool PreProcessLine ()
2154    {
2155      Location loc = Location;
2156      #if FULL_AST
2157      var lineDirective = sbag.GetCurrentLineProcessorDirective();
2158      #endif
2159
2160      int c;
2161
2162      int length = TokenizePreprocessorIdentifier (out c);
2163      if (length == line_default.Length) {
2164        if (!IsTokenIdentifierEqual (line_default))
2165          return false;
2166
2167        current_source = source_file.SourceFile;
2168        if (!hidden_block_start.IsNull) {
2169          current_source.RegisterHiddenScope (hidden_block_start, loc);
2170          hidden_block_start = Location.Null;
2171        }
2172
2173        //ref_line = line;
2174        return true;
2175      }
2176
2177      if (length == line_hidden.Length) {
2178        if (!IsTokenIdentifierEqual (line_hidden))
2179          return false;
2180
2181        if (hidden_block_start.IsNull)
2182          hidden_block_start = loc;
2183
2184        return true;
2185      }
2186
2187      if (length != 0 || c < '0' || c > '9') {
2188        //
2189        // Eat any remaining characters to continue parsing on next line
2190        //
2191        ReadToEndOfLine ();
2192        return false;
2193      }
2194
2195      int new_line = TokenizeNumber (c);
2196      if (new_line < 1) {
2197        //
2198        // Eat any remaining characters to continue parsing on next line
2199        //
2200        ReadToEndOfLine ();
2201        return new_line != 0;
2202      }
2203      #if FULL_AST
2204      lineDirective.LineNumber = new_line;
2205      #endif
2206
2207      c = get_char ();
2208      if (c == ' ') {
2209        // skip over white space
2210        do {
2211          c = get_char ();
2212        } while (c == ' ' || c == '\t');
2213      } else if (c == '"') {
2214        c = 0;
2215      }
2216
2217      if (c != '\n' && c != '/' && c != '"' && c != UnicodeLS && c != UnicodePS) {
2218        //
2219        // Eat any remaining characters to continue parsing on next line
2220        //
2221        ReadToEndOfLine ();
2222
2223        Report.Error (1578, loc, "Filename, single-line comment or end-of-line expected");
2224        return true;
2225      }
2226
2227      string new_file_name = null;
2228      if (c == '"') {
2229        new_file_name = TokenizeFileName (ref c);
2230        #if FULL_AST
2231        lineDirective.FileName = new_file_name;
2232        #endif
2233
2234        // skip over white space
2235        while (c == ' ' || c == '\t') {
2236          c = get_char ();
2237        }
2238      }
2239
2240      if (c == '\n' || c == UnicodeLS || c == UnicodePS) {
2241
2242      } else if (c == '/') {
2243        ReadSingleLineComment ();
2244      } else {
2245        //
2246        // Eat any remaining characters to continue parsing on next line
2247        //
2248        ReadToEndOfLine ();
2249
2250        Error_EndLineExpected ();
2251        return true;
2252      }
2253
2254      if (new_file_name != null) {
2255        current_source = context.LookupFile (source_file, new_file_name);
2256        source_file.AddIncludeFile (current_source);
2257      }
2258
2259      if (!hidden_block_start.IsNull) {
2260        current_source.RegisterHiddenScope (hidden_block_start, loc);
2261        hidden_block_start = Location.Null;
2262      }
2263
2264      //ref_line = new_line;
2265      return true;
2266    }
2267
2268    //
2269    // Handles #define and #undef
2270    //
2271    void PreProcessDefinition (bool is_define, string ident, bool caller_is_taking)
2272    {
2273      if (ident.Length == 0 || ident == "true" || ident == "false"){
2274        Report.Error (1001, Location, "Missing identifier to pre-processor directive");
2275        return;
2276      }
2277
2278      if (ident.IndexOfAny (simple_whitespaces) != -1){
2279        Error_EndLineExpected ();
2280        return;
2281      }
2282
2283      if (!is_identifier_start_character (ident [0]))
2284        Report.Error (1001, Location, "Identifier expected: {0}", ident);
2285     
2286      foreach (char c in ident.Substring (1)){
2287        if (!is_identifier_part_character (c)){
2288          Report.Error (1001, Location, "Identifier expected: {0}",  ident);
2289          return;
2290        }
2291      }
2292
2293      if (!caller_is_taking)
2294        return;
2295
2296      if (is_define) {
2297        //
2298        // #define ident
2299        //
2300        if (context.Settings.IsConditionalSymbolDefined (ident))
2301          return;
2302
2303        source_file.AddDefine (ident);
2304      } else {
2305        //
2306        // #undef ident
2307        //
2308        source_file.AddUndefine (ident);
2309      }
2310    }
2311
2312    byte read_hex (out bool error)
2313    {
2314      int total;
2315      int c = get_char ();
2316
2317      if ((c >= '0') && (c <= '9'))
2318        total = (int) c - (int) '0';
2319      else if ((c >= 'A') && (c <= 'F'))
2320        total = (int) c - (int) 'A' + 10;
2321      else if ((c >= 'a') && (c <= 'f'))
2322        total = (int) c - (int) 'a' + 10;
2323      else {
2324        error = true;
2325        return 0;
2326      }
2327
2328      total *= 16;
2329      c = get_char ();
2330
2331      if ((c >= '0') && (c <= '9'))
2332        total += (int) c - (int) '0';
2333      else if ((c >= 'A') && (c <= 'F'))
2334        total += (int) c - (int) 'A' + 10;
2335      else if ((c >= 'a') && (c <= 'f'))
2336        total += (int) c - (int) 'a' + 10;
2337      else {
2338        error = true;
2339        return 0;
2340      }
2341
2342      error = false;
2343      return (byte) total;
2344    }
2345
2346    //
2347    // Parses #pragma checksum
2348    //
2349    bool ParsePragmaChecksum ()
2350    {
2351      //
2352      // The syntax is ` "foo.txt" "{guid}" "hash"'
2353      //
2354      // guid is predefined hash algorithm guid {406ea660-64cf-4c82-b6f0-42d48172a799} for md5
2355      //
2356      int c = get_char ();
2357
2358      if (c != '"')
2359        return false;
2360
2361      string file_name = TokenizeFileName (ref c);
2362
2363      // TODO: Any white-spaces count
2364      if (c != ' ')
2365        return false;
2366
2367      SourceFile file = context.LookupFile (source_file, file_name);
2368
2369      if (get_char () != '"' || get_char () != '{')
2370        return false;
2371
2372      bool error;
2373      byte[] guid_bytes = new byte [16];
2374      int i = 0;
2375
2376      for (; i < 4; i++) {
2377        guid_bytes [i] = read_hex (out error);
2378        if (error)
2379          return false;
2380      }
2381
2382      if (get_char () != '-')
2383        return false;
2384
2385      for (; i < 10; i++) {
2386        guid_bytes [i] = read_hex (out error);
2387        if (error)
2388          return false;
2389
2390        guid_bytes [i++] = read_hex (out error);
2391        if (error)
2392          return false;
2393
2394        if (get_char () != '-')
2395          return false;
2396      }
2397
2398      for (; i < 16; i++) {
2399        guid_bytes [i] = read_hex (out error);
2400        if (error)
2401          return false;
2402      }
2403
2404      if (get_char () != '}' || get_char () != '"')
2405        return false;
2406
2407      // TODO: Any white-spaces count
2408      c = get_char ();
2409      if (c != ' ')
2410        return false;
2411
2412      if (get_char () != '"')
2413        return false;
2414
2415      // Any length of checksum
2416      List<byte> checksum_bytes = new List<byte> (16);
2417
2418      var checksum_location = Location;
2419      c = peek_char ();
2420      while (c != '"' && c != -1) {
2421        checksum_bytes.Add (read_hex (out error));
2422        if (error)
2423          return false;
2424
2425        c = peek_char ();
2426      }
2427
2428      if (c == '/') {
2429        ReadSingleLineComment ();
2430      } else if (get_char () != '"') {
2431        return false;
2432      }
2433
2434      if (context.Settings.GenerateDebugInfo) {
2435        var chsum = checksum_bytes.ToArray ();
2436
2437        if (file.HasChecksum) {
2438          if (!ArrayComparer.IsEqual (file.Checksum, chsum)) {
2439            // TODO: Report.SymbolRelatedToPreviousError
2440            Report.Warning (1697, 1, checksum_location, "Different checksum values specified for file `{0}'", file.Name);
2441          }
2442        }
2443
2444        file.SetChecksum (guid_bytes, chsum);
2445        current_source.AutoGenerated = true;
2446      }
2447
2448      return true;
2449    }
2450
2451    bool IsTokenIdentifierEqual (char[] identifier)
2452    {
2453      for (int i = 0; i < identifier.Length; ++i) {
2454        if (identifier[i] != id_builder[i])
2455          return false;
2456      }
2457
2458      return true;
2459    }
2460
2461    int TokenizeNumber (int value)
2462    {
2463      number_pos = 0;
2464
2465      decimal_digits (value);
2466      uint ui = (uint) (number_builder[0] - '0');
2467
2468      try {
2469        for (int i = 1; i < number_pos; i++) {
2470          ui = checked ((ui * 10) + ((uint) (number_builder[i] - '0')));
2471        }
2472
2473        return (int) ui;
2474      } catch (OverflowException) {
2475        Error_NumericConstantTooLong ();
2476        return -1;
2477      }
2478    }
2479
2480    string TokenizeFileName (ref int c)
2481    {
2482      var string_builder = new StringBuilder ();
2483      while (c != -1 && c != '\n' && c != UnicodeLS && c != UnicodePS) {
2484        c = get_char ();
2485        if (c == '"') {
2486          c = get_char ();
2487          break;
2488        }
2489
2490        string_builder.Append ((char) c);
2491      }
2492
2493      if (string_builder.Length == 0) {
2494        Report.Warning (1709, 1, Location, "Filename specified for preprocessor directive is empty");
2495      }
2496
2497   
2498      return string_builder.ToString ();
2499    }
2500
2501    int TokenizePragmaNumber (ref int c)
2502    {
2503      number_pos = 0;
2504
2505      int number;
2506
2507      if (c >= '0' && c <= '9') {
2508        number = TokenizeNumber (c);
2509
2510        c = get_char ();
2511
2512        // skip over white space
2513        while (c == ' ' || c == '\t')
2514          c = get_char ();
2515
2516        if (c == ',') {
2517          c = get_char ();
2518        }
2519
2520        // skip over white space
2521        while (c == ' ' || c == '\t')
2522          c = get_char ();
2523      } else {
2524        number = -1;
2525        if (c == '/') {
2526          ReadSingleLineComment ();
2527        } else {
2528          Report.Warning (1692, 1, Location, "Invalid number");
2529
2530          // Read everything till the end of the line or file
2531          ReadToEndOfLine ();
2532        }
2533      }
2534
2535      return number;
2536    }
2537
2538    void ReadToEndOfLine ()
2539    {
2540      int c;
2541      do {
2542        c = get_char ();
2543      } while (c != -1 && c != '\n' && c != UnicodeLS && c != UnicodePS);
2544    }
2545
2546    void ReadSingleLineComment ()
2547    {
2548      if (peek_char () != '/')
2549        Report.Warning (1696, 1, Location, "Single-line comment or end-of-line expected");
2550      if (position_stack.Count == 0)
2551        sbag.StartComment (SpecialsBag.CommentType.Single, startsLine, line, col - 1);
2552      // Read everything till the end of the line or file
2553      int c;
2554      do {
2555        c = get_char ();
2556        if (position_stack.Count == 0)
2557          sbag.PushCommentChar (c);
2558        var pc = peek_char ();
2559        if ((pc == '\n' || pc == -1 || pc == UnicodeLS || pc == UnicodePS) && position_stack.Count == 0)
2560          sbag.EndComment (line, col + 1);
2561      } while (c != -1 && c != '\n' && c != UnicodeLS && c != UnicodePS);
2562    }
2563
2564    /// <summary>
2565    /// Handles #pragma directive
2566    /// </summary>
2567    void ParsePragmaDirective ()
2568    {
2569      int c;
2570      int startCol, endLine, endCol;
2571      int length = TokenizePreprocessorIdentifier (out c, out startCol, out endLine, out endCol);
2572      #if FULL_AST
2573      var pragmaDirective = sbag.GetPragmaPreProcessorDirective();
2574      if (pragmaDirective != null)
2575        pragmaDirective.WarningColumn = startCol;
2576      #endif
2577      if (length == pragma_warning.Length && IsTokenIdentifierEqual (pragma_warning)) {
2578        length = TokenizePreprocessorIdentifier (out c, out startCol, out endLine, out endCol);
2579        #if FULL_AST
2580        if (pragmaDirective != null)
2581          pragmaDirective.DisableRestoreColumn = startCol;
2582        #endif
2583
2584        //
2585        // #pragma warning disable
2586        // #pragma warning restore
2587        //
2588        if (length == pragma_warning_disable.Length) {
2589          bool disable = IsTokenIdentifierEqual (pragma_warning_disable);
2590          #if FULL_AST
2591          if (pragmaDirective != null)
2592            pragmaDirective.Disalbe = disable;
2593          #endif
2594          if (disable || IsTokenIdentifierEqual (pragma_warning_restore)) {
2595            // skip over white space
2596            while (c == ' ' || c == '\t')
2597              c = get_char ();
2598
2599            var loc = Location;
2600
2601            if (c == '\n' || c == '/' || c == UnicodeLS || c == UnicodePS) {
2602              if (c == '/')
2603                ReadSingleLineComment ();
2604
2605              //
2606              // Disable/Restore all warnings
2607              //
2608              if (disable) {
2609                Report.RegisterWarningRegion (loc).WarningDisable (loc.Row);
2610              } else {
2611                Report.RegisterWarningRegion (loc).WarningEnable (loc.Row);
2612              }
2613            } else {
2614              //
2615              // Disable/Restore a warning or group of warnings
2616              //
2617              int code;
2618              do {
2619                var startLoc = loc;
2620                #if FULL_AST
2621                // int read_start = reader.Position;
2622                #endif
2623                code = TokenizePragmaNumber (ref c);
2624                if (code > 0) {
2625                  #if FULL_AST
2626                  var literal = new IntConstant(context.BuiltinTypes, code, startLoc);
2627                  if (pragmaDirective != null)
2628                    pragmaDirective.Codes.Add (literal);
2629                //  literal.ParsedValue = reader.ReadChars (read_start, reader.Position + 1);
2630                  #endif
2631                  if (disable) {
2632                    Report.RegisterWarningRegion (loc).WarningDisable (loc, code, context.Report);
2633                  } else {
2634                    Report.RegisterWarningRegion (loc).WarningEnable (loc, code, context);
2635                  }
2636                }
2637              } while (code >= 0 && c != '\n' && c != -1 && c != UnicodeLS && c != UnicodePS);
2638            }
2639
2640            return;
2641          }
2642        }
2643
2644        Report.Warning (1634, 1, Location, "Expected disable or restore");
2645
2646        // Eat any remaining characters on the line
2647        ReadToEndOfLine ();
2648
2649        return;
2650      }
2651
2652
2653      //
2654      // #pragma checksum
2655      //
2656      if (length == pragma_checksum.Length && IsTokenIdentifierEqual (pragma_checksum)) {
2657        if (c != ' ' || !ParsePragmaChecksum ()) {
2658          Report.Warning (1695, 1, Location,
2659            "Invalid #pragma checksum syntax. Expected \"filename\" \"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\" \"XXXX...\"");
2660        }
2661
2662        return;
2663      }
2664
2665      Report.Warning (1633, 1, Location, "Unrecognized #pragma directive");
2666    }
2667
2668    bool eval_val (string s)
2669    {
2670      if (s == "true")
2671        return true;
2672      if (s == "false")
2673        return false;
2674
2675      return source_file.IsConditionalDefined (s);
2676    }
2677
2678    bool pp_primary (ref string s)
2679    {
2680      s = s.Trim ();
2681      int len = s.Length;
2682
2683      if (len > 0){
2684        char c = s [0];
2685       
2686        if (c == '('){
2687          s = s.Substring (1);
2688          bool val = pp_expr (ref s, false);
2689          if (s.Length > 0 && s [0] == ')'){
2690            s = s.Substring (1);
2691            return val;
2692          }
2693          Error_InvalidDirective ();
2694          return false;
2695        }
2696       
2697        if (is_identifier_start_character (c)){
2698          int j = 1;
2699
2700          while (j < len){
2701            c = s [j];
2702           
2703            if (is_identifier_part_character (c)){
2704              j++;
2705              continue;
2706            }
2707            bool v = eval_val (s.Substring (0, j));
2708            s = s.Substring (j);
2709            return v;
2710          }
2711          bool vv = eval_val (s);
2712          s = "";
2713          return vv;
2714        }
2715      }
2716      Error_InvalidDirective ();
2717      return false;
2718    }
2719   
2720    bool pp_unary (ref string s)
2721    {
2722      s = s.Trim ();
2723      int len = s.Length;
2724
2725      if (len > 0){
2726        if (s [0] == '!'){
2727          if (len > 1 && s [1] == '='){
2728            Error_InvalidDirective ();
2729            return false;
2730          }
2731          s = s.Substring (1);
2732          return ! pp_primary (ref s);
2733        } else
2734          return pp_primary (ref s);
2735      } else {
2736        Error_InvalidDirective ();
2737        return false;
2738      }
2739    }
2740   
2741    bool pp_eq (ref string s)
2742    {
2743      bool va = pp_unary (ref s);
2744
2745      s = s.Trim ();
2746      int len = s.Length;
2747      if (len > 0){
2748        if (s [0] == '='){
2749          if (len > 2 && s [1] == '='){
2750            s = s.Substring (2);
2751            return va == pp_unary (ref s);
2752          } else {
2753            Error_InvalidDirective ();
2754            return false;
2755          }
2756        } else if (s [0] == '!' && len > 1 && s [1] == '='){
2757          s = s.Substring (2);
2758
2759          return va != pp_unary (ref s);
2760
2761        }
2762      }
2763
2764      return va;
2765       
2766    }
2767   
2768    bool pp_and (ref string s)
2769    {
2770      bool va = pp_eq (ref s);
2771
2772      s = s.Trim ();
2773      int len = s.Length;
2774      if (len > 0){
2775        if (s [0] == '&'){
2776          if (len > 2 && s [1] == '&'){
2777            s = s.Substring (2);
2778            return (va & pp_and (ref s));
2779          } else {
2780            Error_InvalidDirective ();
2781            return false;
2782          }
2783        }
2784      }
2785      return va;
2786    }
2787   
2788    //
2789    // Evaluates an expression for `#if' or `#elif'
2790    //
2791    bool pp_expr (ref string s, bool isTerm)
2792    {
2793      bool va = pp_and (ref s);
2794      s = s.Trim ();
2795      int len = s.Length;
2796      if (len > 0){
2797        char c = s [0];
2798       
2799        if (c == '|'){
2800          if (len > 2 && s [1] == '|'){
2801            s = s.Substring (2);
2802            return va | pp_expr (ref s, isTerm);
2803          } else {
2804            Error_InvalidDirective ();
2805            return false;
2806          }
2807        }
2808        if (isTerm) {
2809          Error_EndLineExpected ();
2810          return false;
2811        }
2812      }
2813     
2814      return va;
2815    }
2816
2817    bool eval (string s)
2818    {
2819      bool v = pp_expr (ref s, true);
2820      s = s.Trim ();
2821      if (s.Length != 0){
2822        return false;
2823      }
2824
2825      return v;
2826    }
2827
2828    void Error_NumericConstantTooLong ()
2829    {
2830      Report.Error (1021, Location, "Integral constant is too large");     
2831    }
2832   
2833    void Error_InvalidDirective ()
2834    {
2835      Report.Error (1517, Location, "Invalid preprocessor directive");
2836    }
2837
2838    void Error_UnexpectedDirective (string extra)
2839    {
2840      Report.Error (
2841        1028, Location,
2842        "Unexpected processor directive ({0})", extra);
2843    }
2844
2845    void Error_TokensSeen ()
2846    {
2847      Report.Error (1032, Location,
2848        "Cannot define or undefine preprocessor symbols after first token in file");
2849    }
2850
2851    void Eror_WrongPreprocessorLocation ()
2852    {
2853      Report.Error (1040, Location,
2854        "Preprocessor directives must appear as the first non-whitespace character on a line");
2855    }
2856
2857    void Error_EndLineExpected ()
2858    {
2859      Report.Error (1025, Location, "Single-line comment or end-of-line expected");
2860    }
2861
2862    //
2863    // Raises a warning when tokenizer found documentation comment
2864    // on unexpected place
2865    //
2866    void WarningMisplacedComment (Location loc)
2867    {
2868      if (doc_state != XmlCommentState.Error) {
2869        doc_state = XmlCommentState.Error;
2870        Report.Warning (1587, 2, loc, "XML comment is not placed on a valid language element");
2871      }
2872    }
2873   
2874    //
2875    // if true, then the code continues processing the code
2876    // if false, the code stays in a loop until another directive is
2877    // reached.
2878    // When caller_is_taking is false we ignore all directives except the ones
2879    // which can help us to identify where the #if block ends
2880    bool ParsePreprocessingDirective (bool caller_is_taking)
2881    {
2882      string arg;
2883      bool region_directive = false;
2884
2885      var directive = get_cmd_arg (out arg);
2886
2887      //
2888      // The first group of pre-processing instructions is always processed
2889      //
2890      switch (directive) {
2891      case PreprocessorDirective.Region:
2892        region_directive = true;
2893        arg = "true";
2894        goto case PreprocessorDirective.If;
2895
2896      case PreprocessorDirective.Endregion:
2897        if (ifstack == null || ifstack.Count == 0){
2898          Error_UnexpectedDirective ("no #region for this #endregion");
2899          return true;
2900        }
2901        int pop = ifstack.Pop ();
2902         
2903        if ((pop & REGION) == 0)
2904          Report.Error (1027, Location, "Expected `#endif' directive");
2905         
2906        return caller_is_taking;
2907       
2908      case PreprocessorDirective.If:
2909        if (ifstack == null)
2910          ifstack = new Stack<int> (2);
2911
2912        int flags = region_directive ? REGION : 0;
2913        if (ifstack.Count == 0){
2914          flags |= PARENT_TAKING;
2915        } else {
2916          int state = ifstack.Peek ();
2917          if ((state & TAKING) != 0) {
2918            flags |= PARENT_TAKING;
2919          }
2920        }
2921
2922        if (eval (arg) && caller_is_taking) {
2923          ifstack.Push (flags | TAKING);
2924          return true;
2925        }
2926        sbag.SkipIf ();
2927        ifstack.Push (flags);
2928        return false;
2929
2930      case PreprocessorDirective.Endif:
2931        if (ifstack == null || ifstack.Count == 0){
2932          Error_UnexpectedDirective ("no #if for this #endif");
2933          return true;
2934        } else {
2935          pop = ifstack.Pop ();
2936         
2937          if ((pop & REGION) != 0)
2938            Report.Error (1038, Location, "#endregion directive expected");
2939         
2940          if (arg.Length != 0) {
2941            Error_EndLineExpected ();
2942          }
2943         
2944          if (ifstack.Count == 0)
2945            return true;
2946
2947          int state = ifstack.Peek ();
2948          return (state & TAKING) != 0;
2949        }
2950
2951      case PreprocessorDirective.Elif:
2952        if (ifstack == null || ifstack.Count == 0){
2953          Error_UnexpectedDirective ("no #if for this #elif");
2954          return true;
2955        } else {
2956          int state = ifstack.Pop ();
2957
2958          if ((state & REGION) != 0) {
2959            Report.Error (1038, Location, "#endregion directive expected");
2960            return true;
2961          }
2962
2963          if ((state & ELSE_SEEN) != 0){
2964            Error_UnexpectedDirective ("#elif not valid after #else");
2965            return true;
2966          }
2967
2968          if ((state & TAKING) != 0) {
2969            sbag.SkipIf ();
2970            ifstack.Push (0);
2971            return false;
2972          }
2973
2974          if (eval (arg) && ((state & PARENT_TAKING) != 0)){
2975            ifstack.Push (state | TAKING);
2976            return true;
2977          }
2978
2979          sbag.SkipIf ();
2980          ifstack.Push (state);
2981          return false;
2982        }
2983
2984      case PreprocessorDirective.Else:
2985        if (ifstack == null || ifstack.Count == 0){
2986          Error_UnexpectedDirective ("no #if for this #else");
2987          return true;
2988        } else {
2989          int state = ifstack.Peek ();
2990
2991          if ((state & REGION) != 0) {
2992            Report.Error (1038, Location, "#endregion directive expected");
2993            return true;
2994          }
2995
2996          if ((state & ELSE_SEEN) != 0){
2997            Error_UnexpectedDirective ("#else within #else");
2998            return true;
2999          }
3000
3001          ifstack.Pop ();
3002
3003          if (arg.Length != 0) {
3004            Error_EndLineExpected ();
3005            return true;
3006          }
3007
3008          bool ret = false;
3009          if ((state & PARENT_TAKING) != 0) {
3010            ret = (state & TAKING) == 0;
3011         
3012            if (ret)
3013              state |= TAKING;
3014            else
3015              state &= ~TAKING;
3016          }
3017 
3018          ifstack.Push (state | ELSE_SEEN);
3019         
3020          return ret;
3021        }
3022      case PreprocessorDirective.Define:
3023        if (any_token_seen){
3024          if (caller_is_taking)
3025            Error_TokensSeen ();
3026          return caller_is_taking;
3027        }
3028        PreProcessDefinition (true, arg, caller_is_taking);
3029        return caller_is_taking;
3030
3031      case PreprocessorDirective.Undef:
3032        if (any_token_seen){
3033          if (caller_is_taking)
3034            Error_TokensSeen ();
3035          return caller_is_taking;
3036        }
3037        PreProcessDefinition (false, arg, caller_is_taking);
3038        return caller_is_taking;
3039
3040      case PreprocessorDirective.Invalid:
3041        Report.Error (1024, Location, "Wrong preprocessor directive");
3042        return true;
3043      }
3044
3045      //
3046      // These are only processed if we are in a `taking' block
3047      //
3048      if (!caller_is_taking)
3049        return false;
3050         
3051      switch (directive){
3052      case PreprocessorDirective.Error:
3053        Report.Error (1029, Location, "#error: '{0}'", arg);
3054        return true;
3055
3056      case PreprocessorDirective.Warning:
3057        Report.Warning (1030, 1, Location, "#warning: `{0}'", arg);
3058        return true;
3059
3060      case PreprocessorDirective.Pragma:
3061        if (context.Settings.Version == LanguageVersion.ISO_1) {
3062          Report.FeatureIsNotAvailable (context, Location, "#pragma");
3063        }
3064
3065        ParsePragmaDirective ();
3066        return true;
3067
3068      case PreprocessorDirective.Line:
3069        Location loc = Location;
3070        if (!PreProcessLine ())
3071          Report.Error (1576, loc, "The line number specified for #line directive is missing or invalid");
3072
3073        return caller_is_taking;
3074      }
3075
3076      throw new NotImplementedException (directive.ToString ());
3077    }
3078
3079    private int consume_string (bool quoted)
3080    {
3081      int c;
3082      int pos = 0;
3083      Location start_location = Location;
3084      if (quoted) {
3085        start_location = start_location - 1;
3086        recordNewLine = false;
3087      }
3088
3089#if FULL_AST
3090      int reader_pos = reader.Position;
3091#endif
3092
3093      while (true){
3094        // Cannot use get_char because of \r in quoted strings
3095        if (putback_char != -1) {
3096          c = putback_char;
3097          putback_char = -1;
3098        } else {
3099          c = reader.Read ();
3100        }
3101
3102        if (c == '"') {
3103          ++col;
3104
3105          if (quoted && peek_char () == '"') {
3106            if (pos == value_builder.Length)
3107              Array.Resize (ref value_builder, pos * 2);
3108
3109            value_builder[pos++] = (char) c;
3110            get_char ();
3111            continue;
3112          }
3113
3114          string s;
3115          if (pos == 0)
3116            s = string.Empty;
3117          else if (pos <= 4)
3118            s = InternIdentifier (value_builder, pos);
3119          else
3120            s = new string (value_builder, 0, pos);
3121
3122          ILiteralConstant res = new StringLiteral (context.BuiltinTypes, s, start_location);
3123          val = res;
3124#if FULL_AST
3125          res.ParsedValue = quoted ?
3126            reader.ReadChars (reader_pos - 2, reader.Position - 1) :
3127            reader.ReadChars (reader_pos - 1, reader.Position);
3128#endif
3129          recordNewLine = true;
3130          return Token.LITERAL;
3131        }
3132
3133        if (c == '\n' || c == UnicodeLS || c == UnicodePS) {
3134          if (!quoted) {
3135            Report.Error (1010, Location, "Newline in constant");
3136
3137
3138            // Don't add \r to string literal
3139            if (pos > 1 && value_builder [pos - 1] == '\r') {
3140              advance_line (SpecialsBag.NewLine.Windows);
3141              --pos;
3142            } else {
3143              advance_line (SpecialsBag.NewLine.Unix);
3144            }
3145
3146            val = new StringLiteral (context.BuiltinTypes, new string (value_builder, 0, pos), start_location);
3147            recordNewLine = true;
3148            return Token.LITERAL;
3149          }
3150
3151          advance_line (SpecialsBag.NewLine.Unix);
3152        } else if (c == '\\' && !quoted) {
3153          ++col;
3154          int surrogate;
3155          c = escape (c, out surrogate);
3156          if (c == -1) {
3157            recordNewLine = true;
3158            return Token.ERROR;
3159          }
3160          if (surrogate != 0) {
3161            if (pos == value_builder.Length)
3162              Array.Resize (ref value_builder, pos * 2);
3163
3164            value_builder[pos++] = (char) c;
3165            c = surrogate;
3166          }
3167        } else if (c == -1) {
3168          Report.Error (1039, Location, "Unterminated string literal");
3169          recordNewLine = true;
3170          return Token.EOF;
3171        } else {
3172          ++col;
3173        }
3174
3175        if (pos == value_builder.Length)
3176          Array.Resize (ref value_builder, pos * 2);
3177
3178        value_builder[pos++] = (char) c;
3179      }
3180      recordNewLine = true;
3181    }
3182
3183    private int consume_identifier (int s)
3184    {
3185      int res = consume_identifier (s, false);
3186
3187      if (doc_state == XmlCommentState.Allowed)
3188        doc_state = XmlCommentState.NotAllowed;
3189      startsLine = false;
3190      return res;
3191    }
3192
3193    int consume_identifier (int c, bool quoted)
3194    {
3195      //
3196      // This method is very performance sensitive. It accounts
3197      // for approximately 25% of all parser time
3198      //
3199
3200      int pos = 0;
3201      int column = col;
3202      if (quoted)
3203        --column;
3204
3205      if (c == '\\') {
3206        int surrogate;
3207        c = escape (c, out surrogate);
3208        if (surrogate != 0) {
3209          id_builder [pos++] = (char) c;
3210          c = surrogate;
3211        }
3212      }
3213
3214      id_builder [pos++] = (char) c;
3215
3216      try {
3217        while (true) {
3218          c = reader.Read ();
3219
3220          if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9')) {
3221            id_builder [pos++] = (char) c;
3222            continue;
3223          }
3224
3225          if (c < 0x80) {
3226            if (c == '\\') {
3227              int surrogate;
3228              c = escape (c, out surrogate);
3229              if (is_identifier_part_character ((char) c))
3230                id_builder[pos++] = (char) c;
3231
3232              if (surrogate != 0) {
3233                c = surrogate;
3234              }
3235
3236              continue;
3237            }
3238          } else if (is_identifier_part_character_slow_part ((char) c)) {
3239            id_builder [pos++] = (char) c;
3240            continue;
3241          }
3242
3243          putback_char = c;
3244          break;
3245        }
3246      } catch (IndexOutOfRangeException) {
3247        Report.Error (645, Location, "Identifier too long (limit is 512 chars)");
3248        --pos;
3249        col += pos;
3250      }
3251
3252      col += pos - 1;
3253
3254      //
3255      // Optimization: avoids doing the keyword lookup
3256      // on uppercase letters
3257      //
3258      if (id_builder [0] >= '_' && !quoted) {
3259        int keyword = GetKeyword (id_builder, pos);
3260        if (keyword != -1) {
3261          val = ltb.Create (keyword == Token.AWAIT ? "await" : null, current_source, ref_line, column);
3262          return keyword;
3263        }
3264      }
3265
3266      string s = InternIdentifier (id_builder, pos);
3267#if FULL_AST
3268      if (quoted) {
3269        val = ltb.Create ("@" + s, current_source, ref_line, column - 1);
3270      } else {
3271        val = ltb.Create (s, current_source, ref_line, column);
3272      }
3273#else
3274      val = ltb.Create (s, current_source, ref_line, column);
3275#endif
3276      if (quoted && parsing_attribute_section)
3277        AddEscapedIdentifier (((LocatedToken) val).Location);
3278
3279      return Token.IDENTIFIER;
3280    }
3281
3282    string InternIdentifier (char[] charBuffer, int length)
3283    {
3284      //
3285      // Keep identifiers in an array of hashtables to avoid needless
3286      // allocations
3287      //
3288      var identifiers_group = identifiers [length];
3289      string s;
3290      if (identifiers_group != null) {
3291        if (identifiers_group.TryGetValue (charBuffer, out s)) {
3292          return s;
3293        }
3294      } else {
3295        // TODO: this should be number of files dependant
3296        // corlib compilation peaks at 1000 and System.Core at 150
3297        int capacity = length > 20 ? 10 : 100;
3298        identifiers_group = new Dictionary<char[], string> (capacity, new IdentifiersComparer (length));
3299        identifiers [length] = identifiers_group;
3300      }
3301
3302      char[] chars = new char[length];
3303      Array.Copy (charBuffer, chars, length);
3304
3305      s = new string (charBuffer, 0, length);
3306      identifiers_group.Add (chars, s);
3307
3308      return s;
3309    }
3310   
3311    public int xtoken ()
3312    {
3313      int d, c;
3314
3315      // Whether we have seen comments on the current line
3316      bool comments_seen = false;
3317      while ((c = get_char ()) != -1) {
3318        switch (c) {
3319        case '\t':
3320          col = ((col - 1 + tab_size) / tab_size) * tab_size;
3321          continue;
3322
3323        case ' ':
3324        case '\f':
3325        case '\v':
3326        case 0xa0:
3327        case 0:
3328        case 0xFEFF:  // Ignore BOM anywhere in the file
3329          continue;
3330
3331/*        This is required for compatibility with .NET
3332        case 0xEF:
3333          if (peek_char () == 0xBB) {
3334            PushPosition ();
3335            get_char ();
3336            if (get_char () == 0xBF)
3337              continue;
3338            PopPosition ();
3339          }
3340          break;
3341*/
3342        case '\\':
3343          tokens_seen = true;
3344          return consume_identifier (c);
3345
3346        case '{':
3347          val = ltb.Create (current_source, ref_line, col);
3348          return Token.OPEN_BRACE;
3349        case '}':
3350          val = ltb.Create (current_source, ref_line, col);
3351          return Token.CLOSE_BRACE;
3352        case '[':
3353          // To block doccomment inside attribute declaration.
3354          if (doc_state == XmlCommentState.Allowed)
3355            doc_state = XmlCommentState.NotAllowed;
3356
3357          val = ltb.Create (current_source, ref_line, col);
3358
3359          if (parsing_block == 0 || lambda_arguments_parsing)
3360            return Token.OPEN_BRACKET;
3361
3362          int next = peek_char ();
3363          switch (next) {
3364          case ']':
3365          case ',':
3366            return Token.OPEN_BRACKET;
3367
3368          case ' ':
3369          case '\f':
3370          case '\v':
3371          case '\r':
3372          case '\n':
3373          case UnicodeLS:
3374          case UnicodePS:
3375          case '/':
3376            next = peek_token ();
3377            if (next == Token.COMMA || next == Token.CLOSE_BRACKET)
3378              return Token.OPEN_BRACKET;
3379
3380            return Token.OPEN_BRACKET_EXPR;
3381          default:
3382            return Token.OPEN_BRACKET_EXPR;
3383          }
3384        case ']':
3385          ltb.CreateOptional (current_source, ref_line, col, ref val);
3386          return Token.CLOSE_BRACKET;
3387        case '(':
3388          val = ltb.Create (current_source, ref_line, col);
3389          //
3390          // An expression versions of parens can appear in block context only
3391          //
3392          if (parsing_block != 0 && !lambda_arguments_parsing) {
3393           
3394            //
3395            // Optmize most common case where we know that parens
3396            // is not special
3397            //
3398            switch (current_token) {
3399            case Token.IDENTIFIER:
3400            case Token.IF:
3401            case Token.FOR:
3402            case Token.FOREACH:
3403            case Token.TYPEOF:
3404            case Token.WHILE:
3405            case Token.SWITCH:
3406            case Token.USING:
3407            case Token.DEFAULT:
3408            case Token.DELEGATE:
3409            case Token.OP_GENERICS_GT:
3410              return Token.OPEN_PARENS;
3411            }
3412
3413            // Optimize using peek
3414            int xx = peek_char ();
3415            switch (xx) {
3416            case '(':
3417            case '\'':
3418            case '"':
3419            case '0':
3420            case '1':
3421              return Token.OPEN_PARENS;
3422            }
3423
3424            lambda_arguments_parsing = true;
3425            PushPosition ();
3426            d = TokenizeOpenParens ();
3427            PopPosition ();
3428            lambda_arguments_parsing = false;
3429            return d;
3430          }
3431
3432          return Token.OPEN_PARENS;
3433        case ')':
3434          ltb.CreateOptional (current_source, ref_line, col, ref val);
3435          return Token.CLOSE_PARENS;
3436        case ',':
3437          ltb.CreateOptional (current_source, ref_line, col, ref val);
3438          return Token.COMMA;
3439        case ';':
3440          ltb.CreateOptional (current_source, ref_line, col, ref val);
3441          return Token.SEMICOLON;
3442        case '~':
3443          val = ltb.Create (current_source, ref_line, col);
3444          return Token.TILDE;
3445        case '?':
3446          val = ltb.Create (current_source, ref_line, col);
3447          return TokenizePossibleNullableType ();
3448        case '<':
3449          val = ltb.Create (current_source, ref_line, col);
3450          if (parsing_generic_less_than++ > 0)
3451            return Token.OP_GENERICS_LT;
3452
3453          return TokenizeLessThan ();
3454
3455        case '>':
3456          val = ltb.Create (current_source, ref_line, col);
3457          d = peek_char ();
3458
3459          if (d == '=') {
3460            get_char ();
3461            return Token.OP_GE;
3462          }
3463
3464          if (parsing_generic_less_than > 1 || (parsing_generic_less_than == 1 && d != '>')) {
3465            parsing_generic_less_than--;
3466            return Token.OP_GENERICS_GT;
3467          }
3468
3469          if (d == '>') {
3470            get_char ();
3471            d = peek_char ();
3472
3473            if (d == '=') {
3474              get_char ();
3475              return Token.OP_SHIFT_RIGHT_ASSIGN;
3476            }
3477            return Token.OP_SHIFT_RIGHT;
3478          }
3479
3480          return Token.OP_GT;
3481
3482        case '+':
3483          val = ltb.Create (current_source, ref_line, col);
3484          d = peek_char ();
3485          if (d == '+') {
3486            d = Token.OP_INC;
3487          } else if (d == '=') {
3488            d = Token.OP_ADD_ASSIGN;
3489          } else {
3490            return Token.PLUS;
3491          }
3492          get_char ();
3493          return d;
3494
3495        case '-':
3496          val = ltb.Create (current_source, ref_line, col);
3497          d = peek_char ();
3498          if (d == '-') {
3499            d = Token.OP_DEC;
3500          } else if (d == '=')
3501            d = Token.OP_SUB_ASSIGN;
3502          else if (d == '>')
3503            d = Token.OP_PTR;
3504          else {
3505            return Token.MINUS;
3506          }
3507          get_char ();
3508          return d;
3509
3510        case '!':
3511          val = ltb.Create (current_source, ref_line, col);
3512          if (peek_char () == '='){
3513            get_char ();
3514            return Token.OP_NE;
3515          }
3516          return Token.BANG;
3517
3518        case '=':
3519          val = ltb.Create (current_source, ref_line, col);
3520          d = peek_char ();
3521          if (d == '=') {
3522            get_char ();
3523            return Token.OP_EQ;
3524          }
3525          if (d == '>') {
3526            get_char ();
3527            return Token.ARROW;
3528          }
3529
3530          return Token.ASSIGN;
3531
3532        case '&':
3533          val = ltb.Create (current_source, ref_line, col);
3534          d = peek_char ();
3535          if (d == '&') {
3536            get_char ();
3537            return Token.OP_AND;
3538          }
3539          if (d == '=') {
3540            get_char ();
3541            return Token.OP_AND_ASSIGN;
3542          }
3543          return Token.BITWISE_AND;
3544
3545        case '|':
3546          val = ltb.Create (current_source, ref_line, col);
3547          d = peek_char ();
3548          if (d == '|') {
3549            get_char ();
3550            return Token.OP_OR;
3551          }
3552          if (d == '=') {
3553            get_char ();
3554            return Token.OP_OR_ASSIGN;
3555          }
3556          return Token.BITWISE_OR;
3557
3558        case '*':
3559          val = ltb.Create (current_source, ref_line, col);
3560          if (peek_char () == '='){
3561            get_char ();
3562            return Token.OP_MULT_ASSIGN;
3563          }
3564          return Token.STAR;
3565
3566        case '/':
3567          d = peek_char ();
3568          if (d == '='){
3569            val = ltb.Create (current_source, ref_line, col);
3570            get_char ();
3571            return Token.OP_DIV_ASSIGN;
3572          }
3573          // Handle double-slash comments.
3574          if (d == '/') {
3575            get_char ();
3576            if (doc_processing) {
3577              if (peek_char () == '/') {
3578                get_char ();
3579                // Don't allow ////.
3580                if ((d = peek_char ()) != '/') {
3581                  if (position_stack.Count == 0)
3582                    sbag.PushCommentChar (d);
3583                  if (doc_state == XmlCommentState.Allowed)
3584                    handle_one_line_xml_comment ();
3585                  else if (doc_state == XmlCommentState.NotAllowed)
3586                    WarningMisplacedComment (Location - 3);
3587                }
3588              } else {
3589                if (xml_comment_buffer.Length > 0)
3590                  doc_state = XmlCommentState.NotAllowed;
3591              }
3592            } else {
3593              bool isDoc = peek_char () == '/';
3594              if (position_stack.Count == 0)
3595                sbag.StartComment (isDoc ? SpecialsBag.CommentType.Documentation : SpecialsBag.CommentType.Single, startsLine, line, col - 1);
3596              if (isDoc)
3597                get_char ();
3598            }
3599           
3600            d = peek_char ();
3601            int endLine = line, endCol = col;
3602            while ((d = get_char ()) != -1 && (d != '\n') && d != '\r' && d != UnicodePS && d != UnicodeLS) {
3603              if (position_stack.Count == 0)
3604                sbag.PushCommentChar (d);
3605              endLine = line;
3606              endCol = col;
3607            }
3608            if (position_stack.Count == 0)
3609              sbag.EndComment (endLine, endCol + 1);
3610            any_token_seen |= tokens_seen;
3611            tokens_seen = false;
3612            comments_seen = false;
3613            continue;
3614          } else if (d == '*'){
3615            if (position_stack.Count == 0) {
3616              sbag.StartComment (SpecialsBag.CommentType.Multi, startsLine, line, col);
3617              recordNewLine = false;
3618            }
3619            get_char ();
3620            bool docAppend = false;
3621            if (doc_processing && peek_char () == '*') {
3622              int ch = get_char ();
3623              // But when it is /**/, just do nothing.
3624              if (peek_char () == '/') {
3625                ch = get_char ();
3626                if (position_stack.Count == 0) {
3627                  recordNewLine = true;
3628                  sbag.EndComment (line, col + 1);
3629                }
3630                continue;
3631              } else {
3632                if (position_stack.Count == 0)
3633                  sbag.PushCommentChar (ch);
3634              }
3635              if (doc_state == XmlCommentState.Allowed)
3636                docAppend = true;
3637              else if (doc_state == XmlCommentState.NotAllowed) {
3638                WarningMisplacedComment (Location - 2);
3639              }
3640            }
3641
3642            int current_comment_start = 0;
3643            if (docAppend) {
3644              current_comment_start = xml_comment_buffer.Length;
3645              xml_comment_buffer.Append (Environment.NewLine);
3646            }
3647
3648            while ((d = get_char ()) != -1){
3649              if (d == '*' && peek_char () == '/'){
3650                get_char ();
3651                if (position_stack.Count == 0) {
3652                  recordNewLine = true;
3653                  sbag.EndComment (line, col + 1);
3654                }
3655                comments_seen = true;
3656                break;
3657              } else {
3658                if (position_stack.Count == 0)
3659                  sbag.PushCommentChar (d);
3660              }
3661              if (docAppend)
3662                xml_comment_buffer.Append ((char) d);
3663             
3664              if (d == '\n' || d == UnicodeLS || d == UnicodePS){
3665                any_token_seen |= tokens_seen;
3666                tokens_seen = false;
3667                //
3668                // Reset 'comments_seen' just to be consistent.
3669                // It doesn't matter either way, here.
3670                //
3671                comments_seen = false;
3672              }
3673            }
3674           
3675            if (!comments_seen)
3676              Report.Error (1035, Location, "End-of-file found, '*/' expected");
3677
3678            if (docAppend)
3679              update_formatted_doc_comment (current_comment_start);
3680            continue;
3681          }
3682          val = ltb.Create (current_source, ref_line, col);
3683          return Token.DIV;
3684
3685        case '%':
3686          val = ltb.Create (current_source, ref_line, col);
3687          if (peek_char () == '='){
3688            get_char ();
3689            return Token.OP_MOD_ASSIGN;
3690          }
3691          return Token.PERCENT;
3692
3693        case '^':
3694          val = ltb.Create (current_source, ref_line, col);
3695          if (peek_char () == '='){
3696            get_char ();
3697            return Token.OP_XOR_ASSIGN;
3698          }
3699          return Token.CARRET;
3700
3701        case ':':
3702          val = ltb.Create (current_source, ref_line, col);
3703          if (peek_char () == ':') {
3704            get_char ();
3705            return Token.DOUBLE_COLON;
3706          }
3707          return Token.COLON;
3708
3709        case '0': case '1': case '2': case '3': case '4':
3710        case '5': case '6': case '7': case '8': case '9':
3711          tokens_seen = true;
3712          return is_number (c, false);
3713
3714        case '\n': // white space
3715        case UnicodeLS:
3716        case UnicodePS:
3717          any_token_seen |= tokens_seen;
3718          tokens_seen = false;
3719          comments_seen = false;
3720          continue;
3721
3722        case '.':
3723          tokens_seen = true;
3724          d = peek_char ();
3725          if (d >= '0' && d <= '9')
3726            return is_number (c, true);
3727
3728          ltb.CreateOptional (current_source, ref_line, col, ref val);
3729          return Token.DOT;
3730       
3731        case '#':
3732          if (tokens_seen || comments_seen) {
3733            Eror_WrongPreprocessorLocation();
3734            return Token.ERROR;
3735          }
3736       
3737          if (ParsePreprocessingDirective(true))
3738            continue;
3739          sbag.StartComment(SpecialsBag.CommentType.InactiveCode, false, line, 1);
3740          recordNewLine = false;
3741          bool directive_expected = false;
3742          while ((c = get_char ()) != -1) {
3743            if (col == 1) {
3744              directive_expected = true;
3745            } else if (!directive_expected) {
3746              // TODO: Implement comment support for disabled code and uncomment this code
3747//              if (c == '#') {
3748//                Eror_WrongPreprocessorLocation ();
3749//                return Token.ERROR;
3750//              }
3751              if (c != '#')
3752                sbag.PushCommentChar (c);
3753              continue;
3754            }
3755
3756            if (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\v' || c == UnicodeLS || c == UnicodePS) {
3757              sbag.PushCommentChar (c);
3758              continue;
3759            }
3760
3761            if (c == '#') {
3762              var oldNL = recordNewLine;
3763              recordNewLine = true;
3764              var continueNormalLexing = ParsePreprocessingDirective(false);
3765              recordNewLine = oldNL;
3766              if (continueNormalLexing)
3767                break;
3768              sbag.StartComment(SpecialsBag.CommentType.InactiveCode, false, line, 1);
3769            }
3770            sbag.PushCommentChar (c);
3771            directive_expected = false;
3772          }
3773          recordNewLine = true;
3774          sbag.EndComment (line, col);
3775          if (c != -1) {
3776            tokens_seen = false;
3777            continue;
3778          }
3779
3780          return Token.EOF;
3781               
3782        case '"':
3783          return consume_string (false);
3784
3785        case '\'':
3786          return TokenizeBackslash ();
3787       
3788        case '@':
3789          c = get_char ();
3790          if (c == '"') {
3791            tokens_seen = true;
3792            return consume_string (true);
3793          }
3794
3795          if (is_identifier_start_character (c)){
3796            return consume_identifier (c, true);
3797          }
3798
3799          Report.Error (1646, Location, "Keyword, identifier, or string expected after verbatim specifier: @");
3800          return Token.ERROR;
3801
3802        case EvalStatementParserCharacter:
3803          return Token.EVAL_STATEMENT_PARSER;
3804        case EvalCompilationUnitParserCharacter:
3805          return Token.EVAL_COMPILATION_UNIT_PARSER;
3806        case EvalUsingDeclarationsParserCharacter:
3807          return Token.EVAL_USING_DECLARATIONS_UNIT_PARSER;
3808        case DocumentationXref:
3809          return Token.DOC_SEE;
3810        }
3811
3812        if (is_identifier_start_character (c)) {
3813          tokens_seen = true;
3814          return consume_identifier (c);
3815        }
3816
3817        if (char.IsWhiteSpace ((char) c))
3818          continue;
3819
3820        Report.Error (1056, Location, "Unexpected character `{0}'", ((char) c).ToString ());
3821      }
3822
3823      if (CompleteOnEOF){
3824        if (generated)
3825          return Token.COMPLETE_COMPLETION;
3826       
3827        generated = true;
3828        return Token.GENERATE_COMPLETION;
3829      }
3830     
3831
3832      return Token.EOF;
3833    }
3834
3835    int TokenizeBackslash ()
3836    {
3837#if FULL_AST
3838      int read_start = reader.Position;
3839#endif
3840      Location start_location = Location;
3841      int c = get_char ();
3842      tokens_seen = true;
3843      if (c == '\'') {
3844        val = new CharLiteral (context.BuiltinTypes, (char) c, start_location);
3845        Report.Error (1011, start_location, "Empty character literal");
3846        return Token.LITERAL;
3847      }
3848
3849      if (c == '\n' || c == UnicodeLS || c == UnicodePS) {
3850        Report.Error (1010, start_location, "Newline in constant");
3851        return Token.ERROR;
3852      }
3853
3854      int d;
3855      c = escape (c, out d);
3856      if (c == -1)
3857        return Token.ERROR;
3858      if (d != 0)
3859        throw new NotImplementedException ();
3860
3861      ILiteralConstant res = new CharLiteral (context.BuiltinTypes, (char) c, start_location);
3862      val = res;
3863      c = get_char ();
3864
3865      if (c != '\'') {
3866        Report.Error (1012, start_location, "Too many characters in character literal");
3867
3868        // Try to recover, read until newline or next "'"
3869        while ((c = get_char ()) != -1) {
3870          if (c == '\n' || c == '\'' || c == UnicodeLS || c == UnicodePS)
3871            break;
3872        }
3873      }
3874
3875#if FULL_AST
3876      res.ParsedValue = reader.ReadChars (read_start - 1, reader.Position);
3877#endif
3878
3879      return Token.LITERAL;
3880    }
3881
3882    int TokenizeLessThan ()
3883    {
3884      int d;
3885
3886      // Save current position and parse next token.
3887      PushPosition ();
3888      int generic_dimension = 0;
3889      if (parse_less_than (ref generic_dimension)) {
3890        if (parsing_generic_declaration && (parsing_generic_declaration_doc || token () != Token.DOT)) {
3891          d = Token.OP_GENERICS_LT_DECL;
3892        } else {
3893          if (generic_dimension > 0) {
3894            val = generic_dimension;
3895            DiscardPosition ();
3896            return Token.GENERIC_DIMENSION;
3897          }
3898
3899          d = Token.OP_GENERICS_LT;
3900        }
3901        PopPosition ();
3902        return d;
3903      }
3904
3905      PopPosition ();
3906      parsing_generic_less_than = 0;
3907
3908      d = peek_char ();
3909      if (d == '<') {
3910        get_char ();
3911        d = peek_char ();
3912
3913        if (d == '=') {
3914          get_char ();
3915          return Token.OP_SHIFT_LEFT_ASSIGN;
3916        }
3917        return Token.OP_SHIFT_LEFT;
3918      }
3919
3920      if (d == '=') {
3921        get_char ();
3922        return Token.OP_LE;
3923      }
3924      return Token.OP_LT;
3925    }
3926
3927    //
3928    // Handles one line xml comment
3929    //
3930    private void handle_one_line_xml_comment ()
3931    {
3932      int c;
3933      while ((c = peek_char ()) == ' ') {
3934        if (position_stack.Count == 0)
3935          sbag.PushCommentChar (c);
3936        get_char (); // skip heading whitespaces.
3937      }
3938      while ((c = peek_char ()) != -1 && c != '\n' && c != '\r') {
3939        if (position_stack.Count == 0)
3940          sbag.PushCommentChar (c);
3941        xml_comment_buffer.Append ((char) get_char ());
3942      }
3943      if (c == '\r' || c == '\n')
3944        xml_comment_buffer.Append (Environment.NewLine);
3945    }
3946
3947    //
3948    // Remove heading "*" in Javadoc-like xml documentation.
3949    //
3950    private void update_formatted_doc_comment (int current_comment_start)
3951    {
3952      int length = xml_comment_buffer.Length - current_comment_start;
3953      string [] lines = xml_comment_buffer.ToString (
3954        current_comment_start,
3955        length).Replace ("\r", "").Split ('\n');
3956     
3957      // The first line starts with /**, thus it is not target
3958      // for the format check.
3959      for (int i = 1; i < lines.Length; i++) {
3960        string s = lines [i];
3961        int idx = s.IndexOf ('*');
3962        string head = null;
3963        if (idx < 0) {
3964          if (i < lines.Length - 1)
3965            return;
3966          head = s;
3967        } else
3968          head = s.Substring (0, idx);
3969        foreach (char c in head)
3970          if (c != ' ')
3971            return;
3972        lines [i] = s.Substring (idx + 1);
3973      }
3974      xml_comment_buffer.Remove (current_comment_start, length);
3975      xml_comment_buffer.Insert (current_comment_start, String.Join (Environment.NewLine, lines));
3976    }
3977
3978    //
3979    // Checks if there was incorrect doc comments and raise
3980    // warnings.
3981    //
3982    public void check_incorrect_doc_comment ()
3983    {
3984      if (xml_comment_buffer.Length > 0)
3985        WarningMisplacedComment (Location);
3986    }
3987
3988    //
3989    // Consumes the saved xml comment lines (if any)
3990    // as for current target member or type.
3991    //
3992    public string consume_doc_comment ()
3993    {
3994      if (xml_comment_buffer.Length > 0) {
3995        string ret = xml_comment_buffer.ToString ();
3996        reset_doc_comment ();
3997        return ret;
3998      }
3999      return null;
4000    }
4001
4002    void reset_doc_comment ()
4003    {
4004      xml_comment_buffer.Length = 0;
4005    }
4006
4007    public void cleanup ()
4008    {
4009      if (ifstack != null && ifstack.Count >= 1) {
4010        int state = ifstack.Pop ();
4011        if ((state & REGION) != 0)
4012          Report.Error (1038, Location, "#endregion directive expected");
4013        else
4014          Report.Error (1027, Location, "Expected `#endif' directive");
4015      }
4016    }
4017  }
4018
4019  //
4020  // Indicates whether it accepts XML documentation or not.
4021  //
4022  public enum XmlCommentState {
4023    // comment is allowed in this state.
4024    Allowed,
4025    // comment is not allowed in this state.
4026    NotAllowed,
4027    // once comments appeared when it is NotAllowed, then the
4028    // state is changed to it, until the state is changed to
4029    // .Allowed.
4030    Error
4031  }
4032}
4033
Note: See TracBrowser for help on using the repository browser.