2 // cs-tokenizer.cs: The Tokenizer for the C# compiler
3 // This also implements the preprocessor
5 // Author: Miguel de Icaza (miguel@gnu.org)
6 // Marek Safar (marek.safar@seznam.cz)
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
10 // Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com)
11 // Copyright 2004-2008 Novell, Inc
17 using System
.Collections
.Generic
;
18 using System
.Globalization
;
19 using System
.Diagnostics
;
24 /// Tokenizer for C# source code.
27 public class Tokenizer
: yyParser
.yyInput
31 public readonly T Token
;
32 public KeywordEntry
<T
> Next
;
33 public readonly char[] Value
;
35 public KeywordEntry (string value, T token
)
37 this.Value
= value.ToCharArray ();
42 sealed class IdentifiersComparer
: IEqualityComparer
<char[]>
46 public IdentifiersComparer (int length
)
51 public bool Equals (char[] x
, char[] y
)
53 for (int i
= 0; i
< length
; ++i
)
60 public int GetHashCode (char[] obj
)
63 for (int i
= 0; i
< length
; ++i
)
64 h
= (h
<< 5) - h
+ obj
[i
];
71 // This class has to be used in the parser only, it reuses token
72 // details after each parse
74 public class LocatedToken
79 static LocatedToken
[] buffer
;
82 private LocatedToken ()
86 public static LocatedToken
Create (int row
, int column
)
88 return Create (null, row
, column
);
91 public static LocatedToken
Create (string value, int row
, int column
)
94 // TODO: I am not very happy about the logic but it's the best
95 // what I could come up with for now.
96 // Ideally we should be using just tiny buffer (256 elements) which
97 // is enough to hold all details for currect stack and recycle elements
98 // poped from the stack but there is a trick needed to recycle
102 if (pos
>= buffer
.Length
) {
103 entry
= new LocatedToken ();
105 entry
= buffer
[pos
];
107 entry
= new LocatedToken ();
108 buffer
[pos
] = entry
;
115 entry
.column
= column
;
120 // Used for token not required by expression evaluator
122 [Conditional ("FULL_AST")]
123 public static void CreateOptional (int row
, int col
, ref object token
)
125 token
= Create (row
, col
);
128 public static void Initialize ()
131 buffer
= new LocatedToken
[10000];
135 public Location Location
{
136 get { return new Location (row, column); }
139 public string Value
{
140 get { return value; }
144 enum PreprocessorDirective
150 If
= 3 | RequiresArgument
,
152 Elif
= 5 | RequiresArgument
,
154 Define
= 7 | RequiresArgument
,
155 Undef
= 8 | RequiresArgument
,
158 Pragma
= 11 | CustomArgumentsParsing
,
161 CustomArgumentsParsing
= 1 << 10,
162 RequiresArgument
= 1 << 11
165 SeekableStreamReader reader
;
167 CompilationSourceFile file_name
;
168 CompilerContext context
;
176 bool handle_get_set
= false;
177 bool handle_remove_add
= false;
178 bool handle_where
= false;
179 bool handle_typeof
= false;
180 bool lambda_arguments_parsing
;
181 List
<Location
> escaped_identifiers
;
182 int parsing_generic_less_than
;
183 readonly bool doc_processing
;
186 // Used mainly for parser optimizations. Some expressions for instance
187 // can appear only in block (including initializer, base initializer)
190 public int parsing_block
;
191 internal bool query_parsing
;
194 // When parsing type only, useful for ambiguous nullable types
196 public int parsing_type
;
199 // Set when parsing generic declaration (type or method header)
201 public bool parsing_generic_declaration
;
202 public bool parsing_generic_declaration_doc
;
205 // The value indicates that we have not reach any declaration or
208 public int parsing_declaration
;
210 public bool parsing_attribute_section
;
213 // The special characters to inject on streams to run the unit parser
214 // in the special expression mode. Using private characters from
215 // Plane Sixteen (U+100000 to U+10FFFD)
217 // This character is only tested just before the tokenizer is about to report
218 // an error; So on the regular operation mode, this addition will have no
219 // impact on the tokenizer's performance.
222 public const int EvalStatementParserCharacter
= 0x100000;
223 public const int EvalCompilationUnitParserCharacter
= 0x100001;
224 public const int EvalUsingDeclarationsParserCharacter
= 0x100002;
225 public const int DocumentationXref
= 0x100003;
228 // XML documentation buffer. The save point is used to divide
229 // comments on types and comments on members.
231 StringBuilder xml_comment_buffer
;
234 // See comment on XmlCommentState enumeration.
236 XmlCommentState xml_doc_state
= XmlCommentState
.Allowed
;
239 // Whether tokens have been seen on this line
241 bool tokens_seen
= false;
244 // Set to true once the GENERATE_COMPLETION token has bee
245 // returned. This helps produce one GENERATE_COMPLETION,
246 // as many COMPLETE_COMPLETION as necessary to complete the
247 // AST tree and one final EOF.
252 // Whether a token has been seen on the file
253 // This is needed because `define' is not allowed to be used
254 // after a token has been seen.
261 static readonly KeywordEntry
<int>[][] keywords
;
262 static readonly KeywordEntry
<PreprocessorDirective
>[][] keywords_preprocessor
;
263 static readonly Dictionary
<string, object> keyword_strings
; // TODO: HashSet
264 static readonly NumberStyles styles
;
265 static readonly NumberFormatInfo csharp_format_info
;
268 static readonly char[] pragma_warning
= "warning".ToCharArray ();
269 static readonly char[] pragma_warning_disable
= "disable".ToCharArray ();
270 static readonly char[] pragma_warning_restore
= "restore".ToCharArray ();
271 static readonly char[] pragma_checksum
= "checksum".ToCharArray ();
273 static readonly char[] simple_whitespaces
= new char[] { ' ', '\t' }
;
275 public bool PropertyParsing
{
276 get { return handle_get_set; }
277 set { handle_get_set = value; }
280 public bool EventParsing
{
281 get { return handle_remove_add; }
282 set { handle_remove_add = value; }
285 public bool ConstraintsParsing
{
286 get { return handle_where; }
287 set { handle_where = value; }
290 public bool TypeOfParsing
{
291 get { return handle_typeof; }
292 set { handle_typeof = value; }
296 get { return tab_size; }
297 set { tab_size = value; }
300 public XmlCommentState doc_state
{
301 get { return xml_doc_state; }
303 if (value == XmlCommentState
.Allowed
) {
304 check_incorrect_doc_comment ();
305 reset_doc_comment ();
307 xml_doc_state
= value;
312 // This is used to trigger completion generation on the parser
313 public bool CompleteOnEOF
;
315 void AddEscapedIdentifier (Location loc
)
317 if (escaped_identifiers
== null)
318 escaped_identifiers
= new List
<Location
> ();
320 escaped_identifiers
.Add (loc
);
323 public bool IsEscapedIdentifier (MemberName name
)
325 return escaped_identifiers
!= null && escaped_identifiers
.Contains (name
.Location
);
329 // Values for the associated token returned
331 internal int putback_char
; // Used by repl only
337 const int TAKING
= 1;
338 const int ELSE_SEEN
= 4;
339 const int PARENT_TAKING
= 8;
340 const int REGION
= 16;
343 // pre-processor if stack state:
347 static System
.Text
.StringBuilder string_builder
;
349 const int max_id_size
= 512;
350 static readonly char [] id_builder
= new char [max_id_size
];
352 public static Dictionary
<char[], string>[] identifiers
= new Dictionary
<char[], string>[max_id_size
+ 1];
354 const int max_number_size
= 512;
355 static char [] number_builder
= new char [max_number_size
];
356 static int number_pos
;
358 static char[] value_builder
= new char[256];
367 // This is used when the tokenizer needs to save
368 // the current position as it needs to do some parsing
369 // on its own to deamiguate a token in behalf of the
372 Stack
<Position
> position_stack
= new Stack
<Position
> (2);
380 public int putback_char
;
381 public int previous_col
;
382 public Stack
<int> ifstack
;
383 public int parsing_generic_less_than
;
384 public int current_token
;
387 public Position (Tokenizer t
)
389 position
= t
.reader
.Position
;
391 ref_line
= t
.ref_line
;
394 putback_char
= t
.putback_char
;
395 previous_col
= t
.previous_col
;
396 if (t
.ifstack
!= null && t
.ifstack
.Count
!= 0) {
397 // There is no simple way to clone Stack<T> all
398 // methods reverse the order
399 var clone
= t
.ifstack
.ToArray ();
400 Array
.Reverse (clone
);
401 ifstack
= new Stack
<int> (clone
);
403 parsing_generic_less_than
= t
.parsing_generic_less_than
;
404 current_token
= t
.current_token
;
409 public Tokenizer (SeekableStreamReader input
, CompilationSourceFile file
, CompilerContext ctx
)
411 this.ref_name
= file
;
412 this.file_name
= file
;
418 xml_comment_buffer
= new StringBuilder ();
419 doc_processing
= ctx
.Settings
.DocumentationFile
!= null;
421 if (Environment
.OSVersion
.Platform
== PlatformID
.Win32NT
)
427 // FIXME: This could be `Location.Push' but we have to
428 // find out why the MS compiler allows this
430 Mono
.CSharp
.Location
.Push (file
, file
);
433 public void PushPosition ()
435 position_stack
.Push (new Position (this));
438 public void PopPosition ()
440 Position p
= position_stack
.Pop ();
442 reader
.Position
= p
.position
;
443 ref_line
= p
.ref_line
;
447 putback_char
= p
.putback_char
;
448 previous_col
= p
.previous_col
;
450 parsing_generic_less_than
= p
.parsing_generic_less_than
;
451 current_token
= p
.current_token
;
455 // Do not reset the position, ignore it.
456 public void DiscardPosition ()
458 position_stack
.Pop ();
461 static void AddKeyword (string kw
, int token
)
463 keyword_strings
.Add (kw
, null);
465 AddKeyword (keywords
, kw
, token
);
468 static void AddPreprocessorKeyword (string kw
, PreprocessorDirective directive
)
470 AddKeyword (keywords_preprocessor
, kw
, directive
);
473 static void AddKeyword
<T
> (KeywordEntry
<T
>[][] keywords
, string kw
, T token
)
475 int length
= kw
.Length
;
476 if (keywords
[length
] == null) {
477 keywords
[length
] = new KeywordEntry
<T
>['z' - '_' + 1];
480 int char_index
= kw
[0] - '_';
481 var kwe
= keywords
[length
][char_index
];
483 keywords
[length
][char_index
] = new KeywordEntry
<T
> (kw
, token
);
487 while (kwe
.Next
!= null) {
491 kwe
.Next
= new KeywordEntry
<T
> (kw
, token
);
499 keyword_strings
= new Dictionary
<string, object> ();
501 // 11 is the length of the longest keyword for now
502 keywords
= new KeywordEntry
<int>[11][];
504 AddKeyword ("__arglist", Token
.ARGLIST
);
505 AddKeyword ("__makeref", Token
.MAKEREF
);
506 AddKeyword ("__reftype", Token
.REFTYPE
);
507 AddKeyword ("__refvalue", Token
.REFVALUE
);
508 AddKeyword ("abstract", Token
.ABSTRACT
);
509 AddKeyword ("as", Token
.AS
);
510 AddKeyword ("add", Token
.ADD
);
511 AddKeyword ("base", Token
.BASE
);
512 AddKeyword ("bool", Token
.BOOL
);
513 AddKeyword ("break", Token
.BREAK
);
514 AddKeyword ("byte", Token
.BYTE
);
515 AddKeyword ("case", Token
.CASE
);
516 AddKeyword ("catch", Token
.CATCH
);
517 AddKeyword ("char", Token
.CHAR
);
518 AddKeyword ("checked", Token
.CHECKED
);
519 AddKeyword ("class", Token
.CLASS
);
520 AddKeyword ("const", Token
.CONST
);
521 AddKeyword ("continue", Token
.CONTINUE
);
522 AddKeyword ("decimal", Token
.DECIMAL
);
523 AddKeyword ("default", Token
.DEFAULT
);
524 AddKeyword ("delegate", Token
.DELEGATE
);
525 AddKeyword ("do", Token
.DO
);
526 AddKeyword ("double", Token
.DOUBLE
);
527 AddKeyword ("else", Token
.ELSE
);
528 AddKeyword ("enum", Token
.ENUM
);
529 AddKeyword ("event", Token
.EVENT
);
530 AddKeyword ("explicit", Token
.EXPLICIT
);
531 AddKeyword ("extern", Token
.EXTERN
);
532 AddKeyword ("false", Token
.FALSE
);
533 AddKeyword ("finally", Token
.FINALLY
);
534 AddKeyword ("fixed", Token
.FIXED
);
535 AddKeyword ("float", Token
.FLOAT
);
536 AddKeyword ("for", Token
.FOR
);
537 AddKeyword ("foreach", Token
.FOREACH
);
538 AddKeyword ("goto", Token
.GOTO
);
539 AddKeyword ("get", Token
.GET
);
540 AddKeyword ("if", Token
.IF
);
541 AddKeyword ("implicit", Token
.IMPLICIT
);
542 AddKeyword ("in", Token
.IN
);
543 AddKeyword ("int", Token
.INT
);
544 AddKeyword ("interface", Token
.INTERFACE
);
545 AddKeyword ("internal", Token
.INTERNAL
);
546 AddKeyword ("is", Token
.IS
);
547 AddKeyword ("lock", Token
.LOCK
);
548 AddKeyword ("long", Token
.LONG
);
549 AddKeyword ("namespace", Token
.NAMESPACE
);
550 AddKeyword ("new", Token
.NEW
);
551 AddKeyword ("null", Token
.NULL
);
552 AddKeyword ("object", Token
.OBJECT
);
553 AddKeyword ("operator", Token
.OPERATOR
);
554 AddKeyword ("out", Token
.OUT
);
555 AddKeyword ("override", Token
.OVERRIDE
);
556 AddKeyword ("params", Token
.PARAMS
);
557 AddKeyword ("private", Token
.PRIVATE
);
558 AddKeyword ("protected", Token
.PROTECTED
);
559 AddKeyword ("public", Token
.PUBLIC
);
560 AddKeyword ("readonly", Token
.READONLY
);
561 AddKeyword ("ref", Token
.REF
);
562 AddKeyword ("remove", Token
.REMOVE
);
563 AddKeyword ("return", Token
.RETURN
);
564 AddKeyword ("sbyte", Token
.SBYTE
);
565 AddKeyword ("sealed", Token
.SEALED
);
566 AddKeyword ("set", Token
.SET
);
567 AddKeyword ("short", Token
.SHORT
);
568 AddKeyword ("sizeof", Token
.SIZEOF
);
569 AddKeyword ("stackalloc", Token
.STACKALLOC
);
570 AddKeyword ("static", Token
.STATIC
);
571 AddKeyword ("string", Token
.STRING
);
572 AddKeyword ("struct", Token
.STRUCT
);
573 AddKeyword ("switch", Token
.SWITCH
);
574 AddKeyword ("this", Token
.THIS
);
575 AddKeyword ("throw", Token
.THROW
);
576 AddKeyword ("true", Token
.TRUE
);
577 AddKeyword ("try", Token
.TRY
);
578 AddKeyword ("typeof", Token
.TYPEOF
);
579 AddKeyword ("uint", Token
.UINT
);
580 AddKeyword ("ulong", Token
.ULONG
);
581 AddKeyword ("unchecked", Token
.UNCHECKED
);
582 AddKeyword ("unsafe", Token
.UNSAFE
);
583 AddKeyword ("ushort", Token
.USHORT
);
584 AddKeyword ("using", Token
.USING
);
585 AddKeyword ("virtual", Token
.VIRTUAL
);
586 AddKeyword ("void", Token
.VOID
);
587 AddKeyword ("volatile", Token
.VOLATILE
);
588 AddKeyword ("while", Token
.WHILE
);
589 AddKeyword ("partial", Token
.PARTIAL
);
590 AddKeyword ("where", Token
.WHERE
);
593 AddKeyword ("from", Token
.FROM
);
594 AddKeyword ("join", Token
.JOIN
);
595 AddKeyword ("on", Token
.ON
);
596 AddKeyword ("equals", Token
.EQUALS
);
597 AddKeyword ("select", Token
.SELECT
);
598 AddKeyword ("group", Token
.GROUP
);
599 AddKeyword ("by", Token
.BY
);
600 AddKeyword ("let", Token
.LET
);
601 AddKeyword ("orderby", Token
.ORDERBY
);
602 AddKeyword ("ascending", Token
.ASCENDING
);
603 AddKeyword ("descending", Token
.DESCENDING
);
604 AddKeyword ("into", Token
.INTO
);
606 // Contextual async keywords
607 AddKeyword ("async", Token
.ASYNC
);
608 AddKeyword ("await", Token
.AWAIT
);
610 keywords_preprocessor
= new KeywordEntry
<PreprocessorDirective
>[10][];
612 AddPreprocessorKeyword ("region", PreprocessorDirective
.Region
);
613 AddPreprocessorKeyword ("endregion", PreprocessorDirective
.Endregion
);
614 AddPreprocessorKeyword ("if", PreprocessorDirective
.If
);
615 AddPreprocessorKeyword ("endif", PreprocessorDirective
.Endif
);
616 AddPreprocessorKeyword ("elif", PreprocessorDirective
.Elif
);
617 AddPreprocessorKeyword ("else", PreprocessorDirective
.Else
);
618 AddPreprocessorKeyword ("define", PreprocessorDirective
.Define
);
619 AddPreprocessorKeyword ("undef", PreprocessorDirective
.Undef
);
620 AddPreprocessorKeyword ("error", PreprocessorDirective
.Error
);
621 AddPreprocessorKeyword ("warning", PreprocessorDirective
.Warning
);
622 AddPreprocessorKeyword ("pragma", PreprocessorDirective
.Pragma
);
623 AddPreprocessorKeyword ("line", PreprocessorDirective
.Line
);
625 csharp_format_info
= NumberFormatInfo
.InvariantInfo
;
626 styles
= NumberStyles
.Float
;
628 string_builder
= new System
.Text
.StringBuilder ();
631 int GetKeyword (char[] id
, int id_len
)
634 // Keywords are stored in an array of arrays grouped by their
635 // length and then by the first character
637 if (id_len
>= keywords
.Length
|| keywords
[id_len
] == null)
640 int first_index
= id
[0] - '_';
641 if (first_index
> 'z' - '_')
644 var kwe
= keywords
[id_len
] [first_index
];
651 for (int i
= 1; i
< id_len
; ++i
) {
652 if (id
[i
] != kwe
.Value
[i
]) {
658 } while (res
== 0 && kwe
!= null);
672 if (!handle_remove_add
)
676 if (parsing_declaration
== 0)
677 res
= Token
.EXTERN_ALIAS
;
680 if (peek_token () == Token
.COLON
) {
682 res
= Token
.DEFAULT_COLON
;
686 if (!handle_where
&& !query_parsing
)
691 // A query expression is any expression that starts with `from identifier'
692 // followed by any token except ; , =
694 if (!query_parsing
) {
695 if (lambda_arguments_parsing
) {
701 // HACK: to disable generics micro-parser, because PushPosition does not
702 // store identifiers array
703 parsing_generic_less_than
= 1;
705 case Token
.IDENTIFIER
:
717 next_token
= xtoken ();
718 if (next_token
== Token
.SEMICOLON
|| next_token
== Token
.COMMA
|| next_token
== Token
.EQUALS
)
721 res
= Token
.FROM_FIRST
;
722 query_parsing
= true;
723 if (context
.Settings
.Version
<= LanguageVersion
.ISO_2
)
724 Report
.FeatureIsNotAvailable (context
, Location
, "query expressions");
727 Expression
.Error_VoidInvalidInTheContext (Location
, Report
);
731 // HACK: A token is not a keyword so we need to restore identifiers buffer
732 // which has been overwritten before we grabbed the identifier
733 id_builder
[0] = 'f'; id_builder
[1] = 'r'; id_builder
[2] = 'o'; id_builder
[3] = 'm';
747 case Token
.ASCENDING
:
748 case Token
.DESCENDING
:
755 case Token
.NAMESPACE
:
756 // TODO: some explanation needed
757 check_incorrect_doc_comment ();
761 if (parsing_block
> 0) {
766 // Save current position and parse next token.
769 next_token
= token ();
770 bool ok
= (next_token
== Token
.CLASS
) ||
771 (next_token
== Token
.STRUCT
) ||
772 (next_token
== Token
.INTERFACE
) ||
773 (next_token
== Token
.VOID
);
778 if (next_token
== Token
.VOID
) {
779 if (context
.Settings
.Version
<= LanguageVersion
.ISO_2
)
780 Report
.FeatureIsNotAvailable (context
, Location
, "partial methods");
781 } else if (context
.Settings
.Version
== LanguageVersion
.ISO_1
)
782 Report
.FeatureIsNotAvailable (context
, Location
, "partial types");
787 if (next_token
< Token
.LAST_KEYWORD
) {
788 Report
.Error (267, Location
,
789 "The `partial' modifier can be used only immediately before `class', `struct', `interface', or `void' keyword");
796 // TODO: async, it's modifiers context only
798 if (context
.Settings
.Version
!= LanguageVersion
.Future
) {
803 // TODO: async, it's async block context only
805 if (context
.Settings
.Version
!= LanguageVersion
.Future
) {
816 static PreprocessorDirective
GetPreprocessorDirective (char[] id
, int id_len
)
819 // Keywords are stored in an array of arrays grouped by their
820 // length and then by the first character
822 if (id_len
>= keywords_preprocessor
.Length
|| keywords_preprocessor
[id_len
] == null)
823 return PreprocessorDirective
.Invalid
;
825 int first_index
= id
[0] - '_';
826 if (first_index
> 'z' - '_')
827 return PreprocessorDirective
.Invalid
;
829 var kwe
= keywords_preprocessor
[id_len
][first_index
];
831 return PreprocessorDirective
.Invalid
;
833 PreprocessorDirective res
= PreprocessorDirective
.Invalid
;
836 for (int i
= 1; i
< id_len
; ++i
) {
837 if (id
[i
] != kwe
.Value
[i
]) {
843 } while (res
== PreprocessorDirective
.Invalid
&& kwe
!= null);
848 public Location Location
{
850 return new Location (ref_line
, hidden
? -1 : col
);
854 static bool is_identifier_start_character (int c
)
856 return (c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z') || c
== '_' || Char
.IsLetter ((char)c
);
859 static bool is_identifier_part_character (char c
)
861 if (c
>= 'a' && c
<= 'z')
864 if (c
>= 'A' && c
<= 'Z')
867 if (c
== '_' || (c
>= '0' && c
<= '9'))
873 return Char
.IsLetter (c
) || Char
.GetUnicodeCategory (c
) == UnicodeCategory
.ConnectorPunctuation
;
876 public static bool IsKeyword (string s
)
878 return keyword_strings
.ContainsKey (s
);
882 // Open parens micro parser. Detects both lambda and cast ambiguity.
884 int TokenizeOpenParens ()
889 int bracket_level
= 0;
890 bool is_type
= false;
891 bool can_be_type
= false;
894 ptoken
= current_token
;
897 switch (current_token
) {
898 case Token
.CLOSE_PARENS
:
902 // Expression inside parens is lambda, (int i) =>
904 if (current_token
== Token
.ARROW
)
905 return Token
.OPEN_PARENS_LAMBDA
;
908 // Expression inside parens is single type, (int[])
911 return Token
.OPEN_PARENS_CAST
;
914 // Expression is possible cast, look at next token, (T)null
917 switch (current_token
) {
918 case Token
.OPEN_PARENS
:
921 case Token
.IDENTIFIER
:
935 case Token
.UNCHECKED
:
940 // These can be part of a member access
954 return Token
.OPEN_PARENS_CAST
;
957 return Token
.OPEN_PARENS
;
960 case Token
.DOUBLE_COLON
:
961 if (ptoken
!= Token
.IDENTIFIER
&& ptoken
!= Token
.OP_GENERICS_GT
)
966 case Token
.IDENTIFIER
:
969 if (bracket_level
== 0) {
975 case Token
.OP_GENERICS_LT
:
977 case Token
.DOUBLE_COLON
:
979 if (bracket_level
== 0)
983 can_be_type
= is_type
= false;
1003 if (bracket_level
== 0)
1008 if (bracket_level
== 0) {
1009 bracket_level
= 100;
1010 can_be_type
= is_type
= false;
1014 case Token
.OP_GENERICS_LT
:
1015 case Token
.OPEN_BRACKET
:
1016 if (bracket_level
++ == 0)
1020 case Token
.OP_GENERICS_GT
:
1021 case Token
.CLOSE_BRACKET
:
1025 case Token
.INTERR_NULLABLE
:
1027 if (bracket_level
== 0)
1033 can_be_type
= is_type
= false;
1037 return Token
.OPEN_PARENS
;
1042 public static bool IsValidIdentifier (string s
)
1044 if (s
== null || s
.Length
== 0)
1047 if (!is_identifier_start_character (s
[0]))
1050 for (int i
= 1; i
< s
.Length
; i
++)
1051 if (! is_identifier_part_character (s
[i
]))
1057 bool parse_less_than ()
1060 int the_token
= token ();
1061 if (the_token
== Token
.OPEN_BRACKET
) {
1063 the_token
= token ();
1064 } while (the_token
!= Token
.CLOSE_BRACKET
);
1065 the_token
= token ();
1066 } else if (the_token
== Token
.IN
|| the_token
== Token
.OUT
) {
1067 the_token
= token ();
1069 switch (the_token
) {
1070 case Token
.IDENTIFIER
:
1088 case Token
.OP_GENERICS_GT
:
1097 the_token
= token ();
1099 if (the_token
== Token
.OP_GENERICS_GT
)
1101 else if (the_token
== Token
.COMMA
|| the_token
== Token
.DOT
|| the_token
== Token
.DOUBLE_COLON
)
1103 else if (the_token
== Token
.INTERR_NULLABLE
|| the_token
== Token
.STAR
)
1105 else if (the_token
== Token
.OP_GENERICS_LT
) {
1106 if (!parse_less_than ())
1109 } else if (the_token
== Token
.OPEN_BRACKET
) {
1111 the_token
= token ();
1112 if (the_token
== Token
.CLOSE_BRACKET
)
1114 else if (the_token
== Token
.COMMA
)
1115 goto rank_specifiers
;
1122 bool parse_generic_dimension (out int dimension
)
1127 int the_token
= token ();
1128 if (the_token
== Token
.OP_GENERICS_GT
)
1130 else if (the_token
== Token
.COMMA
) {
1138 public int peek_token ()
1143 the_token
= token ();
1150 // Tonizes `?' using custom disambiguous rules to return one
1151 // of following tokens: INTERR_NULLABLE, OP_COALESCING, INTERR
1153 // Tricky expression look like:
1155 // Foo ? a = x ? b : c;
1157 int TokenizePossibleNullableType ()
1159 if (parsing_block
== 0 || parsing_type
> 0)
1160 return Token
.INTERR_NULLABLE
;
1162 int d
= peek_char ();
1165 return Token
.OP_COALESCING
;
1168 switch (current_token
) {
1169 case Token
.CLOSE_PARENS
:
1174 return Token
.INTERR
;
1178 if (d
== ',' || d
== ';' || d
== '>')
1179 return Token
.INTERR_NULLABLE
;
1180 if (d
== '*' || (d
>= '0' && d
<= '9'))
1181 return Token
.INTERR
;
1185 current_token
= Token
.NONE
;
1187 switch (xtoken ()) {
1194 next_token
= Token
.INTERR
;
1197 case Token
.SEMICOLON
:
1199 case Token
.CLOSE_PARENS
:
1200 case Token
.OPEN_BRACKET
:
1201 case Token
.OP_GENERICS_GT
:
1203 next_token
= Token
.INTERR_NULLABLE
;
1211 if (next_token
== -1) {
1212 switch (xtoken ()) {
1214 case Token
.SEMICOLON
:
1215 case Token
.OPEN_BRACE
:
1216 case Token
.CLOSE_PARENS
:
1218 next_token
= Token
.INTERR_NULLABLE
;
1222 next_token
= Token
.INTERR
;
1230 // All shorcuts failed, do it hard way
1232 while ((ntoken
= xtoken ()) != Token
.EOF
) {
1233 if (ntoken
== Token
.SEMICOLON
)
1236 if (ntoken
== Token
.COLON
) {
1237 if (++colons
== interrs
)
1242 if (ntoken
== Token
.INTERR
) {
1248 next_token
= colons
!= interrs
? Token
.INTERR_NULLABLE
: Token
.INTERR
;
1257 bool decimal_digits (int c
)
1260 bool seen_digits
= false;
1263 if (number_pos
== max_number_size
)
1264 Error_NumericConstantTooLong ();
1265 number_builder
[number_pos
++] = (char) c
;
1269 // We use peek_char2, because decimal_digits needs to do a
1270 // 2-character look-ahead (5.ToString for example).
1272 while ((d
= peek_char2 ()) != -1){
1273 if (d
>= '0' && d
<= '9'){
1274 if (number_pos
== max_number_size
)
1275 Error_NumericConstantTooLong ();
1276 number_builder
[number_pos
++] = (char) d
;
1286 static bool is_hex (int e
)
1288 return (e
>= '0' && e
<= '9') || (e
>= 'A' && e
<= 'F') || (e
>= 'a' && e
<= 'f');
1291 static TypeCode
real_type_suffix (int c
)
1295 return TypeCode
.Single
;
1297 return TypeCode
.Double
;
1299 return TypeCode
.Decimal
;
1301 return TypeCode
.Empty
;
1305 ILiteralConstant
integer_type_suffix (ulong ul
, int c
, Location loc
)
1307 bool is_unsigned
= false;
1308 bool is_long
= false;
1311 bool scanning
= true;
1324 // if we have not seen anything in between
1325 // report this error
1327 Report
.Warning (78, 4, Location
, "The 'l' suffix is easily confused with the digit '1' (use 'L' for clarity)");
1347 if (is_long
&& is_unsigned
){
1348 return new ULongLiteral (context
.BuiltinTypes
, ul
, loc
);
1352 // uint if possible, or ulong else.
1354 if ((ul
& 0xffffffff00000000) == 0)
1355 return new UIntLiteral (context
.BuiltinTypes
, (uint) ul
, loc
);
1357 return new ULongLiteral (context
.BuiltinTypes
, ul
, loc
);
1358 } else if (is_long
){
1359 // long if possible, ulong otherwise
1360 if ((ul
& 0x8000000000000000) != 0)
1361 return new ULongLiteral (context
.BuiltinTypes
, ul
, loc
);
1363 return new LongLiteral (context
.BuiltinTypes
, (long) ul
, loc
);
1365 // int, uint, long or ulong in that order
1366 if ((ul
& 0xffffffff00000000) == 0){
1367 uint ui
= (uint) ul
;
1369 if ((ui
& 0x80000000) != 0)
1370 return new UIntLiteral (context
.BuiltinTypes
, ui
, loc
);
1372 return new IntLiteral (context
.BuiltinTypes
, (int) ui
, loc
);
1374 if ((ul
& 0x8000000000000000) != 0)
1375 return new ULongLiteral (context
.BuiltinTypes
, ul
, loc
);
1377 return new LongLiteral (context
.BuiltinTypes
, (long) ul
, loc
);
1383 // given `c' as the next char in the input decide whether
1384 // we need to convert to a special type, and then choose
1385 // the best representation for the integer
1387 ILiteralConstant
adjust_int (int c
, Location loc
)
1390 if (number_pos
> 9){
1391 ulong ul
= (uint) (number_builder
[0] - '0');
1393 for (int i
= 1; i
< number_pos
; i
++){
1394 ul
= checked ((ul
* 10) + ((uint)(number_builder
[i
] - '0')));
1397 return integer_type_suffix (ul
, c
, loc
);
1399 uint ui
= (uint) (number_builder
[0] - '0');
1401 for (int i
= 1; i
< number_pos
; i
++){
1402 ui
= checked ((ui
* 10) + ((uint)(number_builder
[i
] - '0')));
1405 return integer_type_suffix (ui
, c
, loc
);
1407 } catch (OverflowException
) {
1408 Error_NumericConstantTooLong ();
1409 return new IntLiteral (context
.BuiltinTypes
, 0, loc
);
1411 catch (FormatException
) {
1412 Report
.Error (1013, Location
, "Invalid number");
1413 return new IntLiteral (context
.BuiltinTypes
, 0, loc
);
1417 ILiteralConstant
adjust_real (TypeCode t
, Location loc
)
1419 string s
= new string (number_builder
, 0, number_pos
);
1420 const string error_details
= "Floating-point constant is outside the range of type `{0}'";
1423 case TypeCode
.Decimal
:
1425 return new DecimalLiteral (context
.BuiltinTypes
, decimal.Parse (s
, styles
, csharp_format_info
), loc
);
1426 } catch (OverflowException
) {
1427 Report
.Error (594, Location
, error_details
, "decimal");
1428 return new DecimalLiteral (context
.BuiltinTypes
, 0, loc
);
1430 case TypeCode
.Single
:
1432 return new FloatLiteral (context
.BuiltinTypes
, float.Parse (s
, styles
, csharp_format_info
), loc
);
1433 } catch (OverflowException
) {
1434 Report
.Error (594, Location
, error_details
, "float");
1435 return new FloatLiteral (context
.BuiltinTypes
, 0, loc
);
1439 return new DoubleLiteral (context
.BuiltinTypes
, double.Parse (s
, styles
, csharp_format_info
), loc
);
1440 } catch (OverflowException
) {
1441 Report
.Error (594, loc
, error_details
, "double");
1442 return new DoubleLiteral (context
.BuiltinTypes
, 0, loc
);
1447 ILiteralConstant
handle_hex (Location loc
)
1453 while ((d
= peek_char ()) != -1){
1455 number_builder
[number_pos
++] = (char) d
;
1461 string s
= new String (number_builder
, 0, number_pos
);
1464 if (number_pos
<= 8)
1465 ul
= System
.UInt32
.Parse (s
, NumberStyles
.HexNumber
);
1467 ul
= System
.UInt64
.Parse (s
, NumberStyles
.HexNumber
);
1469 return integer_type_suffix (ul
, peek_char (), loc
);
1470 } catch (OverflowException
){
1471 Error_NumericConstantTooLong ();
1472 return new IntLiteral (context
.BuiltinTypes
, 0, loc
);
1474 catch (FormatException
) {
1475 Report
.Error (1013, Location
, "Invalid number");
1476 return new IntLiteral (context
.BuiltinTypes
, 0, loc
);
1481 // Invoked if we know we have .digits or digits
1483 int is_number (int c
)
1485 ILiteralConstant res
;
1488 int read_start
= reader
.Position
- 1;
1493 if (c
>= '0' && c
<= '9'){
1495 int peek
= peek_char ();
1497 if (peek
== 'x' || peek
== 'X') {
1498 val
= res
= handle_hex (loc
);
1500 res
.ParsedValue
= reader
.ReadChars (read_start
, reader
.Position
- 1);
1503 return Token
.LITERAL
;
1511 // We need to handle the case of
1512 // "1.1" vs "1.string" (LITERAL_FLOAT vs NUMBER DOT IDENTIFIER)
1514 bool is_real
= false;
1516 if (decimal_digits ('.')){
1522 val
= res
= adjust_int (-1, loc
);
1525 res
.ParsedValue
= reader
.ReadChars (read_start
, reader
.Position
- 1);
1527 return Token
.LITERAL
;
1531 if (c
== 'e' || c
== 'E'){
1533 if (number_pos
== max_number_size
)
1534 Error_NumericConstantTooLong ();
1535 number_builder
[number_pos
++] = (char) c
;
1539 if (number_pos
== max_number_size
)
1540 Error_NumericConstantTooLong ();
1541 number_builder
[number_pos
++] = '+';
1543 } else if (c
== '-') {
1544 if (number_pos
== max_number_size
)
1545 Error_NumericConstantTooLong ();
1546 number_builder
[number_pos
++] = '-';
1549 if (number_pos
== max_number_size
)
1550 Error_NumericConstantTooLong ();
1551 number_builder
[number_pos
++] = '+';
1558 var type
= real_type_suffix (c
);
1559 if (type
== TypeCode
.Empty
&& !is_real
) {
1561 res
= adjust_int (c
, loc
);
1565 if (type
== TypeCode
.Empty
) {
1569 res
= adjust_real (type
, loc
);
1575 res
.ParsedValue
= reader
.ReadChars (read_start
, reader
.Position
- (type
== TypeCode
.Empty
? 1 : 0));
1578 return Token
.LITERAL
;
1582 // Accepts exactly count (4 or 8) hex, no more no less
1584 int getHex (int count
, out int surrogate
, out bool error
)
1589 int top
= count
!= -1 ? count
: 4;
1594 for (i
= 0; i
< top
; i
++){
1597 if (c
>= '0' && c
<= '9')
1598 c
= (int) c
- (int) '0';
1599 else if (c
>= 'A' && c
<= 'F')
1600 c
= (int) c
- (int) 'A' + 10;
1601 else if (c
>= 'a' && c
<= 'f')
1602 c
= (int) c
- (int) 'a' + 10;
1608 total
= (total
* 16) + c
;
1610 int p
= peek_char ();
1613 if (!is_hex ((char)p
))
1619 if (total
> 0x0010FFFF) {
1624 if (total
>= 0x00010000) {
1625 surrogate
= ((total
- 0x00010000) % 0x0400 + 0xDC00);
1626 total
= ((total
- 0x00010000) / 0x0400 + 0xD800);
1633 int escape (int c
, out int surrogate
)
1669 v
= getHex (-1, out surrogate
, out error
);
1675 return EscapeUnicode (d
, out surrogate
);
1678 Report
.Error (1009, Location
, "Unrecognized escape sequence `\\{0}'", ((char)d
).ToString ());
1687 int EscapeUnicode (int ch
, out int surrogate
)
1691 ch
= getHex (8, out surrogate
, out error
);
1693 ch
= getHex (4, out surrogate
, out error
);
1697 Report
.Error (1009, Location
, "Unrecognized escape sequence");
1705 if (putback_char
!= -1) {
1713 if (peek_char () == '\n') {
1719 } else if (x
== '\n') {
1727 void advance_line ()
1737 if (putback_char
== -1)
1738 putback_char
= reader
.Read ();
1739 return putback_char
;
1744 if (putback_char
!= -1)
1745 return putback_char
;
1746 return reader
.Peek ();
1749 void putback (int c
)
1751 if (putback_char
!= -1){
1752 Console
.WriteLine ("Col: " + col
);
1753 Console
.WriteLine ("Row: " + line
);
1754 Console
.WriteLine ("Name: " + ref_name
.Name
);
1755 Console
.WriteLine ("Current [{0}] putting back [{1}] ", putback_char
, c
);
1756 throw new Exception ("This should not happen putback on putback");
1758 if (c
== '\n' || col
== 0) {
1759 // It won't happen though.
1769 public bool advance ()
1771 return peek_char () != -1 || CompleteOnEOF
;
1774 public Object Value
{
1780 public Object
value ()
1787 current_token
= xtoken ();
1788 return current_token
;
1791 int TokenizePreprocessorIdentifier (out int c
)
1793 // skip over white space
1796 } while (c
== ' ' || c
== '\t');
1800 while (c
!= -1 && c
>= 'a' && c
<= 'z') {
1801 id_builder
[pos
++] = (char) c
;
1804 int peek
= peek_char ();
1805 if (peek
== 'U' || peek
== 'u') {
1807 c
= EscapeUnicode (c
, out surrogate
);
1808 if (surrogate
!= 0) {
1809 if (is_identifier_part_character ((char) c
)) {
1810 id_builder
[pos
++] = (char) c
;
1821 PreprocessorDirective
get_cmd_arg (out string arg
)
1825 tokens_seen
= false;
1828 var cmd
= GetPreprocessorDirective (id_builder
, TokenizePreprocessorIdentifier (out c
));
1830 if ((cmd
& PreprocessorDirective
.CustomArgumentsParsing
) != 0)
1833 // skip over white space
1834 while (c
== ' ' || c
== '\t')
1837 int has_identifier_argument
= (int)(cmd
& PreprocessorDirective
.RequiresArgument
);
1840 while (c
!= -1 && c
!= '\n') {
1841 if (c
== '\\' && has_identifier_argument
>= 0) {
1842 if (has_identifier_argument
!= 0) {
1843 has_identifier_argument
= 1;
1845 int peek
= peek_char ();
1846 if (peek
== 'U' || peek
== 'u') {
1848 c
= EscapeUnicode (c
, out surrogate
);
1849 if (surrogate
!= 0) {
1850 if (is_identifier_part_character ((char) c
)) {
1851 if (pos
== value_builder
.Length
)
1852 Array
.Resize (ref value_builder
, pos
* 2);
1854 value_builder
[pos
++] = (char) c
;
1860 has_identifier_argument
= -1;
1862 } else if (c
== '/' && peek_char () == '/') {
1864 // Eat single-line comments
1869 } while (c
!= -1 && c
!= '\n');
1874 if (pos
== value_builder
.Length
)
1875 Array
.Resize (ref value_builder
, pos
* 2);
1877 value_builder
[pos
++] = (char) c
;
1882 if (pos
> max_id_size
)
1883 arg
= new string (value_builder
, 0, pos
);
1885 arg
= InternIdentifier (value_builder
, pos
);
1887 // Eat any trailing whitespaces
1888 arg
= arg
.Trim (simple_whitespaces
);
1895 // Handles the #line directive
1897 bool PreProcessLine (string arg
)
1899 if (arg
.Length
== 0)
1902 if (arg
== "default"){
1904 ref_name
= file_name
;
1906 Location
.Push (file_name
, ref_name
);
1908 } else if (arg
== "hidden"){
1916 if ((pos
= arg
.IndexOf (' ')) != -1 && pos
!= 0){
1917 ref_line
= System
.Int32
.Parse (arg
.Substring (0, pos
));
1920 char [] quotes
= { '\"' }
;
1922 string name
= arg
.Substring (pos
). Trim (quotes
);
1923 ref_name
= context
.LookupFile (file_name
, name
);
1924 file_name
.AddIncludeFile (ref_name
);
1926 Location
.Push (file_name
, ref_name
);
1928 ref_line
= System
.Int32
.Parse (arg
);
1939 // Handles #define and #undef
1941 void PreProcessDefinition (bool is_define
, string ident
, bool caller_is_taking
)
1943 if (ident
.Length
== 0 || ident
== "true" || ident
== "false"){
1944 Report
.Error (1001, Location
, "Missing identifier to pre-processor directive");
1948 if (ident
.IndexOfAny (simple_whitespaces
) != -1){
1949 Error_EndLineExpected ();
1953 if (!is_identifier_start_character (ident
[0]))
1954 Report
.Error (1001, Location
, "Identifier expected: {0}", ident
);
1956 foreach (char c
in ident
.Substring (1)){
1957 if (!is_identifier_part_character (c
)){
1958 Report
.Error (1001, Location
, "Identifier expected: {0}", ident
);
1963 if (!caller_is_taking
)
1970 if (context
.Settings
.IsConditionalSymbolDefined (ident
))
1973 file_name
.AddDefine (ident
);
1978 file_name
.AddUndefine (ident
);
1982 byte read_hex (out bool error
)
1985 int c
= get_char ();
1987 if ((c
>= '0') && (c
<= '9'))
1988 total
= (int) c
- (int) '0';
1989 else if ((c
>= 'A') && (c
<= 'F'))
1990 total
= (int) c
- (int) 'A' + 10;
1991 else if ((c
>= 'a') && (c
<= 'f'))
1992 total
= (int) c
- (int) 'a' + 10;
2001 if ((c
>= '0') && (c
<= '9'))
2002 total
+= (int) c
- (int) '0';
2003 else if ((c
>= 'A') && (c
<= 'F'))
2004 total
+= (int) c
- (int) 'A' + 10;
2005 else if ((c
>= 'a') && (c
<= 'f'))
2006 total
+= (int) c
- (int) 'a' + 10;
2013 return (byte) total
;
2017 // Parses #pragma checksum
2019 bool ParsePragmaChecksum ()
2022 // The syntax is ` "foo.txt" "{guid}" "hash"'
2024 int c
= get_char ();
2029 string_builder
.Length
= 0;
2030 while (c
!= -1 && c
!= '\n') {
2037 string_builder
.Append ((char) c
);
2040 if (string_builder
.Length
== 0) {
2041 Report
.Warning (1709, 1, Location
, "Filename specified for preprocessor directive is empty");
2044 // TODO: Any white-spaces count
2048 SourceFile file
= context
.LookupFile (file_name
, string_builder
.ToString ());
2050 if (get_char () != '"' || get_char () != '{')
2054 byte[] guid_bytes
= new byte [16];
2057 for (; i
< 4; i
++) {
2058 guid_bytes
[i
] = read_hex (out error
);
2063 if (get_char () != '-')
2066 for (; i
< 10; i
++) {
2067 guid_bytes
[i
] = read_hex (out error
);
2071 guid_bytes
[i
++] = read_hex (out error
);
2075 if (get_char () != '-')
2079 for (; i
< 16; i
++) {
2080 guid_bytes
[i
] = read_hex (out error
);
2085 if (get_char () != '}' || get_char () != '"')
2088 // TODO: Any white-spaces count
2093 if (get_char () != '"')
2096 // Any length of checksum
2097 List
<byte> checksum_bytes
= new List
<byte> (16);
2100 while (c
!= '"' && c
!= -1) {
2101 checksum_bytes
.Add (read_hex (out error
));
2109 ReadSingleLineComment ();
2110 } else if (get_char () != '"') {
2114 file
.SetChecksum (guid_bytes
, checksum_bytes
.ToArray ());
2115 ref_name
.AutoGenerated
= true;
2119 bool IsTokenIdentifierEqual (char[] identifier
)
2121 for (int i
= 0; i
< identifier
.Length
; ++i
) {
2122 if (identifier
[i
] != id_builder
[i
])
2129 int TokenizePragmaNumber (ref int c
)
2135 if (c
>= '0' && c
<= '9') {
2137 uint ui
= (uint) (number_builder
[0] - '0');
2140 for (int i
= 1; i
< number_pos
; i
++) {
2141 ui
= checked ((ui
* 10) + ((uint) (number_builder
[i
] - '0')));
2145 } catch (OverflowException
) {
2146 Error_NumericConstantTooLong ();
2153 // skip over white space
2154 while (c
== ' ' || c
== '\t')
2161 // skip over white space
2162 while (c
== ' ' || c
== '\t')
2167 ReadSingleLineComment ();
2169 Report
.Warning (1692, 1, Location
, "Invalid number");
2171 // Read everything till the end of the line or file
2174 } while (c
!= -1 && c
!= '\n');
2181 void ReadSingleLineComment ()
2183 if (peek_char () != '/')
2184 Report
.Warning (1696, 1, Location
, "Single-line comment or end-of-line expected");
2186 // Read everything till the end of the line or file
2190 } while (c
!= -1 && c
!= '\n');
2194 /// Handles #pragma directive
2196 void ParsePragmaDirective (string arg
)
2199 int length
= TokenizePreprocessorIdentifier (out c
);
2200 if (length
== pragma_warning
.Length
&& IsTokenIdentifierEqual (pragma_warning
)) {
2201 length
= TokenizePreprocessorIdentifier (out c
);
2204 // #pragma warning disable
2205 // #pragma warning restore
2207 if (length
== pragma_warning_disable
.Length
) {
2208 bool disable
= IsTokenIdentifierEqual (pragma_warning_disable
);
2209 if (disable
|| IsTokenIdentifierEqual (pragma_warning_restore
)) {
2210 // skip over white space
2211 while (c
== ' ' || c
== '\t')
2216 if (c
== '\n' || c
== '/') {
2218 ReadSingleLineComment ();
2221 // Disable/Restore all warnings
2224 Report
.RegisterWarningRegion (loc
).WarningDisable (loc
.Row
);
2226 Report
.RegisterWarningRegion (loc
).WarningEnable (loc
.Row
);
2230 // Disable/Restore a warning or group of warnings
2234 code
= TokenizePragmaNumber (ref c
);
2237 Report
.RegisterWarningRegion (loc
).WarningDisable (loc
, code
, Report
);
2239 Report
.RegisterWarningRegion (loc
).WarningEnable (loc
, code
, Report
);
2242 } while (code
>= 0 && c
!= '\n' && c
!= -1);
2249 Report
.Warning (1634, 1, Location
, "Expected disable or restore");
2256 if (length
== pragma_checksum
.Length
&& IsTokenIdentifierEqual (pragma_checksum
)) {
2257 if (c
!= ' ' || !ParsePragmaChecksum ()) {
2258 Report
.Warning (1695, 1, Location
,
2259 "Invalid #pragma checksum syntax. Expected \"filename\" \"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\" \"XXXX...\"");
2265 Report
.Warning (1633, 1, Location
, "Unrecognized #pragma directive");
2268 bool eval_val (string s
)
2275 return file_name
.IsConditionalDefined (context
, s
);
2278 bool pp_primary (ref string s
)
2287 s
= s
.Substring (1);
2288 bool val
= pp_expr (ref s
, false);
2289 if (s
.Length
> 0 && s
[0] == ')'){
2290 s
= s
.Substring (1);
2293 Error_InvalidDirective ();
2297 if (is_identifier_start_character (c
)){
2303 if (is_identifier_part_character (c
)){
2307 bool v
= eval_val (s
.Substring (0, j
));
2308 s
= s
.Substring (j
);
2311 bool vv
= eval_val (s
);
2316 Error_InvalidDirective ();
2320 bool pp_unary (ref string s
)
2327 if (len
> 1 && s
[1] == '='){
2328 Error_InvalidDirective ();
2331 s
= s
.Substring (1);
2332 return ! pp_primary (ref s
);
2334 return pp_primary (ref s
);
2336 Error_InvalidDirective ();
2341 bool pp_eq (ref string s
)
2343 bool va
= pp_unary (ref s
);
2349 if (len
> 2 && s
[1] == '='){
2350 s
= s
.Substring (2);
2351 return va
== pp_unary (ref s
);
2353 Error_InvalidDirective ();
2356 } else if (s
[0] == '!' && len
> 1 && s
[1] == '='){
2357 s
= s
.Substring (2);
2359 return va
!= pp_unary (ref s
);
2368 bool pp_and (ref string s
)
2370 bool va
= pp_eq (ref s
);
2376 if (len
> 2 && s
[1] == '&'){
2377 s
= s
.Substring (2);
2378 return (va
& pp_and (ref s
));
2380 Error_InvalidDirective ();
2389 // Evaluates an expression for `#if' or `#elif'
2391 bool pp_expr (ref string s
, bool isTerm
)
2393 bool va
= pp_and (ref s
);
2400 if (len
> 2 && s
[1] == '|'){
2401 s
= s
.Substring (2);
2402 return va
| pp_expr (ref s
, isTerm
);
2404 Error_InvalidDirective ();
2409 Error_EndLineExpected ();
2417 bool eval (string s
)
2419 bool v
= pp_expr (ref s
, true);
2428 void Error_NumericConstantTooLong ()
2430 Report
.Error (1021, Location
, "Integral constant is too large");
2433 void Error_InvalidDirective ()
2435 Report
.Error (1517, Location
, "Invalid preprocessor directive");
2438 void Error_UnexpectedDirective (string extra
)
2442 "Unexpected processor directive ({0})", extra
);
2445 void Error_TokensSeen ()
2447 Report
.Error (1032, Location
,
2448 "Cannot define or undefine preprocessor symbols after first token in file");
2451 void Eror_WrongPreprocessorLocation ()
2453 Report
.Error (1040, Location
,
2454 "Preprocessor directives must appear as the first non-whitespace character on a line");
2457 void Error_EndLineExpected ()
2459 Report
.Error (1025, Location
, "Single-line comment or end-of-line expected");
2463 // Raises a warning when tokenizer found documentation comment
2464 // on unexpected place
2466 void WarningMisplacedComment (Location loc
)
2468 if (doc_state
!= XmlCommentState
.Error
) {
2469 doc_state
= XmlCommentState
.Error
;
2470 Report
.Warning (1587, 2, loc
, "XML comment is not placed on a valid language element");
2475 // if true, then the code continues processing the code
2476 // if false, the code stays in a loop until another directive is
2478 // When caller_is_taking is false we ignore all directives except the ones
2479 // which can help us to identify where the #if block ends
2480 bool ParsePreprocessingDirective (bool caller_is_taking
)
2483 bool region_directive
= false;
2485 var directive
= get_cmd_arg (out arg
);
2488 // The first group of pre-processing instructions is always processed
2490 switch (directive
) {
2491 case PreprocessorDirective
.Region
:
2492 region_directive
= true;
2494 goto case PreprocessorDirective
.If
;
2496 case PreprocessorDirective
.Endregion
:
2497 if (ifstack
== null || ifstack
.Count
== 0){
2498 Error_UnexpectedDirective ("no #region for this #endregion");
2501 int pop
= ifstack
.Pop ();
2503 if ((pop
& REGION
) == 0)
2504 Report
.Error (1027, Location
, "Expected `#endif' directive");
2506 return caller_is_taking
;
2508 case PreprocessorDirective
.If
:
2509 if (ifstack
== null)
2510 ifstack
= new Stack
<int> (2);
2512 int flags
= region_directive
? REGION
: 0;
2513 if (ifstack
.Count
== 0){
2514 flags
|= PARENT_TAKING
;
2516 int state
= ifstack
.Peek ();
2517 if ((state
& TAKING
) != 0) {
2518 flags
|= PARENT_TAKING
;
2522 if (eval (arg
) && caller_is_taking
) {
2523 ifstack
.Push (flags
| TAKING
);
2526 ifstack
.Push (flags
);
2529 case PreprocessorDirective
.Endif
:
2530 if (ifstack
== null || ifstack
.Count
== 0){
2531 Error_UnexpectedDirective ("no #if for this #endif");
2534 pop
= ifstack
.Pop ();
2536 if ((pop
& REGION
) != 0)
2537 Report
.Error (1038, Location
, "#endregion directive expected");
2539 if (arg
.Length
!= 0) {
2540 Error_EndLineExpected ();
2543 if (ifstack
.Count
== 0)
2546 int state
= ifstack
.Peek ();
2547 return (state
& TAKING
) != 0;
2550 case PreprocessorDirective
.Elif
:
2551 if (ifstack
== null || ifstack
.Count
== 0){
2552 Error_UnexpectedDirective ("no #if for this #elif");
2555 int state
= ifstack
.Pop ();
2557 if ((state
& REGION
) != 0) {
2558 Report
.Error (1038, Location
, "#endregion directive expected");
2562 if ((state
& ELSE_SEEN
) != 0){
2563 Error_UnexpectedDirective ("#elif not valid after #else");
2567 if ((state
& TAKING
) != 0) {
2572 if (eval (arg
) && ((state
& PARENT_TAKING
) != 0)){
2573 ifstack
.Push (state
| TAKING
);
2577 ifstack
.Push (state
);
2581 case PreprocessorDirective
.Else
:
2582 if (ifstack
== null || ifstack
.Count
== 0){
2583 Error_UnexpectedDirective ("no #if for this #else");
2586 int state
= ifstack
.Peek ();
2588 if ((state
& REGION
) != 0) {
2589 Report
.Error (1038, Location
, "#endregion directive expected");
2593 if ((state
& ELSE_SEEN
) != 0){
2594 Error_UnexpectedDirective ("#else within #else");
2600 if (arg
.Length
!= 0) {
2601 Error_EndLineExpected ();
2606 if ((state
& PARENT_TAKING
) != 0) {
2607 ret
= (state
& TAKING
) == 0;
2615 ifstack
.Push (state
| ELSE_SEEN
);
2619 case PreprocessorDirective
.Define
:
2620 if (any_token_seen
){
2621 Error_TokensSeen ();
2622 return caller_is_taking
;
2624 PreProcessDefinition (true, arg
, caller_is_taking
);
2625 return caller_is_taking
;
2627 case PreprocessorDirective
.Undef
:
2628 if (any_token_seen
){
2629 Error_TokensSeen ();
2630 return caller_is_taking
;
2632 PreProcessDefinition (false, arg
, caller_is_taking
);
2633 return caller_is_taking
;
2635 case PreprocessorDirective
.Invalid
:
2636 Report
.Error (1024, Location
, "Wrong preprocessor directive");
2641 // These are only processed if we are in a `taking' block
2643 if (!caller_is_taking
)
2647 case PreprocessorDirective
.Error
:
2648 Report
.Error (1029, Location
, "#error: '{0}'", arg
);
2651 case PreprocessorDirective
.Warning
:
2652 Report
.Warning (1030, 1, Location
, "#warning: `{0}'", arg
);
2655 case PreprocessorDirective
.Pragma
:
2656 if (context
.Settings
.Version
== LanguageVersion
.ISO_1
) {
2657 Report
.FeatureIsNotAvailable (context
, Location
, "#pragma");
2660 ParsePragmaDirective (arg
);
2663 case PreprocessorDirective
.Line
:
2664 if (!PreProcessLine (arg
))
2667 "The line number specified for #line directive is missing or invalid");
2668 return caller_is_taking
;
2671 throw new NotImplementedException (directive
.ToString ());
2674 private int consume_string (bool quoted
)
2678 Location start_location
= Location
;
2680 start_location
= start_location
- 1;
2683 int reader_pos
= reader
.Position
;
2689 if (quoted
&& peek_char () == '"') {
2690 if (pos
== value_builder
.Length
)
2691 Array
.Resize (ref value_builder
, pos
* 2);
2693 value_builder
[pos
++] = (char) c
;
2702 s
= InternIdentifier (value_builder
, pos
);
2704 s
= new string (value_builder
, 0, pos
);
2706 ILiteralConstant res
= new StringLiteral (context
.BuiltinTypes
, s
, start_location
);
2709 res
.ParsedValue
= quoted
?
2710 reader
.ReadChars (reader_pos
- 2, reader
.Position
- 1) :
2711 reader
.ReadChars (reader_pos
- 1, reader
.Position
);
2714 return Token
.LITERAL
;
2719 Report
.Error (1010, Location
, "Newline in constant");
2720 val
= new StringLiteral (context
.BuiltinTypes
, new string (value_builder
, 0, pos
), start_location
);
2721 return Token
.LITERAL
;
2723 } else if (c
== '\\' && !quoted
) {
2725 c
= escape (c
, out surrogate
);
2728 if (surrogate
!= 0) {
2729 if (pos
== value_builder
.Length
)
2730 Array
.Resize (ref value_builder
, pos
* 2);
2732 value_builder
[pos
++] = (char) c
;
2735 } else if (c
== -1) {
2736 Report
.Error (1039, Location
, "Unterminated string literal");
2740 if (pos
== value_builder
.Length
)
2741 Array
.Resize (ref value_builder
, pos
* 2);
2743 value_builder
[pos
++] = (char) c
;
2747 private int consume_identifier (int s
)
2749 int res
= consume_identifier (s
, false);
2751 if (doc_state
== XmlCommentState
.Allowed
)
2752 doc_state
= XmlCommentState
.NotAllowed
;
2757 int consume_identifier (int c
, bool quoted
)
2760 // This method is very performance sensitive. It accounts
2761 // for approximately 25% of all parser time
2771 c
= escape (c
, out surrogate
);
2772 if (surrogate
!= 0) {
2773 id_builder
[pos
++] = (char) c
;
2778 id_builder
[pos
++] = (char) c
;
2784 if ((c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z') || c
== '_' || (c
>= '0' && c
<= '9')) {
2785 id_builder
[pos
++] = (char) c
;
2792 c
= escape (c
, out surrogate
);
2793 if (is_identifier_part_character ((char) c
))
2794 id_builder
[pos
++] = (char) c
;
2796 if (surrogate
!= 0) {
2802 } else if (Char
.IsLetter ((char) c
) || Char
.GetUnicodeCategory ((char) c
) == UnicodeCategory
.ConnectorPunctuation
) {
2803 id_builder
[pos
++] = (char) c
;
2810 } catch (IndexOutOfRangeException
) {
2811 Report
.Error (645, Location
, "Identifier too long (limit is 512 chars)");
2819 // Optimization: avoids doing the keyword lookup
2820 // on uppercase letters
2822 if (id_builder
[0] >= '_' && !quoted
) {
2823 int keyword
= GetKeyword (id_builder
, pos
);
2824 if (keyword
!= -1) {
2825 val
= LocatedToken
.Create (null, ref_line
, column
);
2830 string s
= InternIdentifier (id_builder
, pos
);
2831 val
= LocatedToken
.Create (s
, ref_line
, column
);
2832 if (quoted
&& parsing_attribute_section
)
2833 AddEscapedIdentifier (((LocatedToken
) val
).Location
);
2835 return Token
.IDENTIFIER
;
2838 static string InternIdentifier (char[] charBuffer
, int length
)
2841 // Keep identifiers in an array of hashtables to avoid needless
2844 var identifiers_group
= identifiers
[length
];
2846 if (identifiers_group
!= null) {
2847 if (identifiers_group
.TryGetValue (charBuffer
, out s
)) {
2851 // TODO: this should be number of files dependant
2852 // corlib compilation peaks at 1000 and System.Core at 150
2853 int capacity
= length
> 20 ? 10 : 100;
2854 identifiers_group
= new Dictionary
<char[], string> (capacity
, new IdentifiersComparer (length
));
2855 identifiers
[length
] = identifiers_group
;
2858 char[] chars
= new char[length
];
2859 Array
.Copy (charBuffer
, chars
, length
);
2861 s
= new string (charBuffer
, 0, length
);
2862 identifiers_group
.Add (chars
, s
);
2866 public int xtoken ()
2870 // Whether we have seen comments on the current line
2871 bool comments_seen
= false;
2872 while ((c
= get_char ()) != -1) {
2875 col
= ((col
- 1 + tab_size
) / tab_size
) * tab_size
;
2883 case 0xFEFF: // Ignore BOM anywhere in the file
2886 /* This is required for compatibility with .NET
2888 if (peek_char () == 0xBB) {
2891 if (get_char () == 0xBF)
2899 return consume_identifier (c
);
2902 val
= LocatedToken
.Create (ref_line
, col
);
2903 return Token
.OPEN_BRACE
;
2905 val
= LocatedToken
.Create (ref_line
, col
);
2906 return Token
.CLOSE_BRACE
;
2908 // To block doccomment inside attribute declaration.
2909 if (doc_state
== XmlCommentState
.Allowed
)
2910 doc_state
= XmlCommentState
.NotAllowed
;
2912 val
= LocatedToken
.Create (ref_line
, col
);
2914 if (parsing_block
== 0 || lambda_arguments_parsing
)
2915 return Token
.OPEN_BRACKET
;
2917 int next
= peek_char ();
2921 return Token
.OPEN_BRACKET
;
2929 next
= peek_token ();
2930 if (next
== Token
.COMMA
|| next
== Token
.CLOSE_BRACKET
)
2931 return Token
.OPEN_BRACKET
;
2933 return Token
.OPEN_BRACKET_EXPR
;
2935 return Token
.OPEN_BRACKET_EXPR
;
2938 LocatedToken
.CreateOptional (ref_line
, col
, ref val
);
2939 return Token
.CLOSE_BRACKET
;
2941 val
= LocatedToken
.Create (ref_line
, col
);
2943 // An expression versions of parens can appear in block context only
2945 if (parsing_block
!= 0 && !lambda_arguments_parsing
) {
2948 // Optmize most common case where we know that parens
2951 switch (current_token
) {
2952 case Token
.IDENTIFIER
:
2960 case Token
.DELEGATE
:
2961 case Token
.OP_GENERICS_GT
:
2962 return Token
.OPEN_PARENS
;
2965 // Optimize using peek
2966 int xx
= peek_char ();
2973 return Token
.OPEN_PARENS
;
2976 lambda_arguments_parsing
= true;
2978 d
= TokenizeOpenParens ();
2980 lambda_arguments_parsing
= false;
2984 return Token
.OPEN_PARENS
;
2986 LocatedToken
.CreateOptional (ref_line
, col
, ref val
);
2987 return Token
.CLOSE_PARENS
;
2989 LocatedToken
.CreateOptional (ref_line
, col
, ref val
);
2992 LocatedToken
.CreateOptional (ref_line
, col
, ref val
);
2993 return Token
.SEMICOLON
;
2995 val
= LocatedToken
.Create (ref_line
, col
);
2998 val
= LocatedToken
.Create (ref_line
, col
);
2999 return TokenizePossibleNullableType ();
3001 val
= LocatedToken
.Create (ref_line
, col
);
3002 if (parsing_generic_less_than
++ > 0)
3003 return Token
.OP_GENERICS_LT
;
3005 return TokenizeLessThan ();
3008 val
= LocatedToken
.Create (ref_line
, col
);
3016 if (parsing_generic_less_than
> 1 || (parsing_generic_less_than
== 1 && d
!= '>')) {
3017 parsing_generic_less_than
--;
3018 return Token
.OP_GENERICS_GT
;
3027 return Token
.OP_SHIFT_RIGHT_ASSIGN
;
3029 return Token
.OP_SHIFT_RIGHT
;
3035 val
= LocatedToken
.Create (ref_line
, col
);
3039 } else if (d
== '=') {
3040 d
= Token
.OP_ADD_ASSIGN
;
3048 val
= LocatedToken
.Create (ref_line
, col
);
3052 } else if (d
== '=')
3053 d
= Token
.OP_SUB_ASSIGN
;
3063 val
= LocatedToken
.Create (ref_line
, col
);
3064 if (peek_char () == '='){
3071 val
= LocatedToken
.Create (ref_line
, col
);
3082 return Token
.ASSIGN
;
3085 val
= LocatedToken
.Create (ref_line
, col
);
3089 return Token
.OP_AND
;
3093 return Token
.OP_AND_ASSIGN
;
3095 return Token
.BITWISE_AND
;
3098 val
= LocatedToken
.Create (ref_line
, col
);
3106 return Token
.OP_OR_ASSIGN
;
3108 return Token
.BITWISE_OR
;
3111 val
= LocatedToken
.Create (ref_line
, col
);
3112 if (peek_char () == '='){
3114 return Token
.OP_MULT_ASSIGN
;
3121 val
= LocatedToken
.Create (ref_line
, col
);
3123 return Token
.OP_DIV_ASSIGN
;
3126 // Handle double-slash comments.
3129 if (doc_processing
) {
3130 if (peek_char () == '/') {
3132 // Don't allow ////.
3133 if ((d
= peek_char ()) != '/') {
3134 if (doc_state
== XmlCommentState
.Allowed
)
3135 handle_one_line_xml_comment ();
3136 else if (doc_state
== XmlCommentState
.NotAllowed
)
3137 WarningMisplacedComment (Location
- 3);
3140 if (xml_comment_buffer
.Length
> 0)
3141 doc_state
= XmlCommentState
.NotAllowed
;
3145 while ((d
= get_char ()) != -1 && d
!= '\n');
3147 any_token_seen
|= tokens_seen
;
3148 tokens_seen
= false;
3149 comments_seen
= false;
3151 } else if (d
== '*'){
3153 bool docAppend
= false;
3154 if (doc_processing
&& peek_char () == '*') {
3156 // But when it is /**/, just do nothing.
3157 if (peek_char () == '/') {
3161 if (doc_state
== XmlCommentState
.Allowed
)
3163 else if (doc_state
== XmlCommentState
.NotAllowed
) {
3164 WarningMisplacedComment (Location
- 2);
3168 int current_comment_start
= 0;
3170 current_comment_start
= xml_comment_buffer
.Length
;
3171 xml_comment_buffer
.Append (Environment
.NewLine
);
3174 while ((d
= get_char ()) != -1){
3175 if (d
== '*' && peek_char () == '/'){
3177 comments_seen
= true;
3181 xml_comment_buffer
.Append ((char) d
);
3184 any_token_seen
|= tokens_seen
;
3185 tokens_seen
= false;
3187 // Reset 'comments_seen' just to be consistent.
3188 // It doesn't matter either way, here.
3190 comments_seen
= false;
3194 Report
.Error (1035, Location
, "End-of-file found, '*/' expected");
3197 update_formatted_doc_comment (current_comment_start
);
3200 val
= LocatedToken
.Create (ref_line
, col
);
3204 val
= LocatedToken
.Create (ref_line
, col
);
3205 if (peek_char () == '='){
3207 return Token
.OP_MOD_ASSIGN
;
3209 return Token
.PERCENT
;
3212 val
= LocatedToken
.Create (ref_line
, col
);
3213 if (peek_char () == '='){
3215 return Token
.OP_XOR_ASSIGN
;
3217 return Token
.CARRET
;
3220 val
= LocatedToken
.Create (ref_line
, col
);
3221 if (peek_char () == ':') {
3223 return Token
.DOUBLE_COLON
;
3227 case '0': case '1': case '2': case '3': case '4':
3228 case '5': case '6': case '7': case '8': case '9':
3230 return is_number (c
);
3232 case '\n': // white space
3233 any_token_seen
|= tokens_seen
;
3234 tokens_seen
= false;
3235 comments_seen
= false;
3241 if (d
>= '0' && d
<= '9')
3242 return is_number (c
);
3244 LocatedToken
.CreateOptional (ref_line
, col
, ref val
);
3248 if (tokens_seen
|| comments_seen
) {
3249 Eror_WrongPreprocessorLocation ();
3253 if (ParsePreprocessingDirective (true))
3256 bool directive_expected
= false;
3257 while ((c
= get_char ()) != -1) {
3259 directive_expected
= true;
3260 } else if (!directive_expected
) {
3261 // TODO: Implement comment support for disabled code and uncomment this code
3263 // Eror_WrongPreprocessorLocation ();
3264 // return Token.ERROR;
3269 if (c
== ' ' || c
== '\t' || c
== '\n' || c
== '\f' || c
== '\v' )
3273 if (ParsePreprocessingDirective (false))
3276 directive_expected
= false;
3280 tokens_seen
= false;
3287 return consume_string (false);
3290 return TokenizeBackslash ();
3296 return consume_string (true);
3299 if (is_identifier_start_character (c
)){
3300 return consume_identifier (c
, true);
3303 Report
.Error (1646, Location
, "Keyword, identifier, or string expected after verbatim specifier: @");
3306 case EvalStatementParserCharacter
:
3307 return Token
.EVAL_STATEMENT_PARSER
;
3308 case EvalCompilationUnitParserCharacter
:
3309 return Token
.EVAL_COMPILATION_UNIT_PARSER
;
3310 case EvalUsingDeclarationsParserCharacter
:
3311 return Token
.EVAL_USING_DECLARATIONS_UNIT_PARSER
;
3312 case DocumentationXref
:
3313 return Token
.DOC_SEE
;
3316 if (is_identifier_start_character (c
)) {
3318 return consume_identifier (c
);
3321 if (char.IsWhiteSpace ((char) c
))
3324 Report
.Error (1056, Location
, "Unexpected character `{0}'", ((char) c
).ToString ());
3329 return Token
.COMPLETE_COMPLETION
;
3332 return Token
.GENERATE_COMPLETION
;
3339 int TokenizeBackslash ()
3341 int c
= get_char ();
3344 val
= new CharLiteral (context
.BuiltinTypes
, (char) c
, Location
);
3345 Report
.Error (1011, Location
, "Empty character literal");
3346 return Token
.LITERAL
;
3350 Report
.Error (1010, Location
, "Newline in constant");
3355 c
= escape (c
, out d
);
3359 throw new NotImplementedException ();
3361 val
= new CharLiteral (context
.BuiltinTypes
, (char) c
, Location
);
3365 Report
.Error (1012, Location
, "Too many characters in character literal");
3367 // Try to recover, read until newline or next "'"
3368 while ((c
= get_char ()) != -1) {
3369 if (c
== '\n' || c
== '\'')
3374 return Token
.LITERAL
;
3377 int TokenizeLessThan ()
3380 if (handle_typeof
) {
3382 if (parse_generic_dimension (out d
)) {
3385 return Token
.GENERIC_DIMENSION
;
3390 // Save current position and parse next token.
3392 if (parse_less_than ()) {
3393 if (parsing_generic_declaration
&& (parsing_generic_declaration_doc
|| token () != Token
.DOT
)) {
3394 d
= Token
.OP_GENERICS_LT_DECL
;
3396 d
= Token
.OP_GENERICS_LT
;
3403 parsing_generic_less_than
= 0;
3412 return Token
.OP_SHIFT_LEFT_ASSIGN
;
3414 return Token
.OP_SHIFT_LEFT
;
3425 // Handles one line xml comment
3427 private void handle_one_line_xml_comment ()
3430 while ((c
= peek_char ()) == ' ')
3431 get_char (); // skip heading whitespaces.
3432 while ((c
= peek_char ()) != -1 && c
!= '\n' && c
!= '\r') {
3433 xml_comment_buffer
.Append ((char) get_char ());
3435 if (c
== '\r' || c
== '\n')
3436 xml_comment_buffer
.Append (Environment
.NewLine
);
3440 // Remove heading "*" in Javadoc-like xml documentation.
3442 private void update_formatted_doc_comment (int current_comment_start
)
3444 int length
= xml_comment_buffer
.Length
- current_comment_start
;
3445 string [] lines
= xml_comment_buffer
.ToString (
3446 current_comment_start
,
3447 length
).Replace ("\r", "").Split ('\n');
3449 // The first line starts with /**, thus it is not target
3450 // for the format check.
3451 for (int i
= 1; i
< lines
.Length
; i
++) {
3452 string s
= lines
[i
];
3453 int idx
= s
.IndexOf ('*');
3456 if (i
< lines
.Length
- 1)
3460 head
= s
.Substring (0, idx
);
3461 foreach (char c
in head
)
3464 lines
[i
] = s
.Substring (idx
+ 1);
3466 xml_comment_buffer
.Remove (current_comment_start
, length
);
3467 xml_comment_buffer
.Insert (current_comment_start
, String
.Join (Environment
.NewLine
, lines
));
3471 // Checks if there was incorrect doc comments and raise
3474 public void check_incorrect_doc_comment ()
3476 if (xml_comment_buffer
.Length
> 0)
3477 WarningMisplacedComment (Location
);
3481 // Consumes the saved xml comment lines (if any)
3482 // as for current target member or type.
3484 public string consume_doc_comment ()
3486 if (xml_comment_buffer
.Length
> 0) {
3487 string ret
= xml_comment_buffer
.ToString ();
3488 reset_doc_comment ();
3495 get { return context.Report; }
3498 void reset_doc_comment ()
3500 xml_comment_buffer
.Length
= 0;
3503 public void cleanup ()
3505 if (ifstack
!= null && ifstack
.Count
>= 1) {
3506 int state
= ifstack
.Pop ();
3507 if ((state
& REGION
) != 0)
3508 Report
.Error (1038, Location
, "#endregion directive expected");
3510 Report
.Error (1027, Location
, "Expected `#endif' directive");
3516 // Indicates whether it accepts XML documentation or not.
3518 public enum XmlCommentState
{
3519 // comment is allowed in this state.
3521 // comment is not allowed in this state.
3523 // once comments appeared when it is NotAllowed, then the
3524 // state is changed to it, until the state is changed to