首页 > 解决方案 > ANTLR4 - 替换操作边界错误|如何使用 TokenStreamRewriter 从原始 AST 中重叠标记上的两个侦听器事件转换文本?

问题描述

您好 ANTLR 创建者/用户,

一些上下文 - 我正在使用 PlSql ANTLR4 解析器将一些查询从 oracle sql 转换为例如 spark sql。我有我的监听器类设置,它扩展了基础监听器。

问题示例 - 假设输入类似于 -

SELECT to_char(to_number(substr(ATTRIBUTE_VALUE,1,4))-3)||'0101') from xyz;

现在,我想替换 || 使用 CONCAT 和 to_char 使用 CAST 作为 STRING,因此最终查询看起来像 -

SELECT CONCAT(CAST(to_number(substr(ATTRIBUTE_VALUE,1,4))-3) as STRING),'0101') from xyz;

在我的侦听器类中,我重写了来自基本侦听器的两个函数来执行此操作 - 连接和 string_function。在这些中,我使用 tokenStreamRewriter 的替换来进行必要的转换。由于 tokenStreamRewriter 被懒惰地评估,我正在运行 ->

java.lang.IllegalArgumentException: replace op boundaries of 
<ReplaceOp@[@38,228:234='to_char',<2193>,3:15]..[@53,276:276=')', 
<2214>,3:63]:"CAST (to_number(substr(ATTRIBUTE_VALUE,1,4))-3 as STRING)"> 
overlap with previous <ReplaceOp@[@38,228:234='to_char',<2193>,3:15].. 
[@56,279:284=''0101'',<2209>,3:66]:"CONCAT 
(to_char(to_number(substr(ATTRIBUTE_VALUE,1,4))-3),'0101')">

显然,问题是我的两个侦听器函数试图替换/转换重叠边界上的文本。

ANTLR4 的领土重叠问题有什么解决办法吗?我敢肯定,人们可能一直都会遇到这样的事情。

我会很感激任何变通办法,即使是在这个时候肮脏的:)

我确实意识到 ANTLR4 不允许我们修改原始 AST,否则这会更容易解决。

谢谢!

标签: antlrantlr4

解决方案


看一下 tokenstreamrewriter 的工作原理可以得出以下理解:

  • 首先,构建所有修改操作的列表
  • 然后,您调用 getText()
  • 在这里,修改操作有所减少。例如,这个想法是将多个插入合并到一个缩减中。它的作用也是避免对同一数据进行多次替换(但我稍后会对此进行扩展)。
  • 然后读取每个令牌,如果为所述令牌索引列出了修改,则 TokenStreamRewriter 执行操作,否则它只是弹出读取的令牌。

我们来看看修改操作是如何实现的:

  • 对于插入,tokenstream rewriter 基本上只是在当前token索引处添加要添加的字符串,然后做一个index+1,有效地进入下一个token
  • 对于替换,tokenstream rewriter 用新字符串替换一系列标记,并将新索引设置为该范围的末尾。

因此,对于 tokenstreamrewriter,重叠替换是不可能的,因为当您替换时,您会跳转到要替换的标记范围的末尾。特别是,如果您删除了重叠检查,则只会操作第一个替换,因为之后,令牌索引会超过其他替换。

基本上,这已经完成了,因为在使用重叠替换时无法轻松判断应该替换哪些标记。您将需要该符号识别和匹配。

因此,您要尝试执行以下操作(对于每个步骤,“*”之间的部分是修改的内容):

*SELECT to_char(to_number(substr(ATTRIBUTE_VALUE,1,4))-3)||'0101')* from xyz;
|
V
CONCAT (*to_char(to_number(substr(ATTRIBUTE_VALUE,1,4))-3)*,'0101') from xyz;
|
V
SELECT CONCAT(CAST(to_number(substr(ATTRIBUTE_VALUE,1,4))-3) as STRING),'0101') from xyz;

为了实现您的转型,您可以这样做替换:

  • 'to_char' -> 'CONCAT(CAST'
  • '||' -> '作为字符串),'

而且,通过在解析标记时使用一点智能,比如是否有“||” 在我的令牌中知道它是否是字符串,你会知道要替换什么。

问候


推荐阅读