您的语法有解析冲突。如果你希望语法正确,就必须解决这些问题。
keyword_signature
,以及一个未使用的非终端
message
. 为了让它在没有警告的情况下编译,我只是删除了它们。我认为这对下面的分析没有任何影响。)
file ::= statement_list .
和
file ::= statement .
statement
一个例子
statement_list
?
您不能同时拥有这两种功能的原因是:
statement_list ::= statements statement .
statements ::= .
总而言之,这意味着从
报表清单
,你可以认出一个
. 所以你的语法是模棱两可的;如果输入是单个语句,则可以将其直接解析为
file
或者可以将其解析为
文件
报表清单
statements statement
陈述
,使用一组不同的操作。
总之,摆脱
file ::= statement .
. 然后可以开始处理其他解析冲突。
更根本的问题也基于以下事实:
statements
可以派生空序列。
statement_list ::= statements .
statement_list ::= statements statement .
statements ::= statements statement STOP .
statements ::= .
如果
报表清单
不是空的,无论它匹配什么,都必须以空开头
接着是
.
陈述
WORD
所以
报表清单
必须匹配以开头的输入
单词
. 但在它改变现状之前
为了继续解析,它需要首先插入空的
声明
. 因此,它需要使用上面引用的最后一条规则进行缩减,然后才能处理
单词
. (如果这一段不完全清楚,请尝试重读,如果你还有问题,请提问。理解这一部分很重要。)
文件
也可能是一个
method
和
方法
. 但是,不像
报表清单
,这真的是从
单词
. 它不是以空字符开头的
声明
声明
而输入实际上是一个
方法
,则解析将失败。
碰巧的是,如果您有
file ::= statement
而不是
file ::= statement_list
陈述
不是以空的开始
任何一个这意味着当解析器看到
在输入开始时,它还不必决定是否要看到
陈述
或者
方法
单词
为了解决这个问题,我们可以观察到
报表清单
必须至少包含一个
陈述
陈述
它在一个房间里
报表清单
STOP
.
).如果我们从这个想法开始,很容易产生一个替代语法,它不需要在开始时列出一个空的列表:
statement_list ::= statements .
statement_list ::= statements STOP .
statements ::= statement .
statements ::= statements STOP statement .
这与你的语法不同,因为它认为
报表清单
是以点分隔的非空列表
陈述
陈述
然后是一个
.
因为我现在已经测试了语法,所以我添加了完整的可测试代码,以说明当我们请求
Minimal Complete Verifiable Example
. (我使用了C和flex,而不是Objective C,但我认为这没有任何区别。)
文件解析器.y:
%include { #include <assert.h> }
file ::= method.
file ::= statement_list.
file ::= .
method ::= signature OBRAC statement_list CBRAC .
signature ::= WORD .
statement_list ::= statements STOP .
statement_list ::= statements .
statements ::= statements STOP statement .
statements ::= statement .
statement ::= expression .
expression ::= simple_expression .
simple_expression ::= WORD simple_message .
simple_message ::= WORD .
%extra_argument { int* status }
%syntax_error { *status = 1; }
%parse_failure { fprintf(stderr, "Parse failed.\n"); }
%parse_accept { fprintf(stderr, "Parse succeeded.\n"); }
文件main.l:
%{
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "parser.h"
void* ParseAlloc(void* (*allocProc)(size_t));
void* Parse(void*, int, int, int*);
void* ParseFree(void*, void(*freeProc)(void*));
void synerr(const char* token) {
fprintf(stderr, "Syntax error handling '%s'\n", token);
}
%}
%option noinput nounput noyywrap nodefault
%x FLUSH
%%
void* parser = ParseAlloc(malloc);
int status = 0;
#define SEND(typ, val) do { \
if (Parse(parser, typ, val, &status), status) { \
synerr(yytext); BEGIN(FLUSH); \
} \
} while(0)
[[:space:]]+ ;
[[:alnum:]]+ { SEND(WORD, 0); }
"[" { SEND(OBRAC, 0); }
"]" { SEND(CBRAC, 0); }
"." { SEND(STOP, 0); }
. { synerr(yytext); BEGIN(FLUSH); }
<FLUSH>.+ ;
<FLUSH>\n { status = 0; BEGIN(INITIAL); }
<<EOF>> { if (status == 0) {
Parse(parser, 0, 0, &status);
if (status) synerr("EOF");
}
ParseFree(parser, free );
return 0;
}
%%
int main(int argc, char** argv) {
return yylex();
}
构建过程:
$ lemon parser.y
$ flex -o main.c main.l
$ gcc -std=c11 -Wall -Wno-unused-variable -o catlan -D_XOPEN_SOURCE=800 main.c parser.c
$ ./catlan <<< 'NSObject'
Parse failed.
Syntax error handling 'EOF'
$ ./catlan <<< 'NSObject description'
Parse succeeded.
$ ./catlan <<< 'NSObject description.'
Parse succeeded.
$ ./catlan <<< 'NSObject description. OtherObject'
Parse failed.
Syntax error handling 'EOF'
$ ./catlan <<< 'NSObject description. OtherObject otherDesc'
Parse succeeded.
$ ./catlan <<< 'NSObject description. OtherObject otherDesc.'
Parse succeeded.
$ ./catlan <<< 'NSObject description. OtherObject otherDesc extra words'
Syntax error handling 'extra'
Parse succeeded.
$ ./catlan <<< 'method [ NSObject desc]'
Parse succeeded.
$ ./catlan <<< 'method [ NSObject desc.]'
Parse succeeded.
$ ./catlan <<< 'method [ NSObject desc extra words]'
Syntax error handling 'extra'
Parse failed.
$ ./catlan <<< 'method [ NSObject desc. Second]'
Syntax error handling ']'
Parse failed.
$ ./catlan <<< 'method [ NSObject desc. Second desc]'
Parse succeeded.