Использование 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, но написано в более функциональном стиле.

Другие вопросы по тегам