Sprache LINQ查询示例如何工作?



我在Sprache存储库中发现了以下代码:

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条款文档说源(在我们的情况下是Parse.WhiteSpace.Many()Parse.Letter.Once().Text())必须是IEnumerable:

from子句中引用的数据源必须为IEnumerableIEnumerable<T>IQueryable<T>等派生类型

但它不是,编译器说这很好!

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

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

同样,最终表达式select first + rest没有考虑到leadingtrailing变量,但最终结果identifier肯定在内部使用了它们。

第二问题:leadingtrailing变量被添加到identifier中的规则机制是什么?

注:如果有人能分享一份关于LINQ查询语法内部工作的包罗一切的文档,那就太好了。我没有找到关于这个话题的资料。

看了大约五分钟的代码后,我观察到:

  1. 解析器是返回中间结果的委托

    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有效。这里是where方法

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抽象的单独实现,与集合无关,它是关于解析文本的。它生成一个嵌套的委托链,该链处理给定的字符串以验证它是否符合特定的语法,并返回描述已解析文本的结构。

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

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

和Then方法https://github.com/sprache/Sprache/blob/develop/src/Sprache/Parse.cs L241

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

最新更新