在 yacc 中处理块

Dealing with blocks in yacc

我正在使用 lex + yacc 为以下简单语言构建解析器:

lines are parsed ok

foo {
   lines of the "foo" category come here
} # closing this block

我的语法中块的以下定义有效:

item : block
     | lines; /* lines without a block */

block: WORD BRACE_OPEN NL lines BRACE_CLOSE
     { printf("category: %s\n",  );}

问题是 printf 发生在 块被解析之后,但我需要获取类别名称(示例中的 "foo" ) 作为解析块内行的信息。

我想出了一个解决方案,但它似乎不是很优雅:

item : line
     | block_open
     | block_close;

block_open : WORD BRACE_OPEN { printf("%s {\n", ); };

block_close : BRACE_CLOSE { printf("}\n"); };

我现在可以在打开块时获取 "category" 名称。但这是正确的还是最好的方法?

谢谢!

The problem is that the printf happens after the block has been parsed

这是因为 yacc 是一个自下而上的解析器。 AST 的叶子在中间节点之前构建。

如果您希望块的类型影响其中的解析,自上而下的方法(如递归下降解析器)可能更自然。

but I need to get the category name ("foo" in the example) as an information to parse lines within the block.

我认为最直接的方法是为每种块使用不同的语法规则,而不是使用通用的 "block" 规则。例如:

foo_block: FOO BRACE_OPEN foo_lines BRACE_CLOSE;

bar_block: BAR BRACE_OPEN bar_lines BRACE_CLOSE;

baz_block: BAZ BRACE_OPEN baz_lines BRACE_CLOSE;

这假设 "foo" 和 "bar" 和 "baz" 是词法分析器知道的关键字,而不仅仅是通用的 WORD。

block: WORD BRACE_OPEN NL lines BRACE_CLOSE
    { printf("category: %s\n",  );}

您可以在任何地方放置一个动作(尽管它可能会导致 s/r 冲突):

block
    : WORD
        { printf("category: %s\n",  );}
      BRACE_OPEN NL lines BRACE_CLOSE
    ;

相当于

block
    : category BRACE_OPEN NL lines BRACE_CLOSE
    ;
category
    : WORD
        { printf("category: %s\n",  );}
    ;

你可能更喜欢后者。