using System;
using System.Collections.Generic;
using System.Linq;
namespace DigitalSamurai.SpellSharp.InputProcessing.Processors
{
public partial class CSharpProcessor
{
public partial class CSharpParsingState : ScopedParsingState<ScopeTypes>
{
private static readonly Token tokenTrue = new Token ("true", 0, 0, new DocumentPosition (0, 0), LexicalElements.True);
private static readonly Token tokenFalse = new Token ("false", 0, 0, new DocumentPosition (0, 0), LexicalElements.False);
private HashSet<string> globalDefinedPreprocessorSymbols = new HashSet<string> ();
private Dictionary<string, bool> localDefinedPreprocessorSymbols = new Dictionary<string, bool> ();
private List<bool> preprocessorConditionStack = new List<bool> ();
public CSharpParsingState (TokenNode currentNode)
: base (currentNode)
{
StartScope (ScopeTypes.None);
ProcessSilentTokens (true);
}
private void ProcessSilentTokens (bool startOfFile)
{
while (HasNode)
{
switch (LexicalElement)
{
case LexicalElements.PreprocessorDefine:
DefineLocalSymbol (startOfFile, true);
break;
case LexicalElements.PreprocessorUndefine:
DefineLocalSymbol (startOfFile, false);
break;
case LexicalElements.PreprocessorIf:
Process_If ();
break;
case LexicalElements.PreprocessorElseIf:
Process_ElseIf ();
break;
case LexicalElements.PreprocessorElse:
Process_Else ();
break;
case LexicalElements.PreprocessorEndIf:
Process_EndIf ();
break;
case LexicalElements.PreprocessorError:
case LexicalElements.PreprocessorWarning:
Process_Feedback ();
break;
case LexicalElements.PreprocessorLine:
case LexicalElements.PreprocessorPragma:
SkipToNextLine ();
break;
case LexicalElements.PreprocessorRegion:
case LexicalElements.PreprocessorEndRegion:
SelectTokenUsingProcessorAndMoveToNext (ProcessorType.XmlFragment);
break;
case LexicalElements.Comment:
case LexicalElements.XmlComment:
SelectTokenUsingProcessorAndMoveToNext (ProcessorType.XmlFragment);
break;
default:
return;
}
}
}
#region -- Preprocessing --
private void SkipConditionalBranch ()
{
int scope = 0;
while ((0 <= scope) && base.MoveToNext ())
{
switch (LexicalElement)
{
case LexicalElements.PreprocessorIf:
scope++;
break;
case LexicalElements.PreprocessorEndIf:
scope--;
break;
case LexicalElements.PreprocessorElseIf:
case LexicalElements.PreprocessorElse:
break;
default:
continue;
}
if (scope == 0)
{
return;
}
}
}
private void DefineLocalSymbol (bool startOfFile, bool isDefined)
{
if (startOfFile)
{
var symbol = Token.Text.Trim ();
if (!symbol.Any ((c) => char.IsWhiteSpace (c)))
{
localDefinedPreprocessorSymbols[symbol] = isDefined;
}
}
base.MoveToNext ();
}
private void Process_If ()
{
var includeSection = ShouldIncludeSection ();
// push condition
preprocessorConditionStack.Add (includeSection);
if (!includeSection)
{
SkipConditionalBranch ();
}
}
private void Process_ElseIf ()
{
if (!CurrentConditionHadPreviousInclusion && ShouldIncludeSection ())
{
CurrentConditionHadPreviousInclusion = true;
}
else
{
SkipConditionalBranch ();
}
}
private void Process_Else ()
{
if (!CurrentConditionHadPreviousInclusion)
{
base.MoveToNext ();
}
else
{
SkipConditionalBranch ();
}
}
private void Process_EndIf ()
{
// pop condition
if (preprocessorConditionStack.Count > 0)
{
preprocessorConditionStack.RemoveAt (preprocessorConditionStack.Count - 1);
}
SkipToNextLine ();
}
private void Process_Feedback ()
{
SkipToNextLine
(
() => SelectTokenUsingProcessor (ProcessorType.XmlFragment)
);
}
private bool ShouldIncludeSection ()
{
try
{
var expressionList = new List<Token> ();
SkipToNextLine
(
() => expressionList.Add (Token)
);
return EvaluateWithSymbolAndParenthesisSubstitution (expressionList, 0);
}
catch
{
return false;
}
}
private bool EvaluateWithSymbolAndParenthesisSubstitution (List<Token> expressionList, int offset)
{
for (int index = offset; index < expressionList.Count; index++)
{
var current = expressionList[index];
switch (current.LexicalElement)
{
case LexicalElements.Unknown:
SubstituteToken
(
expressionList,
index,
IsDefined (current.Text)
);
break;
case LexicalElements.Symbol_ParenthesisOpen:
SubstituteToken
(
expressionList,
index,
EvaluateWithSymbolAndParenthesisSubstitution (expressionList, index + 1)
);
break;
case LexicalElements.Symbol_ParenthesisClose:
var result = EvaluateWithNegationSubstitution (expressionList, offset, ref index);
expressionList.RemoveRange
(
offset - 1,
(index - offset) + 1
);
return result;
}
}
int end = expressionList.Count;
return EvaluateWithNegationSubstitution (expressionList, 0, ref end);
}
private bool EvaluateWithNegationSubstitution (List<Token> expressionList, int offset, ref int end)
{
for (int index = offset; index < end; index++)
{
var current = expressionList[index];
if (current.LexicalElement != LexicalElements.Symbol_ExclamationMark)
{
continue;
}
if (end <= index + 1)
{
throw new ParseException ();
}
var next = expressionList[index + 1];
switch (next.LexicalElement)
{
case LexicalElements.True:
expressionList.RemoveAt (index);
SubstituteToken (expressionList, index, false);
end--;
break;
case LexicalElements.False:
expressionList.RemoveAt (index);
SubstituteToken (expressionList, index, true);
end--;
break;
case LexicalElements.Symbol_ExclamationMark:
expressionList.RemoveRange (index, 2);
end -= 2;
index--;
break;
}
}
return EvaluateWithOperatorSubstitution (expressionList, offset, ref end);
}
private bool EvaluateWithOperatorSubstitution (List<Token> expressionList, int offset, ref int end)
{
SubstituteOperator
(
expressionList,
offset,
ref end,
LexicalElements.Symbol_Ampersand_Ampersand,
(a, b) => a && b
);
SubstituteOperator
(
expressionList,
offset,
ref end,
LexicalElements.Symbol_Pipe_Pipe,
(a, b) => a || b
);
SubstituteOperator
(
expressionList,
offset,
ref end,
LexicalElements.Symbol_EqualsTo_EqualsTo,
(a, b) => a == b
);
SubstituteOperator
(
expressionList,
offset,
ref end,
LexicalElements.Symbol_ExclamationMark_EqualsTo,
(a, b) => a != b
);
if ((end - offset) != 1)
{
throw new ParseException ();
}
return GetValue
(
expressionList[offset]
);
}
private void SubstituteOperator (List<Token> expressionList, int offset, ref int end, LexicalElements operatorElement, Func<bool, bool, bool> performOperator)
{
for (int index = offset; index < end - 2; index++)
{
var left = expressionList[index];
var symbol = expressionList[index + 1];
var right = expressionList[index + 2];
if (symbol.LexicalElement != operatorElement)
{
continue;
}
expressionList.RemoveRange (index, 2);
end -= 2;
SubstituteToken
(
expressionList,
index,
performOperator
(
GetValue (left),
GetValue (right)
)
);
}
}
private bool IsDefined (string symbol)
{
bool isDefined;
if (localDefinedPreprocessorSymbols.TryGetValue (symbol, out isDefined))
{
return isDefined;
}
return globalDefinedPreprocessorSymbols.Contains (symbol);
}
private bool GetValue (Token token)
{
switch (token.LexicalElement)
{
case LexicalElements.True:
return true;
case LexicalElements.False:
return false;
default:
throw new ParseException ();
}
}
private void SubstituteToken (List<Token> expressionList, int index, bool condition)
{
var substitutionToken = condition ? tokenTrue : tokenFalse;
if (expressionList.Count <= index)
{
throw new ParseException ();
}
expressionList[index] = substitutionToken;
}
private bool CurrentConditionHadPreviousInclusion
{
get
{
if (preprocessorConditionStack.Count == 0)
{
throw new InvalidOperationException ("Empty stack");
}
return preprocessorConditionStack[preprocessorConditionStack.Count - 1];
}
set
{
if (preprocessorConditionStack.Count == 0)
{
throw new InvalidOperationException ("Empty stack");
}
preprocessorConditionStack[preprocessorConditionStack.Count - 1] = value;
}
}
#endregion
#region -- Movement --
public override bool MoveToNext ()
{
return MoveToNext (false);
}
public void MoveToNextExplicit ()
{
MoveToNext (true);
}
private bool MoveToNext (bool throwError)
{
if (base.MoveToNext ())
{
ProcessSilentTokens (false);
}
if (HasNode)
{
return true;
}
if (throwError)
{
throw new ParseException ();
}
return false;
}
private void SkipToNextLine (Action action = null)
{
var errorLine = Token.Position.Line;
while (base.MoveToNext () && (errorLine == Token.Position.Line))
{
if (action != null)
{
action ();
}
}
}
public bool SkipTo (params LexicalElements[] skipElements)
{
return SkipTo (false, skipElements);
}
public void SkipToExplicitly (params LexicalElements[] skipElements)
{
SkipTo (true, skipElements);
}
private bool SkipTo (bool throwError, params LexicalElements[] skipElements)
{
int parenthesisBracketCounter = 0;
int squaredBracketCounter = 0;
int curlyBracketCounter = 0;
while (HasNode)
{
if (HasSkippedTo (skipElements, LexicalElement, ref parenthesisBracketCounter, ref squaredBracketCounter, ref curlyBracketCounter))
{
return true;
}
MoveToNext (throwError);
}
return false;
}
public TokenNode PeekSkipOver (params LexicalElements[] skipElements)
{
int parenthesisBracketCounter = 0;
int squaredBracketCounter = 0;
int curlyBracketCounter = 0;
var currentNode = Node;
while (currentNode != null)
{
if (HasSkippedTo (skipElements, LexicalElement, ref parenthesisBracketCounter, ref squaredBracketCounter, ref curlyBracketCounter))
{
return currentNode;
}
currentNode = currentNode.Next;
}
return null;
}
private static bool HasSkippedTo (LexicalElements[] skipElements, LexicalElements currentLexicalElement, ref int parenthesisBracketCounter, ref int squaredBracketCounter, ref int curlyBracketCounter)
{
switch (currentLexicalElement)
{
case LexicalElements.Symbol_ParenthesisClose:
parenthesisBracketCounter--;
break;
case LexicalElements.Symbol_SquaredBracketClose:
squaredBracketCounter--;
break;
case LexicalElements.Symbol_CurlyBracketClose:
curlyBracketCounter--;
break;
}
if ((parenthesisBracketCounter == 0) && (squaredBracketCounter == 0) && (curlyBracketCounter == 0) && (skipElements.Any ((skipElement) => currentLexicalElement == skipElement)))
{
return true;
}
switch (currentLexicalElement)
{
case LexicalElements.Symbol_ParenthesisOpen:
parenthesisBracketCounter++;
break;
case LexicalElements.Symbol_SquaredBracketOpen:
squaredBracketCounter++;
break;
case LexicalElements.Symbol_CurlyBracketOpen:
curlyBracketCounter++;
break;
}
return false;
}
#endregion
public void RollbackScopeAndSkipOver (params LexicalElements[] skipElements)
{
RollbackScope ();
StartScope (ScopeTypes.None);
SkipTo (false, skipElements);
CommitScopeAndMoveToNext ();
}
public void IsLexicalElementExpected (LexicalElements expectedLexicalElement)
{
if (LexicalElement != expectedLexicalElement)
{
throw new ParseException ();
}
}
public bool IsLexicalElementMemberOf (LexicalElements lexicalGroup)
{
return (LexicalElement & lexicalGroup) != 0;
}
public bool HasReachedElementOrEndOfStream (LexicalElements endElement)
{
return !HasNode || (LexicalElement == endElement);
}
}
}
}