优先匹配ANTLR4中的较短标记

优先匹配ANTLR4中的较短标记,antlr,antlr4,Antlr,Antlr4,我目前正在尝试使用ANTLR4编写解析器。我目前的方法是将每个有效的单元和前缀定义为令牌 这里是定义的令牌的一个非常小的子集。我可以做一个简化版的语法作为例子,但似乎没有必要解决这个问题(或者指出我的做法完全错误) 标准测试用例之一是mosm,lexer应该从中生成令牌流MILLI\u或\u-meter-OSMOLE。不幸的是,由于ANTLR优先匹配较长的令牌,因此它会生成令牌流月秒毫秒或米,然后导致解析器引发错误 有没有可能让ANTLR4 lexer首先尝试使用较短的标记进行匹配?向MONTH

我目前正在尝试使用ANTLR4编写解析器。我目前的方法是将每个有效的单元和前缀定义为令牌

这里是定义的令牌的一个非常小的子集。我可以做一个简化版的语法作为例子,但似乎没有必要解决这个问题(或者指出我的做法完全错误)

标准测试用例之一是
mosm
,lexer应该从中生成令牌流
MILLI\u或\u-meter-OSMOLE
。不幸的是,由于ANTLR优先匹配较长的令牌,因此它会生成令牌流
月秒毫秒或米
,然后导致解析器引发错误

有没有可能让ANTLR4 lexer首先尝试使用较短的标记进行匹配?向
MONTH
添加前瞻类型规则不是一个很好的解决方案,因为我需要考虑各种潜在的词法冲突(例如
mol
被词法化为
MONTH-lil
而不是
MOLE
等等)

编辑:

下面的斯蒂凡娜当然是正确的;对于能够回溯的解析器来说,这是一项工作(例如递归下降、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)

但这并不是一个真正的可扩展性或可维护性很强的解决方案,而且像as not这样的解决方案会带来我还没有遇到的其他微妙问题。

您的问题并不需要首选较小的令牌(在这种情况下,本月将永远不会匹配)。您需要一个回溯行为,这取决于文本是否匹配。对吧?

ANTLR严格分离标记化和解析。因此,你的问题的每一个解决方案看起来都像一个黑客


然而,其他解析器生成器专门处理像您这样的问题。Packrat解析器(PEG)正在回溯,允许动态标记化。为此,请尝试。

问题的框架似乎不正确

我目前正在尝试使用ANTLR4编写UCUM解析器。我目前的方法是将每个有效的单元和前缀定义为令牌

但是,根据UCUM的说法:

度量单位统一代码的表达式语法生成无限多的代码,其结果是不可能编译一个包含所有有效单位的表

从lexer中最值得期待的是对度量字符串的明确标识,而不考虑其语义值。类似地,单独的解析器将无法在单位序列(如
月升
摩尔
)之间进行有效选择-这两者都可以合理地应用于泄漏率-除非问题空间在解析器定义中受到静态约束

选择正确的单元解释很可能需要启发式、结构性(明确识别问题空间)或上下文式(考虑问题空间中其他单元的相对性质)


要使用的最佳工具是使您处于实现消除单元字符串歧义所需的启发式的最佳位置的工具。Antlr可以使用解析树漫游器来实现这一点。这是否合适还需要进一步分析。

是的,我有一种不愉快的感觉,情况就是这样。我试图避免使用不同的解析器生成器,因为这个特定的项目已经为另一个组件使用了ANTLR,最好不要堆积依赖项。没错,PEG(无扫描解析器)在这里似乎更合适。因此,我最终用前瞻语义谓词解决了这个问题(并在上面描述了我的解决方案),但我还是接受了你的答案,因为这是处理这个问题最明智的方法。使用能够回溯的解析算法可以解决我发布的问题;
milliu或_meter OSMOLE
是语法中的有效字符串,
MONTH SECOND milliu或_meter
不是。这不是UCUM的人工制品。
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)