【问题标题】:What's a good resource for starting to write a programming language, that's not context free? [closed]什么是开始编写编程语言的好资源,不是上下文无关的? [关闭]
【发布时间】:2008-09-16 00:28:16
【问题描述】:

我正在寻找一种有趣的编程语言,但是我看到的大部分资源都是用于编写一种上下文无关的语言,但是我希望编写一种像 python 一样使用缩进的语言,据我所知意味着它不能是上下文无关的。

【问题讨论】:

  • 我的编程语言课程有点生疏,你能给我一个链接来解释为什么 python 不是 CFG 吗?
  • Python 语言遵循上下文无关语法——他们甚至发布了 BNF! python.org/doc/current/ref/grammar.txt你可以在这里阅读上下文无关语法的正式定义:en.wikipedia.org/wiki/Context_free_grammar
  • 同意,python 绝对是一种上下文无关语言。缩进的变化会导致缩进或取消缩进标记,它们只是语法中的符号。您需要一个特殊的标记器来识别这些标记,但这并不能阻止实际语法是 100% 真实的诚实上下文无关的。

标签: python compiler-construction programming-languages interpreter


【解决方案1】:

简单地说,上下文无关文法不需要符号表即可正确解析代码。上下文相关的语法可以。

D 编程语言是上下文无关文法的一个例子。 C++ 是一种上下文敏感的。 (比如 T*x 是声明 x 是指向 T 的指针,还是将 T 乘以 x ?我们只能通过在符号表中查找 T 来判断它是类型还是变量。)

空白与它无关。

D 使用上下文无关文法来大大简化解析它,以便简单的工具可以解析它(例如语法高亮编辑器)。

【讨论】:

  • T*x 本身或 '=' 左侧被假定为 D 和 GCC 下的指针减速。不久前我碰巧遇到了这种情况。
  • T* x;如果它是一个声明,它本身是有效的,如果它是乘法的,它本身也是有效的。我认为假设它始终是指针声明本身并不正确。 (我可以写 2*3; 所以我也可以,就像 int T=2; int x=3; T*x; 即使结果丢失)一样容易写)
  • 如果有一个不同的符号来声明指针,比如$,上面的例子会变成上下文无关的吗?
  • -1。我相信这个答案至少是非常令人困惑的,如果不是完全错误的话。你能证明你关于符号表的陈述是正确的吗?请解释您如何需要一个符号表来解析提到的上下文相关语言here。请解释您为何声称 C++ 的 grammar。请在区分语法语言时更加小心。请解释为什么 Python 的空格不区分上下文。
【解决方案2】:

您可能想阅读这篇写得很好的关于解析 Python 的文章,Python: Myths about Indentation

虽然我没有尝试使用 yacc 之类的工具编写上下文无关解析器,但我认为可以使用条件词法分析器返回 url 中描述的缩进更改标记。

顺便说一下,这里是python.org的官方python语法:http://www.python.org/doc/current/ref/grammar.txt

【讨论】:

  • 小心:语法和语言是有区别的。
【解决方案3】:

我会先阅读一些关于该主题的文献来熟悉这个问题。 Aho 等人的经典编译器书。人。可能对数学和comp sci 很重,但更平易近人的文本是Jack Crenshaw 的Let's Build a Compiler 文章。这是 Crenshaw 先生在 80 年代后期写的一系列文章,是有史以来关于编译器的最不被重视的文章。该方法简单明了:Crenshaw 先生展示了行之有效的“A”方法。您可以在几个晚上轻松浏览这些内容,并更好地了解编译器的全部内容。需要注意的是,文本中的示例是用 Turbo Pascal 编写的,编译器发出 68K 汇编程序。这些示例很容易移植到更新的编程语言,为此我推荐 Python。但是,如果您想按照示例进行操作,您至少需要Turbo Pascal 5.5a 68K assembler and emulator。该文本在今天仍然具有相关性,使用这些旧技术真的很有趣。我强烈推荐它作为任何人关于编译器的第一本书。好消息是 Python 和 Ruby 等语言是开源的,您可以下载并研究 C 源代码,以便更好地了解它是如何完成的。

【讨论】:

  • 这是一个很好的链接,但这如何回答 OP?
【解决方案4】:

“上下文无关”是一个相对术语。大多数上下文无关解析器实际上解析了上下文无关语言的超集,然后检查生成的解析树以查看它是否有效。例如,以下两个 C 程序根据 C 的上下文无关文法是有效的,但在上下文检查时很快就会失败:

int main()
{
    int i;
    i = 1;
    return 0;
}

int main()
{
    int i;
    i = "Hello, world";
    return 0;
}

没有上下文,i = "Hello, world"; 是一个完全有效的赋值,但在上下文中你可以看到类型都是错误的。如果上下文是char* i; 就可以了。所以上下文无关的解析器不会看到这个赋值有任何问题。直到编译器开始检查类型(取决于上下文)它才会捕获错误。

任何可以用键盘产生的东西都可以被解析为上下文无关的;至少您可以检查所有使用的字符是否有效(仅包含可显示 Unicode 字符的所有字符串的集合是上下文无关语法)。唯一的限制是您的语法有多有用,以及您必须对生成的解析树进行多少上下文相关检查。

像 Python 这样的依赖于空格的语言会降低上下文无关语法的用处,因此以后需要更多的上下文相关检查(其中大部分是在 Python 中通过动态类型在运行时完成的)。但是在需要上下文相关检查之前,上下文无关解析器仍然可以做很多事情。

【讨论】:

  • 这似乎是唯一真正理解“上下文无关”、“语法”和“语言”的含义并解决 OP 的答案。 +1
【解决方案5】:

我不知道任何教程/指南,但您可以尝试查看 tinypy 的源代码,它是类似 Python 语言的一个非常小的实现。

【讨论】:

    【解决方案6】:

    在一种语言中使用缩进并不一定意味着该语言的语法不能是上下文无关的。 IE。缩进将确定语句存在的范围。无论定义在哪个范围内,语句仍然是语句(范围通常可以由编译器/解释器的不同部分处理,通常在语义解析期间)。

    也就是说,一个很好的资源是 antlr 工具 (http://www.antlr.org)。该工具的作者还制作了一本关于使用 antlr (http://www.pragprog.com/titles/tpantlr/the-definitive-antlr-reference) 为语言创建解析器的书。有很好的文档和很多示例语法。

    【讨论】:

      【解决方案7】:

      如果您真的想在语言设计和实现方面有所尝试,您可能希望将以下内容添加到您的书架中:

      • Programming Language Pragmatics,Scott 等人。
      • 编程语言中的设计概念,Turbak 等人。
      • 现代编译器设计,Grune 等人。 (比起 Aho 等人的“龙之书”,我更喜欢这本书。)

      温和的介绍如:

      • Crenshaw 的教程(此处由 @'Jonas Gorauskas' 建议)
      • Parr 的权威 ANTLR 参考
      • Martin Fowler 最近在 DSL 方面的工作

      您还应该考虑您的实现语言。这是不同语言在它们所提供的便利方面巨大不同的领域之一。您应该考虑 LISP、F#/OCaml 和 Gilad Bracha 的新语言 Newspeak 等语言。

      【讨论】:

      • +1 用于编程语言语用学。很棒的书。仅附录中的语言树是无价的。 books.google.com/…page820
      【解决方案8】:

      我建议您手动编写解析器,在这种情况下,包含大量空白不应出现任何实际问题。

      使用解析器生成器的主要问题是很难在解析器中获得良好的错误恢复。如果您计划为您的语言实现 IDE,那么良好的错误恢复对于让 Intellisence 之类的东西正常工作很重要。 Intellisence 始终适用于不完整的句法结构,解析器越能确定用户尝试键入的结构,您可以提供的智能体验就越好。

      如果您编写一个手写的自顶向下解析器,您几乎可以在任何地方实现您想要的任何规则。这使得提供错误恢复变得容易。它还将使您实现重要的空白变得微不足道。您可以简单地将当前缩进级别存储在解析器类中的变量中,并且当您在新行上遇到列位置小于当前缩进级别的标记时,可以停止解析块。此外,您可能会在语法中遇到歧义。大多数广泛使用的“生产”语言都有语法歧义。一个很好的例子是 C# 中的泛型(在表达式上下文中“

      此外,由于您自己设计语言,您应该假设它的设计会迅速发展(对于一些有标准委员会的语言,例如 C++,情况并非如此)。对自动生成的解析器进行更改以处理歧义或改进语言可能需要您对语法进行重大重构,这既烦人又耗时。对手写解析器的更改,尤其是自上而下的解析器,通常是相当本地化的。

      我会说解析器生成器只是一个不错的选择,如果:

      1. 您从不打算编写 IDE,
      2. 该语言具有真的简单的语法,或者
      3. 您需要一个非常快速的解析器,并且可以接受糟糕的用户体验

      【讨论】:

      • C 是使用解析器生成器 (yacc) 构建的。 Scala 是使用组合器构建的,它有点像隐式解析器生成器。手动构建解析器很容易出错,并且在非平凡的情况下难以维护。我强烈不同意你的所有三个理由。 :-)
      • 对于命令行编译器,解析器生成器是可以的。批处理解析器不需要太多的错误恢复。然而,对于交互式场景,我真的认为你需要一个手写的解析器。以我的经验,手写解析器很容易编写,也相对容易维护。
      • 从某种意义上说,我明白你的意思。编辑器与编译器非常不同。但是,一般来说,我认为编辑器解析器只需要自上而下(与 yacc 自下而上相反),因为需要明确选择生产规则。
      • 选择与理论无关。手动编写解析器会产生更好的用户体验。一旦决定手动编写解析器,自上而下的选择就很明显了。自上而下的解析器很容易编写。自下而上的解析器很难编写。
      【解决方案9】:

      您读过 Aho、Sethi、Ullman:“编译器:原理、技术和工具”吗?这是一本经典的语言参考书。

      /艾伦

      【讨论】:

        【解决方案10】:

        如果您以前从未编写过解析器,请从简单的开始。解析器非常微妙,如果您从未研究过编程语言的结构,您可能会在编写它们时遇到各种麻烦。

        阅读 Aho、Sethi 和 Ullman(被称为“龙之书”)是一个不错的计划。与其他贡献者相反,我说您应该首先使用更简单的解析器生成器,例如 Yacc 和 Bison,并且只有当您因为无法使用该工具做某事而被烧毁时,您才应该继续尝试使用 LL(* ) 像 Antlr 这样的解析器。

        【讨论】:

        • 我曾经用 Yacc 和 Bison 编写过一个简单的编译器。还有……尊重 gcc。
        【解决方案11】:

        仅仅因为一种语言使用重要的缩进并不意味着它本质上是上下文相关的。例如,Haskell 使用了重要的缩进,并且(据我所知)它的语法是上下文无关的。

        需要上下文相关语法的源示例可能是来自 Ruby 的 sn-p:

        my_essay = << END_STR
        This is within the string
        END_STR
        
        << self
          def other_method
            ...
          end
        end
        

        另一个例子是 Scala 的 XML 模式:

        def doSomething() = {
          val xml = <code>def val <tag/> class</code>
          xml
        }
        

        作为一般规则,上下文相关语言在任何精确意义上都稍微难以想象,因此不太常见。甚至 Ruby 和 Scala 也不真正算在内,因为它们的上下文敏感特性只包含该语言的一小部分。如果我是你,我会根据灵感来制定我的语法,然后再担心以后的解析方法。我想你会发现你想出的任何东西都会自然地与上下文无关,或者非常接近它。

        最后一点,如果您真的需要上下文相关的解析工具,您可以尝试一些不太严格的正式技术。解析器组合器用于 Scala 的解析。它们有一些烦人的限制(没有词法分析),但它们不是一个坏工具。像 ANTLR 这样的 LL(*) 工具似乎也更擅长表达这种“临时”解析转义。不要尝试将 Yacc 或 Bison 与上下文相关的语法一起使用,它们对于轻松表达这些概念来说太严格了。

        【讨论】:

          【解决方案12】:

          上下文相关的语言?这个是非缩进的:Protium (http://www.protiumble.com)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-06-30
            • 2010-09-06
            • 2011-03-14
            • 1970-01-01
            • 2010-09-18
            • 2015-08-30
            • 1970-01-01
            相关资源
            最近更新 更多