mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
598 lines
16 KiB
598 lines
16 KiB
// <file> |
|
// <copyright see="prj:///doc/copyright.txt"/> |
|
// <license see="prj:///doc/license.txt"/> |
|
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/> |
|
// <version>$Revision$</version> |
|
// </file> |
|
|
|
using System; |
|
using System.Collections.Generic; |
|
using System.Text; |
|
using System.Diagnostics; |
|
|
|
using ICSharpCode.NRefactory.Ast; |
|
|
|
namespace ICSharpCode.NRefactory.Parser.CSharp |
|
{ |
|
internal sealed partial class Parser : AbstractParser |
|
{ |
|
Lexer lexer; |
|
|
|
public Parser(ILexer lexer) : base(lexer) |
|
{ |
|
this.lexer = (Lexer)lexer; |
|
// due to anonymous methods, we always need a compilation unit, so |
|
// create it in the constructor |
|
compilationUnit = new CompilationUnit(); |
|
} |
|
|
|
StringBuilder qualidentBuilder = new StringBuilder(); |
|
|
|
Token t { |
|
[System.Diagnostics.DebuggerStepThrough] |
|
get { |
|
return lexer.Token; |
|
} |
|
} |
|
|
|
Token la { |
|
[System.Diagnostics.DebuggerStepThrough] |
|
get { |
|
return lexer.LookAhead; |
|
} |
|
} |
|
|
|
public void Error(string s) |
|
{ |
|
if (errDist >= MinErrDist) { |
|
this.Errors.Error(la.line, la.col, s); |
|
} |
|
errDist = 0; |
|
} |
|
|
|
public override Expression ParseExpression() |
|
{ |
|
lexer.NextToken(); |
|
Expression expr; |
|
Expr(out expr); |
|
// SEMICOLON HACK : without a trailing semicolon, parsing expressions does not work correctly |
|
if (la.kind == Tokens.Semicolon) lexer.NextToken(); |
|
Expect(Tokens.EOF); |
|
return expr; |
|
} |
|
|
|
public override BlockStatement ParseBlock() |
|
{ |
|
lexer.NextToken(); |
|
compilationUnit = new CompilationUnit(); |
|
|
|
BlockStatement blockStmt = new BlockStatement(); |
|
blockStmt.StartLocation = la.Location; |
|
compilationUnit.BlockStart(blockStmt); |
|
|
|
while (la.kind != Tokens.EOF) { |
|
Token oldLa = la; |
|
Statement(); |
|
if (la == oldLa) { |
|
// did not advance lexer position, we cannot parse this as a statement block |
|
return null; |
|
} |
|
} |
|
|
|
compilationUnit.BlockEnd(); |
|
Expect(Tokens.EOF); |
|
return blockStmt; |
|
} |
|
|
|
public override List<INode> ParseTypeMembers() |
|
{ |
|
lexer.NextToken(); |
|
compilationUnit = new CompilationUnit(); |
|
|
|
TypeDeclaration newType = new TypeDeclaration(Modifiers.None, null); |
|
compilationUnit.BlockStart(newType); |
|
ClassBody(); |
|
compilationUnit.BlockEnd(); |
|
Expect(Tokens.EOF); |
|
return newType.Children; |
|
} |
|
|
|
// Begin ISTypeCast |
|
bool IsTypeCast() |
|
{ |
|
if (la.kind != Tokens.OpenParenthesis) { |
|
return false; |
|
} |
|
if (IsSimpleTypeCast()) { |
|
return true; |
|
} |
|
return GuessTypeCast(); |
|
} |
|
|
|
// "(" ( typeKW [ "[" {","} "]" | "*" ] | void ( "[" {","} "]" | "*" ) ) ")" |
|
// only for built-in types, all others use GuessTypeCast! |
|
bool IsSimpleTypeCast () |
|
{ |
|
// assert: la.kind == _lpar |
|
lexer.StartPeek(); |
|
Token pt = lexer.Peek(); |
|
|
|
if (!IsTypeKWForTypeCast(ref pt)) { |
|
return false; |
|
} |
|
if (pt.kind == Tokens.Question) // TODO: check if IsTypeKWForTypeCast doesn't already to this |
|
pt = lexer.Peek(); |
|
return pt.kind == Tokens.CloseParenthesis; |
|
} |
|
|
|
/* !!! Proceeds from current peek position !!! */ |
|
bool IsTypeKWForTypeCast(ref Token pt) |
|
{ |
|
if (Tokens.TypeKW[pt.kind]) { |
|
pt = lexer.Peek(); |
|
return IsPointerOrDims(ref pt) && SkipQuestionMark(ref pt); |
|
} else if (pt.kind == Tokens.Void) { |
|
pt = lexer.Peek(); |
|
return IsPointerOrDims(ref pt); |
|
} |
|
return false; |
|
} |
|
|
|
/* !!! Proceeds from current peek position !!! */ |
|
bool IsTypeNameOrKWForTypeCast(ref Token pt) |
|
{ |
|
if (Tokens.TypeKW[pt.kind] || pt.kind == Tokens.Void) |
|
return IsTypeKWForTypeCast(ref pt); |
|
else |
|
return IsTypeNameForTypeCast(ref pt); |
|
} |
|
|
|
// TypeName = ident [ "::" ident ] { ["<" TypeNameOrKW { "," TypeNameOrKW } ">" ] "." ident } ["?"] PointerOrDims |
|
/* !!! Proceeds from current peek position !!! */ |
|
bool IsTypeNameForTypeCast(ref Token pt) |
|
{ |
|
// ident |
|
if (!IsIdentifierToken(pt)) { |
|
return false; |
|
} |
|
pt = Peek(); |
|
// "::" ident |
|
if (pt.kind == Tokens.DoubleColon) { |
|
pt = Peek(); |
|
if (!IsIdentifierToken(pt)) { |
|
return false; |
|
} |
|
pt = Peek(); |
|
} |
|
// { ["<" TypeNameOrKW { "," TypeNameOrKW } ">" ] "." ident } |
|
while (true) { |
|
if (pt.kind == Tokens.LessThan) { |
|
do { |
|
pt = Peek(); |
|
if (!IsTypeNameOrKWForTypeCast(ref pt)) { |
|
return false; |
|
} |
|
} while (pt.kind == Tokens.Comma); |
|
if (pt.kind != Tokens.GreaterThan) { |
|
return false; |
|
} |
|
pt = Peek(); |
|
} |
|
if (pt.kind != Tokens.Dot) |
|
break; |
|
pt = Peek(); |
|
if (pt.kind != Tokens.Identifier) { |
|
return false; |
|
} |
|
pt = Peek(); |
|
} |
|
// ["?"] |
|
if (pt.kind == Tokens.Question) { |
|
pt = Peek(); |
|
} |
|
if (pt.kind == Tokens.Times || pt.kind == Tokens.OpenSquareBracket) { |
|
return IsPointerOrDims(ref pt); |
|
} |
|
return true; |
|
} |
|
|
|
// "(" TypeName ")" castFollower |
|
bool GuessTypeCast () |
|
{ |
|
// assert: la.kind == _lpar |
|
StartPeek(); |
|
Token pt = Peek(); |
|
|
|
if (!IsTypeNameForTypeCast(ref pt)) { |
|
return false; |
|
} |
|
|
|
// ")" |
|
if (pt.kind != Tokens.CloseParenthesis) { |
|
return false; |
|
} |
|
// check successor |
|
pt = Peek(); |
|
return Tokens.CastFollower[pt.kind]; |
|
} |
|
// END IsTypeCast |
|
|
|
// Gets if the token is a possible token for an expression start |
|
// Is used to determine if "a is Type ? token" a the start of a ternary |
|
// expression or a type test for Nullable<Type> |
|
bool IsPossibleExpressionStart(int token) |
|
{ |
|
return Tokens.CastFollower[token] || Tokens.UnaryOp[token]; |
|
} |
|
|
|
// ( { [TypeNameOrKWForTypeCast] ident "," } ) |
|
bool IsLambdaExpression() |
|
{ |
|
if (la.kind != Tokens.OpenParenthesis) { |
|
return false; |
|
} |
|
StartPeek(); |
|
Token pt = Peek(); |
|
while (pt.kind != Tokens.CloseParenthesis) { |
|
if (!IsTypeNameOrKWForTypeCast(ref pt)) { |
|
return false; |
|
} |
|
if (IsIdentifierToken(pt)) { |
|
// make ident optional: if implicitly typed lambda arguments are used, IsTypeNameForTypeCast |
|
// has already accepted the identifier |
|
pt = Peek(); |
|
} |
|
if (pt.kind == Tokens.CloseParenthesis) { |
|
break; |
|
} |
|
// require comma between parameters: |
|
if (pt.kind == Tokens.Comma) { |
|
pt = Peek(); |
|
} else { |
|
return false; |
|
} |
|
} |
|
pt = Peek(); |
|
return pt.kind == Tokens.LambdaArrow; |
|
} |
|
|
|
/* Checks whether the next sequences of tokens is a qualident * |
|
* and returns the qualident string */ |
|
/* !!! Proceeds from current peek position !!! */ |
|
bool IsQualident(ref Token pt, out string qualident) |
|
{ |
|
if (IsIdentifierToken(pt)) { |
|
qualidentBuilder.Length = 0; qualidentBuilder.Append(pt.val); |
|
pt = Peek(); |
|
while (pt.kind == Tokens.Dot || pt.kind == Tokens.DoubleColon) { |
|
pt = Peek(); |
|
if (!IsIdentifierToken(pt)) { |
|
qualident = String.Empty; |
|
return false; |
|
} |
|
qualidentBuilder.Append('.'); |
|
qualidentBuilder.Append(pt.val); |
|
pt = Peek(); |
|
} |
|
qualident = qualidentBuilder.ToString(); |
|
return true; |
|
} |
|
qualident = String.Empty; |
|
return false; |
|
} |
|
|
|
/* Skips generic type extensions */ |
|
/* !!! Proceeds from current peek position !!! */ |
|
|
|
/* skip: { "*" | "[" { "," } "]" } */ |
|
/* !!! Proceeds from current peek position !!! */ |
|
bool IsPointerOrDims (ref Token pt) |
|
{ |
|
for (;;) { |
|
if (pt.kind == Tokens.OpenSquareBracket) { |
|
do pt = Peek(); |
|
while (pt.kind == Tokens.Comma); |
|
if (pt.kind != Tokens.CloseSquareBracket) return false; |
|
} else if (pt.kind != Tokens.Times) break; |
|
pt = Peek(); |
|
} |
|
return true; |
|
} |
|
|
|
/* Return the n-th token after the current lookahead token */ |
|
void StartPeek() |
|
{ |
|
lexer.StartPeek(); |
|
} |
|
|
|
Token Peek() |
|
{ |
|
return lexer.Peek(); |
|
} |
|
|
|
Token Peek (int n) |
|
{ |
|
lexer.StartPeek(); |
|
Token x = la; |
|
while (n > 0) { |
|
x = lexer.Peek(); |
|
n--; |
|
} |
|
return x; |
|
} |
|
|
|
/*-----------------------------------------------------------------* |
|
* Resolver routines to resolve LL(1) conflicts: * * |
|
* These resolution routine return a boolean value that indicates * |
|
* whether the alternative at hand shall be choosen or not. * |
|
* They are used in IF ( ... ) expressions. * |
|
*-----------------------------------------------------------------*/ |
|
|
|
/* True, if ident is followed by "=" */ |
|
bool IdentAndAsgn () |
|
{ |
|
return IsIdentifierToken(la) && Peek(1).kind == Tokens.Assign; |
|
} |
|
|
|
bool IdentAndDoubleColon () |
|
{ |
|
return IsIdentifierToken(la) && Peek(1).kind == Tokens.DoubleColon; |
|
} |
|
|
|
bool IsAssignment () { return IdentAndAsgn(); } |
|
|
|
/* True, if ident is followed by ",", "=", "[" or ";" */ |
|
bool IsVarDecl () { |
|
int peek = Peek(1).kind; |
|
return IsIdentifierToken(la) && |
|
(peek == Tokens.Comma || peek == Tokens.Assign || peek == Tokens.Semicolon || peek == Tokens.OpenSquareBracket); |
|
} |
|
|
|
/* True, if the comma is not a trailing one, * |
|
* like the last one in: a, b, c, */ |
|
bool NotFinalComma () { |
|
int peek = Peek(1).kind; |
|
return la.kind == Tokens.Comma && |
|
peek != Tokens.CloseCurlyBrace && peek != Tokens.CloseSquareBracket; |
|
} |
|
|
|
/* True, if "void" is followed by "*" */ |
|
bool NotVoidPointer () { |
|
return la.kind == Tokens.Void && Peek(1).kind != Tokens.Times; |
|
} |
|
|
|
/* True, if "checked" or "unchecked" are followed by "{" */ |
|
bool UnCheckedAndLBrace () { |
|
return la.kind == Tokens.Checked || la.kind == Tokens.Unchecked && |
|
Peek(1).kind == Tokens.OpenCurlyBrace; |
|
} |
|
|
|
/* True, if "." is followed by an ident */ |
|
bool DotAndIdent () { |
|
return la.kind == Tokens.Dot && IsIdentifierToken(Peek(1)); |
|
} |
|
|
|
/* True, if ident is followed by ":" */ |
|
bool IdentAndColon () { |
|
return IsIdentifierToken(la) && Peek(1).kind == Tokens.Colon; |
|
} |
|
|
|
bool IsLabel () { return IdentAndColon(); } |
|
|
|
/* True, if ident is followed by "(" */ |
|
bool IdentAndLPar () { |
|
return IsIdentifierToken(la) && Peek(1).kind == Tokens.OpenParenthesis; |
|
} |
|
|
|
/* True, if "catch" is followed by "(" */ |
|
bool CatchAndLPar () { |
|
return la.kind == Tokens.Catch && Peek(1).kind == Tokens.OpenParenthesis; |
|
} |
|
bool IsTypedCatch () { return CatchAndLPar(); } |
|
|
|
/* True, if "[" is followed by the ident "assembly" */ |
|
bool IsGlobalAttrTarget () { |
|
Token pt = Peek(1); |
|
return la.kind == Tokens.OpenSquareBracket && |
|
IsIdentifierToken(pt) && (pt.val == "assembly" || pt.val == "module"); |
|
} |
|
|
|
/* True, if "[" is followed by "," or "]" */ |
|
bool LBrackAndCommaOrRBrack () { |
|
int peek = Peek(1).kind; |
|
return la.kind == Tokens.OpenSquareBracket && |
|
(peek == Tokens.Comma || peek == Tokens.CloseSquareBracket); |
|
} |
|
|
|
/* True, if "[" is followed by "," or "]" */ |
|
/* or if the current token is "*" */ |
|
bool TimesOrLBrackAndCommaOrRBrack () { |
|
return la.kind == Tokens.Times || LBrackAndCommaOrRBrack(); |
|
} |
|
bool IsPointerOrDims () { return TimesOrLBrackAndCommaOrRBrack(); } |
|
bool IsPointer () { return la.kind == Tokens.Times; } |
|
|
|
|
|
bool SkipGeneric(ref Token pt) |
|
{ |
|
if (pt.kind == Tokens.LessThan) { |
|
do { |
|
pt = Peek(); |
|
if (!IsTypeNameOrKWForTypeCast(ref pt)) return false; |
|
} while (pt.kind == Tokens.Comma); |
|
if (pt.kind != Tokens.GreaterThan) return false; |
|
pt = Peek(); |
|
} |
|
return true; |
|
} |
|
bool SkipQuestionMark(ref Token pt) |
|
{ |
|
if (pt.kind == Tokens.Question) { |
|
pt = Peek(); |
|
} |
|
return true; |
|
} |
|
|
|
/* True, if lookahead is a primitive type keyword, or */ |
|
/* if it is a type declaration followed by an ident */ |
|
bool IsLocalVarDecl () { |
|
if (IsYieldStatement()) { |
|
return false; |
|
} |
|
if ((Tokens.TypeKW[la.kind] && Peek(1).kind != Tokens.Dot) || la.kind == Tokens.Void) { |
|
return true; |
|
} |
|
|
|
StartPeek(); |
|
Token pt = la; |
|
return IsTypeNameOrKWForTypeCast(ref pt) && IsIdentifierToken(pt); |
|
} |
|
|
|
/* True if lookahead is a type argument list (<...>) followed by |
|
* one of "( ) ] } : ; , . ? == !=" */ |
|
bool IsGenericInSimpleNameOrMemberAccess() |
|
{ |
|
Token t = la; |
|
if (t.kind != Tokens.LessThan) return false; |
|
StartPeek(); |
|
return SkipGeneric(ref t) && Tokens.GenericFollower[t.kind]; |
|
} |
|
|
|
bool IsExplicitInterfaceImplementation() |
|
{ |
|
StartPeek(); |
|
Token pt = la; |
|
pt = Peek(); |
|
if (pt.kind == Tokens.Dot || pt.kind == Tokens.DoubleColon) |
|
return true; |
|
if (pt.kind == Tokens.LessThan) { |
|
if (SkipGeneric(ref pt)) |
|
return pt.kind == Tokens.Dot; |
|
} |
|
return false; |
|
} |
|
|
|
/* True, if lookahead ident is "yield" and than follows a break or return */ |
|
bool IsYieldStatement () { |
|
return la.kind == Tokens.Yield && (Peek(1).kind == Tokens.Return || Peek(1).kind == Tokens.Break); |
|
} |
|
|
|
/* True, if lookahead is a local attribute target specifier, * |
|
* i.e. one of "event", "return", "field", "method", * |
|
* "module", "param", "property", or "type" */ |
|
bool IsLocalAttrTarget () { |
|
int cur = la.kind; |
|
string val = la.val; |
|
|
|
return (cur == Tokens.Event || cur == Tokens.Return || |
|
(Tokens.IdentifierTokens[cur] && |
|
(val == "field" || val == "method" || val == "module" || |
|
val == "param" || val == "property" || val == "type"))) && |
|
Peek(1).kind == Tokens.Colon; |
|
} |
|
|
|
bool IsShiftRight() |
|
{ |
|
Token next = Peek(1); |
|
// TODO : Add col test (seems not to work, lexer bug...) : && la.col == next.col - 1 |
|
return (la.kind == Tokens.GreaterThan && next.kind == Tokens.GreaterThan); |
|
} |
|
|
|
bool IsGenericExpression(Expression expr) |
|
{ |
|
if (expr is IdentifierExpression) |
|
return ((IdentifierExpression)expr).TypeArguments.Count > 0; |
|
else if (expr is MemberReferenceExpression) |
|
return ((MemberReferenceExpression)expr).TypeArguments.Count > 0; |
|
else |
|
return false; |
|
} |
|
|
|
bool ShouldConvertTargetExpressionToTypeReference(Expression targetExpr) |
|
{ |
|
if (targetExpr is IdentifierExpression) |
|
return ((IdentifierExpression)targetExpr).TypeArguments.Count > 0; |
|
else if (targetExpr is MemberReferenceExpression) |
|
return ((MemberReferenceExpression)targetExpr).TypeArguments.Count > 0; |
|
else |
|
return false; |
|
} |
|
|
|
TypeReference GetTypeReferenceFromExpression(Expression expr) |
|
{ |
|
if (expr is TypeReferenceExpression) |
|
return (expr as TypeReferenceExpression).TypeReference; |
|
|
|
IdentifierExpression ident = expr as IdentifierExpression; |
|
if (ident != null) { |
|
return new TypeReference(ident.Identifier, ident.TypeArguments); |
|
} |
|
|
|
MemberReferenceExpression member = expr as MemberReferenceExpression; |
|
if (member != null) { |
|
TypeReference targetType = GetTypeReferenceFromExpression(member.TargetObject); |
|
if (targetType != null) { |
|
if (targetType.GenericTypes.Count == 0 && targetType.IsArrayType == false) { |
|
TypeReference tr = new TypeReference(targetType.Type + "." + member.MemberName, member.TypeArguments); |
|
tr.IsGlobal = targetType.IsGlobal; |
|
return tr; |
|
} else { |
|
return new InnerClassTypeReference(targetType, member.MemberName, member.TypeArguments); |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
bool IsMostNegativeIntegerWithoutTypeSuffix() |
|
{ |
|
Token token = la; |
|
if (token.kind == Tokens.Literal) { |
|
return token.val == "2147483648" || token.val == "9223372036854775808"; |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
bool LastExpressionIsUnaryMinus(System.Collections.ArrayList expressions) |
|
{ |
|
if (expressions.Count == 0) return false; |
|
UnaryOperatorExpression uoe = expressions[expressions.Count - 1] as UnaryOperatorExpression; |
|
if (uoe != null) { |
|
return uoe.Op == UnaryOperatorType.Minus; |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
bool StartOfQueryExpression() |
|
{ |
|
if (la.kind == Tokens.From) { |
|
Token p = Peek(1); |
|
if (IsIdentifierToken(p) || Tokens.TypeKW[p.kind]) |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
static bool IsIdentifierToken(Token tk) |
|
{ |
|
return Tokens.IdentifierTokens[tk.kind]; |
|
} |
|
|
|
/// <summary> |
|
/// Adds a child item to a collection stored in the parent node. |
|
/// Also set's the item's parent to <paramref name="parent"/>. |
|
/// Does nothing if item is null. |
|
/// </summary> |
|
static void SafeAdd<T>(INode parent, List<T> list, T item) where T : class, INode |
|
{ |
|
Debug.Assert(parent != null); |
|
Debug.Assert((parent is INullable) ? !(parent as INullable).IsNull : true); |
|
if (item != null) { |
|
list.Add(item); |
|
item.Parent = parent; |
|
} |
|
} |
|
} |
|
}
|
|
|