Я не думаю, что вы можете сделать это только с помощью парсера Sprache, но это возможно в сочетании с некоторыми другими настраиваемыми c logi, интегрированными в него.
public static List<string> ExpectedThings = new List<string>(new[] {
"OneThing",
"AnotherThing",
"YetAnotherThing"
});
public static string SelectThingValue(string thingKey, string thingVal)
{
if (ExpectedThings.IndexOf(thingKey) == -1)
{
throw new ParseException($"Already parsed an instance of '{thingKey}'.");
}
ExpectedThings.Remove(thingKey);
return thingVal;
}
public static readonly Parser<string> ThingParser = (
from key in ExpectedThings.Aggregate((Parser<string>)null, (acc, thing) => {
var nextThingParser = Parse.String(thing).Text();
return acc == null ? nextThingParser : acc.Or(nextThingParser);
})
from eq in Parse.Char('=')
from val in Parse.AnyChar.Except(Parse.LineTerminator).Many().Text()
select SelectThingValue(key, val)
);
public static MyClass ParseThings()
{
const string input = @"OneThing=Foo
AnotherThing=Bar
YetAnotherThing=Baz";
string[] vals = ThingParser.DelimitedBy(Parse.LineEnd).Parse(input).ToArray();
if (ExpectedThings.Any())
{
throw new ParseException($"Missing things in input string: {string.Join(", ", ExpectedThings.Select(thing => $"'{thing}'"))}");
}
return new MyClass(vals[0], vals[1], vals[2]);
}
static void Main(string[] args)
{
MyClass myClass = ParseThings();
}
Идея в том, что вы вводите ожидаемые «вещи» в список ExpectedThings
. Затем ThingParser
создается путем динамического связывания вызовов .Or()
для каждого из этих элементов в списке с использованием функции LINQ Aggregate()
. В части парсера select
он вызывает SelectThingValue()
, который предназначен для удаления того, что было только что проанализировано, из списка, чтобы мы знали, что это уже было проанализировано. Он также проверяет, что вещь еще не была проанализирована, и если да, то выдаст исключение. Последняя проверка, которую он выполняет, - это увидеть, есть ли еще какие-либо элементы в ExpectedThings
, и если да, это означает, что он не проанализировал один из них. И поскольку все они необходимы, мы выдаем здесь ошибку.
Вы можете сделать это намного более структурированным и динамичным c в зависимости от вашего фактического варианта использования, но это работает на основе примера в вашем вопросе . Здесь все также является stati c, но вы также можете изменить это, чтобы позволить вам иметь значения Dynami c в ExpectedThings
.