我怎样才能在Yacc / Bison中处理/创建/循环处理多个%类型的规则?

时间:2011-12-15 21:41:58

标签: c bison yacc

我正在尝试在Yacc / Bison中创建一个LALR(1)解析器,它可以接受具有灵活语法的命令。一个例子是通过调节室内温度(浮子),窗口位置(整数)和吊扇(枚举)来控制房屋内的环境。示例(所需)语法为:

set kitchen 22.5, den 0 24.0, bathroom fast 25, kitchen slow

我坚持如何处理规则中我称之为“room_arg”的堆叠数据。我可以在room_arg arg语句中添加%union类型,设置%type <arg> room_arg并制定“room_arg”规则:

room_arg:  TOK_CIELING_FAN { $$.fan = $1; }
         | TOK_WINDOW      { $$.window_position = $1; }
         | TOK_THERMOSTAT  { $$.temperature = $1; }

但是当谈到将“room_arg”规则缩减为“room_args”时 - 我必须弄清楚哪些堆栈项目是哪个。除此之外,特定房间的“room_cmd”(示例中的“厨房”)不需要在命令中进行分组会增加我的挑战。

更改命令的格式很可爱,但是,唉不可能。

有人可以建议实施策略,或者可能改进解析器逻辑以使事情更简单吗?

批评的语法如下。

%{
typedef enum fan_setting_tag { OFF, SLOW, MEDIUM, FAST } fan_setting;

struct room_arg_tag {
   int          window_position;
   fan_setting  fan;
   float    temperature;
   char *   room_name;
} room_arg;
%}
%token TOK_ROOM
%token TOK_CIELING_FAN
%token TOK_WINDOW
%token TOK_THERMOSTAT
%token TOK_SET

%type <fan>     TOK_CIELING_FAN
%type <window>  TOK_WINDOW
%type <tempr>   TOK_THERMOSTAT
%type <str>     TOK_ROOM

%union {
    int         window;
    float       tempr;
    fan_setting fan;
}

%%

command: /* empty */ | cmd command

cmd: TOK_SET room_cmds { execute_command( ... ); }

room_cmds: room_cmd
         | room_cmd ',' room_cmds

room_cmd:  TOK_ROOM room_args

room_args: room_arg
         | room_arg room_args

room_arg:  TOK_CIELING_FAN
         | TOK_WINDOW
         | TOK_THERMOSTAT

1 个答案:

答案 0 :(得分:1)

这是您真正想要继承属性的情况。你可以用额外的动作来模拟它们,但不幸的是yacc和bison没有给你一种更直接的方法(btyacc提供了合成糖来使这更容易。)

基本上,您所做的是使用嵌入式操作来设置继承的属性,并使用$0在较低级别的规则中访问它们。

使用btyacc你会这样做:

%union {
    int         window;
    float       tempr;
    fan_setting fan;
    room_arg    *room;
}

%token<fan>     TOK_CIELING_FAN
%token<window>  TOK_WINDOW
%token<tempr>   TOK_THERMOSTAT
%token<str>     TOK_ROOM

%type<room> room_cmds(<room>)
%type<>     room_cmd(<room>), room_args(<room>), room_arg(<room>)

%%

command: /* empty */ | cmd command ;

cmd: TOK_SET room_cmds(new_room_arg()) { execute_command( ... ); } ;

room_cmds($room):
           room_cmd($room) { $$ = $room; }
         | room_cmd($room) ',' room_cmds($room) { $$ = $room; }
;

room_cmd($room):  TOK_ROOM room_args($room) { $room->room_name = $1; } ;

room_args($room): 
           room_arg($room)
         | room_arg($room) room_args($room)
;

room_arg($room):
           TOK_CIELING_FAN { $room->fan = $1; }
         | TOK_WINDOW      { $room->window_position = $1; }
         | TOK_THERMOSTAT  { $room->temperature = $1; }
;

在yacc或bison中,这就像是:

cmd: TOK_SET { $$ = new_room_arg(); } room_cmds { execute_command(...)' } '
room_cmds:
      room_cmd { $$ = $<room>0; }
    | room_cmd ',' { $$ = $<room>0; } room_cmds { $$ = $<room>0; }
;
room_cmd: TOK_ROOM { $$ = $<room>0; } room_args { $<room>0->room_name = $1; }

依此类推,这是很难做到的(在正确的地方获得所有正确的类型)并且有些容易出错。

在这种特定情况下,你可以使用一个全局变量(因为没有涉及递归),但在更复杂的情况下这不能很好地工作。