1
ļ»æ// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
2
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
5
using System.Collections;
6
using System.Collections.Generic;
7
using System.Diagnostics;
8
using System.Globalization;
14
namespace ICSharpCode.NRefactory.VB.Parser
16
public class VBLexer : IDisposable
19
bool isAtLineBegin = false; // TODO: handle line begin, if neccessarry
20
bool misreadExclamationMarkAsTypeCharacter;
21
bool encounteredLineContinuation;
27
Stack<XmlModeInfo> xmlModeStack = new Stack<XmlModeInfo>();
29
public VBLexer(TextReader reader)
31
this.reader = new LATextReader(reader);
32
ef = new ExpressionFinder();
35
public VBLexer(TextReader reader, VBLexerMemento state) : this(reader)
37
SetInitialLocation(new TextLocation(state.Line, state.Column));
38
lastToken = new Token(state.PrevTokenKind, 0, 0);
39
ef = new ExpressionFinder(state.ExpressionFinder);
40
lineEnd = state.LineEnd;
41
isAtLineBegin = state.IsAtLineBegin;
42
encounteredLineContinuation = state.EncounteredLineContinuation;
43
misreadExclamationMarkAsTypeCharacter = state.MisreadExclamationMarkAsTypeCharacter;
44
xmlModeStack = new Stack<XmlModeInfo>(state.XmlModeInfoStack.Select(i => (XmlModeInfo)i.Clone()).Reverse());
45
inXmlMode = state.InXmlMode;
50
if (misreadExclamationMarkAsTypeCharacter) {
51
misreadExclamationMarkAsTypeCharacter = false;
52
return new Token(Tokens.ExclamationMark, Col - 1, Line);
57
TextLocation startLocation = new TextLocation(Line, Col);
58
int nextChar = ReaderRead();
60
return new Token(Tokens.EOF, Col, Line, string.Empty);
61
char ch = (char)nextChar;
63
CheckXMLState(startLocation);
64
if (inXmlMode && xmlModeStack.Peek().level <= 0 && !xmlModeStack.Peek().isDocumentStart && !xmlModeStack.Peek().inXmlTag) {
65
XmlModeInfo info = xmlModeStack.Peek();
69
while (peek != -1 && XmlConvert.IsWhitespaceChar((char)peek)) {
71
peek = ReaderPeek(step);
74
if (peek == '<' && (ReaderPeek(step + 1) == '!' || ReaderPeek(step + 1) == '?')) {
76
for (int i = 0; i < step + 2; i++)
77
lastCh = (char)ReaderRead();
80
return ReadXmlCommentOrCData(Col - 2, Line);
82
return ReadXmlProcessingInstruction(Col - 2, Line);
91
XmlModeInfo info = xmlModeStack.Peek();
96
if (ReaderPeek() == '/') {
98
info.inXmlCloseTag = true;
99
return new Token(Tokens.XmlOpenEndTag, new TextLocation(y, x), new TextLocation(Line, Col));
101
if (ReaderPeek() == '%' && ReaderPeek(1) == '=') {
103
ReaderRead(); ReaderRead();
104
return new Token(Tokens.XmlStartInlineVB, new TextLocation(y, x), new TextLocation(Line, Col));
106
if (ReaderPeek() == '?') {
108
Token t = ReadXmlProcessingInstruction(x, y);
111
if (ReaderPeek() == '!') {
113
Token token = ReadXmlCommentOrCData(x, y);
117
info.isDocumentStart = false;
118
info.inXmlTag = true;
119
return new Token(Tokens.XmlOpenTag, x, y);
121
if (ReaderPeek() == '>') {
123
info.inXmlTag = false;
125
return new Token(Tokens.XmlCloseTagEmptyElement, new TextLocation(y, x), new TextLocation(Line, Col));
129
if (info.inXmlCloseTag)
131
info.inXmlTag = info.inXmlCloseTag = false;
132
return new Token(Tokens.XmlCloseTag, x, y);
134
return new Token(Tokens.Assign, x, y);
137
string s = ReadXmlString(ch);
138
return new Token(Tokens.LiteralString, x, y, ch + s + ch, s);
140
if (info.inXmlCloseTag || info.inXmlTag) {
141
if (XmlConvert.IsWhitespaceChar(ch))
143
return new Token(Tokens.Identifier, x, y, ReadXmlIdent(ch));
145
string content = ReadXmlContent(ch);
146
return new Token(Tokens.XmlContent, startLocation, new TextLocation(Line, Col), content, null);
151
#region Standard Mode
152
if (Char.IsWhiteSpace(ch)) {
153
if (HandleLineEnd(ch)) {
155
// second line end before getting to a token
156
// -> here was a blank line
157
// specialTracker.AddEndOfLine(startLocation);
160
return new Token(Tokens.EOL, startLocation, new TextLocation(Line, Col), null, null);
166
if (ReaderPeek() == -1) {
167
errors.Error(Line, Col, String.Format("No EOF expected after _"));
168
return new Token(Tokens.EOF, Col, Line, string.Empty);
170
if (!Char.IsWhiteSpace((char)ReaderPeek())) {
173
string s = ReadIdent('_');
175
return new Token(Tokens.Identifier, x, y, s);
177
encounteredLineContinuation = true;
178
ch = (char)ReaderRead();
180
bool oldLineEnd = lineEnd;
182
while (Char.IsWhiteSpace(ch)) {
183
if (HandleLineEnd(ch)) {
187
if (ReaderPeek() != -1) {
188
ch = (char)ReaderRead();
190
errors.Error(Line, Col, String.Format("No EOF expected after _"));
191
return new Token(Tokens.EOF, Col, Line, string.Empty);
195
errors.Error(Line, Col, String.Format("NewLine expected"));
197
lineEnd = oldLineEnd;
202
while (Char.IsWhiteSpace((char)ReaderPeek())) {
205
if (Char.IsDigit((char)ReaderPeek())) {
208
string s = ReadDate();
209
DateTime time = new DateTime(1, 1, 1, 0, 0, 0);
211
time = DateTime.Parse(s, System.Globalization.CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault);
212
} catch (Exception e) {
213
errors.Error(Line, Col, String.Format("Invalid date time {0}", e));
215
return new Token(Tokens.LiteralDate, x, y, s, time);
217
ReadPreprocessorDirective();
222
if (ch == '[') { // Identifier
224
if (ReaderPeek() == -1) {
225
errors.Error(Line, Col, String.Format("Identifier expected"));
227
ch = (char)ReaderRead();
228
if (ch == ']' || Char.IsWhiteSpace(ch)) {
229
errors.Error(Line, Col, String.Format("Identifier expected"));
233
string s = ReadIdent(ch);
234
if (ReaderPeek() == -1) {
235
errors.Error(Line, Col, String.Format("']' expected"));
237
ch = (char)ReaderRead();
239
errors.Error(Line, Col, String.Format("']' expected"));
241
return new Token(Tokens.Identifier, x, y, s);
243
if (Char.IsLetter(ch)) {
247
string s = ReadIdent(ch, out typeCharacter);
248
if (typeCharacter == '\0') {
249
int keyWordToken = Keywords.GetToken(s);
250
if (keyWordToken >= 0) {
251
// handle 'REM' comments
252
if (keyWordToken == Tokens.Rem) {
256
return new Token(Tokens.EOL, Col, Line, "\n");
262
return new Token(keyWordToken, x, y, s);
267
return new Token(Tokens.Identifier, x, y, s);
270
if (Char.IsDigit(ch)) {
272
return ReadDigit(ch, Col - 1);
276
if (ReaderPeek() == -1) {
277
return ReadOperator('&');
279
ch = (char)ReaderPeek();
280
if (Char.ToUpper(ch, CultureInfo.InvariantCulture) == 'H' || Char.ToUpper(ch, CultureInfo.InvariantCulture) == 'O') {
281
return ReadDigit('&', Col - 1);
283
return ReadOperator('&');
285
if (ch == '\'' || ch == '\u2018' || ch == '\u2019') {
291
return new Token(Tokens.EOL, x, y, "\n");
299
string s = ReadString();
300
if (ReaderPeek() != -1 && (ReaderPeek() == 'C' || ReaderPeek() == 'c')) {
303
errors.Error(Line, Col, String.Format("Chars can only have Length 1 "));
308
return new Token(Tokens.LiteralCharacter, x, y, '"' + s + "\"C", s[0]);
310
return new Token(Tokens.LiteralString, x, y, '"' + s + '"', s);
312
if (ch == '%' && ReaderPeek() == '>') {
317
return new Token(Tokens.XmlEndInlineVB, new TextLocation(y, x), new TextLocation(Line, Col));
320
if (ch == '<' && (ef.NextTokenIsPotentialStartOfExpression || ef.NextTokenIsStartOfImportsOrAccessExpression)) {
321
xmlModeStack.Push(new XmlModeInfo(ef.NextTokenIsStartOfImportsOrAccessExpression));
322
XmlModeInfo info = xmlModeStack.Peek();
326
if (ReaderPeek() == '/') {
328
info.inXmlCloseTag = true;
329
return new Token(Tokens.XmlOpenEndTag, new TextLocation(y, x), new TextLocation(Line, Col));
331
// should we allow <%= at start of an expression? not valid with vbc ...
332
if (ReaderPeek() == '%' && ReaderPeek(1) == '=') {
334
ReaderRead(); ReaderRead();
335
return new Token(Tokens.XmlStartInlineVB, new TextLocation(y, x), new TextLocation(Line, Col));
337
if (ReaderPeek() == '!') {
339
Token t = ReadXmlCommentOrCData(x, y);
342
if (ReaderPeek() == '?') {
344
Token t = ReadXmlProcessingInstruction(x, y);
345
info.isDocumentStart = t.val.Trim().StartsWith("xml", StringComparison.OrdinalIgnoreCase);
348
info.inXmlTag = true;
350
return new Token(Tokens.XmlOpenTag, x, y);
352
Token token = ReadOperator(ch);
359
errors.Error(Line, Col, String.Format("Unknown char({0}) which can't be read", ch));
364
void CheckXMLState(TextLocation startLocation)
366
if (inXmlMode && !xmlModeStack.Any())
367
throw new InvalidOperationException("invalid XML stack state at " + startLocation);
374
Token t = NextInternal();
375
if (t.kind == Tokens.EOL) {
376
Debug.Assert(t.next == null); // NextInternal() must return only 1 token
377
t.next = NextInternal();
378
Debug.Assert(t.next.next == null);
379
if (SkipEOL(prevToken.kind, t.next.kind)) {
383
encounteredLineContinuation = false;
384
// inform EF only once we're sure it's really a token
385
// this means we inform it about EOL tokens "1 token too late", but that's not a problem because
386
// XML literals cannot start immediately after an EOL token
388
if (t.next != null) {
389
// Next() isn't called again when it returns 2 tokens, so we need to process both tokens
390
ef.InformToken(t.next);
396
Debug.Assert(t != null);
400
/// <remarks>see VB language specification 10; pg. 6</remarks>
401
bool SkipEOL(int prevTokenKind, int nextTokenKind)
403
// exception directly after _
404
if (encounteredLineContinuation) {
405
return encounteredLineContinuation = false;
409
// after a comma (,), open parenthesis ((), open curly brace ({), or open embedded expression (<%=)
410
if (new[] { Tokens.Comma, Tokens.OpenParenthesis, Tokens.OpenCurlyBrace, Tokens.XmlStartInlineVB }
411
.Contains(prevTokenKind))
415
// after a member qualifier (. or .@ or ...), provided that something is being qualified (i.e. is not
416
// using an implicit With context)
417
if (new[] { Tokens.Dot, Tokens.DotAt, Tokens.TripleDot }.Contains(prevTokenKind)
418
&& !ef.WasQualifierTokenAtStart)
422
// before a close parenthesis ()), close curly brace (}), or close embedded expression (%>)
423
if (new[] { Tokens.CloseParenthesis, Tokens.CloseCurlyBrace, Tokens.XmlEndInlineVB }
424
.Contains(nextTokenKind))
428
// after a less-than (<) in an attribute context
429
if (prevTokenKind == Tokens.LessThan && ef.InContext(Context.Attribute))
433
// before a greater-than (>) in an attribute context
434
if (nextTokenKind == Tokens.GreaterThan && ef.InContext(Context.Attribute))
438
// after a greater-than (>) in a non-file-level attribute context
439
if (ef.WasNormalAttribute && prevTokenKind == Tokens.GreaterThan)
443
// before and after query operators (Where, Order, Select, etc.)
444
var queryOperators = new int[] { Tokens.From, Tokens.Aggregate, Tokens.Select, Tokens.Distinct,
445
Tokens.Where, Tokens.Order, Tokens.By, Tokens.Ascending, Tokens.Descending, Tokens.Take,
446
Tokens.Skip, Tokens.Let, Tokens.Group, Tokens.Into, Tokens.On, Tokens.While, Tokens.Join };
447
if (ef.InContext(Context.Query)) {
448
// Ascending, Descending, Distinct are special
449
// fixes http://community.sharpdevelop.net/forums/p/12068/32893.aspx#32893
450
var specialQueryOperators = new int[] { Tokens.Ascending, Tokens.Descending, Tokens.Distinct };
451
if (specialQueryOperators.Contains(prevTokenKind) && !queryOperators.Contains(nextTokenKind))
454
if ((queryOperators.Contains(prevTokenKind) || queryOperators.Contains(nextTokenKind)))
459
// after binary operators (+, -, /, *, etc.) in an expression context
460
if (new[] { Tokens.Plus, Tokens.Minus, Tokens.Div, Tokens.DivInteger, Tokens.Times, Tokens.Mod, Tokens.Power,
461
Tokens.Assign, Tokens.NotEqual, Tokens.LessThan, Tokens.LessEqual, Tokens.GreaterThan, Tokens.GreaterEqual,
462
Tokens.Like, Tokens.ConcatString, Tokens.AndAlso, Tokens.OrElse, Tokens.And, Tokens.Or, Tokens.Xor,
463
Tokens.ShiftLeft, Tokens.ShiftRight }.Contains(prevTokenKind) && ef.CurrentBlock.context == Context.Expression)
467
// after assignment operators (=, :=, +=, -=, etc.) in any context.
468
if (new[] { Tokens.Assign, Tokens.ColonAssign, Tokens.ConcatStringAssign, Tokens.DivAssign,
469
Tokens.DivIntegerAssign, Tokens.MinusAssign, Tokens.PlusAssign, Tokens.PowerAssign,
470
Tokens.ShiftLeftAssign, Tokens.ShiftRightAssign, Tokens.TimesAssign }.Contains(prevTokenKind))
477
/// Reads the next token.
479
/// <returns>A <see cref="Token"/> object.</returns>
480
public Token NextToken()
482
if (curToken == null) { // first call of NextToken()
484
//Console.WriteLine("Tok:" + Tokens.GetTokenString(curToken.kind) + " --- " + curToken.val);
488
lastToken = curToken;
490
if (curToken.next == null) {
491
curToken.next = Next();
494
curToken = curToken.next;
496
if (curToken.kind == Tokens.EOF && !(lastToken.kind == Tokens.EOL)) { // be sure that before EOF there is an EOL token
497
curToken = new Token(Tokens.EOL, curToken.col, curToken.line, string.Empty);
498
curToken.next = new Token(Tokens.EOF, curToken.col, curToken.line, string.Empty);
500
//Console.WriteLine("Tok:" + Tokens.GetTokenString(curToken.kind) + " --- " + curToken.val);
505
string ReadIdent(char ch)
508
return ReadIdent(ch, out typeCharacter);
511
string ReadIdent(char ch, out char typeCharacter)
513
typeCharacter = '\0';
515
if (ef.ReadXmlIdentifier) {
516
ef.ReadXmlIdentifier = false;
517
return ReadXmlIdent(ch);
523
while ((peek = ReaderPeek()) != -1 && (Char.IsLetterOrDigit(ch = (char)peek) || ch == '_')) {
525
sb.Append(ch.ToString());
528
return sb.ToString();
531
if ("%&@!#$".IndexOf((char)peek) != -1) {
532
typeCharacter = (char)peek;
534
if (typeCharacter == '!') {
536
if (peek != -1 && (peek == '_' || peek == '[' || char.IsLetter((char)peek))) {
537
misreadExclamationMarkAsTypeCharacter = true;
541
return sb.ToString();
544
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1818:DoNotConcatenateStringsInsideLoops")]
545
Token ReadDigit(char ch, int x)
558
bool isSingle = false;
559
bool isDouble = false;
560
bool isDecimal = false;
562
if (ReaderPeek() == -1) {
564
errors.Error(Line, Col, String.Format("digit expected"));
566
return new Token(Tokens.LiteralInteger, x, y, sb.ToString() ,ch - '0');
569
if (Char.IsDigit((char)ReaderPeek())) {
570
isDouble = true; // double is default
571
if (isHex || isOct) {
572
errors.Error(Line, Col, String.Format("No hexadecimal or oktadecimal floating point values allowed"));
574
while (ReaderPeek() != -1 && Char.IsDigit((char)ReaderPeek())){ // read decimal digits beyond the dot
575
digit += (char)ReaderRead();
578
} else if (ch == '&' && PeekUpperChar() == 'H') {
579
const string hex = "0123456789ABCDEF";
580
sb.Append((char)ReaderRead()); // skip 'H'
581
while (ReaderPeek() != -1 && hex.IndexOf(PeekUpperChar()) != -1) {
582
ch = (char)ReaderRead();
584
digit += Char.ToUpper(ch, CultureInfo.InvariantCulture);
587
} else if (ReaderPeek() != -1 && ch == '&' && PeekUpperChar() == 'O') {
588
const string okt = "01234567";
589
sb.Append((char)ReaderRead()); // skip 'O'
590
while (ReaderPeek() != -1 && okt.IndexOf(PeekUpperChar()) != -1) {
591
ch = (char)ReaderRead();
593
digit += Char.ToUpper(ch, CultureInfo.InvariantCulture);
597
while (ReaderPeek() != -1 && Char.IsDigit((char)ReaderPeek())) {
598
ch = (char)ReaderRead();;
604
if (digit.Length == 0) {
605
errors.Error(Line, Col, String.Format("digit expected"));
606
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), 0);
609
if (ReaderPeek() != -1 && "%&SILU".IndexOf(PeekUpperChar()) != -1 || isHex || isOct) {
610
bool unsigned = false;
611
if (ReaderPeek() != -1) {
612
ch = (char)ReaderPeek();
614
ch = Char.ToUpper(ch, CultureInfo.InvariantCulture);
615
unsigned = ch == 'U';
617
ReaderRead(); // read the U
618
ch = (char)ReaderPeek();
620
ch = Char.ToUpper(ch, CultureInfo.InvariantCulture);
621
if (ch != 'I' && ch != 'L' && ch != 'S') {
622
errors.Error(Line, Col, "Invalid type character: U" + ch);
630
for (int i = 0; i < digit.Length; ++i) {
631
number = number * 8 + digit[i] - '0';
635
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), (ushort)number);
637
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), (short)number);
638
} else if (ch == '%' || ch == 'I') {
640
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), (uint)number);
642
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), (int)number);
643
} else if (ch == '&' || ch == 'L') {
645
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), (ulong)number);
647
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), (long)number);
649
if (number > uint.MaxValue) {
650
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), unchecked((long)number));
652
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), unchecked((int)number));
659
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), UInt16.Parse(digit, isHex ? NumberStyles.HexNumber : NumberStyles.Number));
661
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), Int16.Parse(digit, isHex ? NumberStyles.HexNumber : NumberStyles.Number));
662
} else if (ch == '%' || ch == 'I') {
665
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), UInt32.Parse(digit, isHex ? NumberStyles.HexNumber : NumberStyles.Number));
667
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), Int32.Parse(digit, isHex ? NumberStyles.HexNumber : NumberStyles.Number));
668
} else if (ch == '&' || ch == 'L') {
671
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), UInt64.Parse(digit, isHex ? NumberStyles.HexNumber : NumberStyles.Number));
673
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), Int64.Parse(digit, isHex ? NumberStyles.HexNumber : NumberStyles.Number));
675
ulong number = UInt64.Parse(digit, NumberStyles.HexNumber);
676
if (number > uint.MaxValue) {
677
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), unchecked((long)number));
679
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), unchecked((int)number));
682
} catch (OverflowException ex) {
683
errors.Error(Line, Col, ex.Message);
684
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), 0);
685
} catch (FormatException) {
686
errors.Error(Line, Col, String.Format("{0} is not a parseable number", digit));
687
return new Token(Tokens.LiteralInteger, x, y, sb.ToString(), 0);
690
Token nextToken = null; // if we accidently read a 'dot'
691
if (!isDouble && ReaderPeek() == '.') { // read floating point number
693
if (ReaderPeek() != -1 && Char.IsDigit((char)ReaderPeek())) {
694
isDouble = true; // double is default
695
if (isHex || isOct) {
696
errors.Error(Line, Col, String.Format("No hexadecimal or oktadecimal floating point values allowed"));
699
while (ReaderPeek() != -1 && Char.IsDigit((char)ReaderPeek())){ // read decimal digits beyond the dot
700
digit += (char)ReaderRead();
703
nextToken = new Token(Tokens.Dot, Col - 1, Line);
707
if (ReaderPeek() != -1 && PeekUpperChar() == 'E') { // read exponent
709
digit += (char)ReaderRead();
710
if (ReaderPeek() != -1 && (ReaderPeek() == '-' || ReaderPeek() == '+')) {
711
digit += (char)ReaderRead();
713
while (ReaderPeek() != -1 && Char.IsDigit((char)ReaderPeek())) { // read exponent value
714
digit += (char)ReaderRead();
718
if (ReaderPeek() != -1) {
719
switch (PeekUpperChar()) {
740
return new Token(Tokens.LiteralSingle, x, y, sb.ToString(), Single.Parse(digit, CultureInfo.InvariantCulture));
743
return new Token(Tokens.LiteralDecimal, x, y, sb.ToString(), Decimal.Parse(digit, NumberStyles.Currency | NumberStyles.AllowExponent, CultureInfo.InvariantCulture));
746
return new Token(Tokens.LiteralDouble, x, y, sb.ToString(), Double.Parse(digit, CultureInfo.InvariantCulture));
748
} catch (FormatException) {
749
errors.Error(Line, Col, String.Format("{0} is not a parseable number", digit));
751
return new Token(Tokens.LiteralSingle, x, y, sb.ToString(), 0f);
753
return new Token(Tokens.LiteralDecimal, x, y, sb.ToString(), 0m);
755
return new Token(Tokens.LiteralDouble, x, y, sb.ToString(), 0.0);
759
token = new Token(Tokens.LiteralInteger, x, y, sb.ToString(), Int32.Parse(digit, isHex ? NumberStyles.HexNumber : NumberStyles.Number));
760
} catch (Exception) {
762
token = new Token(Tokens.LiteralInteger, x, y, sb.ToString(), Int64.Parse(digit, isHex ? NumberStyles.HexNumber : NumberStyles.Number));
763
} catch (FormatException) {
764
errors.Error(Line, Col, String.Format("{0} is not a parseable number", digit));
765
// fallback, when nothing helps :)
766
token = new Token(Tokens.LiteralInteger, x, y, sb.ToString(), 0);
767
} catch (OverflowException) {
768
errors.Error(Line, Col, String.Format("{0} is too long for a integer literal", digit));
769
// fallback, when nothing helps :)
770
token = new Token(Tokens.LiteralInteger, x, y, sb.ToString(), 0);
773
token.next = nextToken;
777
void ReadPreprocessorDirective()
779
TextLocation start = new TextLocation(Line, Col - 1);
780
string directive = ReadIdent('#');
781
// TODO : expression parser for PP directives
782
// needed for proper conversion to e. g. C#
783
string argument = ReadToEndOfLine();
784
// this.specialTracker.AddPreprocessingDirective(new PreprocessingDirective(directive, argument.Trim(), start, new AstLocation(start.Line, start.Column + directive.Length + argument.Length)));
792
while ((nextChar = ReaderRead()) != -1) {
796
} else if (ch == '\n') {
797
errors.Error(Line, Col, String.Format("No return allowed inside Date literal"));
803
errors.Error(Line, Col, String.Format("End of File reached before Date literal terminated"));
805
return sb.ToString();
813
while ((nextChar = ReaderRead()) != -1) {
816
if (ReaderPeek() != -1 && ReaderPeek() == '"') {
822
} else if (ch == '\n') {
823
errors.Error(Line, Col, String.Format("No return allowed inside String literal"));
829
errors.Error(Line, Col, String.Format("End of File reached before String terminated "));
831
return sb.ToString();
836
TextLocation startPos = new TextLocation(Line, Col);
838
StringBuilder curWord = specialCommentHash != null ? new StringBuilder() : null;
839
int missingApostrophes = 2; // no. of ' missing until it is a documentation comment
841
while ((nextChar = ReaderRead()) != -1) {
842
char ch = (char)nextChar;
844
if (HandleLineEnd(ch)) {
850
if (missingApostrophes > 0) {
851
if (ch == '\'' || ch == '\u2018' || ch == '\u2019') {
852
if (--missingApostrophes == 0) {
853
// specialTracker.StartComment(CommentType.Documentation, isAtLineBegin, startPos);
857
// specialTracker.StartComment(CommentType.SingleLine, isAtLineBegin, startPos);
858
missingApostrophes = 0;
862
if (specialCommentHash != null) {
863
if (Char.IsLetter(ch)) {
866
string tag = curWord.ToString();
868
if (specialCommentHash.ContainsKey(tag)) {
869
TextLocation p = new TextLocation(Line, Col);
870
string comment = ch + ReadToEndOfLine();
871
// this.TagComments.Add(new TagComment(tag, comment, isAtLineBegin, p, new Location(Col, Line)));
878
// if (missingApostrophes > 0) {
879
// specialTracker.StartComment(CommentType.SingleLine, isAtLineBegin, startPos);
881
// specialTracker.AddString(sb.ToString());
882
// specialTracker.FinishComment(new Location(Col, Line));
885
Token ReadOperator(char ch)
891
switch (ReaderPeek()) {
894
return new Token(Tokens.PlusAssign, x, y);
898
return new Token(Tokens.Plus, x, y);
900
switch (ReaderPeek()) {
903
return new Token(Tokens.MinusAssign, x, y);
907
return new Token(Tokens.Minus, x, y);
909
switch (ReaderPeek()) {
912
return new Token(Tokens.TimesAssign, x, y);
916
return new Token(Tokens.Times, x, y, "*");
918
switch (ReaderPeek()) {
921
return new Token(Tokens.DivAssign, x, y);
925
return new Token(Tokens.Div, x, y);
927
switch (ReaderPeek()) {
930
return new Token(Tokens.DivIntegerAssign, x, y);
934
return new Token(Tokens.DivInteger, x, y);
936
switch (ReaderPeek()) {
939
return new Token(Tokens.ConcatStringAssign, x, y);
943
return new Token(Tokens.ConcatString, x, y);
945
switch (ReaderPeek()) {
948
return new Token(Tokens.PowerAssign, x, y);
952
return new Token(Tokens.Power, x, y);
954
if (ReaderPeek() == '=') {
956
return new Token(Tokens.ColonAssign, x, y);
958
return new Token(Tokens.Colon, x, y);
960
return new Token(Tokens.Assign, x, y);
962
switch (ReaderPeek()) {
965
return new Token(Tokens.LessEqual, x, y);
968
return new Token(Tokens.NotEqual, x, y);
971
switch (ReaderPeek()) {
974
return new Token(Tokens.ShiftLeftAssign, x, y);
978
return new Token(Tokens.ShiftLeft, x, y);
980
return new Token(Tokens.LessThan, x, y);
982
switch (ReaderPeek()) {
985
return new Token(Tokens.GreaterEqual, x, y);
988
if (ReaderPeek() != -1) {
989
switch (ReaderPeek()) {
992
return new Token(Tokens.ShiftRightAssign, x, y);
997
return new Token(Tokens.ShiftRight, x, y);
999
return new Token(Tokens.GreaterThan, x, y);
1001
return new Token(Tokens.Comma, x, y);
1003
// Prevent OverflowException when Peek returns -1
1004
int tmp = ReaderPeek(); int tmp2 = ReaderPeek(1);
1006
if (char.IsDigit((char)tmp))
1007
return ReadDigit('.', Col);
1008
else if ((char)tmp == '@') {
1010
return new Token(Tokens.DotAt, x, y);
1011
} else if ((char)tmp == '.' && tmp2 > 0 && (char)tmp2 == '.') {
1012
ReaderRead(); ReaderRead();
1013
return new Token(Tokens.TripleDot, x, y);
1016
return new Token(Tokens.Dot, x, y);
1018
return new Token(Tokens.OpenParenthesis, x, y);
1020
return new Token(Tokens.CloseParenthesis, x, y);
1022
return new Token(Tokens.OpenCurlyBrace, x, y);
1024
return new Token(Tokens.CloseCurlyBrace, x, y);
1026
return new Token(Tokens.QuestionMark, x, y);
1028
return new Token(Tokens.ExclamationMark, x, y);
1035
Token ReadXmlProcessingInstruction(int x, int y)
1040
while (ReaderPeek() != '?' || ReaderPeek(1) != '>') {
1041
nextChar = ReaderRead();
1044
sb.Append((char)nextChar);
1047
ReaderSkip("?>".Length);
1049
return new Token(Tokens.XmlProcessingInstruction, new TextLocation(y, x), new TextLocation(Line, Col), sb.ToString(), null);
1052
Token ReadXmlCommentOrCData(int x, int y)
1057
if (string.CompareOrdinal(ReaderPeekString("--".Length), "--") == 0) {
1058
ReaderSkip("--".Length);
1059
while ((nextChar = ReaderRead()) != -1) {
1060
sb.Append((char)nextChar);
1061
if (string.CompareOrdinal(ReaderPeekString("-->".Length), "-->") == 0) {
1062
ReaderSkip("-->".Length);
1063
return new Token(Tokens.XmlComment, new TextLocation(y, x), new TextLocation(Line, Col), sb.ToString(), null);
1068
if (string.CompareOrdinal(ReaderPeekString("[CDATA[".Length), "[CDATA[") == 0) {
1069
ReaderSkip("[CDATA[".Length);
1070
while ((nextChar = ReaderRead()) != -1) {
1071
sb.Append((char)nextChar);
1072
if (string.CompareOrdinal(ReaderPeekString("]]>".Length), "]]>") == 0) {
1073
ReaderSkip("]]>".Length);
1074
return new Token(Tokens.XmlCData, new TextLocation(y, x), new TextLocation(Line, Col), sb.ToString(), null);
1079
return new Token(Tokens.XmlComment, new TextLocation(y, x), new TextLocation(Line, Col), sb.ToString(), null);
1082
string ReadXmlContent(char ch)
1087
int next = ReaderPeek();
1089
if (next == -1 || next == '<')
1091
ch = (char)ReaderRead();
1094
return sb.ToString();
1097
string ReadXmlString(char terminator)
1102
while ((nextChar = ReaderRead()) != -1) {
1103
ch = (char)nextChar;
1104
if (ch == terminator) {
1106
} else if (ch == '\n') {
1107
errors.Error(Line, Col, String.Format("No return allowed inside String literal"));
1112
if (ch != terminator) {
1113
errors.Error(Line, Col, String.Format("End of File reached before String terminated "));
1115
return sb.ToString();
1118
string ReadXmlIdent(char ch)
1125
while ((peek = ReaderPeek()) != -1 && (peek == ':' || XmlConvert.IsNCNameChar((char)peek))) {
1126
sb.Append((char)ReaderRead());
1129
return sb.ToString();
1133
char PeekUpperChar()
1135
return Char.ToUpper((char)ReaderPeek(), CultureInfo.InvariantCulture);
1139
/// Skips to the end of the current code block.
1140
/// For this, the lexer must have read the next token AFTER the token opening the
1141
/// block (so that Lexer.Token is the block-opening token, not Lexer.LookAhead).
1142
/// After the call, Lexer.LookAhead will be the block-closing token.
1144
public void SkipCurrentBlock(int targetToken)
1147
int kind = lastToken.kind;
1148
while (kind != Tokens.EOF &&
1149
!(lastKind == Tokens.End && kind == targetToken))
1153
kind = lastToken.kind;
1157
public void SetInitialContext(SnippetType type)
1159
ef.SetContext(type);
1162
public VBLexerMemento Export()
1164
return new VBLexerMemento() {
1167
EncounteredLineContinuation = encounteredLineContinuation,
1168
ExpressionFinder = ef.Export(),
1169
InXmlMode = inXmlMode,
1170
IsAtLineBegin = isAtLineBegin,
1172
PrevTokenKind = prevToken.kind,
1173
MisreadExclamationMarkAsTypeCharacter = misreadExclamationMarkAsTypeCharacter,
1174
XmlModeInfoStack = new Stack<XmlModeInfo>(xmlModeStack.Select(i => (XmlModeInfo)i.Clone()).Reverse())
1178
LATextReader reader;
1182
protected Errors errors = new Errors();
1184
protected Token lastToken = null;
1185
protected Token curToken = null;
1186
protected Token peekToken = null;
1188
string[] specialCommentTags = null;
1189
protected Hashtable specialCommentHash = null;
1190
// List<TagComment> tagComments = new List<TagComment>();
1191
protected StringBuilder sb = new StringBuilder();
1192
// protected SpecialTracker specialTracker = new SpecialTracker();
1194
// used for the original value of strings (with escape sequences).
1195
protected StringBuilder originalValue = new StringBuilder();
1197
public bool SkipAllComments { get; set; }
1198
public bool EvaluateConditionalCompilation { get; set; }
1199
public virtual IDictionary<string, object> ConditionalCompilationSymbols {
1200
get { throw new NotSupportedException(); }
1203
protected static IEnumerable<string> GetSymbols (string symbols)
1205
if (!string.IsNullOrEmpty(symbols)) {
1206
foreach (string symbol in symbols.Split (';', ' ', '\t')) {
1207
string s = symbol.Trim ();
1215
public void SetConditionalCompilationSymbols (string symbols)
1217
throw new NotSupportedException ();
1220
protected int Line {
1231
protected bool recordRead = false;
1232
protected StringBuilder recordedText = new StringBuilder ();
1234
protected int ReaderRead()
1236
int val = reader.Read();
1237
if (recordRead && val >= 0)
1238
recordedText.Append ((char)val);
1239
if ((val == '\r' && reader.Peek() != '\n') || val == '\n') {
1243
} else if (val >= 0) {
1249
protected int ReaderPeek()
1251
return reader.Peek();
1254
protected int ReaderPeek(int step)
1256
return reader.Peek(step);
1259
protected void ReaderSkip(int steps)
1261
for (int i = 0; i < steps; i++) {
1266
protected string ReaderPeekString(int length)
1268
StringBuilder builder = new StringBuilder();
1270
for (int i = 0; i < length; i++) {
1271
int peek = ReaderPeek(i);
1273
builder.Append((char)peek);
1276
return builder.ToString();
1279
public void SetInitialLocation(TextLocation location)
1281
if (lastToken != null || curToken != null || peekToken != null)
1282
throw new InvalidOperationException();
1283
this.line = location.Line;
1284
this.col = location.Column;
1287
public Errors Errors {
1294
/// Returns the comments that had been read and containing tag key words.
1296
// public List<TagComment> TagComments {
1298
// return tagComments;
1302
// public SpecialTracker SpecialTracker {
1304
// return specialTracker;
1309
/// Special comment tags are tags like TODO, HACK or UNDONE which are read by the lexer and stored in <see cref="TagComments"/>.
1311
public string[] SpecialCommentTags {
1313
return specialCommentTags;
1316
specialCommentTags = value;
1317
specialCommentHash = null;
1318
if (specialCommentTags != null && specialCommentTags.Length > 0) {
1319
specialCommentHash = new Hashtable();
1320
foreach (string str in specialCommentTags) {
1321
specialCommentHash.Add(str, null);
1328
/// The current Token. <seealso cref="ICSharpCode.NRefactory.VB.Parser.Token"/>
1330
public Token Token {
1332
// Console.WriteLine("Call to Token");
1338
/// The next Token (The <see cref="Token"/> after <see cref="NextToken"/> call) . <seealso cref="ICSharpCode.NRefactory.VB.Parser.Token"/>
1340
public Token LookAhead {
1342
// Console.WriteLine("Call to LookAhead");
1347
#region System.IDisposable interface implementation
1348
public virtual void Dispose()
1353
lastToken = curToken = peekToken = null;
1354
specialCommentHash = null;
1355
sb = originalValue = null;
1360
/// Must be called before a peek operation.
1362
public void StartPeek()
1364
peekToken = curToken;
1368
/// Gives back the next token. A second call to Peek() gives the next token after the last call for Peek() and so on.
1370
/// <returns>An <see cref="Token"/> object.</returns>
1373
// Console.WriteLine("Call to Peek");
1374
if (peekToken.next == null) {
1375
peekToken.next = Next();
1377
peekToken = peekToken.next;
1381
protected static bool IsIdentifierPart(int ch)
1383
if (ch == 95) return true; // 95 = '_'
1384
if (ch == -1) return false;
1385
return char.IsLetterOrDigit((char)ch); // accept unicode letters
1388
protected static bool IsHex(char digit)
1390
return Char.IsDigit(digit) || ('A' <= digit && digit <= 'F') || ('a' <= digit && digit <= 'f');
1393
protected int GetHexNumber(char digit)
1395
if (Char.IsDigit(digit)) {
1398
if ('A' <= digit && digit <= 'F') {
1399
return digit - 'A' + 0xA;
1401
if ('a' <= digit && digit <= 'f') {
1402
return digit - 'a' + 0xA;
1404
errors.Error(line, col, String.Format("Invalid hex number '" + digit + "'"));
1407
protected TextLocation lastLineEnd = new TextLocation(1, 1);
1408
protected TextLocation curLineEnd = new TextLocation(1, 1);
1409
protected void LineBreak ()
1411
lastLineEnd = curLineEnd;
1412
curLineEnd = new TextLocation (line, col - 1);
1414
protected bool HandleLineEnd(char ch)
1416
// Handle MS-DOS or MacOS line ends.
1418
if (reader.Peek() == '\n') { // MS-DOS line end '\r\n'
1419
ReaderRead(); // LineBreak (); called by ReaderRead ();
1421
} else { // assume MacOS line end which is '\r'
1433
protected void SkipToEndOfLine()
1436
while ((nextChar = reader.Read()) != -1) {
1437
if (nextChar == '\r') {
1438
if (reader.Peek() == '\n')
1442
if (nextChar == '\n') {
1450
protected string ReadToEndOfLine()
1454
while ((nextChar = reader.Read()) != -1) {
1455
char ch = (char)nextChar;
1457
if (nextChar == '\r') {
1458
if (reader.Peek() == '\n')
1462
// Return read string, if EOL is reached
1463
if (nextChar == '\n') {
1466
return sb.ToString();
1472
// Got EOF before EOL
1473
string retStr = sb.ToString();
1474
col += retStr.Length;
1478
public event EventHandler<SavepointEventArgs> SavepointReached;
1480
protected virtual void OnSavepointReached(SavepointEventArgs e)
1482
if (SavepointReached != null) {
1483
SavepointReached(this, e);