【问题标题】:Precedence issue with BisonBison 的优先级问题
【发布时间】:2020-07-28 15:36:44
【问题描述】:

好的,所以我正在尝试使用一个解析器来尝试一种...实验性语言。

主要思想是这样的:

this is a command
this is [another] command

应该输出这个:

   push: command
   push: a
   push: is
   push: this
   push: command
   <start:inline>
   push: another
   <end:inline>
   push: is
   push: this

这是我的词法分析器:

%option noyywrap

%{
#include <stdio.h>

#define YY_DECL int yylex()

#include "parser3.tab.h"

%}

%%

":"                     { return COLON; }
,(\n+)?                 { return COMMA; }

\[                      { return INLINE_START; }
\]                      { return INLINE_END; }
\{                      { return DEFERRED_START; }
\}?                     { return DEFERRED_END; }
\n|\|                   { return NL; }
[ \t]+                  { }
[a-zA-Z0-9!@#$%^&\*\-\+\/\\\"\'\(\)]+   { yylval.str=strdup(yytext); return ID; }

%%

这是我的解析器:

%{

#include <stdio.h>
#include <stdlib.h>

extern int yylex();
extern int yyparse();
extern FILE* yyin;

void yyerror(const char* s);
%}

%union {
    char* str;
}

%token NL 
%token <str> ID
%token COLON
%token BAR
%token COMMA
%token <str> INLINE_START INLINE_END
%token <str> DEFERRED_START DEFERRED_END

%type <str> argument
%type <str> block_start block_end

/****************************************
 Options
 ****************************************/

%glr-parser
%define parse.error verbose
%locations

%start program

%%

block_start : INLINE_START                  { printf("<start:inline>\n"); }
            | DEFERRED_START                { printf("found START\n"); }
            ;

block_end : INLINE_END                      { printf("<end:inline>\n"); }
          | DEFERRED_END                    { printf("found END\n"); }
          ;

block : block_start statement_list block_end { /*printf("found block\n");*/ }
      ;

argument : ID                               
         | block                            { $$ = ""; }
         | COMMA                            { $$ = ""; }                    
         ;

command : argument                          { if (strcmp($argument,"")) printf("  push: \033[1;36m%s\033[0m\n", $argument); }
        | argument command                  { if (strcmp($argument,"")) printf("  push: \033[1;36m%s\033[0m\n", $argument); }
        ;

label : ID COLON command                    { printf("> store: \033[1;36m%s\033[0m\n", $ID); }
      ;

statement_separator : NL
                    ;

statement : command
          | label
          ;

statement_list : statement
               | statement_list statement_separator statement
               | statement_list statement_separator
               ;

program : statement_list
        ;

%%

int main(int argc, char** argv) {
    yyin = fopen(argv[1],"r");

    printf("parsing: %s\n",argv[1]);

    do {
        yyparse();
    } while(!feof(yyin));

    return 0;
}

void yyerror(const char* s) {
    fprintf(stderr, "Parse error: %s\n", s);
    exit(1);
}

目前的输出是:

parsing: test6.art
  push: command
  push: a
  push: is
  push: this
<start:inline>
  push: another
<end:inline>
  push: command
  push: is
  push: this

有什么想法可以强制它在最后一个命令中首先看到“command”和“is”标记(按此特定顺序),然​​后再看到块?

【问题讨论】:

  • 因为你包含了你的词法分析器,所以有几个快速说明:(1),(\n+)?.\n* 完全相同(或者,就此而言,.\n+?,但这可能会使那些认为非贪婪匹配是可能的。)(2)\}? 中的? 毫无意义,因为 (f)lex 永远不会匹配零长度标记。 (反斜杠也是不必要的,因为} 不是元字符。它仅用于终止以{ 开头的内容,无论是宏还是重复。但它不会造成任何伤害,并且可以认为它更具可读性。)我正在努力回答您的实际问题。
  • 请不要再重新创建burninanted flex tag。 flex 词法分析器有一个专用标签,您可以使用它来代替。

标签: c parsing bison flex-lexer yacc


【解决方案1】:

bison 中没有任何东西,即使是 GLR 解析器,也不能让操作无序执行。产生式的动作是在识别出该产生式时准确执行的,不早也不晚。

众所周知,您可以使用右递归列表来反转列表操作:

list_of_things: thing                { show(thing); }
              | thing list_of_things { show(thing); }

但请注意,尽管该动作似乎是关于打印thing,但该动作是针对list_of_things 的动作,并且things 以相反顺序打印的原因是它们被添加到列表以相反的顺序排列,而不是它们以相反的顺序被识别。换句话说,如果您在减少操作中为thing 本身打印thing,您会看到things 以原始顺序打印,尽管它们仍会以相反的顺序添加到列表中(因为添加到列表在列表操作中)。

因此,在您的语法中,command 的参数由 command 操作打印,除非它们是块;如果它们是块,它们会打印在块动作中。这会导致打印输出被打乱:首先打印所有块,按从左到右的顺序,因为这些缩减发生在第一个 command 缩减之前;当command 产生式的累积堆栈最终开始减少时,其他参数将按从右到左的顺序打印。

我认为这里没有简单的“更改它,它会神奇地起作用”的解决方案。就个人而言,我会构建一个参数链接列表,包括块参数。实际上,这将是一种 AST,符合通常的建议,即在解析期间构建 AST 几乎总是正确的做法,尽管一开始似乎走捷径很有吸引力。

还有另一种方法是在块动作中构建要打印的字符串,以便可以将块的语义值设置为最终应该打印的字符串(而不是空字符串)。然后command 的操作可以打印参数。但在我看来,这比构建 AST 要笨重得多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-14
    • 2014-09-25
    • 1970-01-01
    • 1970-01-01
    • 2014-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多