【问题标题】:How are parenthesis vs. lambda vs. tuple expressions parsed in C#如何在 C# 中解析括号、lambda 和元组表达式
【发布时间】:2019-06-24 19:38:36
【问题描述】:

我正在编写自己的编程语言,并且在解决了大部分其他内容之后已经开始解析元组/lambda 表达式。语法与 C# 非常相似,但有一些细微差别。我想知道 C# 是否先行确定是否生成简单的带括号的表达式、元组或 lambda,这似乎不太可能,因为先行 (k) 是不确定的。

现在,我正在通过提前查看某些指标来破解解决方案,这些指标应该为解析器提供关于它试图解析的内容的线索。即检查标识符 @1,然后检查 @2 处的 '='、',' 或 ':'(冒号用于用类型信息注释标识符)。

我的 lambda 语法比 C# 中的语法更灵活一点,所以猜测一下,您可以只解析标识符列表,直到没有更多逗号,然后检查下 2 个标记是否有 ')' 和 '= >'。

有什么我遗漏的技巧吗?

【问题讨论】:

  • 基本上,自定义前瞻。以this 为例。
  • 是的,似乎是我遇到的问题:p 谢谢,很好的发现:)
  • 有一个 LALR(1) 语法,我认为它类似于 this answer 中的 C# lambda 语法。我没有将其标记为重复,因为它不处理元组并且因为您说您想要的语法不同(没有说明它是什么),并且因为您没有明确说明您想要什么样的解析器建造。但也许答案还是有用的。
  • C# Roslyn compiler 是开源的。
  • @rici 我不相信它可以用 LALR(1) 语法来完成,你必须知道标识符后面是什么来确定它是否是一个普通的 ol' 表达式,lambda 参数列表等。所以充其量可能是 LALR(2)。

标签: c# parsing lambda tuples


【解决方案1】:

是的,您需要无限前瞻,因为在某些情况下,(a, b, c, ...) 可以是元组或 lambda 参数列表,具体取决于后面的标记。还有一个歧义,因为(a.b.c.d... 可能是强制类型转换中的类型表达式,也可能是正则括号表达式。

您可以在解析器源代码中看到 C# 如何解决这个问题: http://sourceroslyn.io/#Microsoft.CodeAnalysis.CSharp/Parser/LanguageParser.cs,bab23e84fb31bf9e,references

基本上它保存当前位置并尝试解析可能的产生之一。如果解析失败,它会回到保存的位置(也就是回溯),然后尝试下一个可能的生产。如果所有可能的产生式都失败,它会报告语法错误。

似乎在两遍中运行潜在的解析。在第一遍中,它只检查是否可以解析,并返回一个布尔值。如果这个测试成功,它会运行第二遍,它实际上构造了解析树。

理论上无限前瞻会影响解析器的性能,但实际上这可能不是一个大问题,因为它只发生在某些特定的上下文中,并且元组和 lambda 参数列表通常大小有限。

语言设计者倾向于避免需要前瞻的语法,因为它会增加解析器的复杂性和性能影响。但是 C# 的设计者可能认为添加漂亮的元组语法是值得的。

【讨论】:

  • 这很有趣,我曾想过在解析失败后保存解析状态和回溯,尽管似乎很难衡量用户的意图并产生正确的语法错误。例如如果解析 lambda 只是因为 lambda 的主体有错误而失败,那么我们应该推送该错误。但是,如果由于缺少预期的 '=>' 标记而导致 lambda 解析失败,那么我们不应该推送错误(?)。
  • 另外,我的语言不使用带括号的表达式作为强制转换,我有一个更清晰/明确的语法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多