private List<ICodeElement> ParseElements(ICodeElement parentElement)
{
List<ICodeElement> codeElements = new List<ICodeElement>();
List<ICodeElement> siblingElements = new List<ICodeElement>();
List<ICommentElement> comments = new List<ICommentElement>();
List<AttributeElement> attributes = new List<AttributeElement>();
Stack<ICodeElement> enclosingElementStack = new Stack<ICodeElement>();
StringBuilder elementBuilder = new StringBuilder(DefaultBlockLength);
char nextChar;
bool end = false;
bool lineContinuation = false;
while (TryReadChar() && !end)
{
switch (CurrentChar)
{
//
// Comments
//
case VBSymbol.BeginComment:
CommentElement commentLine = ParseCommentLine();
string commentDirectiveRegionName =
GetCommentDirectiveText(commentLine, Configuration.Formatting.Regions.CommentDirectiveBeginPattern, "Name");
if (commentDirectiveRegionName != null)
{
PushComments(codeElements, comments);
RegionElement regionElement = new RegionElement();
regionElement.Name = commentDirectiveRegionName;
enclosingElementStack.Push(regionElement);
}
else
{
commentDirectiveRegionName = GetCommentDirectiveText(commentLine,
Configuration.Formatting.Regions.CommentDirectiveEndPattern, "Name");
if (commentDirectiveRegionName != null)
{
if (enclosingElementStack.Count == 0 ||
enclosingElementStack.Peek().ElementType != ElementType.Region)
{
this.OnParseError("Unmatched end region directive");
}
ICodeElement enclosingElement = enclosingElementStack.Pop();
//
// Add any processed comments to the region or condition directive.
//
if (comments.Count > 0)
{
foreach (ICommentElement commentElement in comments)
{
enclosingElement.AddChild(commentElement);
}
comments.Clear();
}
//
// Are we processing a nested region or condition directive?
//
if (enclosingElementStack.Count > 0)
{
enclosingElementStack.Peek().AddChild(enclosingElement);
}
else
{
codeElements.Add(enclosingElement);
}
}
else
{
comments.Add(commentLine);
}
}
break;
//
// Preprocessor
//
case VBSymbol.Preprocessor:
//
// TODO: Parse additional preprocessor directives.
//
string line = ReadLine().Trim();
string[] words = line.Split(WhiteSpaceCharacters, StringSplitOptions.RemoveEmptyEntries);
if (words.Length > 0 && VBKeyword.Normalize(words[0]) == VBKeyword.Region)
{
PushComments(codeElements, comments);
RegionElement regionElement = ParseRegion(line);
enclosingElementStack.Push(regionElement);
}
else if (words.Length > 0 &&
(VBKeyword.Normalize(words[0]) == VBKeyword.If ||
VBKeyword.Normalize(words[0]) == VBKeyword.Else ||
VBKeyword.Normalize(words[0]) == VBKeyword.ElseIf))
{
bool isIf;
ConditionDirectiveElement conditionDirective = ParseConditionDirective(line.Trim(), out isIf);
if (isIf)
{
enclosingElementStack.Push(conditionDirective);
}
else
{
if (enclosingElementStack.Count == 0 ||
enclosingElementStack.Peek().ElementType != ElementType.ConditionDirective)
{
this.OnParseError("Expected 'If' preprocessor directive.");
}
else
{
ConditionDirectiveElement previousCondition =
enclosingElementStack.Peek() as ConditionDirectiveElement;
while (previousCondition.ElseCondition != null)
{
previousCondition = previousCondition.ElseCondition;
}
// Add the condition to the end of the condition linked list
previousCondition.ElseCondition = conditionDirective;
}
}
}
else if (words.Length > 1 &&
VBKeyword.Normalize(words[0]) == VBKeyword.End &&
(VBKeyword.Normalize(words[1]) == VBKeyword.Region ||
VBKeyword.Normalize(words[1]) == VBKeyword.If))
{
ICodeElement enclosingElement = null;
if (VBKeyword.Normalize(words[1]) == VBKeyword.Region)
{
if (enclosingElementStack.Count == 0 ||
enclosingElementStack.Peek().ElementType != ElementType.Region)
{
this.OnParseError("Unmatched end region directive");
}
}
else if (enclosingElementStack.Count == 0 ||
enclosingElementStack.Peek().ElementType != ElementType.ConditionDirective)
{
this.OnParseError("Unmatched #End If");
}
enclosingElement = enclosingElementStack.Pop();
//
// If there are any attributes not associated with an element (e.g.
// a condition directive containing only an attribute, then
// throw an error as this is currently not supported.
//
if (enclosingElement.ElementType == ElementType.ConditionDirective &&
attributes.Count > 0)
{
this.OnParseError(
"Cannot arrange files with preprocessor directives containing attributes unassociated to an element");
}
if (comments.Count > 0)
{
foreach (ICommentElement commentElement in comments)
{
enclosingElement.AddChild(commentElement);
}
comments.Clear();
}
if (enclosingElementStack.Count > 0)
{
enclosingElementStack.Peek().AddChild(enclosingElement);
}
else
{
codeElements.Add(enclosingElement);
}
}
else
{
this.OnParseError(
"Cannot arrange files with preprocessor directives " +
"other than #Region, #End Region and conditional compilation directives");
}
break;
//
// Attribute
//
case VBSymbol.BeginAttribute:
nextChar = NextChar;
//
// Parse attribute
//
AttributeElement attributeElement = ParseAttribute(comments.AsReadOnly());
attributes.Add(attributeElement);
codeElements.Add(attributeElement);
comments.Clear();
break;
case VBSymbol.LineContinuation:
if (IsWhiteSpace(PreviousChar) && IsWhiteSpace(NextChar))
{
lineContinuation = true;
}
else
{
elementBuilder.Append(CurrentChar);
}
break;
// Eat any unneeded whitespace
case ' ':
case '\n':
case '\r':
case '\t':
case ':':
if (elementBuilder.Length > 0)
{
string processedText = elementBuilder.ToString().Trim();
if (CurrentChar == '\n')
{
if (!lineContinuation)
{
this.OnParseError(
string.Format(
Thread.CurrentThread.CurrentCulture,
"Unhandled element text '{0}'",
processedText));
}
else
{
lineContinuation = false;
}
}
if (elementBuilder[elementBuilder.Length - 1] != ' ')
{
elementBuilder.Append(' ');
}
}
break;
default:
elementBuilder.Append(CurrentChar);
string upperElementText = elementBuilder.ToString().ToUpperInvariant();
nextChar = NextChar;
if (upperElementText == VBKeyword.End.ToUpperInvariant())
{
end = true;
elementBuilder = new StringBuilder(DefaultBlockLength);
}
else if (upperElementText == VBKeyword.Rem.ToUpperInvariant() &&
nextChar == ' ')
{
CommentElement remCommentLine = ParseCommentLine();
comments.Add(remCommentLine);
elementBuilder = new StringBuilder(DefaultBlockLength);
}
else if (upperElementText == VBKeyword.Option.ToUpperInvariant() &&
IsWhiteSpace(nextChar))
{
ICodeElement optionElement = ParseOption(comments.AsReadOnly());
comments.Clear();
codeElements.Add(optionElement);
elementBuilder = new StringBuilder(DefaultBlockLength);
}
else
{
if (char.IsWhiteSpace(nextChar) || VBSymbol.IsVBSymbol(CurrentChar))
{
string elementText = VBKeyword.Normalize(elementBuilder.ToString());
bool isImplements = elementText.StartsWith(
VBKeyword.Implements, StringComparison.OrdinalIgnoreCase);
bool isInherits = !isImplements && elementText.StartsWith(
VBKeyword.Inherits, StringComparison.OrdinalIgnoreCase);
TypeElement typeElement = parentElement as TypeElement;
if ((isImplements || isInherits) && typeElement != null)
{
InterfaceReferenceType referenceType = InterfaceReferenceType.None;
if (isInherits)
{
referenceType = InterfaceReferenceType.Class;
}
else if (isImplements)
{
referenceType = InterfaceReferenceType.Interface;
}
do
{
EatWhiteSpace(WhiteSpaceTypes.SpaceAndTab);
if (NextChar == VBSymbol.AliasSeparator)
{
EatChar(VBSymbol.AliasSeparator);
}
string typeName = CaptureTypeName();
InterfaceReference interfaceReference =
new InterfaceReference(typeName, referenceType);
typeElement.AddInterface(interfaceReference);
EatWhiteSpace(WhiteSpaceTypes.SpaceAndTab);
} while (NextChar == VBSymbol.AliasSeparator);
elementBuilder = new StringBuilder(DefaultBlockLength);
}
else
{
//
// Try to parse a code element
//
ICodeElement element = TryParseElement(parentElement,
elementBuilder, comments.AsReadOnly(), attributes.AsReadOnly());
if (element != null)
{
// Since more than one field can be declared per line, we need special
// handling here.
FieldElement fieldElement = element as FieldElement;
if (fieldElement != null)
{
if (elementBuilder[0] == VBSymbol.AliasSeparator
&& codeElements[codeElements.Count - 1] is FieldElement)
{
FieldElement previousFieldElement = codeElements[codeElements.Count - 1] as FieldElement;
FieldElement currentField = element as FieldElement;
if (currentField.Access == CodeAccess.None)
{
currentField.Access = previousFieldElement.Access;
}
}
else if (fieldElement.Name.Contains(VBSymbol.AliasSeparator.ToString()))
{
string[] fieldNames = fieldElement.Name.Split(
new char[] {VBSymbol.AliasSeparator}, StringSplitOptions.RemoveEmptyEntries);
for (int fieldIndex = 0; fieldIndex < fieldNames.Length; fieldIndex++)
{
if (fieldIndex == 0)
{
fieldElement.Name = fieldNames[0];
}
else
{
FieldElement siblingFieldElement = fieldElement.Clone() as FieldElement;
siblingFieldElement.Name = fieldNames[fieldIndex].Trim();
siblingElements.Add(siblingFieldElement);
}
}
}
}
if (element is CommentedElement)
{
UsingElement usingElement = element as UsingElement;
//
// If this is the first using statement, then don't attach
// header comments to the element.
//
if (usingElement != null && parentElement == null && codeElements.Count == 0)
{
foreach (ICommentElement commentElement in usingElement.HeaderComments)
{
if (enclosingElementStack.Count > 0)
{
enclosingElementStack.Peek().AddChild(commentElement);
}
else
{
codeElements.Add(commentElement);
}
}
usingElement.ClearHeaderCommentLines();
}
comments.Clear();
}
if (enclosingElementStack.Count > 0)
{
ICodeElement enclosingElement = enclosingElementStack.Peek();
if (enclosingElement.ElementType == ElementType.ConditionDirective)
{
ConditionDirectiveElement conditionDirective = enclosingElement as ConditionDirectiveElement;
while (conditionDirective.ElseCondition != null)
{
conditionDirective = conditionDirective.ElseCondition;
}
enclosingElement = conditionDirective;
}
enclosingElement.AddChild(element);
foreach (ICodeElement additionalElement in siblingElements)
{
enclosingElement.AddChild(additionalElement);
}
}
else
{
codeElements.Add(element);
foreach (ICodeElement additionalElement in siblingElements)
{
codeElements.Add(additionalElement);
}
}
siblingElements.Clear();
elementBuilder = new StringBuilder(DefaultBlockLength);
if (element is IAttributedElement)
{
foreach (AttributeElement attribute in attributes)
{
codeElements.Remove(attribute);
}
attributes = new List<AttributeElement>();
}
}
}
}
}
break;
}
char nextCh = NextChar;
}
if (comments.Count > 0)
{
for (int commentIndex = 0; commentIndex < comments.Count; commentIndex++)
{
ICommentElement comment = comments[commentIndex];
codeElements.Insert(commentIndex, comment);
}
}
//
// Make sure that all region elements have been closed
//
if (enclosingElementStack.Count > 0)
{
if (enclosingElementStack.Peek().ElementType == ElementType.Region)
{
this.OnParseError(
string.Format(
CultureInfo.InvariantCulture,
"Missing end region directive for '{0}'",
enclosingElementStack.Peek().Name));
}
else
{
this.OnParseError("Expected #End If");
}
}
if (elementBuilder.Length > 0)
{
this.OnParseError(
string.Format(
Thread.CurrentThread.CurrentCulture,
"Unhandled element text '{0}'",
elementBuilder));
}
return codeElements;
}