【问题标题】:How can I cope/create/circumenvent a rule in Yacc/Bison that handles multiple %types?如何应对/创建/规避 Yacc/Bison 中处理多个 %types 的规则?
【发布时间】:2011-12-15 21:41:58
【问题描述】:

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

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

我被困在如何处理我在规则中称为“room_arg”的堆积数据。我可以在%union 语句中添加room_arg arg 类型,设置%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

【问题讨论】:

    标签: c bison yacc


    【解决方案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; }
    

    等等,这很难做到正确(在正确的地方获得所有正确的类型)并且有点容易出错。

    在这种特定情况下,您可以只使用全局变量(因为不涉及递归),但这在更复杂的情况下效果不佳。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多