有没有办法使用ANTLR解析器作为搜索器,即找到与给定规则 my_rule
匹配的较长字符串 S
的子字符串 ss
的第一个实例?
从概念上讲,我可以通过在 S[i]
位置查找匹配来完成此操作,递增 i
直到我成功检索到匹配或 S
已用尽 .
但是,实际上这并不能很好地工作,因为 S
中的前缀可能巧合地在我的语法中包含与令牌匹配的字符 . 根据这种情况的发生, S
中的有效字符串 ss
可能会被多次识别,或者被错误地跳过,或者可能会出现很多关于"token recognition error"的错误 .
有没有我想过的方法,或者我不知道的ANTLR功能?
我正在使用ANTLR的Python绑定,如果这很重要的话 .
EXAMPLE :
鉴于以下语法:
grammar test ;
options { language=Python3; }
month returns [val]
: JAN {$val = 1}
| FEB {$val = 2}
| MAR {$val = 3}
| APR {$val = 4}
| MAY {$val = 5}
;
day_number returns [val]
: a=INT {$val = int($a.text)} ;
day returns [val]
: day_number WS? {$val = int($day_number.start.text)}
;
month_and_day returns [val]
: month WS day {$val = ($month.val, $day.val)}
| day WS ('of' WS)? month {$val = ($month.val, $day.val)}
;
WS : [ \n\t]+ ; // whitespace is not ignored
JAN : 'jan' ('.' | 'uary')? ;
FEB : 'feb' ('.' | 'ruary')? ;
MAR : 'mar' ('.' | 'ch')? ;
APR : 'apr' ('.' | 'il')? ;
MAY : 'may' ;
INT
: [1-9]
| '0' [1-9]
| '1' [0-9]
| '2' [0-3]
;
和以下脚本来测试它:
import sys
sys.path.append('gen')
from testParser import testParser
from testLexer import testLexer
from antlr4 import InputStream
from antlr4 import CommonTokenStream, TokenStream
def parse(text: str):
date_input = InputStream(text.lower())
lexer = testLexer(date_input)
stream = CommonTokenStream(lexer)
parser = testParser(stream)
return parser.month_and_day()
for t in ['Jan 6',
'hello Jan 6, 1984',
'hello maybe Jan 6, 1984']:
value = parse(t)
print(value.val)
我得到以下结果:
# First input - good
(1, 6)
# Second input - errors printed to STDERR
line 1:0 token recognition error at: 'h'
line 1:1 token recognition error at: 'e'
line 1:2 token recognition error at: 'l'
line 1:3 token recognition error at: 'l'
line 1:4 token recognition error at: 'o '
line 1:11 token recognition error at: ','
(1, 6)
# Third input - prints errors and throws exception
line 1:0 token recognition error at: 'h'
line 1:1 token recognition error at: 'e'
line 1:2 token recognition error at: 'l'
line 1:3 token recognition error at: 'l'
line 1:4 token recognition error at: 'o '
line 1:9 token recognition error at: 'b'
line 1:10 token recognition error at: 'e'
line 1:12 mismatched input 'jan' expecting INT
Traceback (most recent call last):
File "test_grammar.py", line 25, in <module>
value = parse(t)
File "test_grammar.py", line 19, in parse
return parser.month_and_day()
File "gen/testParser.py", line 305, in month_and_day
localctx._day = self.day()
File "gen/testParser.py", line 243, in day
localctx.val = int((None if localctx._day_number is None else localctx._day_number.start).text)
ValueError: invalid literal for int() with base 10: 'jan'
Process finished with exit code 1
要使用上面概述的增量方法,我需要一种方法来抑制 token recognition error
输出,并将异常包装在 try
或类似的中 . 感觉就像我非常反对谷物,并且很难将这些解析异常与其他错误区分开来 .
(META - 我可以 swear 我已经在4个月前的某个地方问了这个问题,但是我找不到SO,或者ANTLR GitHub跟踪器或者ANTLR Google Group . )
2 回答
最简洁的答案是不 . ANTLR不能作为任何标准的基于正则表达式的工具的替代/等效工具,如
sed
和awk
.更长的答案是肯定的,但有一个混乱的警告 . ANTLR希望解析一个结构化的,基本上无歧义的输入文本 . 通过添加词法分析器规则(在最低优先级/最低位置)可以忽略没有语义意义的文本
这样,忽略了词法分析器中未明确识别的任何内容 .
下一个问题是'normal'文本和关键字之间潜在的语义重叠,例如Jan(姓名) - Jan(月abrev) . 通常,这可以通过向解析器添加_1574249来处理,以区分真实和无意义的错误 . 什么构成真实与无意义可能涉及复杂的角落案例取决于应用程序 .
最后,规则
返回
int
值而不是INT
令牌,因此报告的错误 . 规则应该是我根据@ grosenberg的答案中的一个想法的变体确定的解决方案如下 .
1)添加后备词法分析器规则以匹配现有规则尚未匹配的任何文本 . 不要忽略/跳过这些令牌 .
2)添加一个解析器替代,以匹配感兴趣的规则,或(优先级较低)其他任何东西:
3)在应用程序代码中,查找
None
或填充值:这在匹配时具有预期的效果: