【问题标题】:How to access in BISON, a token rejected using REJECT in FLEX?如何在 BISON 中访问,在 FLEX 中使用 REJECT 拒绝的令牌?
【发布时间】:2019-12-18 07:47:03
【问题描述】:

我知道可以拒绝 lex 中的令牌,如下所示:

    .*    {
             //Reject the entire line but get its value
             yylval->strval = strdup(yytext);
             REJECT;
          };

我这样做是为了获取整行的内容,以便稍后报告错误。所以我想保留这一行并在野牛代码中访问它。拒绝整行后,该行中的标记与其他 lex 规则匹配,并期望被解析并匹配 .y 中的解析规则。 (还是我错了?)

保留被拒绝行的一种方法是将其分配给一个全局变量,这当然不优雅,并且由于其他原因不正确(这是一个可重入的解析器/词法分析器)。

有什么建议吗?

【问题讨论】:

    标签: bison flex-lexer yacc


    【解决方案1】:

    首先,不要为此使用REJECTREJECT 是一个可怕的 hack,它会极大地降低您的词法分析器,并且在解析器中几乎没有实际用例。

    另外,它不会像你写的那样工作。在您拒绝与.* 的匹配后,返回的下一个匹配将是下一个较短的.* 匹配——即除最后一个字符之外的所有字符——而不是另一个模式的匹配(除非除最后一个字符之外的所有字符在该行中恰好匹配另一个具有更高优先级的模式)。

    如果您想捕获并临时存储读取的每一行,最简单的方法是提供您自己的YY_INPUT,它一次读取一行输入。 (如果您的 C 库支持,我建议使用 Posix 2008 getline() 接口。)只需保存这一行(及其长度)即可。

    现在,对于您的实际问题:在可重入词法分析器中将内容保存在何处,以便解析器可以看到它们。

    幸运的是,flex 为a way for you to augment the yyscan_t context object 提供了您自己的任意“额外”数据yyextra。 (在扫描仪代码本身之外,您需要使用访问器yyget_extra(scanner) 来获取此值。)默认情况下,yyextra 的类型为void *,但您可以使用%option extra-type 覆盖它。 yyextra 通常是指向您自己的上下文对象的指针,尽管如果您不需要太多上下文,也可以将 yyextra 设为一个小结构。 (yyget_extrayyset_extra 按值传递 yyextra,这不适用于许多上下文对象。)

    这里有一个(有点)简单的例子。下面的大部分样板都取自this answer 中的骨架;请参阅该链接以了解大多数奇怪之处。

    文件示例.l

    %option noyywrap 8bit noinput nounput nodefault
    %option reentrant bison-bridge bison-locations yylineno
    %option extra-type="struct ScanExtra*"
    %{
      #include "sample.tab.h"
      #define YY_INPUT(buf,result,max_size) do {                       \
        yyextra->linebuf_len = getline(&yyextra->linebuf,              \
                                       &yyextra->linebuf_size, yyin);  \
        if (yyextra->linebuf_len == -1) result = YY_NULL;              \
        else if (yyextra->linebuf_len <= max_size) {                   \
          memcpy(buf, yyextra->linebuf, yyextra->linebuf_len);         \
          result = yyextra->linebuf_len;                               \
        }                                                              \
        else { /* Handle long lines */ }                               \
      } while(0)
    %}
    
    %%
    [[:space:]]      ; /* Ignore whitespace */
    #.*              ; /* Ignore comments */
    [[:alnum:]]+     { yylval->str = strdup(yytext); return WORD; }
    .                { return *yytext; }
    %%
    /* These functions are here for simplicity. Normally, I'd put them in a
     * separate parse_utils.c file.
     * They're declared in sample.h (except for yyerror).
     */
    
    /* Creates a scanner with an initialised ScanExtra */
    yyscan_t myscanner_create(void) {
      struct ScanExtra* extra = calloc(1, sizeof *extra);
      yyscan_t scanner;
      yylex_init_extra(extra, &scanner);
      return scanner;
    }
    
    /* Destroys a scanner */
    void myscanner_destroy(yyscan_t scanner) {
      free(yyget_extra(scanner));
      yylex_destroy(scanner);
    }
    
    #include <ctype.h>
    void yyerror(YYLTYPE* yyllocp, yyscan_t scanner, const char* msg) {
      /* Get rid of trailing whitespace in the current line */
      const char* buf = yyget_extra(scanner)->linebuf;
      int len = yyget_extra(scanner)->linebuf_len;
      while (len && isspace(buf[len - 1])) --len; 
      fprintf(stderr, "Syntax error near '%s' in line %d: '%.*s'\n",
              yyget_text(scanner),
              yyget_lineno(scanner),
              len, buf);
    }
    

    文件 sample.y

    %define api.pure full
    %locations
    %param { yyscan_t scanner }
    
    %code top {
      #include <stdio.h>
    } 
    %code requires {
      typedef void* yyscan_t;
    
      /* I define ScanExtra here so that it goes into the generated header file */
      struct ScanExtra {
        char*   linebuf;
        size_t  linebuf_size;
        ssize_t linebuf_len;
      };
    }
    %code {
      int yylex(YYSTYPE* yylvalp, YYLTYPE* yyllocp, yyscan_t scanner);
      void yyerror(YYLTYPE* yyllocp, yyscan_t scanner, const char* msg);
    }
    
    %union {
      char* str;
    }
    %token <str> WORD
    %%
    item: WORD { free($1); }
        | '(' opt_item_list ')'
    item_list: item | item_list ',' item
    opt_item_list: %empty | item_list
    

    文件样本.h

    #include "sample.tab.h"
    #include "sample.lex.h"
    yyscan_t myscanner_create(void);
    void myscanner_destroy(yyscan_t);
    

    文件:生成文件

    all: sample
    
    sample.lex.c: sample.l
            flex -o $@ --header-file=$(patsubst %.c,%.h,$@) --debug $<
    
    sample.tab.c: sample.y
            bison -o $@ --defines=$(patsubst %.c,%.h,$@) --debug $<
    
    sample: main.c sample.tab.c sample.lex.c sample.h
            $(CC) -o $@ -Wall --std=c11 -ggdb -D_XOPEN_SOURCE=700 $(filter %.c,$^)
    
    clean:
            rm -f sample.tab.c sample.lex.c sample.tab.h sample.lex.h sample
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-13
      • 2011-12-08
      • 1970-01-01
      • 1970-01-01
      • 2012-10-30
      • 1970-01-01
      相关资源
      最近更新 更多