背景:
我正在实现一种类似于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
文件执行了lexing和解析步骤,但是reading that thing is a nightmare它是11628行,没有注释和大量的abbr。
答案 0 :(得分:2)
是的,Yacc文件起初可能有点令人生畏,parse.y
不是最好的文件。你看过各种字符串制作规则了吗?你有什么具体问题吗?
至于实际的解析,词法分析器也会解析数字文字和字符串的情况确实并不少见,例如SO上similar question的接受答案。如果你以这种方式处理事情,那么就不难看出如何去做。在字符串中命中#{
,基本上会启动一个新的解析上下文,再次将其解析为表达式。这意味着示例中的第一个}
不能作为插值的终止,因为它是表达式中文字字符串的一部分。一旦到达表达式的末尾(请记住像;
这样的表达式分隔符),下一个}
就是您需要的那个。
答案 1 :(得分:1)
这不是一个完整的答案,但我希望它对我或跟随我的人都有用。
Matz在他的书的第11章中给出了yylex()
parse.y
函数的详细纲要。它没有直接提到字符串,但它确实描述了词法分析器如何使用lex_state
来解析Ruby中的几个局部模糊结构。
可以在 here 中找到本章英文翻译的复制品。
答案 2 :(得分:1)
Dart还支持插入到像Ruby这样的字符串中的表达式,我已经为它浏览了一些解析器。我相信他们所做的是为插值前的字符串文字和结尾的字符串文字定义单独的标记。所以,如果你标记:
"before ${the + expression} after"
你会得到像这样的代币:
STRING_START "before "
IDENTIFIER the
PLUS
IDENTIFIER expression
STRING " after"
然后在你的解析器中,处理STRING_START
以解析跟随它的插值表达式是一个非常简单的过程。
答案 3 :(得分:1)
请记住,他们没有(在编译时创建AST)。
Ruby字符串可以在运行时汇编并正确插值。因此,所有解析和评估机制必须在运行时可用。在这种意义上,在编译时完成的任何工作都可以被视为优化。
那么为什么这很重要?因为有非常有效的基于堆栈的技术来解析和评估不创建或装饰AST的表达式。从左到右读取(解析)字符串,并且当遇到嵌入的令牌时,它们被评估或推送到堆栈上,或者导致堆栈内容被弹出和评估。
如果表达式相对简单,这是一种简单的实现方法。如果您真的想要在每个字符串中使用该语言的全部功能,那么您需要在运行时使用完整的编译器。不是每个人都这样做。
披露:我写了一个商业语言产品就是这样做的。
答案 4 :(得分:0)
我们的Ruby解析器(参见我的简历)对待Ruby"字符串"作为具有大量子结构的复杂对象,包括字符串开始和结束标记,裸字符串文字片段,许多有趣的标点序列代表各种正则表达式操作符,当然,递归地,大多数Ruby本身用于嵌套在这些字符串中的表达式。
这是通过允许词法分析器在(对于Ruby,许多)特殊lexing模式中检测和生成这样的字符串片段来实现的。解析器具有定义有效令牌序列的(子)语法。这种解析解决了OP的原始问题;解析器知道花括号是否与正则表达式内容中的其他花括号匹配,和/或正则表达式是否完全组装且花括号是匹配的块结尾。
是的,它构建了Ruby代码和正则表达式的AST。
所有这一切的目的是允许我们构建Ruby代码的分析器和转换器。见https://softwarerecs.stackexchange.com/q/11779/101