当我在以下文件上运行parser.y时,我收到以下错误:
myanalyzer.y: warning: 14 nonterminals useless in grammar
myanalyzer.y: warning: 36 rules useless in grammar
myanalyzer.y:7.8-18: fatal error: start symbol main_struct does not derive any sentence
我无法理解代码有什么问题:
%{
#include <stdio.h>
#include <string.h>
extern int line_num;
extern char *yytext;
%}
%start main_struct
%left PLUS
%token PUBLIC 1
...
%token CONSTANT_CHAR 45
%%
main_struct:variables_declaration
class_declaration
functions_declaration
;
constant: INT
|BOOLEAN
|CHAR
|STRING
|FLOAT
|DOUBLE
;
locality: PUBLIC
| PRIVATE
;
identifier1: IDENTIFIER
| identifier1 COMMA IDENTIFIER
;
class_body: /*empty*/
| locality variables_declaration locality functions_declaration identifier1
| locality variables_declaration identifier1
| locality functions_declaration identifier1
| identifier1
;
variables_declaration: /*empty*/
| variables_declaration constant identifier1 SEMICOLON
| variables_declaration constant identifier1 COMMA
;
class_declaration: /*empty*/
| class_declaration CLASS identifier1 BEGIN class_body END
;
functions_declaration: functions_declaration constant identifier1 LEFT_PARENTHESIS vars_in_func RIGHT_PARENTHESIS BEGIN function_body END
| functions_declaration VOID identifier1 LEFT_PARENTHESIS vars_in_func RIGHT_PARENTHESIS BEGIN function_body END
;
vars_in_func: /*empty*/
| constant identifier1
| vars_in_func COMMA constant identifier1
;
function_body: /*empty*/
| variables_declaration identifier1
| identifier1
;
%%
int main ()
{
if ( yyparse() == 0 && error==0){
printf("Accepted\n");
}
else{
printf("Rejected\n");
}
}
我现在第一次和Bison一起工作,所以代码可能不太好但是我想编译它以便我可以开始测试。但是我该如何解决这个错误?
注意:我可以这样做:
main_struct:variables_declaration
| class_declaration
| functions_declaration
;
但这是错误的,因为我希望我的程序能够拥有所有3个声明。
答案 0 :(得分:7)
functions_declaration
没有非递归制作,不像variables_declaration
和class_declaration
都有空制作。如果没有非递归生成,则无法从非终端派生句子,因为派生永远不会终止。
由于句子不能与functions_declaration
匹配,main_struct
也无法匹配,这反过来会使所有非终结者无效。
写入重复非终端有两种常见模式,分别对应于正则表达式运算符*
(0或更多)和+
(1或更多):
optional_repeating_element: /* EMPTY */
| optional_repeating_element element
;
repeating_element : element
| repeating_element element
;
请注意,这些仅在基本情况下有所不同。在这两种情况下,您都需要单独为单个element
定义生产。所以在你的情况下,你可以使用:
functions_declaration: function
| functions_declaration function
;
function: constant identifier1 LEFT_PARENTHESIS vars_in_func RIGHT_PARENTHESIS
BEGIN function_body END
| VOID identifier1 LEFT_PARENTHESIS vars_in_func RIGHT_PARENTHESIS
BEGIN function_body END
;
三个注释:
1)不要手动为您的代币编号。野牛会为你做,结果更加可维护。此外,对于单个字符的终端,使用单引号字符通常更具可读性;这些终端不需要声明,你可以使用一个简单的默认flex规则来处理所有这些终端(. {return *(unsigned char*)yytext;}
。)因此,无论是写还是读都都可以更简单。
2)你对vars_in_func
的定义会接受你可能不想接受的一些句子。你拥有的是:
vars_in_func: /*empty*/
| constant identifier1
| vars_in_func COMMA constant identifier1
;
由于vars_in_func
可以为空(第一次制作),第三次制作允许vars_in_func
以COMMA
开头,因此以下内容有效:
(, int foo)
要正确执行此操作,您需要将空参数列表案例与递归分开。
3)你使用identifier1
(这是一个令人困惑的标识符列表名称)会给你带来麻烦。您允许函数声明声明一个名称列表,但这与声明名称列表的变量声明中的有限前瞻无法区分。考虑两句话:
int variable1, variable2, variable3;
int function1, function2, function3() BEGIN END
当解析器找到第一个,
时,它无法知道后面是整数变量列表,还是具有相同参数和主体的函数列表。我猜你并不是真的打算让第二种情况成为可能,这意味着你不应该在identifier1
(或functions_declaration
)中使用class_declaration
。即使在您的参数列表非终端(vars_in_func
)中,您也有一些奇怪的东西,尽管可以解析; vars_in_func
是逗号分隔的类型列表,后跟逗号分隔的名称列表,因此以下内容是合法的:
void function(int a, b, int c, d)
也许你打算这样做,但我不相信。