当我在以下文件上运行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
Run Code Online (Sandbox Code Playgroud)
我无法理解代码有什么问题:
%{
#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");
}
}
Run Code Online (Sandbox Code Playgroud)
我现在第一次和Bison一起工作,所以代码可能不太好,但我想编译它,所以我可以开始测试了.但是我该如何解决这个错误呢?
注意:我可以这么简单:
main_struct:variables_declaration
| class_declaration
| functions_declaration
;
Run Code Online (Sandbox Code Playgroud)
但这是错误的,因为我希望我的程序能够拥有所有3个声明.
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
;
Run Code Online (Sandbox Code Playgroud)
请注意,这些仅在基本情况下有所不同.在这两种情况下,您都需要单独定义单个生产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
;
Run Code Online (Sandbox Code Playgroud)
三个音符:
1)不要手动为您的代币编号.Bison将为您做到,结果更加可维护.此外,对于单个字符的终端,使用单引号字符通常更具可读性; 这些终端不需要声明,你可以使用一个简单的默认flex规则来处理所有这些终端(. {return *(unsigned char*)yytext;}.)所以它更简单,无论是写还是读.
2)你的定义vars_in_func将接受你可能不想接受的一些句子.你有的是:
vars_in_func: /*empty*/
| constant identifier1
| vars_in_func COMMA constant identifier1
;
Run Code Online (Sandbox Code Playgroud)
由于vars_in_func可以为空(第一次生产),第三次生产允许vars_in_func以a开头COMMA,因此以下内容有效:
(, int foo)
Run Code Online (Sandbox Code Playgroud)
要正确执行此操作,您需要将空参数列表大小写与递归分开.
3)你使用identifier1(对于标识符列表而言这是一个令人困惑的名称)将导致你的问题.您允许函数声明声明一个名称列表,但这与声明名称列表的变量声明中的有限前瞻无法区分.考虑两句话:
int variable1, variable2, variable3;
int function1, function2, function3() BEGIN END
Run Code Online (Sandbox Code Playgroud)
当解析器找到第一个时,,它无法知道后面是整数变量列表,还是具有相同参数和主体的函数列表.我猜你并没有真正打算第二种情况是可能的,这意味着你不应该使用identifier1在functions_declaration(或class_declaration).即使在你的参数列表non-terminal(vars_in_func)中,你也有一些奇怪的东西,尽管可以解析; vars_in_func是逗号分隔的类型列表,后跟逗号分隔的名称列表,因此以下是合法的:
void function(int a, b, int c, d)
Run Code Online (Sandbox Code Playgroud)
也许你打算这样做,但我不相信.