【发布时间】:2016-01-01 16:25:28
【问题描述】:
我刚刚开始与 Bison 合作,但遇到了几个问题。我的语法目标是识别一种“命令管道”语言,其中一个命令的输出可以通过管道传输为另一个命令的输入。每个命令还可以有一个带有可选值的参数列表。这是一个例子:
command1 --param1 test --param2 test2 | command2 | command3 --param3
在前面的示例中,command1 采用两个参数,param1 的值为 test 和 param2 的值为 test2。然后command1 的输出被“传送”到command2,它不带任何参数。最后command2 的结果通过管道传递给command3,它接受一个参数(没有值的参数被认为是一个开关)。
对应的bison语法如下:
%{
#include <string>
#include <iostream>
#include "AST.h"
#include "Evaluator.h"
void yyerror (const char *error);
int yylex();
%}
%code{
Evaluator eval;
}
%union {
char* s;
double d;
AbstractNode* abstractnode;
CmdletCall* cmdletdef;
ParameterDef* parameterdef;
ParameterListDef* paramlistdef;
}
/* declare tokens */
%token EOL
%token<s> PARAMETERNAME
%token<s> BUILTIN
%token<s> IDENTIFIER
%type <parameterdef> parameterDef
%type <cmdletdef> cmdletCall
%type <abstractnode> pipeLineDef
%type <paramlistdef> paramList
%%
input: /* nothing */
| input pipeLineDef EOL {
$2->accept(eval);
delete $2;
std::cout << eval.result() << std::endl;
}
| input EOL {}
;
pipeLineDef: cmdletCall {$$ = $1;}
| pipeLineDef '|' cmdletCall {$$ = new PipeLineDef($1, $3);}
;
cmdletCall: IDENTIFIER {$$ = new CmdletCall($1);}
| cmdletCall paramList {$1->setParameterList($2);}
;
paramList: parameterDef {
$$ = new ParameterListDef;
$$->addChildNode($1);
}
| paramList parameterDef {
$1->addChildNode($2);
$$ = $1;
}
;
parameterDef: PARAMETERNAME {$$ = new ParameterDef($1);}
| parameterDef IDENTIFIER {
$1->setValue($2);
}
;
%%
void yyerror (const char *error)
{
std::cout << error << std::endl;
}
前面的语法有一个shift-reduce冲突,转载如下:
Terminals unused in grammar
BUILTIN
State 10 conflicts: 1 shift/reduce
State 10
7 cmdletCall: cmdletCall paramList .
9 paramList: paramList . parameterDef
PARAMETERNAME shift, and go to state 9
PARAMETERNAME [reduce using rule 7 (cmdletCall)]
$default reduce using rule 7 (cmdletCall)
parameterDef go to state 13
我想消除冲突,但我不确定我应该如何进行(我的理解是移位/减少冲突表明语法不明确)。但是,从我最初的测试来看,一切似乎都按预期工作。
令我困惑的另一点是 Bison 评估终端的方式。例如,如果我将cmdletCall 和paramList 规则改写如下,语法就会中断:
cmdletCall: IDENTIFIER paramList {$$ = new CmdletCall($1); $$->setParameterList($2);}
;
paramList: /*nothing*/
{
$$ = new ParameterListDef;
}
| paramList parameterDef {
$1->addChildNode($2);
$$ = $1;
}
;
如果我如上所示重写语法,那么对于这样的输入:
command1 --param1
在cmdletCall 规则中,$1 对应于IDENTIFIER 令牌的值将是command1 --param1,而不仅仅是command1。
【问题讨论】:
-
虽然 C(或可选的 C++)是 bison 生成代码的语言,但这个问题实际上与这两者无关。标签已编辑。
-
那是两个独立的问题,应该是两个独立的问题。第二个问题几乎可以肯定涉及您的词法分析器(在某种意义上,其中的 C/C++ 代码),尽管我建议您只搜索在您的词法分析器中复制 yytext 的需要;它在所有常见问题解答中。
-
保留第二个问题作为同一问题的一部分,因为它说明了在实践中使用 bison 和 flex 可能遇到的问题。既然你已经给出了这两个问题的答案,把它们放在一起是有意义的:)