Использование Sprache для анализа Enums по идентификаторам?
Я начинаю использовать Sprache для разбора предметного языка для математических выражений. Я знаю, что могу разобрать идентификатор, используя что-то вроде этого:
static readonly Parser<string> Identifier =
from leading in Parse.WhiteSpace.Many()
from first in Parse.Letter.Once()
from rest in Parse.LetterOrDigit.Many()
from trailing in Parse.WhiteSpace.Many()
select new string(first.Concat(rest).ToArray());
Исходя из этого, я хочу создать парсер, который будет успешным только в том случае, если токен идентификатора является одним из текстовых значений Enum. Допустим, у меня есть Enum, называемый Dimension, со значениями Dimension.Location и Dimension.Time. я хочу сделать
static readonly Parser<Dimension> DimensionIdentifier = ...
это происходит только в том случае, если анализируется идентификатор и если строка токена идентификатора является одним из имен перечисления ("Location" или "Time") и возвращает значение перечисления, Dimension.Location или Dimension.Time соответственно, Может кто-нибудь помочь с тем, что, вероятно, простой вопрос? Спасибо!
2 ответа
Очень хорошее решение, украденное отсюда... http://www.codewise-llc.com/blog/2015/8/13/parsing-enum-values-with-sprache
Создайте типизированный вспомогательный класс, чтобы создать синтаксический анализатор для заданного перечисления...
public static class EnumParser<T>
{
public static Parser<T> Create()
{
var names = Enum.GetNames(typeof(T));
var parser = Parse.IgnoreCase(names.First()).Token()
.Return((T)Enum.Parse(typeof(T), names.First()));
foreach (var name in names.Skip(1))
{
parser = parser.Or(Parse.IgnoreCase(name).Token().Return((T)Enum.Parse(typeof(T), name)));
}
return parser;
}
}
Тогда ваш парсер просто такой...
public static Parser<Dimension> Dimension = EnumParser<Dimension>.Create();
И некоторые модульные тесты (измените имя класса на то, что вы используете, я использовал учебник Sprache, чтобы начать)...
[Test]
[TestCase("Time", Dimension.Time)]
[TestCase("Location", Dimension.Location)]
public void ShouldGetProperEnumValue(string enumValueName, Dimension expected)
{
var eValue = QuestionnaireGrammar.Dimension.Parse(enumValueName);
Assert.AreEqual(expected, eValue);
}
[Test]
[ExpectedException]
[TestCase("Fredo")]
public void ShouldFailIfNotInList(string enumValueName)
{
var eValue = QuestionnaireGrammar.Dimension.Parse(enumValueName);
}
Интересная библиотека, с удовольствием узнаю об этом.
Хорошо, довольно легко разбирать парсеры...
Создал копию вашего парсера идентификации и назвал его Identifier2, чтобы сохранить его ясным...
public static readonly Parser<string> Identifier2 =
from leading in Parse.WhiteSpace.Many()
from first in Parse.Letter.Once()
from rest in Parse.LetterOrDigit.Many()
from trailing in Parse.WhiteSpace.Many()
select new string(first.Concat(rest).ToArray());
Затем добавлен составной синтаксический анализатор, который принимает результаты анализатора Identifier2 и использует синтаксический анализатор Dimension...
public static readonly Parser<Dimension> IdentityDimension =
from result in Identifier2
select Dimension.Parse(result);
Хотя вы не уверены в том, что вы покупаете, enum parser, похоже, уже делает все, что делает анализатор идентификаторов.
Я использую следующий подход:
public static Parser<TEnum> ParseEnum()
{
return Enum.GetValues(typeof(TEnum))
.Cast<TEnum>()
.Select(value => Parse.IgnoreCase(Enum.GetName(typeof(TEnum), value)).Return(value))
.Aggregate((x, y) => x.Or(y));
}
Это похоже на ответ dbugger, так как все еще основано на Parse.Or
, но написано в более функциональном стиле.