哪种当代计算机语言是LL(1)?

时间:2017-01-01 11:08:28

标签: compiler-construction formal-languages ll

(我把节日时间花在一些语言理论上。如果这是一个天真的问题,请原谅。)

根据here

  LL语法,特别是LL(1)语法,非常实用   感兴趣,因为这些语法的解析器很容易构建,并且   因此,许多计算机语言被设计为LL(1)。

因此,出于好奇,当代计算机语言是LL(1)? C,Java,C#或Python是否属于这一类?

2 个答案:

答案 0 :(得分:9)

我想我很想用[citation needed]来标记维基百科的引用;这些假设至少是值得怀疑的。例如,有大量基于yacc的编译器构造工具,这使得在实践中使用更强大(同样快速)的LALR算法构建解析器非常容易,有些还实现了更多的功能(并且在大多数实际语法中几乎同样快)GLR算法。几十年来,手写解析器一直没有必要。 [注1]

通过尝试回答问题:

  1. 大多数计算机语言都是技术上的"不是LL,因为它们甚至没有上下文。那将包括需要声明标识符的语言(C,C ++,C#,Java等),以及具有预处理器和/或宏设施的语言(C和C ++等),含有歧义的语言只能是使用语义信息解决(Perl将是这里最糟糕的攻击者,但C和C ++也在那里)。而且,为了传播更多的乐趣,它还包括具有类似Python的布局感知的语言(当然还有Python,还有Haskell)。

    我把恐慌的引用放在"技术上"因为有很多人会说这些例外情况不会被计算在内#34;这是他们的意见,他们有权获得它(无论如何我已经放弃了争论,尽管我不同意这种观点)。例如,您可以通过在应用语法之前预处理文本来消除C / C ++中的预处理器,或者通过插入INDENT,NEWLINE和DEDENT令牌而不是空白来预处理空白感知语言,之后您可以进行某种声明关于神秘的核心语言"。 (这很难应用于C ++模板,只能通过解析文本来消除,因此您无法声明可以在解析之前完成扩展。)

    声称语言不是无上下文的,因为它要求声明标识符可能会引起争议。在某些语言中(不是C / C ++,其中需要语义分析来避免歧义),你可以争辩说,可以在不验证声明规则的情况下构造解析树,这使得该规则不会出现句法" 。但仍然可以在语法上强制执行声明规则;你不能用无上下文语法(例如你可以使用Van Wijngaarden grammar)。

    在任何情况下,常见的解析策略是识别语言的超集,然后通过对生成的解析树的后续分析来拒绝不合格的程序;在这种情况下,问题是超集是否是LL。在我看来,为了使其有意义,有必要将每个有效的程序解析为到正确的语法树,这消除了使用语义分析来消除歧义。

    < / LI>
  2. 解析的目标是生成语法树,而不仅仅是识别文本是否有效。 (你可能会错过这个重要的事实,如果你浏览那些倾向于专注于识别的正式语言教科书,可能是因为语法树的结构不那么有趣。)因此,坚持提出的语法实际上代表句法结构似乎是合理的。语言。

    例如,您可以使用简单的常规语言识别代数表达式:

    (PREFIX* OPERAND POSTFIX*) (INFIX PREFIX* OPERAND POSTFIX*)*
    

    其中PREFIX是一组前缀运算符以及(,POSTFIX是后缀运算符的集合(如果有的话)以及),INFIX是一组中缀运算符(另外,减法,乘法等),而OPERAND是一个标识符或常量。

    那个正则表达式不能正确拒绝带有不平衡括号的表达式,但我们已经同意识别该语言的超集是对的,对吧? : - )

    如果需要,我们可以从PREFIX和POSTFIX集中删除括号,并使OPERAND成为递归生成。得到的语法通常是LL(1)[注2]:

    operand    => IDENTIFIER | CONSTANT | '(' expression ')'
    term       => operand | PREFIX operand | operand POSTFIX
    expression => term | term INFIX expression
    

    然而,问题是该语法不捕获运算符优先级。它甚至没有尝试识别a+b*ca*b+c都是总和的事实,因此在这两种情况下顶级运算符都是+,而不是先发生的任何运算符在表达中。 (如果你正在解析APL,这不会成为问题。但是大多数语言都符合关于运算符优先级的通常期望。)

    这一点很重要,因为LL语法不能处理左递归生成,并且您需要左递归生成才能正确解析左关联运算符。 (也就是说,要正确地将a-b-c解析为((a-b)-c)而不是(a-(b-c)),这将具有不同的价值。)同样,你可以说这是一个狡辩,因为你可以发布 - 处理解析树以纠正关联性。这当然是正确的,但结果是用于解析的语法与用于解释语言语法的语法不同。这可能会或可能不会打扰你。

    值得添加的是,有些语言(Haskell,Prolog,可能还有更多)可以在程序文本中定义运算符及其优先级。这显然使得无法在没有语义分析的情况下生成正确的语法树,并且解析这些语言的常用方法是精确地使用简化的&#34;交替操作数和运算符&#34;上面概述的语言。一旦运算符优先级全部已知,大概在解析结束时,所有表达式然后使用诸如Shunting Yard算法之类的东西重新分析,以便产生正确的解析。

  3. 我们假设我们放弃了上述异议,并且只是问&#34;哪些常用的编程语言可能会使用LL解析器?&#34;

    但是,为了避免作弊,我们应该确实要求LL解析器具有固定的前瞻,而不需要回溯。如果你允许任意前瞻和回溯,那么你大大扩展了可解析语言的领域,但我认为你不再属于LL领域。这将消除,例如,模板和无预处理器的C ++子集,即使常见的编译器实现使用递归下降解析器,因为需要回溯才能解决"Most Vexing Parse"歧义。 (无论如何,你无法真正从C ++中删除模板,而使用模板进行解析非常困难。[注3])

    Java和Python都被设计为LL(1)&#34;伪可解析&#34;。我相信Haskell也属于这一类。没有语义信息就无法正确解析C(经典歧义:(x)*(y)是强制转换还是乘法? - 这取决于x是否已被typedef修改或声明为变量)但我和#39;我很确定它可以使用非回溯递归下降解析器捕获。我没有深入研究C#,但this answer by Eric Lippert表明在某些情况下它需要回溯。

  4. 注释

    1. 当然,人们仍然这样做,并且在许多情况下有充分的理由(例如,产生更好的错误消息)。但是&#34;构建LALR解析器很困难&#34;是是一个很好的理由,因为它不是。

    2. 那不是LL,因为我没有左因素。但这很容易做到;我将它留作练习。

    3. 请参阅Is C++ context-free or context-sensitive?。还有Todd Veldhuizen的经典C++ Templates are Turing Complete

答案 1 :(得分:1)

如果我更广泛地看待“计算机语言”而不是“编程语言”,我相信您可以考虑几种声明性语言,尽管这也可能取决于您认为的当代语言。

可能的候选人:

  • XML
  • LLVM IR
  • 许多配置语言,例如 INI 文件
  • 一些网络协议
  • 某些数据格式,例如 CSV 文件

大多数(全部?)描述正则表达式的语言都不是正则表达式,而是 LL(1)。

对于实际的编程语言,这些语言可能都太老了,不能被视为当代语言,而且许多都有可能违反 LL(k) 的通用扩展。

  • 汇编语言
  • 基础
  • LISP
  • Wirth 语言:Pascal、Modula、Oberon
  • 序言

我对上述所有语言的了解不足以得出结论,这就是为什么我建议它们可能候选。如果您知道其中任何一处存在句法歧义,请在评论中告诉我。