0

我有以下语法:

cmds
    : cmd+
    ;

cmd
    : include_cmd  |  other_cmd
    ;

include_cmd
    : INCLUDE  DOUBLE_QUOTE  FILE_NAME  DOUBLE_QUOTE
    ;

other_cmd
    : CMD_NAME  ARG+
    ;


INCLUDE
    : '#include'
    ;

DOUBLE_QUOTE
    : '"'
    ;

CMD_NAME
    : ('a'..'z')*
    ;

ARG
    : ('a'..'z' | 'A'..'Z' | '0'..'9' | '_')+
    ;

FILE_NAME
    : ('a'..'z' | 'A'..'Z' | '0'..'9' | '_' | '.')+
    ;

所以 CMD_NAME、ARG 和 FILE_NAME 的区别不大,CMD_NAME 必须是小写字母,ARG 可以有大写字母和“_”,而 FILE_NAME 可以有“.”。

但这有一个问题,当我用-#include“abc”测试规则时,'abc'被解释为CMD_NAME而不是FILE_NAME,我认为这是因为CMD_NAME在语法文件中的FILE_NAME之前,这导致解析错误。

我是否必须依靠 predict 之类的技术来处理这个问题?除了依赖宿主编程语言,还有纯 EBNF 解决方案吗?

谢谢。

4

1 回答 1

1

但这有一个问题,当我用-#include“abc”测试规则时,'abc'被解释为CMD_NAME而不是FILE_NAME,我认为这是因为CMD_NAME在语法文件中的FILE_NAME之前,这导致解析错误。

所有有效CMD_NAMEs 的集合与所有有效 s 的集合相交FILE_NAME。输入abc符合两者的条件。词法分析器将输入与列出的第一个规则匹配(正如您所怀疑的),因为它是第一个匹配的。

我是否必须依靠[谓词]之类的技术来处理这个问题?除了依赖宿主编程语言,还有纯 EBNF 解决方案吗?

这取决于你愿意接受的语法。考虑将您的include_cmd规则更改为更传统的规则,如下所示:

include_cmd : INCLUDE STRING;

STRING 
    : '"' ~('"'|'\r'|'\n')* '"' {String text = getText(); setText(text.substring(1, text.length() - 1));}
    ;

现在 input#include "abc"变成了 tokens [INCLUDE : #include] [STRING : abc]

我认为语法不应该负责确定文件名是否有效:有效的文件并不意味着有效的文件,并且语法必须理解 OS 文件命名约定(有效字符、路径等) 这可能与语法本身无关。我认为如果您愿意放弃FILE_NAME上述规则之类的规则,那您会没事的。

另外值得注意的是,您的CMD_NAME规则匹配零长度输入。考虑更改('a'..'z')*为,('a'..'z')+除非 aCMD_NAME真的可以为空。


请记住,您也会ARG遇到与FILE_NAME. 它在 之后列出CMD_NAME,因此符合这两个规则的任何输入(abc同样)都将命中CMD_NAME。考虑将这些规则分解成更传统的规则,如下所示:

other_cmd : ID (ID | NUMBER)+ SEMI;   //instead of CMD_NAME ARG+
ID        : ('a'..'z'|'A'..'Z'|'_')+; //instead of CMD_NAME, "id" part of ARG
NUMBER    : ('0'..'9')+;              //"number" part of ARG
SEMI      : ';';

我添加了规则SEMI来标记命令的结束。否则解析器将不知道输入a b c d应该是一个带有三个参数的命令 ( a(b,c,d)) 还是两个各有一个参数的命令 ( a(b), c(d))。

于 2013-02-03T23:16:17.780 回答