将子树从AST的一部分移动到另一部分

时间:2013-05-07 09:37:15

标签: antlr antlr3 abstract-syntax-tree

我正在开发一种将Oracle SQL转换为ANSI SQL的工具。我有一个语法可以解析Oracle SQL和ANSI SQL。

我想从AST的where子句部分提取Oracle外连接表达式,并在AST的from子句部分的末尾插入新的join子句,用于匹配的select或子查询。

具有重写规则的树分析器可以进行这种类型的树转换吗?

即。获取从Oracle SQL生成的AST

SELECT
  a.columna, b.columnb
FROM
  tablea a,
  tableb b
WHERE
  a.columna2 (+) = b.columnb2 (+)
  AND
  a.columna3 = 'foo'
  AND
  b.columnb3 = 'bar'

并将其转换为ANSI SQL的AST

SELECT
  a.columna, b.columnb
FROM
  tablea a FULL OUTER JOIN tableb b ON (a.columna2 = b.columnb2 )
WHERE
  a.columna3 = 'foo'
  and
  b.columnb3 = 'bar'

注意1:tableatableb的表引用将从FROM子句中删除,并替换为引用相同表和表别名的JOIN子句。

注意2:由于sql_condition比较两侧存在OuterJoinIndicator(+),Oracle连接条件被标识为FULL OUTER JOIN

注3:连接条件比较从WHERE子句中删除,用于构造连接子句ON条件[删除了OuterJoinIndicator]。

2 个答案:

答案 0 :(得分:1)

是的,这很有可能,特别是因为你有一个识别Oracle和ANSI SQL的语法。我曾经从AREV BASIC到Visual BASIC编写了一个翻译器,并进行了许多类似的转换。

在我的项目中,我使用了ANTLR 2并编写了一个主树语法,它根据我语法中的所有规则完全没有做任何事情。然后我使用ANTLR 2的子类来覆盖特定的规则来进行转换。我喜欢这个,因为它让我在传递中建立翻译并在一次传递中保持所有表达式处理,在另一次传递中控制结构等等。

ANTLR 3不提供语法子类化,因此您将无法使用该方法。您将需要一个完整的树语法来打印出结果树。就个人而言,我会首先编写树语法并使其正常工作。然后我会复制该语法并删除所有操作,但是可以选择重写AST。然后修改转换所需的规则。如果你做了很多转换,你可能想要使用多次传递,每次传递一个树语法。您可能有一两个分数来进行分析,以帮助推动后来的传球。在我的BASIC翻译项目中,我在分析过程中控制了流量分析,数据流分析和死代码删除。

如果您需要帮助编写特定转换,则需要共享树语法。有很多树语法习语可以包围你的头脑。如果您需要帮助,Terence的ANTLR 3书籍将是一个有价值的购买。如果你还没有编写树语法,那么当你遇到困难时就会发布问题。选择正确的根节点很重要。如果您想了解如何构建树和树解析器,您可以查看我的C语法。它是ANTLR 2,但树木构建概念是相同的。 http://www.antlr3.org/grammar/cgram/grammars/

您是否需要保留评论和格式?这增加了另一层复杂性,我建议创建另一个问题。

答案 1 :(得分:0)

如果你有两个不同的语法,你很可能会发现语法中的“小差异”导致这些子句的AST相当不同,所以你真正的问题是将一个树的结构转换为一个树。另一种结构。并且你必须为整个树分段执行此操作,因为这些差异遍布整个语法。 YMMV。

ANTLR的树解析器很可能会让你识别任意片段;这些肯定是在其他语法AST中产生等价物的线索。但是你必须编写许多这样的片段,并且代码对应的例程来逐节点地组装等效的树。作为大型语法(例如Oracle SQL)的一般规则,这可能是相当多的工作。你可以这样做。

另一种选择是program transformation系统。这些工具允许您编写表面语法模式(例如,Oracle SQL和ANSI SQL中的短语)来直接编码和应用转换。以这种方式编写转换相当容易恕我直言。你最终会写这样的东西:

 source domain Oracle.
 target domain ANSISQL.
 rule xlate_Oracle_SELECT(c: columns, t1: table, t2: table,
                          c1: column, c2: column,
                          more_conditions: conditions):SQL_phrase
     "SELECT \c FROM \t1, \t2 WHERE c1 (+) = c2 (+) and \more_conditions";
  =>
      "SELECT \c FROM  \t1 FULL OUTER JOIN \t2 on ( c1 = c2 ) WHERE \more_conditions";

(反斜杠-ID是模式变量,可以匹配该位置声明的语法类型合法的任意子树。)

这个工作的原因是转换工具用第一个语法解析第一个模式,因此获得它可以在第一个语法的树上匹配的树,同样使用 second解析第二个模式语法,得到一个遵循第二语法规则的替换树。转换引擎匹配第一个模式的树,并将树替换为第二个模式。因此,这样的规则将一小组蓝树节点从蓝树变换为所需树类型的一小组绿色节点。如果您想要准确的翻译,颜色类比应该清楚地表明您必须将所有蓝色节点翻译成绿色节点。

你需要额外的规则来翻译各种子条款,只是为了解释语法上的差异,例如:

rule translate column(t: IDENTIFIER, c: IDENTIFIER, ):table->table
    "\t.\c" -> " \toSQLidentifier\(\t\).\toSQLidentifier\(\c\)";

这可以通过调用自定义函数 toSQLidentifier 进行字符串黑客攻击来处理两种语言拼写标识符的方式的差异。

我不认为ANTLR支持这种转换规则。你可以用很多代码来模拟它。

如果你对这两种语言都有一个“联合”语法(这就是你所暗示的),你可能会避免这种情况,但这通常会让你得到一个非常模糊的语法,这是一个很大的麻烦。如果您已成功完成此操作,则只需应用语言不同的翻译规则(例如,所有内容都是蓝色节点)。

你也可以破解它:从左到右扫描树;将相同的部分(相当于它看起来比它看起来更难)打印出来,在它们不同的地方,相当于替换。这是一种非常脆弱的方法。