gpt4 book ai didi

string - 在Flex/Bison中实现字符串插值

转载 作者:行者123 更新时间:2023-12-02 04:01:39 26 4
gpt4 key购买 nike

我目前正在为我设计的语言编写翻译。

词法分析器/解析器(GLR)是用Flex / Bison编写的,而主要的解释器是用D编写的-到目前为止,一切都可以正常工作。

问题是我还想添加字符串插值,即识别包含特定模式(例如"[some expression]")的字符串文字并转换包含的表达式。我认为这应该在解析器级别的相应语法操作中完成。

我的想法是将插入的字符串转换/处理为简单连接后的样子(因为它现在可以正常工作)。

例如。

print "this is the [result]. yay!"




print "this is the " + result + ". yay!"


但是,我对如何在Bison中做到这一点感到困惑:基本上,如何告诉它重新解析特定的字符串(在构造主要AST的同时)?

有任何想法吗?

最佳答案

如果确实需要,可以通过生成reentrant parser重新解析该字符串。尽管我想您可以使用flex的缓冲区堆栈将某些东西与默认扫描仪一起弄混,但您可能还需要reentrant scanner。确实,值得学习如何基于避免不必要的全局变量的通用原理构建可重入的解析器和扫描器,无论您是否为此目的而需要它们。

但是,您实际上不需要重新解析任何内容。您可以一次完成整个解析。您只需要扫描仪中有足够的智能即可了解嵌套插值。

基本思想是让扫描程序将带有插值的字符串文字拆分为一系列令牌,这些令牌可以由解析器轻松组装为适当的AST。由于扫描程序可能从单个字符串文字中返回多个令牌,因此我们需要引入start condition来跟踪扫描当前是否在字符串文字中。而且,由于插值可以嵌套,因此我们将使用flex的可选start condition stack(通过%option stack启用)来跟踪嵌套上下文。

这是一个粗略的草图。

如前所述,扫描程序具有额外的启动条件:SC_PROGRAM是默认值,默认值在扫描程序扫描常规程序文本时生效,而SC_STRING在扫描程序扫描字符串时有效。仅需要SC_PROGRAM是因为flex没有提供正式接口来检查启动条件堆栈是否为空;除了嵌套之外,它与INITIAL顶级启动条件相同。开始条件堆栈用于跟踪插值标记(在此示例中为[]),这是必需的,因为插值表达式可能使用方括号(例如,作为数组下标),甚至可能包含嵌套插值字符串。由于除一个例外,SC_PROGRAMINITIAL相同,因此我们将其设为包含规则。

%option stack
%s SC_PROGRAM
%x SC_STRING
%%


由于我们使用单独的开始条件来分析字符串文字,因此我们在解析时也可以规范转义序列。并非所有应用程序都希望这样做,但这很普遍。但这并不是真正答案的重点,因此我省略了大部分细节。更有趣的是嵌入式插值表达式的处理方式,特别是深度嵌套的表达式。

最终结果是将字符串文字转换为一系列标记,可能表示嵌套结构。为了避免在扫描程序中实际进行解析,我们不做任何尝试来创建AST节点或以其他方式重写字符串文字的尝试;相反,我们只是将引号字符本身传递给解析器,以分隔字符串文字的顺序:

["]                 { yy_push_state(SC_STRING);    return '"'; }
<SC_STRING>["] { yy_pop_state(); return '"'; }


插值标记使用一组非常相似的规则:

<*>"["              { yy_push_state(SC_PROGRAM);   return '['; }
<INITIAL>"]" { return ']'; }
<*>"]" { yy_pop_state(); return ']'; }


上面的第二条规则避免了在启动条件堆栈为空时弹出(因为它将处于 INITIAL状态)。不必在扫描仪中发出错误消息。我们可以将无与伦比的右括号传递给解析器,然后解析器将执行必要的任何错误恢复。

为了结束 SC_STRING状态,我们需要返回字符串的令牌,可能包括转义序列:

<SC_STRING>{
[^[\\"]+ { yylval.str = strdup(yytext); return T_STRING; }

\\n { yylval.chr = '\n'; return T_CHAR; }
\\t { yylval.chr = '\t'; return T_CHAR; }
/* ... Etc. */
\\x[[:xdigit]]{2} { yylval.chr = strtoul(yytext, NULL, 16);
return T_CHAR; }
\\. { yylval.chr = yytext[1]; return T_CHAR; }
}


将这样的转义字符返回到解析器可能不是最佳策略。通常我会使用内部扫描器缓冲区来累积整个字符串。但这只是出于说明目的。 (这里省略了一些错误处理;在各种极端情况下,包括换行处理和令人讨厌的情况,其中程序中的最后一个字符是未终止的字符串文字中的反斜杠。)

在解析器中,我们只需要为插入的字符串插入一个串联节点。唯一的麻烦是,我们不希望在不带任何插值的字符串文字的常见情况下插入这样的节点,因此我们使用两种语法生成方式,一种用于正好包含一个片段的字符串,另一个用于具有两件或更多件:

string : '"' piece '"'                 { $$ = $2; }
| '"' piece piece_list '"' { $$ = make_concat_node(
prepend_to_list($2, $3));
}
piece : T_STRING { $$ = make_literal_node($1); }
| '[' expr ']' { $$ = $2; }
piece_list
: piece { $$ = new_list($1); }
| piece_list piece { $$ = append_to_list($1, $2); }

关于string - 在Flex/Bison中实现字符串插值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57971191/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com