野牛的相互递归

时间:2012-11-11 16:52:19

标签: bison yacc

我正在尝试使用flex / bison为类似verilog的语言创建一个解析器(或者更具体地说是使用一些额外的构造的verilog,用于生成verilog代码)并且我无法弄清楚如何构建我的野牛用于处理语言中可用的一般类型构造的规则。

这种结构的一般例子是:

(
output wire          up_o,
input  wire [4:0]    up_i,
///////////////////////////////////////////////////////////
// A comment
///////////////////////////////////////////////////////////
`my_if (`$a eq `$b)
  input  wire        a_{n}_clk_i,
  output wire        a_{n}_rst_o,
`my_endif
output wire                                           out_o
);

原则上my_if可以嵌套,( );内的所有内容都可以在my_if块内,而不是my_if。这让我相信我目前的想法是有缺陷的,而我根本不明白如何进行这种相互递归。

似乎导致我出现问题的是,,它终止了除了最后一行之外的每一行。我的词法分析器忽略了注释并将行标记为罚款,我还特意解析了my_if行。

以下是我现在的相关野牛规则,

portdecla:
'(' ports ')' ';'
;

ports:
port
| ports ',' port
| ports ',' ifs
| ifs
;

ifs:
ifhead ports TENDIF
| if
;

ifheadport解析my_if和输入/输出行罚款。

非常感谢有关如何创建这些规则的任何想法

1 个答案:

答案 0 :(得分:3)

我在理解你的问题时遇到了一些麻烦。如果你描述了这个问题,那将会容易得多,而不仅仅是模糊地断言它可能与逗号有关。当然,它确实与逗号有关:你的语法坚持在ifs之后和port之后都有逗号。 (另外,要么你没有正确地复制和粘贴,要么ifs有些奇怪。)

实际上,您不需要ifs,但您需要区分声明末尾的if和任何其他if

port_declaration: tail_ports;

ports:  port ','
     |  port ',' ports
     |  if
     |  if ports
     ;
tail_ports
     :  port
     |  tail_if
     |  port ',' tail_ports
     |  if tail_ports
     ;
if   :  IF '(' condition ')' ports END_IF
     ;
tail_if
     :  IF '(' condition ')' tail_ports END_IF
     ;

(我正确地写了这个,因为它更容易理解,恕我直言,并且从右递归列表中构建AST也更容易。参见Levine,第66页。在现代机器上,确实没有问题解析堆栈增长;默认的最大堆栈深度为10,000;如果这还不够,可以增加它。)

编辑:我认为我终于有了上述权利。

这种问题通常通过两次解析来解决;首先是一个预处理过程,其输出被送入真正的解析器。令牌化只进行一次;预处理解析器不区分语言标记;它只关心它自己的令牌。 (C预处理器可以使用##运算符融合令牌,但它永远不会分割令牌;这通常证明是一种合理的方法。)这可以用bison实现,尽管获得正确的数据流有点像挑战;我个人更喜欢lexer将令牌推送到解析器的lemon方法,因为这样更容易扩展。

但是,在您的特定情况下,您希望预处理器只接受完整的短语(port),因此,正如您所说,挑战是让,正确,即使是最后一个port {1}}被埋在if构造的深处。因此,上述方法区分iftail_if

构建AST有几种方法,具体取决于您是否可以在分析时评估条件。如果可以的话,那么你可以完全放弃不需要的端口,这可能是最简单的解决方案。

如果您需要在应用条件之前完成整个解析,那么您仍然有几个选项:显而易见的是构建一个节点为portconditional_list的树,或者代码生成样式,您可以在其中有效地构建操作向量build-portjump-if-condition-false。这两个选项都需要某种形式的标记联合。

/编辑(以下内容仍然不正确。)

typedef struct PortList {
  Port* port;
  struct PortList* next;
} PortList;

typedef struct ConditionalPortList {
  Condition* cond;  /* Optional condition; if absent, unconditional */
  PortList*  ports;
} ConditionalPortList;

typedef struct PortDeclaration {
  ConditionalPortList* clause;
  struct PortDeclaration *next;
} PortDeclaration;

这很容易构建(它可能比这个片段更容易,我有点不习惯。)(警告:未经验证):

%union {
   PortList*            port_list;
   ConditionalPortList* conditional;
   PortDeclaration*     port_declaration;
   /* ... */
}
%type <port_list> ports
%type <conditional> if always
%type <port_declaration> port_tail port_declaration
/* ... */

%%

ports:  port             { $$ = NewPortList($1, NULL); }
     |  port ',' ports   { $$ = NewPortList($1, $3); }
     ;

if: IF '(' condition ')' ports ENDIF
                         { $$ = NewConditionalPortList($3, $5); }
  ;

always: ports            { $$ = NewConditionalPortList(NULL, $1); }
      ;

port_tail: if
         | if port_tail  { $$ = NewPortDeclaration($1, $2); }
         | if always port_tail
                         { $$ = NewPortDeclaration($1, NewPortDeclaration($2, $3); }
         ;

port_declaration:
           '(' always ')' ';'    { $$ = $2; }
         | '(' port_tail ')' ';' { $$ = $2; }
         | '(' always port_tail ')' ';'
                                 { $$ = NewPortDeclaration($2, $3); }
         ;