Antlr:识别日期和数字的最简单方法是?

时间:2020-03-06 14:48:29  来源:igfitidea点击:

解析同一语法中的有效日期和数字的最简单(最短,最少的规则,并且没有警告)的方法是什么?我的问题是,匹配有效月份(1-12)的词法分析器规则将匹配任何出现的1-12. 因此,如果我只想匹配一个数字,则需要一个解析规则,例如:

number: (MONTH|INT);

当我为日和年添加词法分析器规则时,它只会变得更加复杂。我想要一个像这样的日期解析规则:

date: month '/' day ( '/' year )? -> ^('DATE' year month day);

我不在乎月,日和年是解析规则还是词法分析器规则,只要最终得到相同的树结构即可。我还需要能够识别其他地方的数字,例如:

foo: STRING OP number -> ^(OP STRING number);
STRING: ('a'..'z')+;
OP: ('<'|'>');

解决方案

问题是我们似乎想在词法分析器和/或者解析器中执行语法和语义检查。这是一个常见的错误,只有在非常简单的语言中才有可能发生。

我们真正需要做的是在词法分析器和解析器中更广泛地接受它,然后执行语义检查。我们对词法的严格程度取决于我们,但是我们有两个基本选择,具体取决于我们是否需要在当月的几天之前接受零:1)真正接受INT,2)定义DATENUM为仅接受有效日期但无效的INT的令牌。我建议使用第二种方法,因为稍后在代码中将需要进行较少的语义检查(因为INT将可以在语法级别进行验证,因此我们只需要在日期上执行语义检查。第一种方法:

INT: '0'..'9'+;

第二种方法:

DATENUM: '0' '1'..'9';
INT: '0' | SIGN? '1'..'9' '0'..'9'*;

在词法分析器中接受使用这些规则后,日期字段将为:

date: INT '/' INT ( '/' INT )?

或者:

date: (INT | DATENUM) '/' (INT | DATENUM) ('/' (INT | DATENUM) )?

之后,我们将对AST执行语义运行以确保日期有效。

但是,如果我们对在语法中执行语义检查一无所知,则ANTLR允许解析器中使用语义谓词,因此我们可以创建一个日期字段来检查以下值:

date: month=INT '/' day=INT ( year='/' INT )? { year==null ? (/* First check /*) : (/* Second check */)}

但是,当我们执行此操作时,我们会将特定于语言的代码嵌入语法中,并且无法跨目标移植。