Sprache LINQ 查询示例如何工作?

How the Sprache LINQ query example works?

我在 Sprache repository 中看到了以下代码:

Parser<string> identifier =
    from leading in Parse.WhiteSpace.Many()
    from first in Parse.Letter.Once().Text()
    from rest in Parse.LetterOrDigit.Many().Text()
    from trailing in Parse.WhiteSpace.Many()
    select first + rest;

var id = identifier.Parse(" abc123  ");

我在这里看到一个矛盾:from 子句 docs 说来源(Parse.WhiteSpace.Many()Parse.Letter.Once().Text() 在我们的例子中)必须是 IEnumerable

The data source referenced in the from clause must have a type of IEnumerable, IEnumerable<T>, or a derived type such as IQueryable<T>

但事实并非如此,编译器说没问题!

我认为有一些隐式转换为 IEnumerable,但没有:Parse.WhiteSpace.Many() returns Parser<IEnumerable<T>>Parse.Letter.Once().Text() returns Parser<string>(类型不是 IEnumerable)。

第一个问题:为什么编译器允许这段代码?

此外,最终表达式select first + rest没有考虑leadingtrailing变量,但最终结果identifier肯定会在内部使用它们.

第二个问题:通过什么将rule\mechanismleadingtrailing变量添加到identifier?

P.S。 如果有人分享有关 LINQ 查询语法内部工作的包罗万象的文档,那就太好了。我没有找到关于这个主题的任何内容。

在查看代码大约五分钟后,我观察到:

  1. 解析器是一个代表returns一个中间结果

    public delegate IResult<T> Parser<out T>(IInput input);
    
  2. 有声明的 linq 兼容方法允许 linq 语法,如:

    public static Parser<U> Select<T, U>(this Parser<T> parser, Func<T, U> convert)
     {
         if (parser == null) throw new ArgumentNullException(nameof(parser));
         if (convert == null) throw new ArgumentNullException(nameof(convert));
    
         return parser.Then(t => Return(convert(t)));
     }
    

https://github.com/sprache/Sprache/blob/develop/src/Sprache/Parse.cs#L357

语法 from x in set 工作需要 IEnumerable 接口是不正确的,您只需要具有正确名称的特定扩展方法即可接受正确的参数集。所以上面使 select 有效。这里有方法

public static Parser<T> Where<T>(this Parser<T> parser, Func<T, bool> predicate)
    {
        if (parser == null) throw new ArgumentNullException(nameof(parser));
        if (predicate == null) throw new ArgumentNullException(nameof(predicate));

        return i => parser(i).IfSuccess(s =>
            predicate(s.Value) ? s : Result.Failure<T>(i,
                $"Unexpected {s.Value}.",
                new string[0]));
    }

https://github.com/sprache/Sprache/blob/develop/src/Sprache/Parse.cs#L614

等等。

这是 linq 抽象的单独实现,与集合无关,它是关于解析文本的。它生成一个嵌套的委托链,处理给定的字符串以验证它是否符合特定的语法和描述已解析文本的 returns 结构。

这回答了第一个问题。对于第二个,您需要遵循代码:除第一个映射到 SelectMany 函数之外的所有 from x in set

public static Parser<V> SelectMany<T, U, V>(
        this Parser<T> parser,
        Func<T, Parser<U>> selector,
        Func<T, U, V> projector)
    {
        if (parser == null) throw new ArgumentNullException(nameof(parser));
        if (selector == null) throw new ArgumentNullException(nameof(selector));
        if (projector == null) throw new ArgumentNullException(nameof(projector));

        return parser.Then(t => selector(t).Select(u => projector(t, u)));
    }

https://github.com/sprache/Sprache/blob/develop/src/Sprache/Parse.cs#L635

然后方法 https://github.com/sprache/Sprache/blob/develop/src/Sprache/Parse.cs#L241

在那里您会看到,如果第一个成功(匹配的前导空格),那么第二个(单字母解析器)将应用于字符串的其余部分。因此,它不是一个集合,它处理导致解析字符串的一系列事件。