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.
436 lines
9.8 KiB
436 lines
9.8 KiB
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) |
|
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) |
|
|
|
using System; |
|
using System.Collections.Generic; |
|
using System.Diagnostics; |
|
using System.Linq; |
|
using System.Text; |
|
|
|
using ICSharpCode.NRefactory.VB.Ast; |
|
using ICSharpCode.NRefactory.VB.Visitors; |
|
|
|
namespace ICSharpCode.NRefactory.VB.Parser |
|
{ |
|
internal partial class VBParser : IDisposable |
|
{ |
|
VBLexer lexer; |
|
Stack<AstNode> stack; |
|
CompilationUnit compilationUnit; |
|
int errDist = MinErrDist; |
|
|
|
const int MinErrDist = 2; |
|
const string ErrMsgFormat = "-- line {0} col {1}: {2}"; // 0=line, 1=column, 2=text |
|
|
|
public VBParser(VBLexer lexer) |
|
{ |
|
this.errors = lexer.Errors; |
|
errors.SynErr = new ErrorCodeProc(SynErr); |
|
this.lexer = (VBLexer)lexer; |
|
this.stack = new Stack<AstNode>(); |
|
} |
|
|
|
#region Infrastructure |
|
void NodeStart(AstNode node) |
|
{ |
|
stack.Push(node); |
|
} |
|
|
|
void NodeEnd(AstNode currentNode, Role role) |
|
{ |
|
AstNode node = stack.Pop(); |
|
Debug.Assert(currentNode == node); |
|
stack.Peek().AddChildUntyped(node, role); |
|
} |
|
|
|
void AddTerminal(Role<VBTokenNode> role) |
|
{ |
|
stack.Peek().AddChild(new VBTokenNode(t.Location, t.EndLocation), role); |
|
} |
|
|
|
void AddChild<T>(T childNode, Role<T> role) where T : AstNode |
|
{ |
|
if (childNode != null) { |
|
stack.Peek().AddChild(childNode, role); |
|
} |
|
} |
|
|
|
StringBuilder qualidentBuilder = new StringBuilder(); |
|
|
|
Token t |
|
{ |
|
[System.Diagnostics.DebuggerStepThrough] |
|
get { |
|
return lexer.Token; |
|
} |
|
} |
|
|
|
Token la |
|
{ |
|
[System.Diagnostics.DebuggerStepThrough] |
|
get { |
|
return lexer.LookAhead; |
|
} |
|
} |
|
|
|
Token Peek(int n) |
|
{ |
|
lexer.StartPeek(); |
|
Token x = la; |
|
while (n > 0) { |
|
x = lexer.Peek(); |
|
n--; |
|
} |
|
return x; |
|
} |
|
|
|
public void Error(string s) |
|
{ |
|
if (errDist >= MinErrDist) { |
|
this.Errors.Error(la.line, la.col, s); |
|
} |
|
errDist = 0; |
|
} |
|
#endregion |
|
|
|
#region Parse |
|
public void Parse() |
|
{ |
|
ParseRoot(); |
|
} |
|
|
|
public AstType ParseAstType() |
|
{ |
|
// TODO |
|
return null; |
|
} |
|
|
|
// public Expression ParseExpression() |
|
// { |
|
// lexer.SetInitialContext(SnippetType.Expression); |
|
// lexer.NextToken(); |
|
// Location startLocation = la.Location; |
|
// Expression expr; |
|
// Expr(out expr); |
|
// while (la.kind == Tokens.EOL) lexer.NextToken(); |
|
// if (expr != null) { |
|
// expr.StartLocation = startLocation; |
|
// expr.EndLocation = t.EndLocation; |
|
// expr.AcceptVisitor(new SetParentVisitor(), null); |
|
// } |
|
// Expect(Tokens.EOF); |
|
// return expr; |
|
// } |
|
|
|
// public BlockStatement ParseBlock() |
|
// { |
|
// lexer.NextToken(); |
|
// compilationUnit = new CompilationUnit(); |
|
// |
|
// Location startLocation = la.Location; |
|
// Statement st; |
|
// Block(out st); |
|
// if (st != null) { |
|
// st.StartLocation = startLocation; |
|
// if (t != null) |
|
// st.EndLocation = t.EndLocation; |
|
// else |
|
// st.EndLocation = la.Location; |
|
// st.AcceptVisitor(new SetParentVisitor(), null); |
|
// } |
|
// Expect(Tokens.EOF); |
|
// return st as BlockStatement; |
|
// } |
|
|
|
// public List<AstNode> ParseTypeMembers() |
|
// { |
|
// lexer.NextToken(); |
|
// TypeDeclaration newType = new TypeDeclaration(Modifiers.None, null); |
|
// BlockStart(newType); |
|
// ClassBody(newType); |
|
// BlockEnd(); |
|
// Expect(Tokens.EOF); |
|
// newType.AcceptVisitor(new SetParentVisitor(), null); |
|
// return newType.Children; |
|
// } |
|
#endregion |
|
|
|
#region Conflict Resolvers |
|
bool IsAliasImportsClause() |
|
{ |
|
return IsIdentifierToken(la) && Peek(1).Kind == Tokens.Assign; |
|
} |
|
|
|
static bool IsIdentifierToken(Token tk) |
|
{ |
|
return Tokens.IdentifierTokens[tk.kind] || tk.kind == Tokens.Identifier; |
|
} |
|
#endregion |
|
|
|
/* True, if "." is followed by an ident */ |
|
bool DotAndIdentOrKw () { |
|
int peek = Peek(1).kind; |
|
return la.kind == Tokens.Dot && (peek == Tokens.Identifier || peek >= Tokens.AddHandler); |
|
} |
|
|
|
bool IsIdentifiedExpressionRange() |
|
{ |
|
// t = Select |
|
// la = Identifier |
|
// Peek(1) = As or Assign |
|
Token token = Peek(1); |
|
return IsIdentifierToken(la) && (token.kind == Tokens.As || token.kind == Tokens.Assign); |
|
} |
|
|
|
bool IsQueryExpression() |
|
{ |
|
return (la.kind == Tokens.From || la.kind == Tokens.Aggregate) && IsIdentifierToken(Peek(1)); |
|
} |
|
|
|
bool IsEndStmtAhead() |
|
{ |
|
int peek = Peek(1).kind; |
|
return la.kind == Tokens.End && (peek == Tokens.EOL || peek == Tokens.Colon); |
|
} |
|
|
|
bool IsNotClosingParenthesis() { |
|
return la.kind != Tokens.CloseParenthesis; |
|
} |
|
|
|
/* |
|
True, if ident is followed by "=" or by ":" and "=" |
|
*/ |
|
bool IsNamedAssign() { |
|
return Peek(1).kind == Tokens.ColonAssign; |
|
} |
|
|
|
bool IsObjectCreation() { |
|
return la.kind == Tokens.As && Peek(1).kind == Tokens.New; |
|
} |
|
|
|
bool IsNewExpression() { |
|
return la.kind == Tokens.New; |
|
} |
|
|
|
/* |
|
True, if "<" is followed by the ident "assembly" or "module" |
|
*/ |
|
bool IsGlobalAttrTarget () { |
|
Token pt = Peek(1); |
|
return la.kind == Tokens.LessThan && ( string.Equals(pt.val, "assembly", StringComparison.InvariantCultureIgnoreCase) || string.Equals(pt.val, "module", StringComparison.InvariantCultureIgnoreCase)); |
|
} |
|
|
|
/* |
|
True if the next token is a "(" and is followed by "," or ")" |
|
*/ |
|
bool IsDims() |
|
{ |
|
int peek = Peek(1).kind; |
|
return la.kind == Tokens.OpenParenthesis |
|
&& (peek == Tokens.Comma || peek == Tokens.CloseParenthesis); |
|
} |
|
|
|
/* |
|
True if the next token is an identifier |
|
*/ |
|
bool IsLoopVariableDeclaration() |
|
{ |
|
if (!IsIdentifierToken(la)) |
|
return false; |
|
lexer.StartPeek(); |
|
Token x = lexer.Peek(); |
|
if (x.kind == Tokens.OpenParenthesis) { |
|
do { |
|
x = lexer.Peek(); |
|
} while (x.kind == Tokens.Comma); |
|
if (x.kind != Tokens.CloseParenthesis) |
|
return false; |
|
x = lexer.Peek(); |
|
} |
|
return x.kind == Tokens.As || x.kind == Tokens.Assign; |
|
} |
|
|
|
bool IsSize() |
|
{ |
|
return la.kind == Tokens.OpenParenthesis; |
|
} |
|
|
|
/* |
|
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; |
|
} |
|
|
|
/* |
|
True, if the next token is "Else" and this one |
|
if followed by "If" |
|
*/ |
|
bool IsElseIf() |
|
{ |
|
int peek = Peek(1).kind; |
|
return la.kind == Tokens.Else && peek == Tokens.If; |
|
} |
|
|
|
/* |
|
True if the next token is goto and this one is |
|
followed by minus ("-") (this is allowd in in |
|
error clauses) |
|
*/ |
|
bool IsNegativeLabelName() |
|
{ |
|
int peek = Peek(1).kind; |
|
return la.kind == Tokens.GoTo && peek == Tokens.Minus; |
|
} |
|
|
|
/* |
|
True if the next statement is a "Resume next" statement |
|
*/ |
|
bool IsResumeNext() |
|
{ |
|
int peek = Peek(1).kind; |
|
return la.kind == Tokens.Resume && peek == Tokens.Next; |
|
} |
|
|
|
/// <summary> |
|
/// Returns True, if ident/literal integer is followed by ":" |
|
/// </summary> |
|
bool IsLabel() |
|
{ |
|
return (la.kind == Tokens.Identifier || la.kind == Tokens.LiteralInteger) |
|
&& Peek(1).kind == Tokens.Colon; |
|
} |
|
|
|
/// <summary> |
|
/// Returns true if a property declaration is an automatic property. |
|
/// </summary> |
|
bool IsAutomaticProperty() |
|
{ |
|
lexer.StartPeek(); |
|
Token tn = la; |
|
int braceCount = 0; |
|
|
|
// look for attributes |
|
while (tn.kind == Tokens.LessThan) { |
|
while (braceCount > 0 || tn.kind != Tokens.GreaterThan) { |
|
tn = lexer.Peek(); |
|
if (tn.kind == Tokens.OpenParenthesis) |
|
braceCount++; |
|
if (tn.kind == Tokens.CloseParenthesis) |
|
braceCount--; |
|
} |
|
Debug.Assert(tn.kind == Tokens.GreaterThan); |
|
tn = lexer.Peek(); |
|
} |
|
|
|
// look for modifiers |
|
var allowedTokens = new[] { |
|
Tokens.Public, Tokens.Protected, |
|
Tokens.Friend, Tokens.Private |
|
}; |
|
|
|
while (allowedTokens.Contains(tn.kind)) |
|
tn = lexer.Peek(); |
|
|
|
if (tn.Kind != Tokens.Get && tn.Kind != Tokens.Set) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
bool IsNotStatementSeparator() |
|
{ |
|
return la.kind == Tokens.Colon && Peek(1).kind == Tokens.EOL; |
|
} |
|
|
|
static bool IsMustOverride(AttributedNode node) |
|
{ |
|
return node.Modifiers.HasFlag(Modifiers.MustOverride); |
|
} |
|
|
|
/* Writes the type name represented through the expression into the string builder. */ |
|
/* Returns true when the expression was converted successfully, returns false when */ |
|
/* There was an unknown expression (e.g. TypeReferenceExpression) in it */ |
|
// bool WriteFullTypeName(StringBuilder b, Expression expr) |
|
// { |
|
// MemberReferenceExpression fre = expr as MemberReferenceExpression; |
|
// if (fre != null) { |
|
// bool result = WriteFullTypeName(b, fre.TargetObject); |
|
// if (b.Length > 0) b.Append('.'); |
|
// b.Append(fre.MemberName); |
|
// return result; |
|
// } else if (expr is SimpleNameExpression) { |
|
// b.Append(((SimpleNameExpression)expr).Identifier); |
|
// return true; |
|
// } else { |
|
// return false; |
|
// } |
|
// } |
|
|
|
void EnsureIsZero(Expression expr) |
|
{ |
|
if (!(expr is PrimitiveExpression) || (expr as PrimitiveExpression).StringValue != "0") |
|
Error("lower bound of array must be zero"); |
|
} |
|
|
|
public bool ParseMethodBodies { get; set; } |
|
|
|
public VBLexer Lexer { |
|
get { |
|
return lexer; |
|
} |
|
} |
|
|
|
public Errors Errors { |
|
get { |
|
return errors; |
|
} |
|
} |
|
|
|
public CompilationUnit CompilationUnit { |
|
get { |
|
return compilationUnit; |
|
} |
|
} |
|
|
|
void SynErr(int n) |
|
{ |
|
if (errDist >= MinErrDist) { |
|
errors.SynErr(lexer.LookAhead.line, lexer.LookAhead.col, n); |
|
} |
|
errDist = 0; |
|
} |
|
|
|
void SemErr(string msg) |
|
{ |
|
if (errDist >= MinErrDist) { |
|
errors.Error(lexer.Token.line, lexer.Token.col, msg); |
|
} |
|
errDist = 0; |
|
} |
|
|
|
void Expect(int n) |
|
{ |
|
if (lexer.LookAhead.kind == n) { |
|
lexer.NextToken(); |
|
} else { |
|
SynErr(n); |
|
} |
|
} |
|
|
|
#region System.IDisposable interface implementation |
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")] |
|
public void Dispose() |
|
{ |
|
errors = null; |
|
if (lexer != null) { |
|
lexer.Dispose(); |
|
} |
|
lexer = null; |
|
} |
|
#endregion |
|
} |
|
}
|
|
|