我正在尝试为格式化语言编写一个编译器。这种语言有一个start和end属性以及一组文档和文本属性。 第一个是文档本身的信息,第二个是实际文档(标题,段落,列表......通常)。第一个集必须始终遵循start属性,并且必须包含用户可能喜欢的任何顺序的所有属性。
假设我的标记为PROP1,PROP2,PROP3和PROP4,我可以对所有属性使用递归和OR,以便用户可以定义他想要的任何文档属性。
doc_properties
: /* empty */
: doc_properties property
;
property
: PROP1
: PROP2
: PROP3
: PROP4
;
但我如何让他定义所有这些并且只有一次。我认为的一种方式(我想避免的简单粗暴的方式)是因为我只有4个文档属性,我可以只做一个或所有可能的组合。我很确定还有另一种方式。有什么帮助吗?
到目前为止我的语法非常简单而且很小
%{ /* C Stuff */ %}
/* union and error stuff and tokens */
%%
source
: /* empty */
| entry_point doc_properties txt_properties exit_point
;
entry_point
: SLASH BLOCK_S LBRACE DOC RBRACE
;
doc_properties
: /* This is where my question goes */
;
txt_properties
: /* empty */
;
exit_point
: SLASH BLOCK_E LBRACE DOC RBRACE
;
%%
int main (int argc, char* argv[])
{
/* various checks for the arguments and the input output files */
yyin = fopen(argv[1], "r");
yyout = fopen(fn, "w");
//do{
yyparse();
//}while(!feof(yyin));
fclose(yyin);
fclose(yyout);
return 0;
}
void yyerror(const char* str) {
fprintf(stderr,"syntax error[%d]: %s\n",yylineno, str);
}
同样在一个不相关的注释中,在do-while循环中使用yyparse()或者只使用一次它有什么区别?因为我看到了这两种方式,而do-while循环对我来说更有意义(因为它请求令牌再解析)我不确定函数是重复自身还是其他...
答案 0 :(得分:3)
有许多语法规则最好通过语义检查而不是语法本身来强制执行。例如,在类似C语言中,break
构造只能出现在循环(和开关)中,但是接受它就像任何常规语句一样简单,而稍后,在语义分析通过,拒绝对break
的无效使用。
您可以使用类似的模式:接受PROP
的任意组合,然后拒绝那些不符合您的约束的组合。当然,您也可以在解析时执行此操作,在适当的时候使用YYERROR
引发错误。
对于你的第二个问题,yyparse
只能被调用一次,但当然它负责反复调用扫描器(yylex
)。请注意,Bison提供“推送解析器”,其中你负责反复调用yylex
,并将其结果传递给yyparse
(反复也是如此)。有关详细信息,请参阅http://www.gnu.org/software/bison/manual/bison.html#Push-Decl。