该语言的一个基本语法是LALR(2),而bison不会生成LALR的解析器。
任何LALR(2)语法都可以进行机械修改,以生成具有兼容解析树的LALR 1语法,但我不知道有什么自动工具可以做到这一点。
手动进行转换是可能的,但很烦人,但请注意,您需要调整操作以恢复正确的解析树:
%{
typedef struct IdList { char* id; struct IdList* next; };
typedef struct Def { char* lhs; IdList* rhs; };
typedef struct DefList { Def* def; struct DefList* next; };
%}
union {
Def* def;
DefList* defs;
char* id;
}
%type <def> ophead
%type <defs> opdefs
%token <id> ID
%%
prog : opdefs { $1->def->rhs = IdList_reverse($1->def->rhs);
DefList_show(DefList_reverse($1)); }
ophead: ID '@' ID { $$ = Def_new($1);
$$->rhs = IdList_push($$->rhs, $3); }
opdefs: ophead { $$ = DefList_push(NULL, $1); }
| opdefs ID { $1->def->rhs = IdList_push($1->def->rhs, $2); }
| opdefs ophead { $1->def->rhs = IdList_reverse($1->def->rhs);
$$ = DefList_push($1, $2); }
讽刺的是,这个精确的问题是
bison
因为制作不需要
;
终止器。Bison使用自己来生成解析器,它在lexer中解决了这个问题,而不是像上面概述的那样跳过循环。在lexer中,一旦
ID
则扫描继续到下一个非空白字符。如果这是
:
,则lexer返回
identifier-definition
代币否则,非空白字符将返回到输入流
identifier
返回令牌。
这里有一种在lexer中实现的方法:
%x SEEK_AT
%%
/* See below for explanation, if needed */
static int deferred_eof = 0;
if (deferred_eof) { deferred_eof = 0; return 0; }
[[:alpha:]][[:alnum:]_]* yylval = strdup(yytext); BEGIN(SEEK_AT);
[[:space:]]+ ; /* ignore whitespace */
/* Could be other rules here */
. return *yytext; /* Let the parser handle errors */
<SEEK_AT>{
[[:space:]]+ ; /* ignore whitespace */
"@" BEGIN(INITIAL); return ID_AT;
. BEGIN(INITIAL); yyless(0); return ID;
<EOF> BEGIN(INITIAL); deferred_eof = 1; return ID;
}
在
SEEK_AT
启动条件,我们只感兴趣
@
。如果我们找到一个,那么ID就是
def
,我们返回正确的令牌类型。如果我们找到任何其他内容(除了空白),我们将使用
yyless
,并返回
身份证件
令牌类型。请注意
yylval
已从的初始扫描设置
身份证件
,所以这里不必担心。
上述代码中唯一复杂的部分是
EOF
处理。一旦
地球观测卫星
已检测到,无法将其重新插入输入流
无年
也没有
unputc
。让扫描仪读取
地球观测卫星
再一次因此,它需要得到充分处理。不幸的是,在
查找(_A)
启动条件,充分处理
地球观测卫星
需要发送两个令牌:首先是已检测到的
身份证件
标记,然后是0
yyparse
将识别为输入结束。如果没有push解析器,我们无法从一个扫描程序操作发送两个令牌,因此我们需要注册接收到
地球观测卫星
,并在下一次呼叫扫描仪时进行检查。
在第一条规则插入顶部之前缩进的代码
yylex
函数,因此它可以声明局部变量,并在扫描开始之前执行任何需要执行的操作。如上所述,这个lexer不是可重入的,但它是可重启的,因为持久状态在
if (deferred_eof)
行动要使其重新进入市场,您只需要将
deferred_eof
在
yystate
结构,而不是使其成为静态局部。