Tokenisation/ParenthesisedTokenExtractor.cs
using System.Collections.Generic;
using Expressive.Exceptions;
namespace Expressive.Tokenisation
{
internal class ParenthesisedTokenExtractor : ITokenExtractor
{
private readonly char endingCharacter;
private readonly char startingCharacter;
public ParenthesisedTokenExtractor(char singleCharacter) : this(singleCharacter, singleCharacter)
{
}
public ParenthesisedTokenExtractor(char startingCharacter, char endingCharacter)
{
this.startingCharacter = startingCharacter;
this.endingCharacter = endingCharacter;
}
public Token ExtractToken(string expression, int currentIndex, Context context)
{
var character = expression[currentIndex];
if (character != this.startingCharacter)
{
return null;
}
var extracted = GetString(expression, currentIndex, this.endingCharacter);
if (string.IsNullOrWhiteSpace(extracted))
{
throw new MissingTokenException($"Missing closing token '{this.endingCharacter}'", this.endingCharacter);
}
return new Token(extracted, currentIndex);
}
private static string GetString(string expression, int startIndex, char expectedEndingCharacter)
{
var index = startIndex;
var foundEndingCharacter = false;
var character = expression[index];
var isEscape = false;
while (index < expression.Length && !foundEndingCharacter)
{
if (index != startIndex &&
character == expectedEndingCharacter &&
!isEscape) // Make sure the escape character wasn't previously used.
{
foundEndingCharacter = true;
}
if (character == '\\' && !isEscape)
{
isEscape = true;
}
else
{
isEscape = false;
}
index++;
if (index == expression.Length)
{
break;
}
character = expression[index];
}
return foundEndingCharacter ? expression.Substring(startIndex, index - startIndex) : null;
}
}
}