我正在尝试在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
答案 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; }
依此类推,这是很难做到的(在正确的地方获得所有正确的类型)并且有些容易出错。
在这种特定情况下,你可以使用一个全局变量(因为没有涉及递归),但在更复杂的情况下这不能很好地工作。