分号插入ala谷歌与flex一起去

时间:2012-05-31 02:32:48

标签: parsing go lex flex-lexer lexer

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

来自Go文档:

  

分号

     

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

     

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

break continue fallthrough return ++ -- ) }
     

词法分析器总是在令牌后插入分号。这可能是   总结为“如果新行出现在可能结束的令牌之后   声明,插入分号“。

     

在结束括号之前也可以省略分号,所以   

之类的陈述
go func() { for { dst <- <-src } }()
     

不需要分号。 Idiomatic Go程序仅在分号中有分号   诸如for循环子句之类的地方,用于分隔初始化程序,   条件和延续元素。他们也是必要的   如果您编写代码,则在一行上分隔多个语句   方式。

     

一个警告。你永远不应该把控件的开头括号   下一行的结构(if,for,switch或select)。如果你这样做,   分号将在大括号之前插入,这可能会导致   有害的影响。像这样写下

if i < f() {
    g()
}
     

不喜欢这个

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

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

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

3 个答案:

答案 0 :(得分:5)

您可以通过在必要时插入分号的函数传递lexer结果标记。一旦检测到需要插入,下一个令牌就可以放回到输入流中,基本上在下一轮回复它。

下面是一个在换行符之前插入SEMICOLON的示例,当它跟随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");}
     ;
%%

并且lexer是通过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

输出非常量令牌需要一些额外的工作 - 我试图保持这个例子简单,只是为了给出这个想法。

答案 1 :(得分: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;
       }
     }

答案 2 :(得分:0)

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

Unauthorized

然后假设在 ")" 之后你想插入一个 SEMICOLON 然后你设置 ins_token = 1 并在其他令牌中你重置 ins_token = 0

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

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

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

%{
    ins_token = 0
%}