【问题标题】:How does the ruby interperter parse double quoted stringsruby interperter如何解析双引号字符串
【发布时间】:2014-01-30 23:00:06
【问题描述】:

背景

我正在实现一种类似于 Ruby 的语言called Sapphire,以此来尝试我对编程语言并发性的一些想法。我正在尝试使用嵌入式代码复制 Ruby 的双引号字符串,我觉得这对程序员非常有用。

问题

任何 Ruby 解释器如何将带有嵌入代码的双引号字符串转换为 AST?

例如:

puts "The value of foo is #{@foo}."

puts "this is an example of unmatched braces in code: #{ foo.go('}') }"

详情

我遇到的问题是如何确定哪个} 关闭了代码块。代码块中可以有其他大括号,只要稍加努力,它们就可以无与伦比。词法分析器可以在字符串中找到代码块的开头,但没有解析器的帮助,它无法确定哪个字符是该块的结尾。

它看起来像 Ruby 的 parse.y 文件同时执行词法分析和解析步骤,但 reading that thing is a nightmare 它有 11628 行长,没有 cmets 和很多缩写。

【问题讨论】:

  • 另外,字符串文字必须解决一个类似的棘手问题:%Q{{hi}} #=> "{hi}"
  • Alex,如果你把它输入到 IRB 中,你的例子会报错。 Ruby 将在第一个右大括号上结束字符串。这就是为什么你可以在这个结构中选择你的符号。即 %Q|{hi}|打开构造的符号就是关闭它的符号。例外情况是 {[()]} 关闭。
  • Michael:heredocs 很简单。开始符号 cannot 出现在行首,除了它关闭heredoc。如果您找到终止符号,则它必须是结尾。
  • 在 pry(另一个 repl)中,它没有显示任何错误,也不应该显示任何错误。我提出了这个要点,因为我无法轻易解释:gist.github.com/nedzadarek/8744476 在 Ruby 版本 1.9.3 和 2.0 上进行了测试
  • 对于heredocs,结束符号必须单独出现在新行中,不能有任何其他字符。其他出现的 symbol 都是有效的。所以考虑到STsymbol ST (见ST 后面的空格)不会关闭heredoc。 2. 有一种特殊的语法:var1=<<-ST,可以让你在终止字符串之前放置空格(`ST` 是有效的)。 3. heredocs 可以出现多次,a,b=<<S,<<S 是 heredocs 的第一行有效。

标签: ruby parsing language-design lexer


【解决方案1】:

没错,Yacc 文件一开始可能有点令人生畏,parse.y 不是最好的文件。你看过各种字符串产生规则吗?您有什么具体问题吗?

至于实际的解析,词法分析器也解析数字文字和字符串确实并不少见,参见例如在 SO 上接受similar question 的答案。如果你以这种方式处理事情,不难看出如何去做。在字符串中点击#{,基本上会启动一个新的解析上下文,该上下文会再次被解析为表达式。这意味着您的示例中的第一个 } 不能是插值的终止符,因为它是表达式中文字字符串的一部分。一旦到达表达式的末尾(请记住表达式分隔符,例如 ;),下一个 } 就是您需要的。

【讨论】:

    【解决方案2】:

    这不是一个完整的答案,但我希望它对我或关注我的人有用。

    Matz 在他的书的第 11 章中对parse.yyylex() 函数进行了非常详细的概述。它没有直接提到字符串,但它确实描述了词法分析器如何使用lex_state 来解析 Ruby 中的几个局部歧义结构。

    本章的英文译本可以在here找到。

    【讨论】:

      【解决方案3】:

      请记住,他们不必(在编译时创建 AST)。

      Ruby 字符串可以在运行时组装并正确插入。因此,所有解析和评估机制都必须在运行时可用。从这个意义上说,在编译时完成的任何工作都可以被视为优化。

      那么为什么这很重要?因为有非常有效的基于堆栈的技术来解析和评估不创建或装饰 AST 的表达式。字符串是从左到右读取(解析)的,当遇到嵌入的标记时,它们要么被评估或压入堆栈,要么导致堆栈内容被弹出和评估。

      只要表达式相对简单,这是一种简单的技术实现。如果您真的想要在每个字符串中使用语言的全部功能,那么您需要在运行时使用完整的编译器。不是每个人都这样做。

      披露:我写了一个商业语言产品就是这样做的。

      【讨论】:

        【解决方案4】:

        Dart 还支持插入到字符串中的表达式,例如 Ruby,我已经浏览了一些解析器。我相信他们所做的是为插值之前的字符串文字和最后的字符串文字定义单独的标记。所以如果你标记化:

        "before ${the + expression} after"
        

        你会得到类似的令牌:

        STRING_START "before "
        IDENTIFIER   the
        PLUS
        IDENTIFIER   expression
        STRING       " after"
        

        然后在您的解析器中,处理STRING_START 以解析它后面的插值表达式是一个非常简单的过程。

        【讨论】:

          【解决方案5】:

          我们的 Ruby 解析器(请参阅我的简历)将 Ruby“字符串”视为具有许多子结构的复杂对象,包括字符串开始和结束标记、裸字符串文字片段、代表各种正则表达式运算符的许多有趣的标点符号序列,当然还有,递归地,大部分 Ruby 本身用于嵌套在此类字符串中的表达式。

          这是通过允许词法分析器以(对于 Ruby,许多)特殊词法分析模式检测和生成此类字符串片段来实现的。解析器有一个(子)语法,定义了有效的标记序列。而这种解析解决了OP原来的问题;解析器知道大括号是否与正则表达式内容中的其他大括号匹配,和/或正则表达式是否已完全组装并且大括号是匹配的块结束。

          是的,它构建了 Ruby 代码和正则表达式的 AST。

          所有这些的目的是让我们能够构建 Ruby 代码的分析器和转换器。见https://softwarerecs.stackexchange.com/q/11779/101

          【讨论】:

            猜你喜欢
            • 2021-10-24
            • 1970-01-01
            • 2020-03-17
            • 2022-07-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多