String Flex/Bison中字符串插值的实现

String Flex/Bison中字符串插值的实现,string,d,bison,yacc,string-interpolation,String,D,Bison,Yacc,String Interpolation,我目前正在为我设计的语言编写一个翻译程序 lexer/parser(GLR)是用Flex/Bison编写的,主要的解释器是用D编写的——到目前为止,一切都工作得很完美 我还想添加字符串插值,即识别包含特定模式的字符串文字(例如,“[some expression]”),并转换包含的表达式。我认为这应该在解析器级别上,从相应的语法操作中完成 我的想法是转换/处理插入的字符串,使其看起来像简单的连接(因为它现在可以工作) 例如 至 print "this is the " + result + ".

我目前正在为我设计的语言编写一个翻译程序

lexer/parser(GLR)是用Flex/Bison编写的,主要的解释器是用D编写的——到目前为止,一切都工作得很完美

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

我的想法是转换/处理插入的字符串,使其看起来像简单的连接(因为它现在可以工作)

例如

print "this is the " + result + ". yay!"
然而,对于如何在Bison中做到这一点,我有点困惑:基本上,我如何告诉它重新解析一个特定的字符串(在构造主AST时)


有什么想法吗?

如果您真的需要,您可以通过生成。您可能还需要一个,尽管我想您可以使用flex的缓冲堆栈,将一些东西与默认扫描仪组合在一起。事实上,值得学习如何根据避免不必要的全局变量的一般原则构建可重入解析器和扫描器,无论您是否需要它们用于此特定目的

但是你不需要重新分析任何东西;您可以在一次过程中完成整个解析。你只需要在你的扫描器中有足够的智能,让它知道嵌套插值

基本思想是让扫描器通过插值将字符串文本分割成一系列标记,解析器可以轻松地将这些标记组装成适当的AST。由于扫描程序可能会从单个字符串文本中返回多个标记,因此我们需要引入一个标记来跟踪扫描当前是否在字符串文本中。由于插值可能是嵌套的,我们将使用flex的可选选项,通过
%选项堆栈启用
,以跟踪嵌套的上下文

这是一张草图

如上所述,扫描仪具有额外的启动条件:
SC_程序
,默认值,在扫描仪扫描常规程序文本时生效;以及
SC_字符串
,在扫描仪扫描字符串时生效<代码>SC_程序
之所以需要,是因为flex没有提供官方界面来检查启动条件堆栈是否为空;除了嵌套之外,它与
初始
顶级启动条件相同。起始条件堆栈用于跟踪插值标记(本例中为
[
]
),这是必需的,因为插值表达式可能使用括号(例如,作为数组下标)或甚至可能包含嵌套的插值字符串。由于
SC_程序
INITIAL
相同(只有一个例外),因此我们将其作为一个包含性规则

%option stack
%s SC_PROGRAM
%x SC_STRING
%%
因为我们使用一个单独的开始条件来分析字符串文本,所以我们还可以在解析时规范化转义序列。并非所有的应用程序都希望这样做,但这是很常见的。但由于这不是这个答案的真正意义,我省略了大部分细节。更有趣的是嵌入式插值表达式的处理方式,特别是嵌套较深的表达式

最终结果将是将字符串文本转换为一系列标记,可能表示嵌套结构。为了避免在扫描程序中实际解析,我们不尝试创建AST节点或重写字符串文本;相反,我们只是将引号字符本身传递给解析器,从而划分字符串文字片段的序列:

["]                 { yy_push_state(SC_STRING);    return '"'; }
<SC_STRING>["]      { yy_pop_state();              return '"'; }
向解析器返回这样的转义字符可能不是最好的策略;通常我会使用内部扫描缓冲区来累积整个字符串。但出于说明的目的,它很简单。(这里省略了一些错误处理;有各种各样的情况,包括换行符处理和程序中最后一个字符是未终止字符串文字中的反斜杠的恼人情况。)

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

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); }

我想我会建议您不要使用这种语言特性,而不是提供解决方案。文字就是文字:解析在其边界处停止。
<*>"["              { yy_push_state(SC_PROGRAM);   return '['; }
<INITIAL>"]"        {                              return ']'; }
<*>"]"              { yy_pop_state();              return ']'; } 
<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); }