使用memoize

时间:2017-03-21 20:20:13

标签: java parsing antlr3 java-6

上下文

我正在对ANTLR3中的语法分析器进行一些更改,以识别我认为有效的某些网址(例如,如果它以wwwhttp开头,或者如果它以.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选项,我必须停用它才能使其正常工作。由于性能很重要,我希望能够保持记忆功能。

使用memoize时为什么会失败?

我对生成的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时,不会发生这种情况。

我知道如何解决这个问题?

0 个答案:

没有答案