我目前正试图使用ANTLR4编写一个UCUM解析器。我目前的方法包括将每个有效的单元和前缀定义为令牌。
这里是定义的令牌的一个非常小的子集。我可以做一个语法的精简版作为例子,但似乎没有必要解决这个问题(或者指出我做这件事的方式完全错误)。
MILLI_OR_METRE: 'm' ;
OSMOLE: 'osm' ;
MONTH: 'mo' ;
SECOND: 's' ;
标准测试用例之一是mosm
,lexer应该从中生成令牌流MILLI_OR_METRE OSMOLE
。不幸的是,由于ANTLR优先匹配较长的令牌,它生成了令牌流MONTH SECOND MILLI_OR_METRE
,然后导致解析器引发错误。
有可能让ANTLR4 lexer先尝试使用较短的令牌进行匹配吗?向MONTH
添加前瞻类型规则并不是一个好的解决方案,因为我需要考虑各种潜在的词法冲突(例如,mol
被词法转换为MONTH LITRE
而不是MOLE
,等等)。
编辑:
下面的StefanA当然是正确的;这是一个能够回溯的解析器的工作(例如递归下降、packrat、PEG以及可能的其他各种…Coco/R是一个合理的包)。为了避免添加对另一个解析器生成器的依赖(或将项目的其他部分从ANTLR移动到这个新生成器),我已经破解了这样的问题:
MONTH: 'mo' { _input.La(1) != 's' && _input.La(1) != 'l' && _input.La(1) != '_' }? ;
// (note: this is a C# project; java would use _input.LA instead)
但这并不是一个真正的可扩展或可维护的解决方案,而且我还没有遇到其他微妙的问题。
您的问题不需要首选较小的令牌(在这种情况下,MONTH永远不会匹配)。你需要一个回溯行为取决于文本是否匹配。正确的
ANTLR严格分离标记化和解析。因此,你的问题的每一个解决方案都会像黑客一样。
然而,其他解析器生成器专门处理像您这样的问题。Packrat解析器(PEG)正在回溯并允许动态标记化。试着用蒸过的来达到这个目的。
似乎问题的框架不正确。
我目前正在尝试使用ANTLR4编写一个UCUM解析器。我目前的方法包括将每个有效的单元和前缀定义为令牌。
但是,根据UCUM:
度量单位统一代码的表达式语法会生成无限多的代码,因此不可能编译所有有效单位的表。
lexer最值得期待的是对测量字符串的明确标识,而不考虑其语义值。类似地,除非问题空间在解析器定义中受到静态约束,否则单独的解析器将无法在MONTH LITRE
和MOLE
这样的单元序列之间进行有效选择——这两种序列都可以合理地应用于泄漏率。
最有可能需要启发式、结构化(明确识别问题空间)或上下文(考虑问题空间中其他单元的相对性质)来选择正确的单元解释。
使用的最佳工具是使您处于最佳位置来实现消除单元字符串歧义所需的启发式方法的工具。Antlr可以使用解析树遍历器来完成。这是否是适当的做法需要进一步分析。