我正在对ANTLR3中的语法分析器进行一些更改,以识别我认为有效的某些网址(例如,如果它以www
或http
开头,或者如果它以.com
)
我现在的主要规则是这样的:
url returns [UrlToken token]
: scheme_full? subdomain? middle_url valid_domain_end? port? path_full?
{isValidUrl($scheme_full.text, $subdomain.text, $valid_domain_end.text, $path_full.text, $middle_url.text)}?
{$token = urlTokenFor(
StringUtils.defaultString($scheme_full.text),
StringUtils.defaultString($subdomain.text) + StringUtils.defaultString($middle_url.text) + StringUtils.defaultString($valid_domain_end.text),
StringUtils.defaultString($path_full.text),
StringUtils.defaultString($port.text),
StringUtils.defaultString(null) ); }
;
isValidUrl
的代码是:
private boolean isValidUrl(String scheme, String subdomain, String valid_end, String path, String middle) {
if (scheme != null || subdomain != null || valid_end != null) return true;
if (path != null && middle.contains(".")) return true;
return false;
}
主要思想是我让子部分可选地匹配,然后使用某个Java函数检查它是否是有效的 URL,然后(如果它是有效的)它返回{{ 1}}。
我是这样做的,因为检查URL是否有效的逻辑太复杂了,无法将其留给解析器,产生了很多我无法解决的歧义。
现在,目前正在为我工作,并且正在匹配我想要的网址。
问题是在更改之前我使用的是UrlToken
选项,我必须停用它才能使其正常工作。由于性能很重要,我希望能够保持记忆功能。
我对生成的Java代码进行了一些调试。
这是memoize
规则的生成代码的一部分:
url
这是// grammars/Url.g:94:5: ( scheme_full )?
int alt3=2;
int LA3_0 = input.LA(1);
if ( (LA3_0==LETTERS) ) {
int LA3_1 = input.LA(2);
if ( (LA3_1==SYMBOL) ) {
int LA3_3 = input.LA(3);
if ( (LA3_3==SYMBOL) ) {
int LA3_4 = input.LA(4);
// Inside synpred3_Url(), scheme_full() is executed
if ( ((synpred3_Url()&&(matchesScheme(input.LT(1))))) ) {
alt3=1;
}
}
}
}
switch (alt3) {
case 1 :
// grammars/Url.g:0:0: scheme_full
{
pushFollow(FOLLOW_scheme_full_in_url100);
scheme_full2=scheme_full();
state._fsp--;
if (state.failed) return retval;
}
break;
}
生成的代码示例:
scheme_full
这是我到目前为止得出的结论:
每个规则都成为一个Java函数,每个规则执行两次,
public final Url.scheme_full_return scheme_full() throws RecognitionException {
Url.scheme_full_return retval = new Url.scheme_full_return();
retval.start = input.LT(1);
int scheme_full_StartIndex = input.index();
try {
if ( state.backtracking>0 && alreadyParsedRule(input, 13) ) { return retval; }
// grammars/Url.g:149:3: ( scheme scheme_separator )
// grammars/Url.g:149:5: scheme scheme_separator
{
pushFollow(FOLLOW_scheme_in_scheme_full374);
scheme();
state._fsp--;
if (state.failed) return retval;
pushFollow(FOLLOW_scheme_separator_in_scheme_full376);
scheme_separator();
state._fsp--;
if (state.failed) return retval;
}
retval.stop = input.LT(-1);
}
catch (RecognitionException re) {
reportError(re);
recover(input,re);
}
finally {
if ( state.backtracking>0 ) { memoize(input, 13, scheme_full_StartIndex); }
}
return retval;
}
,scheme_full
等术语的返回值属于subdomain
类型,其中包含开始和停止标记。
当ParserRuleReturnScope
设置为memoize
时,如果结果成功,第一次执行术语时,会缓存它,以便第二次执行它时不会需要重新做一遍。当第一次成功时(在将结果放入缓存之前),结果(true
)具有正确设置的开始和结束标记。
问题在于第二次执行并使用缓存结果时,停止令牌进入retval
(尽管第一次执行成功)。可以在null
代码中看到,当返回scheme_full
时,它还没有设置停止令牌。
最后一件事成为问题,因为在执行谓词(retval
)时,它使用术语的文本属性,例如使用isValidUrl
。并且因为结束标记位于$scheme_full.text
中,所以将text属性调用到范围会返回null
字符串,因此我的验证失败,即使匹配成功也是如此。
当null
设置为memoize
时,不会发生这种情况。
我知道如何解决这个问题?