是否可以使用ANTLR解析器作为搜索器,即找到与给定规则ss
匹配的较长字符串S
的子字符串my_rule
的第一个实例?
从概念上讲,我可以通过在位置S[i]
上查找匹配项,递增i
直到成功检索到匹配项或S
用尽来完成此操作。
但是,实际上这并不是很好,因为S
中的前缀可能恰好具有与我的语法中的标记匹配的字符。取决于发生这种情况的方式,ss
中的有效字符串S
可能会被识别几次,或者不规律地被跳过,或者可能会打印很多有关“令牌识别错误”的错误。
是否有我没有想到的方法,或者我不知道的ANTLR功能?
如果这很重要,我正在使用ANTLR的Python绑定。
示例:
给出以下语法:
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-我可以发誓,我已经在大约4个月前的某个地方问过这个问题,但是我在SO,ANTLR GitHub跟踪器或ANTLR Google上找不到任何内容组。)
答案 0 :(得分:2)
有没有一种方法可以将ANTLR解析器用作搜索器,即找到 匹配的较长字符串
ss
的子字符串S
的第一个实例 给定规则my_rule
?
简短的回答是“否”。 ANTLR不能替代/等效于任何基于正则表达式的标准工具,例如sed
和awk
。
更长的答案是肯定的,但有一些麻烦的警告。 ANTLR希望解析结构化的,基本明确的输入文本。通过添加词法分析器规则(最低优先级/底部位置)可以忽略不具有语义意义的文本
IGNORE : . -> skip;
这样,将忽略词法分析器中未明确识别的所有内容。
下一个问题是“普通”文本和关键字之间可能存在语义重叠,例如 Jan(名称)-Jan(简称abrev)。通常,可以通过在解析器中添加BaseErrorListener
来区分真实错误和无意义错误来解决此问题。构成真实与无意义的内容可能会涉及复杂的极端情况,具体取决于应用程序。
最后,规则
day_number returns [val]
: a=INT {$val = int($a.text)} ;
返回一个int
值而不是一个INT
令牌,因此正在报告错误。规则应该是
day_number : INT ;
答案 1 :(得分:0)
基于@grosenberg答案的一个想法,我确定的解决方案如下。
1)添加后备词法分析器规则,以匹配现有规则尚未匹配的任何文本。 不要忽略/跳过这些标记。
OTHER : . ;
2)添加解析器替代项,以匹配感兴趣的规则或其他(优先级较低的)其他规则:
month_and_day_or_null returns [val]
: month_and_day {$val = $month_and_day.val}
| . {$val = None}
;
3)在应用程序代码中,查找None
或填充的值:
def parse(text: str):
date_input = InputStream(text.lower())
lexer = testLexer(date_input)
stream = CommonTokenStream(lexer)
parser = testParser(stream)
return parser.month_and_day_or_null()
for t in ['Jan 6',
'hello Jan 6, 1984',
'hello maybe Jan 6, 1984']:
for i in range(len(t)):
value = parse(t[i:])
if value.val:
print(f"Position {i}: {value.val}")
break
这在比赛时具有预期的效果:
Position 0: (1, 6)
Position 6: (1, 6)
Position 12: (1, 6)