
4 changed files with 925 additions and 23 deletions
-
1ILSpy/ILSpy.csproj
-
894ILSpy/Languages/CSharpLexer.cs
-
6ILSpy/SearchPane.cs
-
47ILSpy/SearchStrategies.cs
@ -0,0 +1,894 @@ |
|||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
|||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Globalization; |
|||
using System.IO; |
|||
using System.Text; |
|||
|
|||
namespace ICSharpCode.ILSpy |
|||
{ |
|||
public class LATextReader : TextReader |
|||
{ |
|||
List<int> buffer; |
|||
TextReader reader; |
|||
|
|||
public LATextReader(TextReader reader) |
|||
{ |
|||
this.buffer = new List<int>(); |
|||
this.reader = reader; |
|||
} |
|||
|
|||
public override int Peek() |
|||
{ |
|||
return Peek(0); |
|||
} |
|||
|
|||
public override int Read() |
|||
{ |
|||
int c = Peek(); |
|||
buffer.RemoveAt(0); |
|||
return c; |
|||
} |
|||
|
|||
public int Peek(int step) |
|||
{ |
|||
while (step >= buffer.Count) { |
|||
buffer.Add(reader.Read()); |
|||
} |
|||
|
|||
if (step < 0) |
|||
return -1; |
|||
|
|||
return buffer[step]; |
|||
} |
|||
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
if (disposing) |
|||
reader.Dispose(); |
|||
base.Dispose(disposing); |
|||
} |
|||
} |
|||
|
|||
public enum LiteralFormat : byte |
|||
{ |
|||
None, |
|||
DecimalNumber, |
|||
HexadecimalNumber, |
|||
OctalNumber, |
|||
StringLiteral, |
|||
VerbatimStringLiteral, |
|||
CharLiteral, |
|||
DateTimeLiteral |
|||
} |
|||
|
|||
public class Literal |
|||
{ |
|||
internal readonly LiteralFormat literalFormat; |
|||
internal readonly object literalValue; |
|||
internal readonly string val; |
|||
internal Literal next; |
|||
|
|||
public LiteralFormat LiteralFormat { |
|||
get { return literalFormat; } |
|||
} |
|||
|
|||
public object LiteralValue { |
|||
get { return literalValue; } |
|||
} |
|||
|
|||
public string Value { |
|||
get { return val; } |
|||
} |
|||
|
|||
public Literal(string val, object literalValue, LiteralFormat literalFormat) |
|||
{ |
|||
this.val = val; |
|||
this.literalValue = literalValue; |
|||
this.literalFormat = literalFormat; |
|||
} |
|||
} |
|||
|
|||
internal abstract class AbstractLexer |
|||
{ |
|||
LATextReader reader; |
|||
int col = 1; |
|||
int line = 1; |
|||
|
|||
protected Literal lastToken = null; |
|||
protected Literal curToken = null; |
|||
protected Literal peekToken = null; |
|||
|
|||
string[] specialCommentTags = null; |
|||
protected StringBuilder sb = new StringBuilder(); |
|||
|
|||
// used for the original value of strings (with escape sequences).
|
|||
protected StringBuilder originalValue = new StringBuilder(); |
|||
|
|||
public bool SkipAllComments { get; set; } |
|||
public bool EvaluateConditionalCompilation { get; set; } |
|||
public virtual IDictionary<string, object> ConditionalCompilationSymbols { |
|||
get { throw new NotSupportedException(); } |
|||
} |
|||
|
|||
protected static IEnumerable<string> GetSymbols(string symbols) |
|||
{ |
|||
if (!string.IsNullOrEmpty(symbols)) { |
|||
foreach (string symbol in symbols.Split(';', ' ', '\t')) { |
|||
string s = symbol.Trim(); |
|||
if (s.Length == 0) |
|||
continue; |
|||
yield return s; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public virtual void SetConditionalCompilationSymbols(string symbols) |
|||
{ |
|||
throw new NotSupportedException(); |
|||
} |
|||
|
|||
protected int Line { |
|||
get { |
|||
return line; |
|||
} |
|||
} |
|||
protected int Col { |
|||
get { |
|||
return col; |
|||
} |
|||
} |
|||
|
|||
protected bool recordRead = false; |
|||
protected StringBuilder recordedText = new StringBuilder(); |
|||
|
|||
protected int ReaderRead() |
|||
{ |
|||
int val = reader.Read(); |
|||
if (recordRead && val >= 0) |
|||
recordedText.Append((char)val); |
|||
if ((val == '\r' && reader.Peek() != '\n') || val == '\n') { |
|||
++line; |
|||
col = 1; |
|||
LineBreak(); |
|||
} else if (val >= 0) { |
|||
col++; |
|||
} |
|||
return val; |
|||
} |
|||
|
|||
protected int ReaderPeek() |
|||
{ |
|||
return reader.Peek(); |
|||
} |
|||
|
|||
protected int ReaderPeek(int step) |
|||
{ |
|||
return reader.Peek(step); |
|||
} |
|||
|
|||
protected void ReaderSkip(int steps) |
|||
{ |
|||
for (int i = 0; i < steps; i++) { |
|||
ReaderRead(); |
|||
} |
|||
} |
|||
|
|||
protected string ReaderPeekString(int length) |
|||
{ |
|||
StringBuilder builder = new StringBuilder(); |
|||
|
|||
for (int i = 0; i < length; i++) { |
|||
int peek = ReaderPeek(i); |
|||
if (peek != -1) |
|||
builder.Append((char)peek); |
|||
} |
|||
|
|||
return builder.ToString(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The current Token. <seealso cref="ICSharpCode.NRefactory.Parser.Token"/>
|
|||
/// </summary>
|
|||
public Literal Token { |
|||
get { |
|||
return lastToken; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The next Token (The <see cref="Token"/> after <see cref="NextToken"/> call) . <seealso cref="ICSharpCode.NRefactory.Parser.Token"/>
|
|||
/// </summary>
|
|||
public Literal LookAhead { |
|||
get { |
|||
return curToken; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Constructor for the abstract lexer class.
|
|||
/// </summary>
|
|||
protected AbstractLexer(TextReader reader) |
|||
{ |
|||
this.reader = new LATextReader(reader); |
|||
} |
|||
|
|||
#region System.IDisposable interface implementation
|
|||
public virtual void Dispose() |
|||
{ |
|||
reader.Close(); |
|||
reader = null; |
|||
lastToken = curToken = peekToken = null; |
|||
sb = originalValue = null; |
|||
} |
|||
#endregion
|
|||
|
|||
/// <summary>
|
|||
/// Must be called before a peek operation.
|
|||
/// </summary>
|
|||
public void StartPeek() |
|||
{ |
|||
peekToken = curToken; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gives back the next token. A second call to Peek() gives the next token after the last call for Peek() and so on.
|
|||
/// </summary>
|
|||
/// <returns>An <see cref="Token"/> object.</returns>
|
|||
public Literal Peek() |
|||
{ |
|||
// Console.WriteLine("Call to Peek");
|
|||
if (peekToken.next == null) { |
|||
peekToken.next = Next(); |
|||
} |
|||
peekToken = peekToken.next; |
|||
return peekToken; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads the next token and gives it back.
|
|||
/// </summary>
|
|||
/// <returns>An <see cref="Token"/> object.</returns>
|
|||
public virtual Literal NextToken() |
|||
{ |
|||
if (curToken == null) { |
|||
curToken = Next(); |
|||
//Console.WriteLine(ICSharpCode.NRefactory.Parser.CSharp.Tokens.GetTokenString(curToken.kind) + " -- " + curToken.val + "(" + curToken.kind + ")");
|
|||
return curToken; |
|||
} |
|||
|
|||
lastToken = curToken; |
|||
|
|||
if (curToken.next == null) { |
|||
curToken.next = Next(); |
|||
} |
|||
|
|||
curToken = curToken.next; |
|||
//Console.WriteLine(ICSharpCode.NRefactory.Parser.CSharp.Tokens.GetTokenString(curToken.kind) + " -- " + curToken.val + "(" + curToken.kind + ")");
|
|||
return curToken; |
|||
} |
|||
|
|||
protected abstract Literal Next(); |
|||
|
|||
protected static bool IsIdentifierPart(int ch) |
|||
{ |
|||
if (ch == 95) return true; // 95 = '_'
|
|||
if (ch == -1) return false; |
|||
return char.IsLetterOrDigit((char)ch); // accept unicode letters
|
|||
} |
|||
|
|||
protected static bool IsHex(char digit) |
|||
{ |
|||
return Char.IsDigit(digit) || ('A' <= digit && digit <= 'F') || ('a' <= digit && digit <= 'f'); |
|||
} |
|||
|
|||
protected int GetHexNumber(char digit) |
|||
{ |
|||
if (Char.IsDigit(digit)) { |
|||
return digit - '0'; |
|||
} |
|||
if ('A' <= digit && digit <= 'F') { |
|||
return digit - 'A' + 0xA; |
|||
} |
|||
if ('a' <= digit && digit <= 'f') { |
|||
return digit - 'a' + 0xA; |
|||
} |
|||
return 0; |
|||
} |
|||
protected void LineBreak() |
|||
{ |
|||
} |
|||
protected bool HandleLineEnd(char ch) |
|||
{ |
|||
// Handle MS-DOS or MacOS line ends.
|
|||
if (ch == '\r') { |
|||
if (reader.Peek() == '\n') { // MS-DOS line end '\r\n'
|
|||
ReaderRead(); // LineBreak (); called by ReaderRead ();
|
|||
return true; |
|||
} else { // assume MacOS line end which is '\r'
|
|||
LineBreak(); |
|||
return true; |
|||
} |
|||
} |
|||
if (ch == '\n') { |
|||
LineBreak(); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
protected void SkipToEndOfLine() |
|||
{ |
|||
int nextChar; |
|||
while ((nextChar = reader.Read()) != -1) { |
|||
if (nextChar == '\r') { |
|||
if (reader.Peek() == '\n') |
|||
reader.Read(); |
|||
nextChar = '\n'; |
|||
} |
|||
if (nextChar == '\n') { |
|||
++line; |
|||
col = 1; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected string ReadToEndOfLine() |
|||
{ |
|||
sb.Length = 0; |
|||
int nextChar; |
|||
while ((nextChar = reader.Read()) != -1) { |
|||
char ch = (char)nextChar; |
|||
|
|||
if (nextChar == '\r') { |
|||
if (reader.Peek() == '\n') |
|||
reader.Read(); |
|||
nextChar = '\n'; |
|||
} |
|||
// Return read string, if EOL is reached
|
|||
if (nextChar == '\n') { |
|||
++line; |
|||
col = 1; |
|||
return sb.ToString(); |
|||
} |
|||
|
|||
sb.Append(ch); |
|||
} |
|||
|
|||
// Got EOF before EOL
|
|||
string retStr = sb.ToString(); |
|||
col += retStr.Length; |
|||
return retStr; |
|||
} |
|||
} |
|||
|
|||
internal sealed class Lexer : AbstractLexer |
|||
{ |
|||
bool isAtLineBegin = true; |
|||
|
|||
public Lexer(TextReader reader) : base(reader) |
|||
{ |
|||
} |
|||
|
|||
protected override Literal Next() |
|||
{ |
|||
char ch; |
|||
if (Line == 1 && Col == 1) { |
|||
isAtLineBegin = true; |
|||
} |
|||
|
|||
while (true) { |
|||
int nextChar = ReaderRead(); |
|||
if (nextChar == -1) |
|||
break; |
|||
|
|||
Literal token = null; |
|||
|
|||
switch (nextChar) { |
|||
case ' ': |
|||
case '\t': |
|||
continue; |
|||
case '\r': |
|||
case '\n': |
|||
HandleLineEnd((char)nextChar); |
|||
isAtLineBegin = true; |
|||
continue; |
|||
case '"': |
|||
token = ReadString(); |
|||
isAtLineBegin = false; |
|||
break; |
|||
case '\'': |
|||
token = ReadChar(); |
|||
isAtLineBegin = false; |
|||
break; |
|||
default: |
|||
isAtLineBegin = false; // non-ws chars are handled here
|
|||
ch = (char)nextChar; |
|||
if (Char.IsLetter(ch) || ch == '_' || ch == '\\') { |
|||
int x = Col - 1; // Col was incremented above, but we want the start of the identifier
|
|||
int y = Line; |
|||
bool canBeKeyword; |
|||
string s = ReadIdent(ch, out canBeKeyword); |
|||
return new Literal(null, null, LiteralFormat.None); |
|||
} else if (Char.IsDigit(ch)) { |
|||
token = ReadDigit(ch, Col - 1); |
|||
} |
|||
break; |
|||
} |
|||
|
|||
// try error recovery (token = null -> continue with next char)
|
|||
if (token != null) { |
|||
return token; |
|||
} |
|||
} |
|||
|
|||
return new Literal(null, null, LiteralFormat.None); |
|||
} |
|||
|
|||
// The C# compiler has a fixed size length therefore we'll use a fixed size char array for identifiers
|
|||
// it's also faster than using a string builder.
|
|||
const int MAX_IDENTIFIER_LENGTH = 512; |
|||
char[] identBuffer = new char[MAX_IDENTIFIER_LENGTH]; |
|||
|
|||
string ReadIdent(char ch, out bool canBeKeyword) |
|||
{ |
|||
int peek; |
|||
int curPos = 0; |
|||
canBeKeyword = true; |
|||
while (true) { |
|||
if (ch == '\\') { |
|||
peek = ReaderPeek(); |
|||
canBeKeyword = false; |
|||
string surrogatePair; |
|||
ReadEscapeSequence(out ch, out surrogatePair); |
|||
if (surrogatePair != null) { |
|||
for (int i = 0; i < surrogatePair.Length - 1; i++) { |
|||
if (curPos < MAX_IDENTIFIER_LENGTH) { |
|||
identBuffer[curPos++] = surrogatePair[i]; |
|||
} |
|||
} |
|||
ch = surrogatePair[surrogatePair.Length - 1]; |
|||
} |
|||
} |
|||
|
|||
if (curPos < MAX_IDENTIFIER_LENGTH) { |
|||
if (ch != '\0') // only add character, if it is valid
|
|||
// prevents \ from being added
|
|||
identBuffer[curPos++] = ch; |
|||
} else { |
|||
while (IsIdentifierPart(ReaderPeek())) { |
|||
ReaderRead(); |
|||
} |
|||
break; |
|||
} |
|||
peek = ReaderPeek(); |
|||
if (IsIdentifierPart(peek) || peek == '\\') { |
|||
ch = (char)ReaderRead(); |
|||
} else { |
|||
break; |
|||
} |
|||
} |
|||
return new String(identBuffer, 0, curPos); |
|||
} |
|||
|
|||
Literal ReadDigit(char ch, int x) |
|||
{ |
|||
unchecked { // prevent exception when ReaderPeek() = -1 is cast to char
|
|||
int y = Line; |
|||
sb.Length = 0; |
|||
sb.Append(ch); |
|||
string prefix = null; |
|||
string suffix = null; |
|||
|
|||
bool ishex = false; |
|||
bool isunsigned = false; |
|||
bool islong = false; |
|||
bool isfloat = false; |
|||
bool isdouble = false; |
|||
bool isdecimal = false; |
|||
|
|||
char peek = (char)ReaderPeek(); |
|||
|
|||
if (ch == '.') { |
|||
isdouble = true; |
|||
|
|||
while (Char.IsDigit((char)ReaderPeek())) { // read decimal digits beyond the dot
|
|||
sb.Append((char)ReaderRead()); |
|||
} |
|||
peek = (char)ReaderPeek(); |
|||
} else if (ch == '0' && (peek == 'x' || peek == 'X')) { |
|||
ReaderRead(); // skip 'x'
|
|||
sb.Length = 0; // Remove '0' from 0x prefix from the stringvalue
|
|||
while (IsHex((char)ReaderPeek())) { |
|||
sb.Append((char)ReaderRead()); |
|||
} |
|||
if (sb.Length == 0) { |
|||
sb.Append('0'); // dummy value to prevent exception
|
|||
} |
|||
ishex = true; |
|||
prefix = "0x"; |
|||
peek = (char)ReaderPeek(); |
|||
} else { |
|||
while (Char.IsDigit((char)ReaderPeek())) { |
|||
sb.Append((char)ReaderRead()); |
|||
} |
|||
peek = (char)ReaderPeek(); |
|||
} |
|||
|
|||
Literal nextToken = null; // if we accidently read a 'dot'
|
|||
if (peek == '.') { // read floating point number
|
|||
ReaderRead(); |
|||
peek = (char)ReaderPeek(); |
|||
if (!Char.IsDigit(peek)) { |
|||
nextToken = new Literal(null, null, LiteralFormat.None); |
|||
peek = '.'; |
|||
} else { |
|||
isdouble = true; // double is default
|
|||
sb.Append('.'); |
|||
|
|||
while (Char.IsDigit((char)ReaderPeek())) { // read decimal digits beyond the dot
|
|||
sb.Append((char)ReaderRead()); |
|||
} |
|||
peek = (char)ReaderPeek(); |
|||
} |
|||
} |
|||
|
|||
if (peek == 'e' || peek == 'E') { // read exponent
|
|||
isdouble = true; |
|||
sb.Append((char)ReaderRead()); |
|||
peek = (char)ReaderPeek(); |
|||
if (peek == '-' || peek == '+') { |
|||
sb.Append((char)ReaderRead()); |
|||
} |
|||
while (Char.IsDigit((char)ReaderPeek())) { // read exponent value
|
|||
sb.Append((char)ReaderRead()); |
|||
} |
|||
isunsigned = true; |
|||
peek = (char)ReaderPeek(); |
|||
} |
|||
|
|||
if (peek == 'f' || peek == 'F') { // float value
|
|||
ReaderRead(); |
|||
suffix = "f"; |
|||
isfloat = true; |
|||
} else if (peek == 'd' || peek == 'D') { // double type suffix (obsolete, double is default)
|
|||
ReaderRead(); |
|||
suffix = "d"; |
|||
isdouble = true; |
|||
} else if (peek == 'm' || peek == 'M') { // decimal value
|
|||
ReaderRead(); |
|||
suffix = "m"; |
|||
isdecimal = true; |
|||
} else if (!isdouble) { |
|||
if (peek == 'u' || peek == 'U') { |
|||
ReaderRead(); |
|||
suffix = "u"; |
|||
isunsigned = true; |
|||
peek = (char)ReaderPeek(); |
|||
} |
|||
|
|||
if (peek == 'l' || peek == 'L') { |
|||
ReaderRead(); |
|||
peek = (char)ReaderPeek(); |
|||
islong = true; |
|||
if (!isunsigned && (peek == 'u' || peek == 'U')) { |
|||
ReaderRead(); |
|||
suffix = "Lu"; |
|||
isunsigned = true; |
|||
} else { |
|||
suffix = isunsigned ? "uL" : "L"; |
|||
} |
|||
} |
|||
} |
|||
|
|||
string digit = sb.ToString(); |
|||
string stringValue = prefix + digit + suffix; |
|||
|
|||
if (isfloat) { |
|||
float num; |
|||
if (float.TryParse(digit, NumberStyles.Any, CultureInfo.InvariantCulture, out num)) { |
|||
return new Literal(stringValue, num, LiteralFormat.DecimalNumber); |
|||
} else { |
|||
return new Literal(stringValue, 0f, LiteralFormat.DecimalNumber); |
|||
} |
|||
} |
|||
if (isdecimal) { |
|||
decimal num; |
|||
if (decimal.TryParse(digit, NumberStyles.Any, CultureInfo.InvariantCulture, out num)) { |
|||
return new Literal(stringValue, num, LiteralFormat.DecimalNumber); |
|||
} else { |
|||
return new Literal(stringValue, 0m, LiteralFormat.DecimalNumber); |
|||
} |
|||
} |
|||
if (isdouble) { |
|||
double num; |
|||
if (double.TryParse(digit, NumberStyles.Any, CultureInfo.InvariantCulture, out num)) { |
|||
return new Literal(stringValue, num, LiteralFormat.DecimalNumber); |
|||
} else { |
|||
|
|||
return new Literal(stringValue, 0d, LiteralFormat.DecimalNumber); |
|||
} |
|||
} |
|||
|
|||
// Try to determine a parsable value using ranges.
|
|||
ulong result; |
|||
if (ishex) { |
|||
if (!ulong.TryParse(digit, NumberStyles.HexNumber, null, out result)) { |
|||
|
|||
return new Literal(stringValue.ToString(), 0, LiteralFormat.HexadecimalNumber); |
|||
} |
|||
} else { |
|||
if (!ulong.TryParse(digit, NumberStyles.Integer, null, out result)) { |
|||
|
|||
return new Literal(stringValue.ToString(), 0, LiteralFormat.DecimalNumber); |
|||
} |
|||
} |
|||
|
|||
if (result > long.MaxValue) { |
|||
islong = true; |
|||
isunsigned = true; |
|||
} else if (result > uint.MaxValue) { |
|||
islong = true; |
|||
} else if (islong == false && result > int.MaxValue) { |
|||
isunsigned = true; |
|||
} |
|||
|
|||
Literal token; |
|||
|
|||
LiteralFormat literalFormat = ishex ? LiteralFormat.HexadecimalNumber : LiteralFormat.DecimalNumber; |
|||
if (islong) { |
|||
if (isunsigned) { |
|||
ulong num; |
|||
if (ulong.TryParse(digit, ishex ? NumberStyles.HexNumber : NumberStyles.Number, CultureInfo.InvariantCulture, out num)) { |
|||
token = new Literal(stringValue, num, literalFormat); |
|||
} else { |
|||
|
|||
token = new Literal(stringValue, 0UL, literalFormat); |
|||
} |
|||
} else { |
|||
long num; |
|||
if (long.TryParse(digit, ishex ? NumberStyles.HexNumber : NumberStyles.Number, CultureInfo.InvariantCulture, out num)) { |
|||
token = new Literal(stringValue, num, literalFormat); |
|||
} else { |
|||
|
|||
token = new Literal(stringValue, 0L, literalFormat); |
|||
} |
|||
} |
|||
} else { |
|||
if (isunsigned) { |
|||
uint num; |
|||
if (uint.TryParse(digit, ishex ? NumberStyles.HexNumber : NumberStyles.Number, CultureInfo.InvariantCulture, out num)) { |
|||
token = new Literal(stringValue, num, literalFormat); |
|||
} else { |
|||
|
|||
token = new Literal(stringValue, (uint)0, literalFormat); |
|||
} |
|||
} else { |
|||
int num; |
|||
if (int.TryParse(digit, ishex ? NumberStyles.HexNumber : NumberStyles.Number, CultureInfo.InvariantCulture, out num)) { |
|||
token = new Literal(stringValue, num, literalFormat); |
|||
} else { |
|||
|
|||
token = new Literal(stringValue, 0, literalFormat); |
|||
} |
|||
} |
|||
} |
|||
token.next = nextToken; |
|||
return token; |
|||
} |
|||
} |
|||
|
|||
Literal ReadString() |
|||
{ |
|||
int x = Col - 1; |
|||
int y = Line; |
|||
|
|||
sb.Length = 0; |
|||
originalValue.Length = 0; |
|||
originalValue.Append('"'); |
|||
bool doneNormally = false; |
|||
int nextChar; |
|||
while ((nextChar = ReaderRead()) != -1) { |
|||
char ch = (char)nextChar; |
|||
|
|||
if (ch == '"') { |
|||
doneNormally = true; |
|||
originalValue.Append('"'); |
|||
break; |
|||
} |
|||
|
|||
if (ch == '\\') { |
|||
originalValue.Append('\\'); |
|||
string surrogatePair; |
|||
originalValue.Append(ReadEscapeSequence(out ch, out surrogatePair)); |
|||
if (surrogatePair != null) { |
|||
sb.Append(surrogatePair); |
|||
} else { |
|||
sb.Append(ch); |
|||
} |
|||
} else if (HandleLineEnd(ch)) { |
|||
// call HandleLineEnd to ensure line numbers are still correct after the error
|
|||
|
|||
break; |
|||
} else { |
|||
originalValue.Append(ch); |
|||
sb.Append(ch); |
|||
} |
|||
} |
|||
|
|||
if (!doneNormally) { |
|||
|
|||
} |
|||
|
|||
return new Literal(originalValue.ToString(), sb.ToString(), LiteralFormat.StringLiteral); |
|||
} |
|||
|
|||
Literal ReadVerbatimString() |
|||
{ |
|||
sb.Length = 0; |
|||
originalValue.Length = 0; |
|||
originalValue.Append("@\""); |
|||
int nextChar; |
|||
while ((nextChar = ReaderRead()) != -1) { |
|||
char ch = (char)nextChar; |
|||
|
|||
if (ch == '"') { |
|||
if (ReaderPeek() != '"') { |
|||
originalValue.Append('"'); |
|||
break; |
|||
} |
|||
originalValue.Append("\"\""); |
|||
sb.Append('"'); |
|||
ReaderRead(); |
|||
} else if (HandleLineEnd(ch)) { |
|||
sb.Append("\r\n"); |
|||
originalValue.Append("\r\n"); |
|||
} else { |
|||
sb.Append(ch); |
|||
originalValue.Append(ch); |
|||
} |
|||
} |
|||
|
|||
if (nextChar == -1) { |
|||
|
|||
} |
|||
|
|||
return new Literal(originalValue.ToString(), sb.ToString(), LiteralFormat.VerbatimStringLiteral); |
|||
} |
|||
|
|||
readonly char[] escapeSequenceBuffer = new char[12]; |
|||
|
|||
/// <summary>
|
|||
/// reads an escape sequence
|
|||
/// </summary>
|
|||
/// <param name="ch">The character represented by the escape sequence,
|
|||
/// or '\0' if there was an error or the escape sequence represents a character that
|
|||
/// can be represented only be a suggorate pair</param>
|
|||
/// <param name="surrogatePair">Null, except when the character represented
|
|||
/// by the escape sequence can only be represented by a surrogate pair (then the string
|
|||
/// contains the surrogate pair)</param>
|
|||
/// <returns>The escape sequence</returns>
|
|||
string ReadEscapeSequence(out char ch, out string surrogatePair) |
|||
{ |
|||
surrogatePair = null; |
|||
|
|||
int nextChar = ReaderRead(); |
|||
if (nextChar == -1) { |
|||
ch = '\0'; |
|||
return String.Empty; |
|||
} |
|||
int number; |
|||
char c = (char)nextChar; |
|||
int curPos = 1; |
|||
escapeSequenceBuffer[0] = c; |
|||
switch (c) { |
|||
case '\'': |
|||
ch = '\''; |
|||
break; |
|||
case '\"': |
|||
ch = '\"'; |
|||
break; |
|||
case '\\': |
|||
ch = '\\'; |
|||
break; |
|||
case '0': |
|||
ch = '\0'; |
|||
break; |
|||
case 'a': |
|||
ch = '\a'; |
|||
break; |
|||
case 'b': |
|||
ch = '\b'; |
|||
break; |
|||
case 'f': |
|||
ch = '\f'; |
|||
break; |
|||
case 'n': |
|||
ch = '\n'; |
|||
break; |
|||
case 'r': |
|||
ch = '\r'; |
|||
break; |
|||
case 't': |
|||
ch = '\t'; |
|||
break; |
|||
case 'v': |
|||
ch = '\v'; |
|||
break; |
|||
case 'u': |
|||
case 'x': |
|||
// 16 bit unicode character
|
|||
c = (char)ReaderRead(); |
|||
number = GetHexNumber(c); |
|||
escapeSequenceBuffer[curPos++] = c; |
|||
|
|||
for (int i = 0; i < 3; ++i) { |
|||
if (IsHex((char)ReaderPeek())) { |
|||
c = (char)ReaderRead(); |
|||
int idx = GetHexNumber(c); |
|||
escapeSequenceBuffer[curPos++] = c; |
|||
number = 16 * number + idx; |
|||
} else { |
|||
break; |
|||
} |
|||
} |
|||
ch = (char)number; |
|||
break; |
|||
case 'U': |
|||
// 32 bit unicode character
|
|||
number = 0; |
|||
for (int i = 0; i < 8; ++i) { |
|||
if (IsHex((char)ReaderPeek())) { |
|||
c = (char)ReaderRead(); |
|||
int idx = GetHexNumber(c); |
|||
escapeSequenceBuffer[curPos++] = c; |
|||
number = 16 * number + idx; |
|||
} else { |
|||
break; |
|||
} |
|||
} |
|||
if (number > 0xffff) { |
|||
ch = '\0'; |
|||
surrogatePair = char.ConvertFromUtf32(number); |
|||
} else { |
|||
ch = (char)number; |
|||
} |
|||
break; |
|||
default: |
|||
ch = '\0'; |
|||
break; |
|||
} |
|||
return new String(escapeSequenceBuffer, 0, curPos); |
|||
} |
|||
|
|||
Literal ReadChar() |
|||
{ |
|||
int x = Col - 1; |
|||
int y = Line; |
|||
int nextChar = ReaderRead(); |
|||
if (nextChar == -1 || HandleLineEnd((char)nextChar)) { |
|||
return null; |
|||
} |
|||
char ch = (char)nextChar; |
|||
char chValue = ch; |
|||
string escapeSequence = String.Empty; |
|||
if (ch == '\\') { |
|||
string surrogatePair; |
|||
escapeSequence = ReadEscapeSequence(out chValue, out surrogatePair); |
|||
if (surrogatePair != null) { |
|||
|
|||
} |
|||
} |
|||
|
|||
unchecked { |
|||
if ((char)ReaderRead() != '\'') { |
|||
|
|||
} |
|||
} |
|||
return new Literal("'" + ch + escapeSequence + "'", chValue, LiteralFormat.CharLiteral); |
|||
} |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue