5

我有兴趣在我的 flex 文件中添加分号插入 ala Google Go。

来自 Go 文档:

分号

与 C 一样,Go 的形式语法使用分号来终止语句;与 C 不同,这些分号不会出现在源代码中。相反,词法分析器使用一个简单的规则在扫描时自动插入分号,因此输入文本大部分没有分号。

规则是这样的。如果换行符之前的最后一个标记是标识符(包括 int 和 float64 之类的词)、基本文字(例如数字或字符串常量)或其中一个标记

break continue fallthrough return ++ -- ) }

词法分析器总是在标记后插入一个分号。这可以概括为“如果换行符出现在可以结束语句的标记之后,则插入分号”。

分号也可以在右大括号之前省略,所以像这样的语句

go func() { for { dst <- <-src } }()

不需要分号。惯用的 Go 程序仅在诸如 for 循环子句之类的地方使用分号来分隔初始值设定项、条件和延续元素。如果您以这种方式编写代码,它们还必须在一行中分隔多个语句。

一个警告。永远不要将控制结构(if、for、switch 或 select)的左大括号放在下一行。如果这样做,将在大括号前插入分号,这可能会导致不良影响。像这样写它们

if i < f() {
    g()
}

不像这样

if i < f()  // wrong! 
{           // wrong!
    g()     // wrong!
}           // wrong!

我将如何执行此操作(如何在流中插入令牌,如何查看匹配的最后一个令牌以查看它是否是一个好主意等)?

我也在使用野牛,但 Go 似乎只是使用他们的词法分析器来插入分号。

4

3 回答 3

8

您可以通过在必要时插入分号的函数传递词法分析器结果标记。在检测到需要插入时,可以将下一个标记放回输入流,基本上在下一回合再次对其进行词法分析。

下面是一个在换行符之前插入分号的示例,当它跟在 WORD 之后。野牛文件“insert.y”是这样的:

%{
#include <stdio.h>

void yyerror(const char *str) {
  printf("ERROR: %s\n", str);
}

int main() {
  yyparse();
  return 0;
}
%} 
%union {
  char *string;
}
%token <string> WORD
%token SEMICOLON NEWLINE
%%
input: 
     | input WORD          {printf("WORD: %s\n", $2); free($2);}
     | input SEMICOLON     {printf("SEMICOLON\n");}
     ;
%%

词法分析器是由 flex 生成的:

%{
#include <string.h>
#include "insert.tab.h"
int f(int token);
%}
%option noyywrap
%%
[ \t]          ;
[^ \t\n;]+     {yylval.string = strdup(yytext); return f(WORD);}
;              {return f(SEMICOLON);}
\n             {int token = f(NEWLINE); if (token != NEWLINE) return token;}
%%
int insert = 0;

int f(int token) {
  if (insert && token == NEWLINE) {
    unput('\n');
    insert = 0;
    return SEMICOLON;
  } else {
    insert = token == WORD;
    return token;
  }
}

用于输入

abc def
ghi
jkl;

它打印

WORD: abc
WORD: def
SEMICOLON
WORD: ghi
SEMICOLON
WORD: jkl
SEMICOLON

取消一个非常量标记需要一些额外的工作——我试图让这个例子保持简单,只是为了给出这个想法。

于 2012-06-04T21:47:28.050 回答
1

更改词法分析器规则\n}查看词法分析器返回的最后一个标记。这将要求您的词法分析器记录为每个规则返回的最后一个标记。

然后您的换行规则将如下所示:

\n   { if (newline_is_semi(last_token)) {
          return SEMICOLON;
       }
     }

newline_is_semi将检查 last_token 是否在您列出的令牌列表中。

处理右大括号前的可选分号:匹配 '}' 时检查 last_token 是否为 SEMICOLON,如果不是,则取消输入 '}' 并返回 SEMICOLON

'}'  { if (last_token != SEMICOLON) {
          unput('}');
          return SEMICOLON;
       }
     }
于 2012-06-05T15:10:17.370 回答
0

一种简单的方法是创建一个全局变量

%{
    ins_token = 0
%}

然后假设在“)”之后要插入分号,然后设置 ins_token = 1 并在其他令牌中重置 ins_token = 0

现在,在“)”之后出现“\n”,然后检查 ins_token == 1 是否返回分号,否则忽略它并始终重置 ins_token = 0。

ins_token 充当一个标志。当您希望插入分号时设置标志。在获取 \n 时,它将检查该标志,如果设置了它将插入分号。

这是因为 flex 不记得之前的标记。

[\n] { if (ins_token == 1) { ins_token = 0; return SEMICOLON; } }
")"  { ins_token = 1; }

...other tokens
...  { ins_token = 0; }
于 2021-03-26T11:15:11.093 回答