using System;
using System.IO;
using System.Text;
using System.Threading;
using AnticipatingMinds.Genesis.CodeDOM;
using AnticipatingMinds.Genesis.CodeDOM.Utilities;
using System.Diagnostics;
using System.CodeDom.Compiler;
using System.Collections;
using AnticipatingMinds.Genesis.CodeParser;
using TAnticipatingMinds.Genesis.CodeParser.CSharpInternals.CSharpTokenType;
using AnticipatingMinds.Genesis.CodeParser.CSharpInternals;
using AnticipatingMinds.PlatformServices.Globalization;
namespace AnticipatingMinds.Genesis.CodeParser{
/// <summary>
/// Summary description for CSharpParser.
/// </summary>
[System.Security.Permissions.StrongNameIdentityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand,
PublicKey=
"00240000048000009400000006020000002400005253413100040000010001009D309779C258129573FC313836474C75C4CE9F4" +
"107524FA0B9A6DB2E52754459C5A8946E4CBD5B98ACDB2413C5AFD38C1DF00C9A946713E867237B47F9D9CC473D4A853EACBEAB" +
"799EC0A271B468D4B6D52301A414A7772F05FEBD2BA7D0A2835F0D45E401C3C37F9E7B991D29F07DA88E20BB3839A34A2739AB6" +
"56B5204C8BC")]
public class CSharpParser : AnticipatingMinds.Genesis.CodeParser.CodeParser
{
public CSharpParser(string sourceCode,string fileName,CodeAssembly codeAssembly):base(sourceCode,fileName,codeAssembly)
{
this.sourceCode = sourceCode;
}
public void InitializeParser(ManualResetEvent cancelEvent)
{
this.cancelEvent = cancelEvent;
//Instantiate new compile unit and errors collection for this parse
parsingErrors = new CompilerErrorCollection();
compileUnit = new CodeCompileUnit(FileName);
compileUnit.Language = CodeLanguage.CSharp;
OnParsingStarted(FileName);
ReportProgress();
}
public void TokenizeSource(bool processComments)
{
if(CodeAssembly != null)
tokens = new CSharpTokenStream(SourceCode, !processComments,CodeAssembly.CompilationConstants);
else
tokens = new CSharpTokenStream(SourceCode, !processComments,new string[0]);
//If we need to process comments do it now (before parsing) and remove comments from the tokens stream so parser would work
//fine.
CodeCommentCollection codeComments = null;
if(processComments)
codeComments = ExtractCommentsFromTokens();
}
public CodeCompileUnit Parse(ManualResetEvent cancelEvent,bool processComments)
{
InitializeParser(cancelEvent);
TokenizeSource(processComments);
try
{
//The only valid element for compile unit is namespace or a type declaration
//if there is no namespace explicity specified
//anonymouse namesapce is assumed.
//ECMA 334. Page 345, line 41
RecordPosition(CompileUnit.Namespace);
ParseNamespaceBody(CompileUnit.Namespace);
}
catch(Exception e)
{
ParserException parserException = new ParserException("Unexpected Parser Exception",e);
parserException.CurrentPosition.FileName = FileName;
if(tokens != null && tokens.Current != null)
{
parserException.CurrentPosition.Line = tokens.Current.Line;
parserException.CurrentPosition.Column = tokens.Current.Column;
parserException.CurrentPosition.Position = tokens.Current.Position;
}
Errors.Add(new CompilerError(FileName,parserException.CurrentPosition.Line,parserException.CurrentPosition.Column,"",parserException.ToString()));
throw parserException;
}
CodeCompileUnitUtils.RefreshCompileUnitElementsReferences(compileUnit);
// if(processComments)
// {
// ProcessAndAssignComments(codeComments);
// }
ReportProgress();
OnParsingEnded(compileUnit,parsingErrors);
return compileUnit;
}
public override CodeAssemblyFile Parse(ManualResetEvent cancelEvent)
{
return Parse(cancelEvent,false);
}
public CodeStatementBlock ParseStatements(ManualResetEvent cancelEvent)
{
InitializeParser(cancelEvent);
TokenizeSource(false);
CodeStatementBlock statmentsBlock = null;
try
{
statmentsBlock = ParseStatementBlock(null);
}
catch(Exception e)
{
ParserException parserException = new ParserException("Unexpected Parser Exception",e);
parserException.CurrentPosition.FileName = FileName;
if(tokens != null && tokens.Current != null)
{
parserException.CurrentPosition.Line = tokens.Current.Line;
parserException.CurrentPosition.Column = tokens.Current.Column;
parserException.CurrentPosition.Position = tokens.Current.Position;
}
Errors.Add(new CompilerError(FileName,parserException.CurrentPosition.Line,parserException.CurrentPosition.Column,"",parserException.ToString()));
throw parserException;
}
CodeCompileUnitUtils.RefreshCodeElementsReferences(statmentsBlock);
ReportProgress();
OnParsingEnded(compileUnit,parsingErrors);
return statmentsBlock;
}
public CodeExpression ParseExpression(ManualResetEvent cancelEvent)
{
InitializeParser(cancelEvent);
TokenizeSource(false);
CodeExpression expression = null;
try
{
expression = ParseExpression();
}
catch(Exception e)
{
ParserException parserException = new ParserException("Unexpected Parser Exception",e);
parserException.CurrentPosition.FileName = FileName;
if(tokens != null && tokens.Current != null)
{
parserException.CurrentPosition.Line = tokens.Current.Line;
parserException.CurrentPosition.Column = tokens.Current.Column;
parserException.CurrentPosition.Position = tokens.Current.Position;
}
Errors.Add(new CompilerError(FileName,parserException.CurrentPosition.Line,parserException.CurrentPosition.Column,"",parserException.ToString()));
throw parserException;
}
CodeCompileUnitUtils.RefreshCodeElementsReferences(expression);
ReportProgress();
OnParsingEnded(compileUnit,parsingErrors);
return expression;
}
private CodeCommentCollection ExtractCommentsFromTokens()
{
//Preprocess tokens to extract collection of comments.
//and remove comments from tokens collection
CodeCommentCollection comments = new CodeCommentCollection();
while(tokens.CurrentType != T.TID_EOF)
{
//Process documentation comments
if(tokens.CurrentType == T.TID_SINGLE_LINE_COMMENT && tokens.Current.Name.Length > 0 && tokens.Current.Name[0] == '/')
{
CodeComment comment = new CodeComment();
RecordPosition(comment);
comment.Style = CodeComment.CodeCommentStyle.Documentation;
do
{
comment.Lines.Add(tokens.Current.Name.Substring(1));
tokens.Remove();
}while(tokens.CurrentType != T.TID_EOF && tokens.CurrentType == T.TID_SINGLE_LINE_COMMENT && tokens.Current.Name.Length > 0 && tokens.Current.Name[0] == '/');
comments.Add(comment);
continue;
}
//Process single line comments
if(tokens.CurrentType == T.TID_SINGLE_LINE_COMMENT)
{
CodeComment comment = new CodeComment();
RecordPosition(comment);
comment.Style = CodeComment.CodeCommentStyle.SingleLine;
do
{
comment.Lines.Add(tokens.Current.Name);
tokens.Remove();
} while(tokens.CurrentType != T.TID_EOF && tokens.CurrentType == T.TID_SINGLE_LINE_COMMENT);
comments.Add(comment);
continue;
}
//Process multi line comments
if(tokens.CurrentType == T.TID_MULTI_LINE_COMMENT)
{
CodeComment comment = new CodeComment();
RecordPosition(comment);
comment.Style = CodeComment.CodeCommentStyle.Multiline;
do
{
//Split multiline comment by lines
using(StringReader commentReader = new StringReader(tokens.Current.Name))
{
string line;
while ((line = commentReader.ReadLine()) != null)
{
comment.Lines.Add(line);
}
}
tokens.Remove();
}while(tokens.CurrentType != T.TID_EOF && tokens.CurrentType == T.TID_MULTI_LINE_COMMENT);
comments.Add(comment);
continue;
}
tokens.Next();
}
tokens.Rewind(0);
return comments;
}
private void ProcessAndAssignComments(CodeCommentCollection comments)
{
///Comments.
///It will be very hard to parse comments at the same time we parse code elemnts
///The reason is that we always would have to check for one. We can never expect
///certain token. It would multiply ifs greatly. To avoid it, we process comments
///after we processed the rest.
///The algorithm is quite simple. We add all code elements into an array and sort it
///base on element position.
ArrayList allCodeElements = new ArrayList();
CodeDomWalker.WalkCompileUnit(CompileUnit,new CodeDomWalker.WalkerCallback(BuildCodeElementsArray),allCodeElements);
allCodeElements.Sort(new CodeElementPositionComparer(true));
//Now, let's assign code comments to a particular code elements
foreach(CodeComment comment in comments)
{
if(comment.Style == CodeComment.CodeCommentStyle.Documentation)
{
foreach(CodeElement codeElement in allCodeElements)
{
if(comment.SourcePosition.Position < codeElement.SourcePosition.Position &&
(codeElement is CodeTypeDeclaration || codeElement is CodeTypeMemberDeclaration))
{
if(codeElement.DocumentationComment != null)
{
foreach(String line in comment.Lines)
codeElement.DocumentationComment.Lines.Add(line);
}
else
{
codeElement.DocumentationComment = comment;
}
break;
}
}
}
else
{
foreach(CodeElement codeElement in allCodeElements)
{
if(comment.SourcePosition.Position < codeElement.SourcePosition.Position)
{
if(codeElement.Comment != null)
{
foreach(String line in comment.Lines)
codeElement.Comment.Lines.Add(line);
}
else
{
codeElement.Comment = comment;
}
break;
}
}
}
while(allCodeElements.Count > 0 && (allCodeElements[0] as CodeElement).SourcePosition.Position < comment.SourcePosition.Position)
allCodeElements.RemoveAt(0);
}
}
private CodeDomWalker.WalkerCallbackReturn BuildCodeElementsArray(CodeElement element,CodeDomWalker.CallBackNotificationType notification,CodeDomWalkerContext walkerContext,object applicationData)
{
if(notification == CodeDomWalker.CallBackNotificationType.OnElement)
{
(applicationData as ArrayList).Add(element);
}
return CodeDomWalker.WalkerCallbackReturn.Next;
}
private bool IsCanceled()
{
if(cancelEvent != null)
return cancelEvent.WaitOne(0,false);
else
return false;
}
private void ParseNamespace(CodeNamespace parentNamespace)
{
if(tokens.CurrentType != T.TID_NAMESPACE)
return;
CodeNamespace codeNamespace = new CodeNamespace();
codeNamespace.CompileUnit = parentNamespace.CompileUnit;
RecordPosition(codeNamespace);
Eat(T.TID_NAMESPACE);
CSharpToken currentToken = tokens.Current;
if(currentToken.TokenType != T.TID_IDENTIFIER)
{
ReportUnexpectedTokenError(T.TID_IDENTIFIER,currentToken.TokenType);
if(currentToken.TokenType != T.TID_OPENCURLY)
ReportUnexpectedTokenError(T.TID_OPENCURLY,currentToken.TokenType);
return;
}
string namespaceName = ParseDottedName();
if(tokens.CurrentType != T.TID_OPENCURLY)
{
ReportUnexpectedTokenError(T.TID_OPENCURLY,tokens.CurrentType);
return;
}
Eat(T.TID_OPENCURLY);
codeNamespace.Name = namespaceName;
ParseNamespaceBody(codeNamespace);
if(tokens.CurrentType != T.TID_CLOSECURLY)
{
ReportUnexpectedTokenError(T.TID_CLOSECURLY,currentToken.TokenType);
return;
}
Eat(T.TID_CLOSECURLY);
//Optional semilon
if(tokens.CurrentType == T.TID_SEMICOLON)
Eat(T.TID_SEMICOLON);
parentNamespace.NestedNamespaces.Add(codeNamespace);
}
private void ParseNamespaceBody(CodeNamespace codeNamespace)
{
//Namespace starts with using directives or does not start at all!
ParseNamespaceImports(codeNamespace);
//Parse attributes section here. Attributes defined in here could be either global or
//related to the class. Global attributes will have scope defined.
//class one wont.
//So...
if(codeNamespace == this.CompileUnit.Namespace) //Anonymouse namespace
CompileUnit.GlobalAttributes.AddRange(ParseGlobalAttributes());
int elementStartMark = tokens.Mark();
while(tokens.CurrentType != T.TID_EOF && tokens.CurrentType != T.TID_CLOSECURLY)
{
if(IsCanceled())
return;
switch(tokens.CurrentType)
{
case T.TID_NAMESPACE:
ParseNamespace(codeNamespace);
elementStartMark = tokens.Mark();
break;
case T.TID_INTERFACE:
case T.TID_STRUCT:
case T.TID_CLASS:
{
//Rewind to the beginign of the element
tokens.Rewind(elementStartMark);
ParseClassStructInterfaceDeclaration(codeNamespace,null);
elementStartMark = tokens.Mark();
break;
}
case T.TID_ENUM:
{
//Rewind to the beginign of the element
tokens.Rewind(elementStartMark);
ParseEnumDeclaration(codeNamespace,null);
elementStartMark = tokens.Mark();
break;
}
case T.TID_DELEGATE:
{
//Rewind to the beginign of the element
tokens.Rewind(elementStartMark);
ParseDelegateDeclaration(codeNamespace,null);
elementStartMark = tokens.Mark();
break;
}
default:
tokens.Next();
break;
}
}
}
private void ParseMethodDeclaration(CodeTypeDeclaration parent)
{
methodScopeNameManager.Clear();
CodeAttributeCollection methodAttributes = ParseAttributes();
CodeElementPosition sourcePosition = new CodeElementPosition(CompileUnit.SourceFileName,tokens.Current.Line,tokens.Current.Column,tokens.Current.Position);
CodeTypeMemberDeclaration.MemberDeclarationModifiers modifiers = ParseMemberDeclarationModifiers();
CodeTypeReference returnType = new CodeTypeReference("System.Void");
string methodName = string.Empty;
bool isDestructor = false;
bool isConstructor = false;
//Is it destructor?
if(tokens.CurrentType == T.TID_TILDE)
{
isDestructor = true;
tokens.Next();
methodName = ParseDottedName();
}
else
{
//Could be a return type or a constructor
//Id it contructor?
if(tokens.CurrentType == T.TID_IDENTIFIER &&
tokens.Current.Name == parent.Name &&
tokens.Peek(1).TokenType == T.TID_OPENPAREN)
{
//Definetly constructor!
Eat(T.TID_IDENTIFIER);
methodName = ".ctor";
isConstructor = true;
}
else
{
RecordPosition(returnType);
returnType.TypeName = ParseTypeName();
methodName = ParseDottedName();
}
}
Eat(T.TID_OPENPAREN);
CodeMethodParameterCollection parameters = ParseParametersList();
Eat(T.TID_CLOSEPAREN);
//Create appropriate method:
if(isConstructor)
{
CodeTypeConstructorDeclaration constructor = new CodeTypeConstructorDeclaration();
constructor.Parameters.AddRange(parameters);
constructor.Attributes.AddRange(methodAttributes);
constructor.SourcePosition = sourcePosition;
constructor.Modifiers = modifiers;
//Parse base
if(tokens.CurrentType == T.TID_COLON)
{
Eat(T.TID_COLON);
if(tokens.CurrentType == T.TID_BASE)
{
constructor.InitializerType = CodeTypeConstructorDeclaration.ConstructorInitializerType.Base;
Eat(T.TID_BASE);
}
else
if(tokens.CurrentType == T.TID_THIS)
{
constructor.InitializerType = CodeTypeConstructorDeclaration.ConstructorInitializerType.This;
Eat(T.TID_THIS);
}
Eat(T.TID_OPENPAREN);
constructor.InitializerArguments.AddRange(ParseArgumentsList());
Eat(T.TID_CLOSEPAREN);
}
constructor.Statements = ParseStatementBlock(null);
//constructor.DeclaringType = parent;
AddMemberToCodeType(parent,constructor);
}
else
{
if(isDestructor)
{
CodeTypeDestructorDeclaration destructor = new CodeTypeDestructorDeclaration();
destructor.Attributes.AddRange(methodAttributes);
destructor.Parameters.AddRange(parameters);
destructor.Modifiers = modifiers;
AddMemberToCodeType(parent,destructor);
destructor.Statements = ParseStatementBlock(null);
destructor.ReturnType = null;
destructor.SourcePosition = sourcePosition;
}
else
{
//Just a method!
CodeTypeMethodDeclaration method = new CodeTypeMethodDeclaration();
method.Name = methodName;
//method.DeclaringType = parent;
method.Attributes.AddRange(methodAttributes);
method.Parameters.AddRange(parameters);
method.Modifiers = modifiers;
AddMemberToCodeType(parent,method);
method.Statements = ParseStatementBlock(null);
method.ReturnType = returnType;
method.SourcePosition = sourcePosition;
}
}
}
private void ParsePropertyDeclaration(CodeTypeDeclaration parent)
{
methodScopeNameManager.Clear();
CodeTypePropertyDeclaration property = new CodeTypePropertyDeclaration();
//property.DeclaringType = parent;
property.Attributes.AddRange(ParseAttributes());
RecordPosition(property);
property.Modifiers = ParseMemberDeclarationModifiers();
CodeTypeReference returnType = new CodeTypeReference();
RecordPosition(returnType);
returnType.TypeName = ParseTypeName();
property.ReturnType = returnType;
property.Name = ParseDottedName();
Debug.Assert(property.Name != null && property.Name.Length != 0,"Property name cannot be empty string.");
Debug.Assert(returnType.TypeName != null && returnType.TypeName.Length != 0,"Property return type name cannot be empty string.");
//property starts
if(!Eat(T.TID_OPENCURLY))
{
RecoverBySkippingCurlies(false);
return;
}
//Get accessros
while(true)
{
if(IsCanceled())
return;
CodeAttributeCollection accessorAttributes = ParseAttributes();
if(tokens.CurrentType != T.TID_IDENTIFIER) //get or set
break;
string accessorType = tokens.Current.Name;
tokens.Next();
if(accessorType == "get")
{
property.HasGetter = true;
property.GetAccessorAttributes.AddRange(accessorAttributes);
property.GetAccessorStatements = ParseStatementBlock(null);
}
else
if(accessorType == "set")
{
property.HasSetter = true;
//In the set block there is a parameter - value is proerty method paramter
//it is hidden in the grammar but it is there
//so - add it!
methodScopeNameManager.Register("value",new CodeMethodParameter(returnType,"value"));
property.SetAccessorAttributes.AddRange(accessorAttributes);
property.SetAccessorStatements = ParseStatementBlock(null);
}
else
{
ReportUnexpectedTokenError(T.TID_PROPERTY_GET_OR_SET,tokens.CurrentType);
break;
}
}
AddMemberToCodeType(parent,property);
Eat(T.TID_CLOSECURLY);
}
private void ParseFieldDeclaration(CodeTypeDeclaration parent)
{
CodeTypeFieldDeclaration fields = new CodeTypeFieldDeclaration();
RecordPosition(fields);
fields.Attributes.AddRange(ParseAttributes());
fields.Modifiers = ParseMemberDeclarationModifiers();
fields.DeclarationType = new CodeTypeReference();
RecordPosition(fields.DeclarationType);
fields.DeclarationType.TypeName = ParseTypeName();
fields.DeclaredFields.AddRange(ParseVariableDeclarationList());
Eat(T.TID_SEMICOLON);
if(fields.DeclaredFields.Count != 0)
{
AddMemberToCodeType(parent,fields);
}
else
{
//Error
//skip statements till the next semicolomn
RecoverBySkippingToSemicolon();
}
}
/// <summary>
/// Parses declaration of delegate
/// </summary>
/// <param name="codeNamespace"></param>
/// <param name="parent"></param>
private void ParseDelegateDeclaration(CodeNamespace codeNamespace,CodeTypeDeclaration parent)
{
CodeTypeDelegateDeclaration delegateDeclaration = new CodeTypeDelegateDeclaration();
delegateDeclaration.Attributes.AddRange(ParseAttributes());
RecordPosition(delegateDeclaration);
delegateDeclaration.Modifiers = ParseTypeDeclarationModifiers();
Eat(T.TID_DELEGATE);
CodeTypeReference returnType = new CodeTypeReference();
RecordPosition(returnType);
returnType.TypeName = ParseTypeName();
delegateDeclaration.ReturnType = returnType;
delegateDeclaration.Name = ParseDottedName();
Eat(T.TID_OPENPAREN);
delegateDeclaration.Parameters.AddRange(ParseParametersList());
Eat(T.TID_CLOSEPAREN);
Eat(T.TID_SEMICOLON);
//Delegate can be declared inside class or a namespace.
if(parent == null)
{
codeNamespace.DeclaredTypes.Add(delegateDeclaration);
}
else
{
if(parent is CodeClassDeclaration)
((CodeClassDeclaration) parent).NestedTypes.Add(delegateDeclaration);
if(parent is CodeStructDeclaration)
((CodeStructDeclaration) parent).NestedTypes.Add(delegateDeclaration);
}
}
private void ParseIndexerDeclaration(CodeTypeDeclaration parent)
{
methodScopeNameManager.Clear();
CodeTypeIndexerDeclaration indexer = new CodeTypeIndexerDeclaration();
//indexer.DeclaringType = parent;
indexer.Attributes.AddRange(ParseAttributes());
RecordPosition(indexer);
indexer.Modifiers = ParseMemberDeclarationModifiers();
CodeTypeReference returnType = new CodeTypeReference();
RecordPosition(returnType);
returnType.TypeName = ParseTypeName();
indexer.ReturnType = returnType;
if(tokens.CurrentType == T.TID_THIS)
{
indexer.Name = "this";
Eat(T.TID_THIS);
}
else
{
indexer.Name = ParseDottedName();
}
//When name is in form of id.id.this ParseDottedName eats this.
//check for it and appaend it if needed
if(tokens.Peek(-1).TokenType == T.TID_THIS)
{
indexer.Name += "this";
}
Eat(T.TID_OPENSQUARE);
indexer.Parameters.AddRange(ParseParametersList());
Eat(T.TID_CLOSESQUARE);
//property starts
if(!Eat(T.TID_OPENCURLY))
return;
//Get accessros
while(true)
{
if(IsCanceled())
return;
CodeAttributeCollection accessorAttributes = ParseAttributes();
if(tokens.CurrentType != T.TID_IDENTIFIER) //get or set
break;
string accessorType = tokens.Current.Name;
tokens.Next();
if(accessorType == "get")
{
indexer.GetAccessorAttributes.AddRange(accessorAttributes);
indexer.GetAccessorStatements = ParseStatementBlock(null);
}
else
if(accessorType == "set")
{
//In the set block there is a parameter - value is proerty method paramter
//it is hidden in the grammar but it is there
//so - add it!
methodScopeNameManager.Register("value",new CodeMethodParameter(returnType,"value"));
indexer.SetAccessorAttributes.AddRange(accessorAttributes);
indexer.SetAccessorStatements = ParseStatementBlock(null);
}
else
{
ReportUnexpectedTokenError(T.TID_PROPERTY_GET_OR_SET,tokens.CurrentType);
break;
}
}
Eat(T.TID_CLOSECURLY);
AddMemberToCodeType(parent,indexer);
}
private void ParseEventDeclaration(CodeTypeDeclaration parent)
{
methodScopeNameManager.Clear();
CodeAttributeCollection eventAttributes = ParseAttributes();
CodeElementPosition sourcePosition = new CodeElementPosition(CompileUnit.SourceFileName,tokens.Current.Line,tokens.Current.Column,tokens.Current.Position);
CodeTypeMemberDeclaration.MemberDeclarationModifiers modifiers = ParseMemberDeclarationModifiers();
Eat(T.TID_EVENT);
CodeTypeReference eventType = new CodeTypeReference();
RecordPosition(eventType);
eventType.TypeName = ParseTypeName();
bool areEventAcessorsDeclared = false;
int currentPosition = tokens.Mark();
//Skip the name
ParseDottedName();
if(tokens.CurrentType == T.TID_OPENCURLY)
areEventAcessorsDeclared = true;
tokens.Rewind(currentPosition);
if(areEventAcessorsDeclared)
{
//An event with add/remove blocks declaration
CodeTypeEventDeclaration eventDeclaration = new CodeTypeEventDeclaration();
eventDeclaration.DeclarationType = eventType;
eventDeclaration.SourcePosition = sourcePosition;
eventDeclaration.Modifiers = modifiers;
eventDeclaration.Attributes.AddRange(eventAttributes);
eventDeclaration.Name= ParseDottedName();
//eventDeclaration.DeclaringType = parent;
//property starts
if(!Eat(T.TID_OPENCURLY))
return;
//Get accessros
while(true)
{
if(IsCanceled())
return;
CodeAttributeCollection accessorAttributes = ParseAttributes();
if(tokens.CurrentType != T.TID_IDENTIFIER) //get or set
break;
string accessorType = tokens.Current.Name;
tokens.Next();
//Accessors define parameter - value
//it is the same thing as with properties - it is implicit but it is there!
methodScopeNameManager.Register("value",new CodeMethodParameter(eventType,"value"));
if(accessorType == "add")
{
eventDeclaration.AddAccessorAttributes.AddRange(accessorAttributes);
eventDeclaration.AddAccessorStatements = ParseStatementBlock(null);
}
else
if(accessorType == "remove")
{
eventDeclaration.RemoveAccessorAttributes.AddRange(accessorAttributes);
eventDeclaration.RemoveAccessorStatements = ParseStatementBlock(null);
}
else
{
ReportUnexpectedTokenError(T.TID_EVENT_ADD_OR_REMOVE,tokens.CurrentType);
break;
}
}
AddMemberToCodeType(parent,eventDeclaration);
Eat(T.TID_CLOSECURLY);
}
else
{
CodeTypeEventListDeclaration eventListDeclaration = new CodeTypeEventListDeclaration();
eventListDeclaration.DeclarationType = eventType;
eventListDeclaration.SourcePosition = sourcePosition;
eventListDeclaration.Modifiers = modifiers;
eventListDeclaration.Attributes.AddRange(eventAttributes);
//eventListDeclaration.DeclaringType = parent;
//Field like declaration
eventListDeclaration.DeclaredEvents.AddRange(ParseVariableDeclarationList());
Eat(T.TID_SEMICOLON);
AddMemberToCodeType(parent,eventListDeclaration);
}
}
private void ParseOperatorDeclaration(CodeTypeDeclaration parent)
{
methodScopeNameManager.Clear();
CodeAttributeCollection operatorAttributes = ParseAttributes();
CodeElementPosition sourcePosition = new CodeElementPosition(CompileUnit.SourceFileName,tokens.Current.Line,tokens.Current.Column,tokens.Current.Position);
CodeTypeMemberDeclaration.MemberDeclarationModifiers modifiers = ParseMemberDeclarationModifiers();
if(tokens.CurrentType == T.TID_EXPLICIT ||
tokens.CurrentType == T.TID_IMPLICIT)
{
//Cast operator
CodeTypeConversionOperatorDeclaration castOperator = new CodeTypeConversionOperatorDeclaration();
castOperator.SourcePosition = sourcePosition;
castOperator.Attributes.AddRange(operatorAttributes);
//castOperator.DeclaringType = parent;
castOperator.Modifiers = modifiers;
if(tokens.CurrentType == T.TID_EXPLICIT)
castOperator.Explicit = true;
else
castOperator.Explicit = false;
tokens.Next();
Eat(T.TID_OPERATOR);
CodeTypeReference returnType = new CodeTypeReference();
RecordPosition(returnType);
returnType.TypeName = ParseTypeName();
castOperator.ReturnType = returnType;
Eat(T.TID_OPENPAREN);
CodeMethodParameterCollection paremeters = new CodeMethodParameterCollection(ParseParametersList());
Eat(T.TID_CLOSEPAREN);
if(paremeters.Count>0)
castOperator.Parameter = paremeters[0];
castOperator.Statements = ParseStatementBlock(null);
AddMemberToCodeType(parent,castOperator);
}
else
{
//Binary or unary operator
CodeTypeReference operatorReturnType = new CodeTypeReference();
RecordPosition(operatorReturnType);
operatorReturnType.TypeName = ParseTypeName();
Eat(T.TID_OPERATOR);
CSharpToken operation = tokens.Current;
tokens.Next();
Eat(T.TID_OPENPAREN);
CodeMethodParameterCollection paremeters = new CodeMethodParameterCollection(ParseParametersList());
Eat(T.TID_CLOSEPAREN);
Debug.Assert(paremeters.Count != 0,"Is it unknow operator?");
if(paremeters.Count == 1)
{
//Unary operator
CodeTypeUnaryOperatorDeclaration unaryOp= new CodeTypeUnaryOperatorDeclaration();
unaryOp.Attributes.AddRange(operatorAttributes);
//unaryOp.DeclaringType = parent;
unaryOp.Modifiers = modifiers;
unaryOp.Operation = operation.GetUnaryOperation();
unaryOp.Parameter = paremeters[0];
unaryOp.ReturnType = operatorReturnType;
unaryOp.Statements = ParseStatementBlock(null);
unaryOp.SourcePosition = sourcePosition;
AddMemberToCodeType(parent,unaryOp);
}
else
{
//Binary operator
CodeTypeBinaryOperatorDeclaration binaryOp= new CodeTypeBinaryOperatorDeclaration();
binaryOp.Attributes.AddRange(operatorAttributes);
//binaryOp.DeclaringType = parent;
binaryOp.Modifiers = modifiers;
binaryOp.Operation = operation.GetBinaryOperation();
binaryOp.SourcePosition = sourcePosition;
binaryOp.LeftParameter = paremeters[0];
if(paremeters.Count > 1)
binaryOp.RightParameter = paremeters[1];
binaryOp.ReturnType = operatorReturnType;
binaryOp.Statements = ParseStatementBlock(null);
AddMemberToCodeType(parent,binaryOp);
}
}
}
/// <summary>
/// Parses a variable declaration members list.
/// </summary>
/// <returns></returns>
private CodeVariableDeclarationMemberCollection ParseVariableDeclarationList()
{
CodeVariableDeclarationMemberCollection variables = new CodeVariableDeclarationMemberCollection();
do
{
if(IsCanceled())
return variables;
CodeVariableDeclarationMember variable = new CodeVariableDeclarationMember();
RecordPosition(variable);
variable.Name = ParseDottedName();
if(tokens.CurrentType == T.TID_EQUAL)
{
Eat(T.TID_EQUAL);
variable.Initializer = ParseInitializer();
}
if(tokens.CurrentType == T.TID_COMMA)
Eat(T.TID_COMMA);
if(variable.Name.Length != 0)
{
variables.Add(variable);
}
}while (tokens.CurrentType == T.TID_IDENTIFIER);
return variables;
}
private CodeExpression ParseInitializer()
{
if(tokens.CurrentType == T.TID_OPENCURLY)
{
CodeArrayInitializerExpression arrayInitializer = new CodeArrayInitializerExpression();
RecordPosition(arrayInitializer);
//Array initializer!
Eat(T.TID_OPENCURLY);
while(tokens.CurrentType != T.TID_CLOSECURLY)
{
if(tokens.CurrentType == T.TID_EOF)
{
return arrayInitializer;
}
arrayInitializer.Initializers.Add(ParseInitializer());
if(tokens.CurrentType != T.TID_COMMA)
break;
Eat(T.TID_COMMA);
}
Eat(T.TID_CLOSECURLY);
return arrayInitializer;
}
else
{
//Regular expression initializer
return ParseExpression();
}
}
private void ParseEnumDeclaration(CodeNamespace declaringNamespace, CodeTypeDeclaration parent)
{
CodeEnumDeclaration enumDeclaration = new CodeEnumDeclaration();
enumDeclaration.Attributes.AddRange(ParseAttributes());
RecordPosition(enumDeclaration);
enumDeclaration.Modifiers = ParseTypeDeclarationModifiers();
Eat(T.TID_ENUM);
enumDeclaration.Name = ParseDottedName();
enumDeclaration.EnumBase = new CodeTypeReference(typeof(int));
if(tokens.CurrentType == T.TID_COLON)
{
Eat(T.TID_COLON);
enumDeclaration.EnumBase = new CodeTypeReference();
RecordPosition(enumDeclaration.EnumBase);
enumDeclaration.EnumBase.TypeName = ParseTypeName();
}
Eat(T.TID_OPENCURLY);
while(tokens.CurrentType != T.TID_CLOSECURLY && tokens.CurrentType != T.TID_EOF)
{
CodeEnumMember enumMember = new CodeEnumMember();
RecordPosition(enumMember);
enumMember.Attributes.AddRange(ParseAttributes());
enumMember.Name = ParseDottedName();
if(tokens.CurrentType == T.TID_EQUAL)
{
Eat(T.TID_EQUAL);
enumMember.Value = ParseExpression();
}
enumDeclaration.Members.Add(enumMember);
if(tokens.CurrentType == T.TID_COMMA)
{
Eat(T.TID_COMMA);
}
}
Eat(T.TID_CLOSECURLY);
//Optional semilon
if(tokens.CurrentType == T.TID_SEMICOLON)
Eat(T.TID_SEMICOLON);
//Enum can be declared inside class or a namespace.
if(parent == null)
{
declaringNamespace.DeclaredTypes.Add(enumDeclaration);
}
else
{
if(parent is CodeClassDeclaration)
((CodeClassDeclaration) parent).NestedTypes.Add(enumDeclaration);
if(parent is CodeStructDeclaration)
((CodeStructDeclaration) parent).NestedTypes.Add(enumDeclaration);
}
}
/// <summary>
/// This function parses class declaration including all its members.
/// </summary>
/// <param name="declaringNamespace"></param>
/// <param name="parent"></param>
private void ParseClassStructInterfaceDeclaration(CodeNamespace declaringNamespace, CodeTypeDeclaration parent)
{
ReportProgress();
CodeAttributeCollection attributes = new CodeAttributeCollection(ParseAttributes());
CodeElementPosition sourcePosition = new CodeElementPosition(CompileUnit.SourceFileName,tokens.Current.Line,tokens.Current.Column,tokens.Current.Position);
CodeTypeDeclaration.TypeDeclarationModifiers modifiers = ParseTypeDeclarationModifiers();
CodeTypeDeclaration codeType = null;
switch(tokens.CurrentType)
{
case T.TID_CLASS:
{
codeType = new CodeClassDeclaration();
Eat(T.TID_CLASS);
break;
}
case T.TID_INTERFACE:
{
codeType = new CodeInterfaceDeclaration();
Eat(T.TID_INTERFACE);
break;
}
case T.TID_STRUCT:
{
codeType = new CodeStructDeclaration();
Eat(T.TID_STRUCT);
break;
}
default:
{
ReportError("CS0116",tokens.Current,tokens.Peek(1));
//try to recover.
RecoverBySkippingCurlies(true);
return;
}
}
if(codeType != null)
{
codeType.Attributes.AddRange(attributes);
codeType.SourcePosition = sourcePosition;
codeType.Modifiers = modifiers;
if(parent is CodeClassDeclaration)
((CodeClassDeclaration) parent).NestedTypes.Add(codeType);
if(parent is CodeStructDeclaration)
((CodeStructDeclaration) parent).NestedTypes.Add(codeType);
//Do not add nested types to the list of namespace declared types
if(parent == null)
declaringNamespace.DeclaredTypes.Add(codeType);
codeType.Name = ParseDottedName();
}
if(tokens.CurrentType == T.TID_COLON)
{
CodeTypeReferenceCollection baseTypes = new CodeTypeReferenceCollection();
//Parse base types
while(true)
{
if(IsCanceled())
return;
tokens.Next();
CodeTypeReference baseTypeReference = new CodeTypeReference();
RecordPosition(baseTypeReference);
baseTypeReference.TypeName = ParseTypeName();
baseTypes.Add(baseTypeReference);
if(tokens.CurrentType == T.TID_COMMA)
{
continue;
}
else
{
break;
}
}
if(codeType is CodeClassDeclaration)
((CodeClassDeclaration) codeType).BaseTypes.AddRange(baseTypes);
if(codeType is CodeInterfaceDeclaration)
((CodeInterfaceDeclaration) codeType).Interfaces.AddRange(baseTypes);
if(codeType is CodeStructDeclaration)
((CodeStructDeclaration) codeType).Interfaces.AddRange(baseTypes);
}
if(!Eat(T.TID_OPENCURLY))
return;
//Look eahead for class elements
int elementStartMark = tokens.Mark();
bool firstToken = true;
while(tokens.CurrentType != T.TID_EOF && tokens.CurrentType != T.TID_CLOSECURLY)
{
if(IsCanceled())
return;
//Now, what is it?
CSharpToken currentToken = tokens.Current;
// if this is first token after we just parsed an element
//then get attributes & modifiers out of the way.
if(firstToken)
{
//Parse modifiers just to get them out of th way.
ParseMemberDeclarationModifiers();
ParseAttributes();
}
switch(tokens.CurrentType)
{
case T.TID_CLASS:
case T.TID_STRUCT:
case T.TID_INTERFACE:
{
//Rewind to the beginign of the element
tokens.Rewind(elementStartMark);
ParseClassStructInterfaceDeclaration(declaringNamespace,codeType);
elementStartMark = tokens.Mark();
firstToken = true;
break;
}
case T.TID_THIS:
{
//Rewind to the beginign of the element
tokens.Rewind(elementStartMark);
ParseIndexerDeclaration(codeType);
elementStartMark = tokens.Mark();
firstToken = true;
break;
}
case T.TID_OPERATOR:
{
//Rewind to the beginign of the element
tokens.Rewind(elementStartMark);
ParseOperatorDeclaration(codeType);
elementStartMark = tokens.Mark();
firstToken = true;
break;
}
case T.TID_OPENPAREN:
{
//Rewind to the beginign of the element
tokens.Rewind(elementStartMark);
ParseMethodDeclaration(codeType);
elementStartMark = tokens.Mark();
firstToken = true;
break;
}
case T.TID_EVENT:
{
//Rewind to the beginign of the element
tokens.Rewind(elementStartMark);
ParseEventDeclaration(codeType);
elementStartMark = tokens.Mark();
firstToken = true;
break;
}
case T.TID_DELEGATE:
{
//Rewind to the beginign of the element
tokens.Rewind(elementStartMark);
ParseDelegateDeclaration(declaringNamespace,codeType);
elementStartMark = tokens.Mark();
firstToken = true;
break;
}
case T.TID_ENUM:
{
//Rewind to the beginign of the element
tokens.Rewind(elementStartMark);
ParseEnumDeclaration(declaringNamespace,codeType);
elementStartMark = tokens.Mark();
firstToken = true;
break;
}
case T.TID_EQUAL:
case T.TID_SEMICOLON:
{
//Rewind to the beginign of the element
tokens.Rewind(elementStartMark);
ParseFieldDeclaration(codeType);
elementStartMark = tokens.Mark();
firstToken = true;
break;
}
case T.TID_OPENCURLY:
{
//Rewind to the beginign of the element
tokens.Rewind(elementStartMark);
ParsePropertyDeclaration(codeType);
elementStartMark = tokens.Mark();
firstToken = true;
break;
}
default:
firstToken = false;
tokens.Next();
break;
}
}
Eat(T.TID_CLOSECURLY);
//Optional semicolon
if(tokens.CurrentType == T.TID_SEMICOLON)
Eat(T.TID_SEMICOLON);
}
/// <summary>
/// This function parses type declaration modifiers.
/// </summary>
/// <returns></returns>
private CodeTypeDeclaration.TypeDeclarationModifiers ParseTypeDeclarationModifiers()
{
CodeTypeDeclaration.TypeDeclarationModifiers modifiers = 0;
while(true)
{
if(IsCanceled())
return modifiers;
switch(tokens.CurrentType)
{
case T.TID_NEW:
modifiers |= CodeTypeDeclaration.TypeDeclarationModifiers.New;
break;
case T.TID_PUBLIC:
modifiers |= CodeTypeDeclaration.TypeDeclarationModifiers.Public;
break;
case T.TID_PROTECTED:
modifiers |= CodeTypeDeclaration.TypeDeclarationModifiers.Family;
break;
case T.TID_INTERNAL:
modifiers |= CodeTypeDeclaration.TypeDeclarationModifiers.Assembly;
break;
case T.TID_PRIVATE:
modifiers |= CodeTypeDeclaration.TypeDeclarationModifiers.Private;
break;
case T.TID_SEALED:
modifiers |= CodeTypeDeclaration.TypeDeclarationModifiers.Sealed;
break;
case T.TID_ABSTRACT:
modifiers |= CodeTypeDeclaration.TypeDeclarationModifiers.Abstract;
break;
case T.TID_UNSAFE:
modifiers |= CodeTypeDeclaration.TypeDeclarationModifiers.Unsafe;
break;
default:
{
if(((modifiers & CodeTypeDeclaration.TypeDeclarationModifiers.Family) != 0) &&
((modifiers & CodeTypeDeclaration.TypeDeclarationModifiers.Assembly) != 0))
{
modifiers &= ~CodeTypeDeclaration.TypeDeclarationModifiers.Family;
modifiers &= ~CodeTypeDeclaration.TypeDeclarationModifiers.Assembly;
modifiers |= CodeTypeDeclaration.TypeDeclarationModifiers.FamilyAndAssembly;
}
return modifiers;
}
}
tokens.Next();
}
}
/// <summary>
/// This function parses member declaration modifiers 'new public ...'
/// </summary>
/// <returns></returns>
private CodeTypeMemberDeclaration.MemberDeclarationModifiers ParseMemberDeclarationModifiers()
{
CodeTypeMemberDeclaration.MemberDeclarationModifiers modifiers = 0;
while(true)
{
if(IsCanceled())
return modifiers;
switch(tokens.CurrentType)
{
case T.TID_NEW:
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.New;
break;
case T.TID_PUBLIC:
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.Public;
break;
case T.TID_PROTECTED:
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.Protected;
break;
case T.TID_INTERNAL:
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.Assembly;
break;
case T.TID_PRIVATE:
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.Private;
break;
case T.TID_STATIC:
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.Static;
break;
case T.TID_UNSAFE:
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.Unsafe;
break;
case T.TID_VIRTUAL:
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.Virtual;
break;
case T.TID_SEALED:
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.Sealed;
break;
case T.TID_OVERRIDE:
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.Override;
break;
case T.TID_ABSTRACT:
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.Abstract;
break;
case T.TID_EXTERN:
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.Extern;
break;
case T.TID_READONLY:
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.ReadOnly;
break;
case T.TID_CONST:
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.Const;
break;
case T.TID_VOLATILE:
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.Volatile;
break;
default:
{
if(((modifiers & CodeTypeMemberDeclaration.MemberDeclarationModifiers.Protected) != 0) &&
((modifiers & CodeTypeMemberDeclaration.MemberDeclarationModifiers.Assembly) != 0))
{
modifiers &= ~CodeTypeMemberDeclaration.MemberDeclarationModifiers.Protected;
modifiers &= ~CodeTypeMemberDeclaration.MemberDeclarationModifiers.Assembly;
modifiers |= CodeTypeMemberDeclaration.MemberDeclarationModifiers.ProtectedAndAssembly;
}
return modifiers;
}
}
tokens.Next();
}
}
/// <summary>
/// This function parses a set of attribute declarations in form
/// [Attribute, Attribute, attribute(a,b)]
/// [Attribute]
/// </summary>
/// <returns></returns>
private CodeAttributeCollection ParseGlobalAttributes()
{
return ParseAttributes(true);
}
private CodeAttributeCollection ParseAttributes()
{
return ParseAttributes(false);
}
/// <summary>
/// This function parses a set of attribute declarations in form
/// [Attribute, Attribute, attribute(a,b)]
/// [Attribute]
/// </summary>
/// <returns></returns>
private CodeAttributeCollection ParseAttributes(bool parseGlobalAttributesOnly)
{
CodeAttributeCollection attributesCollection = new CodeAttributeCollection();
//Check if we have an attributes section.
CodeAttribute attribute;
while(true)
{
int attributeStartPosition = tokens.Mark();
if(IsCanceled())
return attributesCollection;
if(tokens.CurrentType != T.TID_OPENSQUARE)
return attributesCollection;
attribute = new CodeAttribute();
RecordPosition(attribute);
Eat(T.TID_OPENSQUARE);
//Check for attribute scope
string attributesScope = string.Empty;
//Check if declaration has a scope to it. [<scopename>:
if(tokens.Peek(1).TokenType == T.TID_COLON)
{
attributesScope = tokens.Current.Name;
tokens.Next();
Eat(T.TID_COLON);
}
if(parseGlobalAttributesOnly && attributesScope.Length == 0)
{
tokens.Rewind(attributeStartPosition);
return attributesCollection;
}
while(true)
{
if(IsCanceled())
return attributesCollection;
attribute.Scope = attributesScope;
attribute.AttributeType = new CodeTypeReference();
RecordPosition(attribute.AttributeType);
attribute.AttributeType.TypeName = ParseDottedName();
//Check if attribute has an argument list
if(tokens.CurrentType == T.TID_OPENPAREN) //it does
{
Eat(T.TID_OPENPAREN);
attribute.Arguments.AddRange(ParseArgumentsList());
if(!Eat(T.TID_CLOSEPAREN))
{
tokens.SkipToUntilOneOf(T.TID_CLOSESQUARE);
break;
}
}
attributesCollection.Add(attribute);
if(tokens.CurrentType != T.TID_COMMA)
break;
Eat(T.TID_COMMA);
attribute = new CodeAttribute();
RecordPosition(attribute);
//This is protection against [a,b,] kind of declaration
if(tokens.CurrentType == T.TID_CLOSESQUARE)
break;
}
Eat(T.TID_CLOSESQUARE);
}
}
/// <summary>
/// This function parses an argument list in the following forms
/// exp,exp,exp
/// name = exp, exp,name = exp
/// </summary>
/// <returns></returns>
private CodeArgumentCollection ParseArgumentsList()
{
CodeArgumentCollection arguments = new CodeArgumentCollection();
//Check if we have anything to process
if(tokens.CurrentType == T.TID_CLOSEPAREN)
return arguments;
int argumentPosition = 0;
while(true)
{
if(IsCanceled())
break;
if(tokens.CurrentType == T.TID_EOF)
break;
CodeArgument argument = new CodeArgument();
RecordPosition(argument);
arguments.Add(argument);
if(tokens.CurrentType == T.TID_IDENTIFIER && tokens.Peek(1).TokenType == T.TID_EQUAL)
{
argument.Name = tokens.Current.Name;
tokens.Next();
Eat(T.TID_EQUAL);
}
if(tokens.CurrentType == T.TID_REF)
{
argument.Modifier = CodeArgument.CodeArgumentModifier.Ref;
Eat(T.TID_REF);
}
if(tokens.CurrentType == T.TID_OUT)
{
argument.Modifier = CodeArgument.CodeArgumentModifier.Out;
Eat(T.TID_OUT);
}
argument.Value = ParseExpression();
if(tokens.CurrentType != T.TID_COMMA)
break;
argumentPosition++;
Eat(T.TID_COMMA);
}
return arguments;
}
/// <summary>
/// This function parses an parameters list in the following forms
/// ref int a,string b,type t
/// </summary>
/// <returns></returns>
private CodeMethodParameterCollection ParseParametersList()
{
CodeMethodParameterCollection parameters = new CodeMethodParameterCollection();
//Check if there are any parameters at all
if(tokens.CurrentType == T.TID_CLOSEPAREN || tokens.CurrentType == T.TID_CLOSESQUARE)
return parameters;
while(true)
{
if(IsCanceled())
break;
if(tokens.CurrentType == T.TID_EOF)
break;
CodeMethodParameter parameter = new CodeMethodParameter();
RecordPosition(parameter);
parameters.Add(parameter);
parameter.Attributes.AddRange(ParseAttributes());
if(tokens.CurrentType == T.TID_REF)
{
parameter.Modifier = CodeMethodParameter.CodeMethodParameterModifier.Ref;
Eat(T.TID_REF);
}
if(tokens.CurrentType == T.TID_OUT)
{
parameter.Modifier = CodeMethodParameter.CodeMethodParameterModifier.Out;
Eat(T.TID_OUT);
}
if(tokens.CurrentType == T.TID_PARAMS)
{
parameter.IsParameterArray = true;
Eat(T.TID_PARAMS);
}
parameter.Type = new CodeTypeReference();
RecordPosition(parameter.Type);
parameter.Type.TypeName = ParseTypeName();
//Name is optional
if(tokens.CurrentType == T.TID_IDENTIFIER)
parameter.Name = ParseDottedName();
//if name is not empty add to method scope manager
if(parameter.Name.Length != 0)
{
methodScopeNameManager.Register(parameter.Name,parameter);
}
if(tokens.CurrentType != T.TID_COMMA)
break;
Eat(T.TID_COMMA);
}
return parameters;
}
/// <summary>
/// Parse a++ or b-- form of expressions.
/// </summary>
/// <param name="leftOperand"></param>
/// <returns></returns>
private CodeExpression ParsePostFixExpression(CodeExpression leftOperand)
{
if(tokens.CurrentType == T.TID_PLUSPLUS)
{
Eat(T.TID_PLUSPLUS);
CodeUnaryExpression unaryExpression = new CodeUnaryExpression();
RecordPosition(unaryExpression);
unaryExpression.Operator = CodeUnaryOperatorType.PostfixIncrement;
unaryExpression.Operand = leftOperand;
return unaryExpression;
}
if(tokens.CurrentType == T.TID_MINUSMINUS)
{
Eat(T.TID_MINUSMINUS);
CodeUnaryExpression unaryExpression = new CodeUnaryExpression();
RecordPosition(unaryExpression);
unaryExpression.Operator = CodeUnaryOperatorType.PostfixDecrement;
unaryExpression.Operand = leftOperand;
return unaryExpression;
}
return leftOperand;
}
private CodeExpression ParseNewExpression()
{
CodeElementPosition createObjectPosition = GetCurrentPosition();
Eat(T.TID_NEW);
string typeName;
int mark = tokens.Mark();
typeName = ParseTypeName();
tokens.Rewind(mark);
if(typeName.IndexOf("[") >=0)
{
CodeArrayCreateExpression arrayCreateExpression = new CodeArrayCreateExpression();
arrayCreateExpression.SourcePosition = createObjectPosition;
int declarationStarts = tokens.Mark();
arrayCreateExpression.CreateType = new CodeTypeReference();
RecordPosition(arrayCreateExpression.CreateType);
arrayCreateExpression.CreateType.TypeName = ParseTypeName();
int initializersStarts = tokens.Mark();
tokens.Rewind(declarationStarts);
//Skip type name but leave brackets intact
if(tokens.Current.IsPredefinedType())
tokens.Next();
else
ParseDottedName();
Eat(T.TID_OPENSQUARE);
while(tokens.CurrentType != T.TID_CLOSESQUARE)
{
if(tokens.CurrentType != T.TID_COMMA)
arrayCreateExpression.SizeInitializers.Add(ParseExpression());
if(tokens.CurrentType == T.TID_CLOSESQUARE)
break;
if(tokens.CurrentType == T.TID_COMMA)
Eat(T.TID_COMMA);
}
Eat(T.TID_CLOSESQUARE);
//Skip all nested arrays
tokens.Rewind(declarationStarts);
ParseTypeName();
//Is there initializers?
if(tokens.CurrentType == T.TID_OPENCURLY)
{
CodeExpression initializer = ParseInitializer();
Debug.Assert(initializer is CodeArrayInitializerExpression,"Must be array initializer!");
if(initializer is CodeArrayInitializerExpression)
arrayCreateExpression.Initializer = (CodeArrayInitializerExpression) initializer;
}
return arrayCreateExpression;
}
else
{
CodeObjectCreateExpression objectCreateExpression = new CodeObjectCreateExpression();
objectCreateExpression.SourcePosition = createObjectPosition;
CodeTypeReference createdType = new CodeTypeReference();
RecordPosition(createdType);
objectCreateExpression.CreateType = createdType;
createdType.TypeName = ParseTypeName();
// a = new A();
Eat(T.TID_OPENPAREN);
objectCreateExpression.Arguments.AddRange(ParseArgumentsList());
Eat(T.TID_CLOSEPAREN);
return objectCreateExpression;
}
}
private CodeExpression ParseStackAllocExpression()
{
return ParseExpressionSnippet();
}
private CodeMethodInvokeExpression ParseMethodCall(CodeExpression targetObject)
{
CodeMethodInvokeExpression methodInvokeExpression = new CodeMethodInvokeExpression();
RecordPosition(methodInvokeExpression);
CodeNamedReferenceExpression methodReference = new CodeUnresolvedReferenceExpression();
string referencedMethodName = string.Empty;
//Somecalls may not have identifier to specify method name
//for example casted delegate will not have method name
//or there is a cast parenthesized expression
//((MyDelegate)myDelegatesArray[2]) - this is an input target expression
if(tokens.CurrentType == T.TID_IDENTIFIER)
{
RecordPosition(methodReference);
referencedMethodName = tokens.Current.Name;
Eat(T.TID_IDENTIFIER);
}
Eat(T.TID_OPENPAREN);
//If name is not empty than it is method invokation by name
//otherwise it is invokation through a delegate
if(referencedMethodName.Length != 0)
{
//By default make metho reference as unresolved expression and resolve it later during linking
methodReference.TargetObject = targetObject;
methodReference.Name = referencedMethodName;
methodInvokeExpression.MethodReferenceExpression = methodReference;
//But ther is a special case:
//If it is method invokation by name and there is no
//target object - let's check paramers and variables.
//if parameter or variable with such name exists - it is a delegate call!
//Method names always are resolved through local parameters and variables first!
if(targetObject == null)
{
CodeElement referencedElement = methodScopeNameManager.Lookup(referencedMethodName);
if(referencedElement != null)
{
if(referencedElement is CodeMethodParameter)
{
CodeMethodParameterReferenceExpression paramReference = new CodeMethodParameterReferenceExpression();
paramReference.SourcePosition = methodReference.SourcePosition;
paramReference.MethodParameter = (CodeMethodParameter) referencedElement;
methodInvokeExpression.MethodReferenceExpression = paramReference;
}
else
{
if(referencedElement is CodeVariableDeclarationStatement)
{
CodeVariableReferenceExpression variableReference = new CodeVariableReferenceExpression();
variableReference.SourcePosition = methodReference.SourcePosition;
variableReference.VariableDeclaration = (CodeVariableDeclarationStatement) referencedElement;
variableReference.VariableName = referencedMethodName;
methodInvokeExpression.MethodReferenceExpression = variableReference;
}
else
{
Debug.Assert(false,"Should not be here!");
}
}
}
}
}
else
{
methodInvokeExpression.MethodReferenceExpression = targetObject;
}
methodInvokeExpression.Arguments.AddRange(ParseArgumentsList());
Eat(T.TID_CLOSEPAREN);
return methodInvokeExpression;
}
private CodeIndexerInvokeExpression ParseIndexerExpression(CodeExpression targetObject)
{
Debug.Assert(tokens.CurrentType == T.TID_OPENSQUARE);
CodeIndexerInvokeExpression indexerExpression = new CodeIndexerInvokeExpression();
RecordPosition(indexerExpression);
indexerExpression.IndexerReferenceExpression = targetObject;
Eat(T.TID_OPENSQUARE);
indexerExpression.Arguments.AddRange(ParseArgumentsList());
Eat(T.TID_CLOSESQUARE);
return indexerExpression;
}
private CodeParenthesizedExpression ParseParenthesizedExpression()
{
CodeParenthesizedExpression parenthesizedExpression = new CodeParenthesizedExpression();
RecordPosition(parenthesizedExpression);
Eat(T.TID_OPENPAREN);
parenthesizedExpression.Expression = ParseSubExpression(0);
Eat(T.TID_CLOSEPAREN);
return parenthesizedExpression;
}
private CodeExpression ParseSubExpression(int precedence)
{
return ParseSubExpression(precedence,null);
}
private CodeExpression ParseSubExpression(int precedence,CodeExpression leftOperand)
{
if(leftOperand == null)
{
//First check unary operation.
CodeUnaryOperatorType unaryOperator = tokens.Current.GetUnaryOperation();
if(unaryOperator != CodeUnaryOperatorType.None)
{
//It can be an unary expression but it can also be just a number
//-12; +3; check it and parse accordinly
if(
(unaryOperator == CodeUnaryOperatorType.UnaryNegation || unaryOperator == CodeUnaryOperatorType.UnaryPlus) &&
tokens.Peek(1).TokenType == T.TID_NUMBER)
{
Eat(tokens.CurrentType);
if(unaryOperator == CodeUnaryOperatorType.UnaryNegation)
tokens.Current.Name = tokens.Current.Name.Insert(0,"-");
else
tokens.Current.Name = tokens.Current.Name.Insert(0,"+");
tokens.Current.Position--;
tokens.Current.Column--;
if(tokens.Current.Column < 0)
{
tokens.Current.Column = 0;
tokens.Current.Line--;
}
return ParseSubExpression(precedence,leftOperand);
}
//Notice that we do not return from the switch
//we rather break. Reason is that unary operator is not an end
//of the expression. Consider this:
//int i = sizeof(string) + 12;
// ^^^^^^^^^^^^^^
//We need postfix processing ffor
//int i = sizeof(string)++;
// ^^
CodeUnaryExpression unaryExpression = new CodeUnaryExpression();
RecordPosition(unaryExpression);
Eat(tokens.CurrentType);
unaryExpression.Operator = unaryOperator;
switch(unaryOperator)
{
case CodeUnaryOperatorType.SizeOf:
case CodeUnaryOperatorType.TypeOf:
{
Eat(T.TID_OPENPAREN);
CodeTypeReferenceExpression typeReferenceExpression = new CodeTypeReferenceExpression();
RecordPosition(typeReferenceExpression);
CodeTypeReference referencedType = new CodeTypeReference();
RecordPosition(referencedType);
referencedType.TypeName = ParseTypeName();
typeReferenceExpression.ReferencedType = referencedType;
Eat (T.TID_CLOSEPAREN);
unaryExpression.Operand = typeReferenceExpression;
leftOperand = ParsePostFixExpression(unaryExpression);
break;
}
case CodeUnaryOperatorType.Checked:
case CodeUnaryOperatorType.Unchecked:
{
Eat(T.TID_OPENPAREN);
unaryExpression.Operand = ParseSubExpression (0);
Eat (T.TID_CLOSEPAREN);
leftOperand = ParsePostFixExpression(unaryExpression);
break;
}
case CodeUnaryOperatorType.True:
case CodeUnaryOperatorType.False:
{
//This two oprators do not have operand
unaryExpression.Operand = null;
leftOperand = unaryExpression;
break;
}
default:
{
unaryExpression.Operand = ParseSubExpression (0);
leftOperand = unaryExpression;
break;
}
}
}
//If we still did not determine left operand it means that
//there is no unary operation and as such we are dealing with terminal symbol
if(leftOperand == null)
{
switch(tokens.CurrentType)
{
case T.TID_NEW:
{
leftOperand = ParseNewExpression();
break;
}
case T.TID_STACKALLOC:
{
Eat(T.TID_STACKALLOC);
leftOperand = ParseStackAllocExpression();
break;
}
case T.TID_STRING_LIT:
{
CodePrimitiveExpression primitiveExpression = new CodePrimitiveExpression();
RecordPosition(primitiveExpression);
primitiveExpression.Value = tokens.Current.Name;
Eat(tokens.CurrentType);
leftOperand = ParsePostFixExpression(primitiveExpression);
break;
}
case T.TID_VERBATIM_STRING_LIT:
{
CodePrimitiveExpression primitiveExpression = new CodePrimitiveExpression();
RecordPosition(primitiveExpression);
primitiveExpression.Value = tokens.Current.Name;
primitiveExpression.Format = CodePrimitiveExpressionFormat.VerbatimString;
Eat(tokens.CurrentType);
leftOperand = ParsePostFixExpression(primitiveExpression);
break;
}
case T.TID_CHAR_LIT:
{
CodePrimitiveExpression primitiveExpression = new CodePrimitiveExpression();
RecordPosition(primitiveExpression);
if(tokens.Current.Name.ToUpper().StartsWith(@"\X"))
{
string hexChar = tokens.Current.Name.Substring(2);
primitiveExpression.Value = (char) int.Parse(hexChar,System.Globalization.NumberStyles.HexNumber);
primitiveExpression.Format = CodePrimitiveExpressionFormat.HexidecimalEscapeSequence;
}
else
if(tokens.Current.Name.ToUpper().StartsWith(@"\U"))
{
string hexChar = tokens.Current.Name.Substring(2);
primitiveExpression.Value = (char) int.Parse(hexChar,System.Globalization.NumberStyles.HexNumber);
primitiveExpression.Format = CodePrimitiveExpressionFormat.UnicodeEscapeSequence;
}
else
{
switch(tokens.Current.Name)
{
case @"\'":
{
primitiveExpression.Value = '\'';
break;
}
case @"\""":
{
primitiveExpression.Value = '\"';
break;
}
case @"\\":
{
primitiveExpression.Value = '\\';
break;
}
case @"\0":
{
primitiveExpression.Value = '\0';
break;
}
case @"\a":
{
primitiveExpression.Value = '\a';
break;
}
case @"\b":
{
primitiveExpression.Value = '\b';
break;
}
case @"\f":
{
primitiveExpression.Value = '\f';
break;
}
case @"\n":
{
primitiveExpression.Value = '\n';
break;
}
case @"\r":
{
primitiveExpression.Value = '\r';
break;
}
case @"\t":
{
primitiveExpression.Value = '\t';
break;
}
case @"\v":
{
primitiveExpression.Value = '\v';
break;
}
default:
{
try
{
primitiveExpression.Value = Char.Parse(tokens.Current.Name);
}
catch
{
//Add parser error here.
ReportUnexpectedTokenError(T.TID_CHAR,tokens.CurrentType);
}
break;
}
}
}
Eat(tokens.CurrentType);
leftOperand = ParsePostFixExpression(primitiveExpression);
break;
}
case T.TID_NUMBER:
{
string number = tokens.Current.Name.ToUpper();
CodePrimitiveExpression primitiveExpression = new CodePrimitiveExpression();
RecordPosition(primitiveExpression);
primitiveExpression.Value = ParseNumber(tokens.Current.Name);
if(tokens.Current.Name.ToUpper().IndexOf("0X") != -1)
primitiveExpression.Format = CodePrimitiveExpressionFormat.HexidecimalNumber;
Eat(tokens.CurrentType);
leftOperand = ParsePostFixExpression(primitiveExpression);
break;
}
case T.TID_THIS:
{
CodeThisReferenceExpression thisReferenceExpression = new CodeThisReferenceExpression();
RecordPosition(thisReferenceExpression);
Eat(tokens.CurrentType);
leftOperand = ParsePostFixExpression(thisReferenceExpression);
break;
}
case T.TID_NULL:
{
CodeNullReferenceExpression nullReferenceExpression = new CodeNullReferenceExpression();
RecordPosition(nullReferenceExpression);
Eat(tokens.CurrentType);
leftOperand = ParsePostFixExpression(nullReferenceExpression);
break;
}
case T.TID_BASE:
{
CodeBaseReferenceExpression baseReferenceExpression = new CodeBaseReferenceExpression();
RecordPosition(baseReferenceExpression);
Eat(tokens.CurrentType);
leftOperand = ParsePostFixExpression(baseReferenceExpression);
break;
}
case T.TID_OPENPAREN:
{
//We need to parse either cast or parenthesized expression
//look ahead to figure it out
//what can it be:
//(A)(a) -> cast or a method call if (A) is a delegate
//(A)(a,b,c) -> always method call
//(A*)&(a) ->cast
//(A*)(&a) -> cast
//(a) & (a) -> expresssion (bitwise and)
//(string)"123" -> cast
//(char)'char' -> cast
//(A) this -> cast
//(A) null -> cast
int mark = tokens.Mark();
Eat(T.TID_OPENPAREN);
CodeTypeReference castedType = new CodeTypeReference();
RecordPosition(castedType);
castedType.TypeName = ParseTypeName();
//
if( tokens.CurrentType == T.TID_CLOSEPAREN &&
tokens.Peek(1).TokenType != T.TID_CLOSEPAREN &&
tokens.Peek(1).TokenType != T.TID_OPENSQUARE &&
tokens.Peek(1).TokenType != T.TID_CLOSESQUARE &&
tokens.Peek(1).TokenType != T.TID_CLOSECURLY &&
tokens.Peek(1).TokenType != T.TID_COMMA &&
tokens.Peek(1).TokenType != T.TID_COLON &&
(
(tokens.Peek(1).TokenType == T.TID_AMPERSAND && castedType.IsPointer) ||
(tokens.Peek(1).GetBinaryOperation() == CodeBinaryOperatorType.None)
)
)
{
tokens.Rewind(mark);
CodeTypeCastExpression typeCastExp = new CodeTypeCastExpression();
RecordPosition(typeCastExp);
Eat(T.TID_OPENPAREN);
//This is a type cast
typeCastExp.TargetType = new CodeTypeReference();
RecordPosition(typeCastExp.TargetType);
typeCastExp.TargetType.TypeName = ParseTypeName();
Eat(T.TID_CLOSEPAREN);
typeCastExp.Expression = ParseSubExpression(0);
if(typeCastExp.Expression == null)
{
//Oops it was parenthesized expression! Bummer
tokens.Rewind(mark);
leftOperand = ParsePostFixExpression(ParseParenthesizedExpression());
break;
}
leftOperand = ParsePostFixExpression(typeCastExp);
break;
}
else
{
//It is just a parenthesized expression
tokens.Rewind(mark);
leftOperand = ParsePostFixExpression(ParseParenthesizedExpression());
break;
}
}
case T.TID_IDENTIFIER:
{
if(tokens.Peek(1).TokenType == T.TID_OPENPAREN)
{
leftOperand = ParsePostFixExpression(ParseMethodCall(leftOperand));
break;
}
//Identifier that starts the call. Variable?,Parameter?, Type?, Unresolved?
CodeElement referencedElement = methodScopeNameManager.Lookup(tokens.Current.Name);
if(referencedElement != null)
{
if(referencedElement is CodeMethodParameter)
{
CodeMethodParameterReferenceExpression paramReference = new CodeMethodParameterReferenceExpression();
RecordPosition(paramReference);
Eat(T.TID_IDENTIFIER);
paramReference.MethodParameter = (CodeMethodParameter) referencedElement;
leftOperand = ParsePostFixExpression(paramReference);
break;
}
else
{
if(referencedElement is CodeVariableDeclarationStatement)
{
CodeVariableReferenceExpression varReference = new CodeVariableReferenceExpression();
RecordPosition(varReference);
varReference.VariableName = tokens.Current.Name;
Eat(T.TID_IDENTIFIER);
varReference.VariableDeclaration = (CodeVariableDeclarationStatement) referencedElement;
leftOperand = ParsePostFixExpression(varReference);
break;
}
else
{
Debug.Assert(false,"Should not be here!");
}
}
}
else
{
//Not a variable & not a parameter.
//It is either type or unresolved reference.
if(tokens.Current.IsPredefinedType())
{
CodeTypeReferenceExpression typeReference = new CodeTypeReferenceExpression();
RecordPosition(typeReference);
CodeTypeReference referencedType = new CodeTypeReference(tokens.Current.GetPredefinedTypeName());
RecordPosition(referencedType);
typeReference.ReferencedType = referencedType;
Eat(T.TID_IDENTIFIER);
leftOperand = ParsePostFixExpression(typeReference);
break;
}
else
{
CodeUnresolvedReferenceExpression memberReference = new CodeUnresolvedReferenceExpression();
RecordPosition(memberReference);
memberReference.Name = tokens.Current.Name;
Eat(T.TID_IDENTIFIER);
leftOperand = ParsePostFixExpression(memberReference);
break;
}
}
Debug.Assert(false,"Why did we end up here? what other cases are possible???");
break;
}
default:
{
if(tokens.Current.IsPredefinedType())
{
CodeTypeReferenceExpression typeReferenceExpression = new CodeTypeReferenceExpression();
RecordPosition(typeReferenceExpression);
CodeTypeReference referencedType = new CodeTypeReference();
RecordPosition(referencedType);
referencedType.TypeName = tokens.Current.GetPredefinedTypeName();
typeReferenceExpression.ReferencedType = referencedType;
Eat(tokens.CurrentType);
leftOperand = ParsePostFixExpression(typeReferenceExpression);
break;
}
if(tokens.CurrentType == T.TID_SEMICOLON)
return null;
Debug.Assert(false,"Why did we end up here? what other cases are possible???");
ReportError("CS1525",tokens.Current,null,tokens.Current.Name);
break;
}
}
}
}
CodeBinaryExpression binaryExpresion = null;
while(tokens.CurrentType == T.TID_OPENPAREN || tokens.CurrentType == T.TID_OPENSQUARE || tokens.Current.GetBinaryOperation() != CodeBinaryOperatorType.None)
{
if(tokens.CurrentType == T.TID_OPENSQUARE)
{
leftOperand = ParsePostFixExpression(ParseIndexerExpression(leftOperand));
continue;
}
if(tokens.CurrentType == T.TID_OPENPAREN) //- it is a call of a delegate
{
leftOperand = ParsePostFixExpression(ParseMethodCall(leftOperand));
continue;
}
CodeBinaryOperatorType operationType = tokens.Current.GetBinaryOperation();
switch(operationType)
{
case CodeBinaryOperatorType.MemberSelection:
{
Eat(tokens.CurrentType); //Could be dot or arrow
if(tokens.Peek(1).TokenType == T.TID_OPENPAREN)
{
leftOperand = ParsePostFixExpression(ParseMethodCall(leftOperand));
break;
}
else
{
CodeNamedReferenceExpression unresolverReference = new CodeUnresolvedReferenceExpression();
RecordPosition(unresolverReference);
unresolverReference.Name = tokens.Current.Name;
unresolverReference.TargetObject = leftOperand;
Eat(T.TID_IDENTIFIER);
leftOperand = ParsePostFixExpression(unresolverReference);
break;
}
}
case CodeBinaryOperatorType.Assign:
case CodeBinaryOperatorType.AdditionAssignment:
case CodeBinaryOperatorType.SubtractionAssignment:
case CodeBinaryOperatorType.MultiplicationAssignment:
case CodeBinaryOperatorType.DivisionAssignment:
case CodeBinaryOperatorType.ModulusAssignment:
case CodeBinaryOperatorType.BitwiseAndAssignment:
case CodeBinaryOperatorType.ExclusiveOrAssignment:
case CodeBinaryOperatorType.BitwiseOrAssignment:
case CodeBinaryOperatorType.LeftShiftAssignment:
case CodeBinaryOperatorType.RightShiftAssignment:
{
Eat(tokens.CurrentType);
binaryExpresion = new CodeBinaryExpression();
binaryExpresion.Operator = operationType;
binaryExpresion.LeftOperand = leftOperand;
binaryExpresion.RightOperand = ParseSubExpression(0);
RecordPosition(binaryExpresion);
break;
}
case CodeBinaryOperatorType.Question:
{
if(precedence != 0)
return leftOperand;
CodeConditionalExpression conditionalExpression = new CodeConditionalExpression();
RecordPosition(conditionalExpression);
conditionalExpression.Condition = leftOperand;
Eat(T.TID_QUESTION);
conditionalExpression.TrueExpression = ParseSubExpression(0);
Eat(T.TID_COLON);
conditionalExpression.FalseExpression = ParseSubExpression(0);
leftOperand = conditionalExpression;
break;
}
case CodeBinaryOperatorType.Is:
{
Eat(T.TID_IS);
binaryExpresion = new CodeBinaryExpression();
RecordPosition(binaryExpresion);
binaryExpresion.Operator = operationType;
binaryExpresion.LeftOperand = leftOperand;
binaryExpresion.RightOperand = new CodeTypeReferenceExpression();
RecordPosition(binaryExpresion.RightOperand);
(binaryExpresion.RightOperand as CodeTypeReferenceExpression).ReferencedType = new CodeTypeReference();
RecordPosition((binaryExpresion.RightOperand as CodeTypeReferenceExpression).ReferencedType);
(binaryExpresion.RightOperand as CodeTypeReferenceExpression).ReferencedType.TypeName = ParseTypeName();
leftOperand = binaryExpresion;
binaryExpresion = null; // expression can have continuation: if(a is Type && a==2)
break;
}
case CodeBinaryOperatorType.As:
{
Eat(T.TID_AS);
binaryExpresion = new CodeBinaryExpression();
RecordPosition(binaryExpresion);
binaryExpresion.Operator = operationType;
binaryExpresion.LeftOperand = leftOperand;
binaryExpresion.RightOperand = new CodeTypeReferenceExpression();
RecordPosition(binaryExpresion.RightOperand);
(binaryExpresion.RightOperand as CodeTypeReferenceExpression).ReferencedType = new CodeTypeReference();
RecordPosition((binaryExpresion.RightOperand as CodeTypeReferenceExpression).ReferencedType);
(binaryExpresion.RightOperand as CodeTypeReferenceExpression).ReferencedType.TypeName = ParseTypeName();
leftOperand = binaryExpresion;
binaryExpresion = null;
break;
}
default:
{
binaryExpresion = new CodeBinaryExpression();
RecordPosition(binaryExpresion);
binaryExpresion.Operator = operationType;
binaryExpresion.LeftOperand = leftOperand;
int newPrecedence = CSharpToken.GetBinaryOperatorPrecedence(operationType);
if(newPrecedence >= precedence)
{
Eat(tokens.CurrentType);
binaryExpresion.RightOperand = ParseSubExpression(newPrecedence);
leftOperand = binaryExpresion;
binaryExpresion = null;
break;
}
else
{
return leftOperand;
}
}
}
}
if(binaryExpresion == null)
return leftOperand;
else
return binaryExpresion;
}
/// <summary>
/// This function parses an expression
/// </summary>
/// <returns></returns>
private CodeExpression ParseExpression()
{
int expressionStarts = tokens.Mark();
CodeExpression expression = ParseSubExpression(0);
if(expression == null && tokens.CurrentType != T.TID_SEMICOLON)
{
tokens.Rewind(expressionStarts);
return ParseExpressionSnippet();
}
return expression;
}
private CodeExpression ParseExpressionSnippet()
{
//Expression ends either on ')' or ';' or ',' in between everething else is expression
CodeExpressionSnippet expression = new CodeExpressionSnippet();
int parenthethiesCount = 0;
int curliesCount = 0;
int squerliesCount = 0;
int startSourcePosition = tokens.Current.Position;
int isQuestionOpCount = 0;
while(true)
{
if(IsCanceled())
return expression;
if(tokens.CurrentType == T.TID_EOF ||
(parenthethiesCount == 0 && curliesCount == 0 && squerliesCount == 0 &&
(tokens.CurrentType == T.TID_SEMICOLON ||
tokens.CurrentType == T.TID_COMMA ||
tokens.CurrentType == T.TID_CLOSECURLY)))
break;
if(tokens.CurrentType == T.TID_CLOSEPAREN && parenthethiesCount == 0)
break;
if(tokens.CurrentType == T.TID_OPENPAREN)
{
parenthethiesCount++;
}
if(tokens.CurrentType == T.TID_CLOSEPAREN)
parenthethiesCount--;
if(tokens.CurrentType == T.TID_OPENCURLY)
curliesCount++;
if(tokens.CurrentType == T.TID_CLOSECURLY)
curliesCount--;
if(tokens.CurrentType == T.TID_OPENSQUARE)
squerliesCount++;
if(tokens.CurrentType == T.TID_CLOSESQUARE)
squerliesCount--;
if(tokens.CurrentType == T.TID_QUESTION)
isQuestionOpCount++;
if(tokens.CurrentType == T.TID_COLON)
if(isQuestionOpCount != 0)
isQuestionOpCount--;
else
break;
tokens.Next();
}
if(tokens.Current.Position - startSourcePosition > 0)
expression.Text = sourceCode.Substring(startSourcePosition,tokens.Current.Position - startSourcePosition);
return expression;
}
public CodeStatement ParseStatement()
{
//Start parsing statements
switch(tokens.CurrentType)
{
case T.TID_SEMICOLON:
{
CodeStatement statement = new CodeEmptyStatement();
RecordPosition(statement);
Eat(T.TID_SEMICOLON);
return statement;
}
case T.TID_BREAK:
{
CodeStatement statement = new CodeBreakStatement();
RecordPosition(statement);
Eat(T.TID_BREAK);
if(tokens.CurrentType == T.TID_SEMICOLON)
Eat(T.TID_SEMICOLON);
return statement;
}
case T.TID_CONTINUE:
{
CodeStatement statement = new CodeContinueStatement();
RecordPosition(statement);
Eat(T.TID_CONTINUE);
if(tokens.CurrentType == T.TID_SEMICOLON)
Eat(T.TID_SEMICOLON);
return statement;
}
case T.TID_GOTO:
{
return ParseGotoStatement();
}
case T.TID_RETURN:
{
return ParseReturnStatement();
}
case T.TID_THROW:
{
return ParseThrowStatement();
}
case T.TID_LOCK:
{
return ParseLockStatement();
}
case T.TID_USING:
{
return ParseUsingStatement();
}
case T.TID_FIXED:
{
return ParseFixedStatement();
}
case T.TID_TRY:
{
return ParseTryStatement();
}
case T.TID_IF:
{
return ParseIfStatement();
}
case T.TID_SWITCH:
{
return ParseSwitchStatement();
}
case T.TID_WHILE:
{
return ParseWhileStatement();
}
case T.TID_DO:
{
return ParseDoStatement();
}
case T.TID_FOR:
{
return ParseForStatement();
}
case T.TID_FOREACH:
{
return ParseForEachStatement();
}
case T.TID_CHECKED:
{
//It is nested checked block
Eat(T.TID_CHECKED);
CodeStatementBlock nestedBlock = ParseStatementBlock(null);
nestedBlock.Modifiers = CodeStatementBlock.BlockDeclarationModifiers.Checked;
return nestedBlock;
}
case T.TID_UNCHECKED:
{
//It is nested checked block
Eat(T.TID_UNCHECKED);
CodeStatementBlock nestedBlock = ParseStatementBlock(null);
nestedBlock.Modifiers = CodeStatementBlock.BlockDeclarationModifiers.Unchecked;
return nestedBlock;
}
case T.TID_UNSAFE:
{
//It is nested checked block
Eat(T.TID_UNSAFE);
CodeStatementBlock nestedBlock = ParseStatementBlock(null);
nestedBlock.Modifiers = CodeStatementBlock.BlockDeclarationModifiers.Unsafe;
return nestedBlock;
}
case T.TID_OPENCURLY:
{
return ParseStatementBlock(null);
}
default:
{
//Label
if(tokens.CurrentType == T.TID_IDENTIFIER && tokens.Peek(1).TokenType == T.TID_COLON)
{
CodeLabelStatement labelStatement = new CodeLabelStatement();
RecordPosition(labelStatement);
labelStatement.Label = tokens.Current.Name;
Eat(T.TID_IDENTIFIER);
Eat(T.TID_COLON);
return labelStatement;
}
if(tokens.CurrentType == T.TID_CONST || tokens.CurrentType == T.TID_IDENTIFIER || tokens.Current.IsPredefinedType())
{
//Looks like type declaration
//let's verify
int mark = tokens.Mark();
if(tokens.CurrentType == T.TID_CONST)
Eat(T.TID_CONST);
string typeName = ParseTypeName();
if(tokens.CurrentType == T.TID_IDENTIFIER) //It is a type declaration <type> identifier!
{
tokens.Rewind(mark);
return ParseVariableDeclarationStatement();
}
tokens.Rewind(mark);
}
//If we found ourselves here it can mean only one thing: We are about to parse an expression!
return ParseExpressionStatement();
}
}
}
private CodeVariableDeclarationStatement ParseVariableDeclarationStatement()
{
CodeVariableDeclarationStatement declarationStatement = new CodeVariableDeclarationStatement();
RecordPosition(declarationStatement);
if(tokens.CurrentType == T.TID_CONST)
{
declarationStatement.IsConst = true;
Eat(T.TID_CONST);
}
declarationStatement.DeclarationType = new CodeTypeReference();
RecordPosition(declarationStatement.DeclarationType);
declarationStatement.DeclarationType.TypeName = ParseTypeName();
declarationStatement.DeclaredVariables.AddRange(ParseVariableDeclarationList());
foreach(CodeVariableDeclarationMember variable in declarationStatement.DeclaredVariables)
{
methodScopeNameManager.Register(variable.Name,declarationStatement);
}
if(tokens.CurrentType == T.TID_SEMICOLON)
Eat(T.TID_SEMICOLON);
return declarationStatement;
}
private CodeStatement ParseExpressionStatement()
{
CodeExpressionStatement expressionStatement = new CodeExpressionStatement();
RecordPosition(expressionStatement);
expressionStatement.Expression = ParseExpression();
if(tokens.CurrentType == T.TID_SEMICOLON)
Eat(T.TID_SEMICOLON);
return expressionStatement;
}
private CodeStatement ParseDoStatement()
{
CodeDoStatement doStatement = new CodeDoStatement();
RecordPosition(doStatement);
Eat(T.TID_DO);
doStatement.Statement = (CodeEmbeddedStatement) ParseStatement();
Eat(T.TID_WHILE);
Eat(T.TID_OPENPAREN);
doStatement.Condition = ParseExpression();
Eat(T.TID_CLOSEPAREN);
Eat(T.TID_SEMICOLON);
return doStatement;
}
private CodeStatement ParseWhileStatement()
{
CodeWhileStatement whileStatement = new CodeWhileStatement();
RecordPosition(whileStatement);
Eat(T.TID_WHILE);
Eat(T.TID_OPENPAREN);
whileStatement.Condition = ParseExpression();
Eat(T.TID_CLOSEPAREN);
whileStatement.Statement = (CodeEmbeddedStatement) ParseStatement();
if(tokens.CurrentType == T.TID_SEMICOLON)
Eat(T.TID_SEMICOLON);
return whileStatement;
}
private CodeStatement ParseForStatement()
{
CodeForStatement forStatement = new CodeForStatement();
RecordPosition(forStatement);
Eat(T.TID_FOR);
Eat(T.TID_OPENPAREN);
if(tokens.CurrentType != T.TID_SEMICOLON) //If not an empty initializer
{
//Parse initializers
while(true)
{
if(IsCanceled())
break;
forStatement.Initializers.Add(ParseStatement());
//ParseStatement() has eatten our semicolon if it was there but it had left
//colon if it was there.
if(tokens.CurrentType == T.TID_COMMA)
{
Eat(T.TID_COMMA);
continue;
}
else
{
break;
}
}
}
else
{
Eat(T.TID_SEMICOLON);
}
if(tokens.CurrentType != T.TID_SEMICOLON)
forStatement.Condition = ParseExpression();
Eat(T.TID_SEMICOLON);
if(tokens.CurrentType != T.TID_CLOSEPAREN) //If not an empty iterators list
{
//Parse iterators
while(true)
{
if(IsCanceled())
return forStatement;
forStatement.Iterators.Add(ParseStatement());
//ParseStatement() has eatten our semicolon if it was there but it had left
//colon if it was there.
if(tokens.CurrentType == T.TID_COMMA)
{
Eat(T.TID_COMMA);
continue;
}
else
{
break;
}
}
}
Eat(T.TID_CLOSEPAREN);
forStatement.Statement = (CodeEmbeddedStatement) ParseStatement();
if(tokens.CurrentType == T.TID_SEMICOLON)
Eat(T.TID_SEMICOLON);
return forStatement;
}
private CodeStatement ParseForEachStatement()
{
CodeForEachStatement foreachStatement = new CodeForEachStatement();
RecordPosition(foreachStatement);
Eat(T.TID_FOREACH);
Eat(T.TID_OPENPAREN);
foreachStatement.Type = new CodeTypeReference();
RecordPosition(foreachStatement.Type);
foreachStatement.Type.TypeName = ParseTypeName();
foreachStatement.IteratorName = ParseDottedName();
Eat(T.TID_IN);
foreachStatement.IteratedCollection = ParseExpression();
Eat(T.TID_CLOSEPAREN);
foreachStatement.Statement = (CodeEmbeddedStatement) ParseStatement();
if(tokens.CurrentType == T.TID_SEMICOLON)
Eat(T.TID_SEMICOLON);
return foreachStatement;
}
/// <summary>
/// This function parses a statment block
/// </summary>
/// <param name="parent">parent block</param>
/// <returns></returns>
private CodeStatementBlock ParseStatementBlock(CodeStatementBlock parent)
{
CodeStatementBlock block = new CodeStatementBlock();
RecordPosition(block);
if(tokens.CurrentType == T.TID_SEMICOLON)
{
Eat(T.TID_SEMICOLON);
return null; //no implementation
}
Eat(T.TID_OPENCURLY);
int blockMark = methodScopeNameManager.Mark();
while(tokens.CurrentType != T.TID_CLOSECURLY && tokens.CurrentType != T.TID_EOF)
{
int lastTokenPosition = tokens.Mark();
block.Statements.Add(ParseStatement());
//If we are not moving - we stuck in the endless cycle.
//try to recover.
if(lastTokenPosition == tokens.Mark())
{
RecoverBySkippingToSemicolon();
}
}
//Skip closing curly
if(tokens.CurrentType == T.TID_CLOSECURLY)
Eat(T.TID_CLOSECURLY);
//If block ends with semicolon - eat it too.
if(tokens.CurrentType == T.TID_SEMICOLON)
Eat(T.TID_SEMICOLON);
methodScopeNameManager.Rewind(blockMark);
return block;
}
private CodeStatement ParseIfStatement()
{
CodeIfStatement ifStatement = new CodeIfStatement();
RecordPosition(ifStatement);
Eat(T.TID_IF);
Eat(T.TID_OPENPAREN);
ifStatement.Condition = ParseExpression();
Eat(T.TID_CLOSEPAREN);
ifStatement.TrueStatement = (CodeEmbeddedStatement) ParseStatement();
if(tokens.CurrentType == T.TID_ELSE)
{
Eat(T.TID_ELSE);
ifStatement.FalseStatement = (CodeEmbeddedStatement) ParseStatement();
}
return ifStatement;
}
private CodeStatement ParseSwitchStatement()
{
CodeSwitchStatement switchStatement = new CodeSwitchStatement();
RecordPosition(switchStatement);
Eat(T.TID_SWITCH);
Eat(T.TID_OPENPAREN);
switchStatement.SwitchExpression = ParseExpression();
Eat(T.TID_CLOSEPAREN);
Eat(T.TID_OPENCURLY);
while(tokens.CurrentType != T.TID_EOF &&
(tokens.CurrentType == T.TID_CASE || tokens.CurrentType == T.TID_DEFAULT))
{
CodeStatementCollection statementsCollection;
if(tokens.CurrentType == T.TID_CASE)
{
Eat(T.TID_CASE);
CodeSwitchCase switchCase = new CodeSwitchCase();
switchCase.Expression = ParseExpression();
Eat(T.TID_COLON);
switchStatement.Cases.Add(switchCase);
statementsCollection = switchCase.Statements;
}
else
{
switchStatement.DefaultCaseStatements = new CodeStatementCollection();
statementsCollection = switchStatement.DefaultCaseStatements;
Eat(T.TID_DEFAULT);
Eat(T.TID_COLON);
}
while(tokens.CurrentType != T.TID_EOF &&
(tokens.CurrentType != T.TID_CASE &&
tokens.CurrentType != T.TID_DEFAULT &&
tokens.CurrentType != T.TID_CLOSECURLY))
{
statementsCollection.Add(ParseStatement());
}
}
Eat(T.TID_CLOSECURLY);
if(tokens.CurrentType == T.TID_SEMICOLON)
Eat(T.TID_SEMICOLON);
return switchStatement;
}
private CodeStatement ParseTryStatement()
{
CodeTryStatement tryStatement = new CodeTryStatement();
RecordPosition(tryStatement);
Eat(T.TID_TRY);
tryStatement.Statements = ParseStatementBlock(null);
while(tokens.CurrentType == T.TID_CATCH)
{
CodeCatchClause catchClause = new CodeCatchClause();
RecordPosition(catchClause);
Eat(T.TID_CATCH);
if(tokens.CurrentType == T.TID_OPENPAREN) //Does filter present?
{
Eat(T.TID_OPENPAREN);
catchClause.CatchExceptionType = new CodeTypeReference();
RecordPosition(catchClause.CatchExceptionType);
catchClause.CatchExceptionType.TypeName = ParseTypeName();
if(tokens.CurrentType == T.TID_IDENTIFIER) //Local name is also present
catchClause.LocalName = ParseDottedName();
Eat(T.TID_CLOSEPAREN);
}
catchClause.Statements = ParseStatementBlock(null);
tryStatement.CatchClauses.Add(catchClause);
}
if(tokens.CurrentType == T.TID_FINALLY)
{
CodeFinallyClause finallyClause = new CodeFinallyClause();
RecordPosition(finallyClause);
Eat(T.TID_FINALLY);
finallyClause.Statements = ParseStatementBlock(null);
tryStatement.FinallyClause = finallyClause;
}
return tryStatement;
}
private CodeStatement ParseFixedStatement()
{
CodeFixedStatement fixedStatement = new CodeFixedStatement();
RecordPosition(fixedStatement);
Eat(T.TID_FIXED);
Eat(T.TID_OPENPAREN);
fixedStatement.PointerType = new CodeTypeReference();
RecordPosition(fixedStatement.PointerType);
fixedStatement.PointerType.TypeName = ParseTypeName();
fixedStatement.DeclaredPointers.AddRange(ParseVariableDeclarationList());
// foreach(CodeVariableDeclarationMember variable in fixedStatement.DeclaredPointers)
// {
// methodScopeNameManager.Register(variable.Name,declarationStatement);
// }
Eat(T.TID_CLOSEPAREN);
fixedStatement.Statement = (CodeEmbeddedStatement) ParseStatement();
if(tokens.CurrentType == T.TID_SEMICOLON)
Eat(T.TID_SEMICOLON);
return fixedStatement;
}
private CodeStatement ParseLockStatement()
{
CodeLockStatement lockStatement = new CodeLockStatement();
RecordPosition(lockStatement);
Eat(T.TID_LOCK);
Eat(T.TID_OPENPAREN);
lockStatement.Expression = ParseExpression();
Eat(T.TID_CLOSEPAREN);
CodeStatementBlock lockStatements = new CodeStatementBlock();
lockStatement.Statement = (CodeEmbeddedStatement) ParseStatement();
if(tokens.CurrentType == T.TID_SEMICOLON)
Eat(T.TID_SEMICOLON);
return lockStatement;
}
private CodeStatement ParseUsingStatement()
{
CodeUsingStatement usingStatement = new CodeUsingStatement();
RecordPosition(usingStatement);
Eat(T.TID_USING);
Eat(T.TID_OPENPAREN);
int iScopeMark = methodScopeNameManager.Mark();
int tokensMark = tokens.Mark();
//resource aquisition may be an expression or variable declaration. Look eahead to figure out what is what!
//if currnt token is ident. it could be a beginign of type name.
//if it is try to parse it.
if(tokens.CurrentType == T.TID_IDENTIFIER)
{
ParseTypeName();
}
//After we parsed allegied type - see if next token is identifier - if it is
//it is vaviable declaration - otherwise - it is an expression.
//If it was not identifier all along - it is an expression than.
if(tokens.CurrentType == T.TID_IDENTIFIER)
{
tokens.Rewind(tokensMark);
usingStatement.ResourceAquisition = ParseVariableDeclarationStatement();
}
else
{
tokens.Rewind(tokensMark);
CodeExpressionStatement expressionStatement = new CodeExpressionStatement();
expressionStatement.Expression = ParseExpression();
usingStatement.ResourceAquisition = expressionStatement;
}
Eat(T.TID_CLOSEPAREN);
CodeStatementBlock usingStatements = new CodeStatementBlock();
usingStatement.Statement = (CodeEmbeddedStatement) ParseStatement();
if(tokens.CurrentType == T.TID_SEMICOLON)
Eat(T.TID_SEMICOLON);
methodScopeNameManager.Rewind(iScopeMark);
return usingStatement;
}
private CodeStatement ParseThrowStatement()
{
CodeThrowStatement throwStatement = new CodeThrowStatement();
RecordPosition(throwStatement);
Eat(T.TID_THROW);
if(tokens.CurrentType != T.TID_SEMICOLON)
throwStatement.ToThrow = ParseExpression();
Eat(T.TID_SEMICOLON);
return throwStatement;
}
private CodeStatement ParseReturnStatement()
{
CodeReturnStatement returnStatement = new CodeReturnStatement();
RecordPosition(returnStatement);
Eat(T.TID_RETURN);
returnStatement.Expression = ParseExpression();
Eat(T.TID_SEMICOLON);
return returnStatement;
}
private CodeStatement ParseGotoStatement()
{
switch(tokens.Peek(1).TokenType)
{
case T.TID_IDENTIFIER:
{
CodeGotoStatement gotoStatement = new CodeGotoStatement();
RecordPosition(gotoStatement);
Eat(T.TID_GOTO);
gotoStatement.TargetLabel = tokens.Current.Name;
Eat(T.TID_IDENTIFIER);
Eat(T.TID_SEMICOLON);
return gotoStatement;
}
case T.TID_CASE:
{
CodeSwitchGotoStatement gotoStatement = new CodeSwitchGotoStatement();
RecordPosition(gotoStatement);
Eat(T.TID_GOTO);
Eat(T.TID_CASE);
gotoStatement.TargetExpression = ParseExpression();
Eat(T.TID_SEMICOLON);
return gotoStatement;
}
case T.TID_DEFAULT:
{
CodeSwitchGotoStatement gotoStatement = new CodeSwitchGotoStatement();
RecordPosition(gotoStatement);
Eat(T.TID_GOTO);
Eat(T.TID_DEFAULT);
gotoStatement.TargetExpression = null;
Eat(T.TID_SEMICOLON);
return gotoStatement;
}
}
return null;
}
/// <summary>
/// This function parses a set of using declarations i form
/// using...;
/// using...;
/// etc
/// </summary>
/// <param name="codeNamespace"></param>
private void ParseNamespaceImports(CodeNamespace codeNamespace)
{
while(tokens.Current.TokenType == T.TID_USING)
{
CodeNamespaceImport import = new CodeNamespaceImport();
RecordPosition(import);
string alias = string.Empty;
string typeName = string.Empty;
if(!Eat(T.TID_USING))
{
tokens.SkipToUntilOneOf(T.TID_USING, T.TID_SEMICOLON,T.TID_OPENSQUARE);
continue;
}
if(tokens.CurrentType == T.TID_IDENTIFIER && tokens.Peek(1).TokenType == T.TID_EQUAL)
{
//alias
alias = ParseDottedName();
Eat(T.TID_EQUAL);
typeName = ParseTypeName();
}
else
{
//namespace
typeName = ParseDottedName();
}
import.Alias = alias;
import.ImportedName = typeName;
codeNamespace.Imports.Add(import);
if(!Eat(T.TID_SEMICOLON))
{
tokens.SkipToUntilOneOf(T.TID_USING, T.TID_SEMICOLON,T.TID_OPENSQUARE);
continue;
}
}
}
private string ParseDottedName()
{
CSharpToken currentToken = tokens.Current;
if(currentToken.TokenType != T.TID_IDENTIFIER)
{
ReportUnexpectedTokenError(T.TID_IDENTIFIER,currentToken.TokenType);
return string.Empty;
}
StringBuilder nameBuilder = new StringBuilder();
while(true)
{
if(IsCanceled())
return string.Empty;
if(currentToken.TokenType == T.TID_IDENTIFIER)
{
nameBuilder.Append(currentToken.Name);
}
currentToken = tokens.Next();
if(currentToken.TokenType == T.TID_DOT)
{
nameBuilder.Append('.');
currentToken = tokens.Next();
}
else
{
break;
}
}
return nameBuilder.ToString();
}
private string ParseTypeName()
{
string typeReference = "System.Void";
CSharpToken currentToken = tokens.Current;
if(currentToken.TokenType == T.TID_IDENTIFIER || currentToken.IsPredefinedType())
{
if(currentToken.TokenType == T.TID_IDENTIFIER)
{
typeReference = ParseDottedName();
//we are at next token
}
else
{
typeReference = currentToken.GetPredefinedTypeName();
tokens.Next();
}
//Parse array if suitable
while(true)
{
if(IsCanceled())
return string.Empty;
if(tokens.CurrentType != T.TID_OPENSQUARE)
break;
typeReference+= "[";
tokens.Next();
int openSquares = 1;
while(tokens.CurrentType != T.TID_EOF)
{
//Arrays can be nested
//String[pr.PRlengthA[0]]
//to parse this correcly we need to balance number of square brackets in between.
typeReference+= tokens.Current.Name;
if(tokens.CurrentType == T.TID_OPENSQUARE)
openSquares++;
if(tokens.CurrentType == T.TID_CLOSESQUARE)
{
openSquares--;
if(openSquares == 0)
{
Eat(T.TID_CLOSESQUARE);
break;
}
}
tokens.Next();
}
}
while(tokens.CurrentType == T.TID_STAR)
{
typeReference+= '*';
//Move to next token
tokens.Next();
}
currentToken = tokens.Current;
}
return typeReference;
}
public override CompilerErrorCollection Errors
{
get
{
if(parsingErrors == null)
parsingErrors = new CompilerErrorCollection();
return parsingErrors;
}
}
private CodeCompileUnit CompileUnit
{
get
{
if(compileUnit == null)
compileUnit = new CodeCompileUnit();
return compileUnit;
}
}
private void ReportProgress()
{
if(tokens != null && currentProgress != tokens.PercentsConsumed)
{
currentProgress = tokens.PercentsConsumed;
OnParsingProgress(currentProgress);
}
}
/// <summary>
/// Eats current token.
/// </summary>
/// <param name="tokenType"></param>
/// <returns></returns>
private bool Eat (CSharpTokenType tokenType)
{
if(tokens.Current.TokenType != tokenType)
{
ReportUnexpectedTokenError(tokens.Current.TokenType,tokenType);
return false;
}
tokens.Next();
return true;
}
private CodeElementPosition GetCurrentPosition()
{
CodeElementPosition position = new CodeElementPosition();
position.FileName = compileUnit.SourceFileName;
position.Line = tokens.Current.Line;
position.Column = tokens.Current.Column;
position.Position = tokens.Current.Position;
return position;
}
private void RecordPosition(CodeElement element)
{
element.SourcePosition.FileName = CompileUnit.SourceFileName;
element.SourcePosition.Line = tokens.Current.Line;
element.SourcePosition.Column = tokens.Current.Column;
element.SourcePosition.Position = tokens.Current.Position;
}
private string GetCSErrorMessage(string errorCode)
{
if(compilerErrorsResourceManager == null)
lock(typeof(CSharpParser))
{
if(compilerErrorsResourceManager == null)
compilerErrorsResourceManager = AssemblyResourceManager.GetInstance("CSharpParser.CompilerErrors");
}
string fetchedString = compilerErrorsResourceManager.GetString(errorCode);
//Check that fetchedString is not null
Debug.Assert(fetchedString != null,string.Format("String resource '{0} was not found",errorCode));
if(fetchedString == null)
fetchedString = errorCode;
return fetchedString;
}
private void ReportUnexpectedTokenError(CSharpTokenType expectedTokenType, CSharpTokenType foundTokenType)
{
string errorCode = String.Empty;
string[] errorArguments = null;
switch(expectedTokenType)
{
case T.TID_IDENTIFIER:
{
errorCode = "CS1001";
break;
}
case T.TID_SEMICOLON:
{
errorCode = "CS1002";
break;
}
default:
{
errorCode = "AM1001";
errorArguments = new string[] {expectedTokenType.ToString(),foundTokenType.ToString()};
break;
}
}
ReportError(errorCode,(tokens.CurrentType == T.TID_EOF ? tokens.Current : tokens.Peek(1)),tokens.Current,errorArguments);
}
private CompilerError ReportError(string errorNumber,CSharpToken currentToken,CSharpToken peekedToken,params string[] arguments)
{
if(arguments == null)
arguments = new string[0];
int line = -1;
int column = -1;
if(currentToken != null && peekedToken != null)
{
line = peekedToken.Line;
column = peekedToken.Column;
}
else
{
if(currentToken != null)
{
line = currentToken.Line;
column = currentToken.Column;
}
}
CompilerError error = new CompilerError(CompileUnit.SourceFileName,line,column,errorNumber,String.Format(GetCSErrorMessage(errorNumber),arguments));
Errors.Add(error);
OnParsingError(error);
return error;
}
private void AddMemberToCodeType(CodeTypeDeclaration codeType,CodeTypeMemberDeclaration member)
{
if(codeType is CodeClassDeclaration)
((CodeClassDeclaration) codeType).Members.Add(member);
if(codeType is CodeInterfaceDeclaration)
((CodeInterfaceDeclaration) codeType).Members.Add(member);
if(codeType is CodeStructDeclaration)
((CodeStructDeclaration) codeType).Members.Add(member);
}
private void RecoverBySkippingToSemicolon()
{
while(tokens.CurrentType != T.TID_SEMICOLON && tokens.CurrentType != T.TID_EOF)
tokens.Next();
Eat(T.TID_SEMICOLON);
}
private void RecoverBySkippingCurlies(bool lookForOpenCurly)
{
if(lookForOpenCurly)
while(tokens.CurrentType != T.TID_OPENCURLY && tokens.CurrentType != T.TID_EOF)
tokens.Next();
if(tokens.CurrentType == T.TID_OPENCURLY)
Eat(T.TID_OPENCURLY);
//if not end of file look for closing curly
int curlies = 1; //we found open.
if(tokens.CurrentType != T.TID_EOF)
while(curlies != 0 && tokens.CurrentType != T.TID_EOF)
{
if(tokens.CurrentType == T.TID_OPENCURLY)
curlies++;
if(tokens.CurrentType == T.TID_CLOSECURLY)
curlies--;
tokens.Next();
}
if(tokens.CurrentType == T.TID_CLOSECURLY)
Eat(T.TID_CLOSECURLY);
}
private object ParseDecimalNumber(string numberString)
{
bool isHex = numberString.IndexOf("0X") != -1;
bool isLong = numberString.IndexOf("L") != -1;
bool isUnsigned = numberString.IndexOf("U") != -1;
bool isNegative = numberString.Length > 0 && numberString[0] == '-';
int negativeFactor = isNegative ? -1:1;
//Strip all the suffixes and prefixes
if(numberString.IndexOf("0X") != -1)
numberString = numberString.Remove(numberString.IndexOf("0X"),2);
numberString = numberString.Trim('U','L');
//Two special cases
if(numberString == "-2147483648")
return (int)(-2147483648);
if(numberString == "-9223372036854775808")
return (long)(-9223372036854775808);
if(numberString.Length > 0 && (numberString[0] == '-' || numberString[0]=='+'))
numberString = numberString.Substring(1);
//Fair game now - try to parse.
//Punch MS employee how did not provide parsing functiosn that do not throw an exception.
//Per spec try to parse the number and fit it in smallest possible type
System.Globalization.NumberStyles numberStyle = System.Globalization.NumberStyles.Number;
if(isHex)
numberStyle = System.Globalization.NumberStyles.HexNumber;
//Int
if(!isLong && !isUnsigned)
{
try
{
return negativeFactor*int.Parse(numberString,numberStyle);
}
catch
{
}
}
//UInt
if(!isLong)
{
try
{
return negativeFactor*uint.Parse(numberString,numberStyle);
}
catch
{
}
}
//Long
if(!isUnsigned)
{
try
{
return negativeFactor*long.Parse(numberString,numberStyle);
}
catch
{
}
}
//Ulong
try
{
return ulong.Parse(numberString,numberStyle);
}
catch
{
}
//If we find ourselves here - we could not parse number
//check out why, can be just an error in source code
Debug.Assert(false,"Cannot parse number: " + numberString);
return numberString;
}
private object ParseRealNumber(string numberString)
{
bool ifFloat = numberString.IndexOf("F") != -1;
bool isDouble = numberString.IndexOf("D") != -1;
bool isDecimal = numberString.IndexOf("M") != -1;
numberString = numberString.Trim('F','D','M');
//Fair game now - try to parse.
//Punch MS employee how did not provide parsing functiosn that do not throw an exception.
//Per spec try to parse the number and fit it in smallest possible type
System.Globalization.NumberStyles numberStyle = System.Globalization.NumberStyles.AllowLeadingSign |
System.Globalization.NumberStyles.AllowDecimalPoint |
System.Globalization.NumberStyles.AllowExponent;
//float
if(!isDouble && !isDecimal)
{
try
{
return float.Parse(numberString,numberStyle);
}
catch
{
}
}
//double
if(!isDecimal)
{
try
{
return double.Parse(numberString,numberStyle);
}
catch
{
}
}
//Decimal
try
{
return decimal.Parse(numberString,numberStyle);
}
catch
{
}
//If we find ourselves here - we could not parse number
//check out why, can be just an error in source code
Debug.Assert(false,"Cannot parse number: " + numberString);
return numberString;
}
private object ParseNumber(string numberString)
{
numberString = numberString.Trim();
numberString = numberString.ToUpper();
//Check for hexidecimals first
if(numberString.IndexOf("0X") != -1)
return ParseDecimalNumber(numberString);
if(numberString.IndexOf(".") == -1 && numberString.IndexOfAny("FDM".ToCharArray()) == -1)
{
//Decimal number
return ParseDecimalNumber(numberString);
}
else
{
//Real number
return ParseRealNumber(numberString);
}
}
private CSharpTokenStream tokens = null;
private byte currentProgress = 0;
private static AssemblyResourceManager compilerErrorsResourceManager = null;
//Current compile unit & errors collection
private CompilerErrorCollection parsingErrors = null;
private CodeCompileUnit compileUnit = null;
private string sourceCode;
private ScopedNameManagers methodScopeNameManager = new ScopedNameManagers();
private ManualResetEvent cancelEvent;
}
}
|