首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用Sprache解析可以以任何顺序出现的行?

如何使用Sprache解析可以以任何顺序出现的行?
EN

Stack Overflow用户
提问于 2020-05-28 16:55:49
回答 2查看 148关注 0票数 2

我使用Sprache来解析文件的一部分,如下所示:

代码语言:javascript
复制
OneThing=Foo
AnotherThing=Bar
YetAnotherThing=Baz

这三行都是强制的,但它们可以按任意顺序出现。我为各行提供了解析器,如下所示:

代码语言:javascript
复制
public static readonly Parser<string> OneThing = (
    from open in Parse.String("OneThing=")
    from rest in Parse.AnyChar.Except(Parse.LineTerminator).Many().Text()
    from newLine in Parse.LineEnd
    select rest
);

我将它们组合在一起来解析整个部分,如下所示:

代码语言:javascript
复制
public static readonly Parser<MyClass> Section = (
    from oneThing in SectionGrammar.OneThing
    from anaotherThing in SectionGrammar.AnotherThing
    from yetAnotherThing in SectionGrammar.YetAnotherThing
    select new MyClass(oneThing, anotherThing, yetAnotherThing)
);

但是,只有当这些行以OneThing、AnotherThing、YetAnotherThing的顺序出现时,这种方法才有效。我如何才能改变这一点,以允许行以任何顺序出现,但仍然强制每行都应该出现一次?

任何帮助都非常感谢!谢谢

EN

回答 2

Stack Overflow用户

发布于 2020-05-28 17:53:34

我对Sprache很天真,但一种可能比较冗长的方法是为每行选择每个选项的元组,然后在最后的select中过滤元组数组。类似于:

代码语言:javascript
复制
public static readonly Parser<MyClass> Section = (
select a from (from oneThing in SectionGrammar.OneThing select Tuple.Create(oneThing, null, null))
    .Or(from anotherThing in SectionGrammar.AnotherThing select Tuple.Create(null, anotherThing, null))
    .Or(from yetAnotherThing in SectionGrammar.YetAnotherThing select Tuple.Create(null, null, yetAnotherThing))

select b from (from oneThing in SectionGrammar.OneThing select Tuple.Create(oneThing, null, null))
    .Or(from anotherThing in SectionGrammar.AnotherThing select Tuple.Create(null, anotherThing, null))
    .Or(from yetAnotherThing in SectionGrammar.YetAnotherThing select Tuple.Create(null, null, yetAnotherThing))

select c from (from oneThing in SectionGrammar.OneThing select Tuple.Create(oneThing, null, null))
    .Or(from anotherThing in SectionGrammar.AnotherThing select Tuple.Create(null, anotherThing, null))
    .Or(from yetAnotherThing in SectionGrammar.YetAnotherThing select Tuple.Create(null, null, yetAnotherThing))

select new MyClass(
    new[] { a, b, c }.Where(i => i.Item1 != null).Select(i => i.Item1).First(),
    new[] { a, b, c }.Where(i => i.Item2 != null).Select(i => i.Item2).First(),
    new[] { a, b, c }.Where(i => i.Item3 != null).Select(i => i.Item3).First()
));

但我觉得应该有更好的方法。如果你告诉我有20行代码有独特的解析,并且可以有不同的顺序,那么上述代码的可扩展性也不是很好。

票数 0
EN

Stack Overflow用户

发布于 2020-08-05 23:01:38

我不认为您可以单独使用Sprache解析器来完成此任务,但可以与集成到其中的其他一些自定义逻辑结合使用。

代码语言:javascript
复制
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列表中。然后,通过使用LINQ的Aggregate()函数动态链接列表中每个项目的.Or()调用来构建ThingParser。在解析器的select部分,它调用SelectThingValue()来从列表中删除刚刚解析的内容,这样我们就知道该内容已经被解析了。它还会检查以确保这个东西还没有被解析,如果已经解析了,它将抛出一个异常。它所做的最后一次检查是查看ExpectedThings中是否还有任何项,如果有,这意味着它没有解析其中的一个项。因为所有这些都是必需的,所以我们在这里抛出一个错误。

您完全可以根据您的实际用例使其更具结构化和动态性,但这是基于您的问题中的示例。这里的一切也是静态的,但您也可以更改它,以允许在ExpectedThings中使用动态值。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62060732

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档