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 */)}
但是,当我们执行此操作时,我们会将特定于语言的代码嵌入语法中,并且无法跨目标移植。