ANTLR - 如何确定哪种解析树“最适合”某些代码

4

我正在使用ANTLR构建一个程序,要求用户输入一些Java代码,并将其转换为等效的C#代码。在我的程序中,我要求用户输入一些Java代码并对其进行解析。到目前为止,我一直认为他们会输入一些可以单独解析为有效编译单元的内容,例如:

package foo;

class A { ... }
class B { ... }
class C { ... }

然而,并非总是这样。他们可能只是从类的内部输入代码:
public void method1() {
    ...
}

public void method2() {
    ...
}

或者是方法内部:

System.out.print("hello ");
System.out.println("world!");

甚至只是一个表达式:
context.getSystemService(Context.ACTIVITY_SERVICE)

如果我尝试通过调用 parser.compilationUnit() 来解析这样的片段,那么它将无法正确工作,因为大部分代码都被解析为错误节点。我需要根据代码的性质调用正确的方法,例如 parser.expression()parser.blockStatements()。然而,我不想要求用户明确指出这一点。推断我正在解析哪种类型的代码的最佳方法是什么?


3
在你的语法中创建一个新的顶级解析器规则,将所有可能的输入作为备选项,并依赖于它。你的语法中已经有了需要放入这个新规则中的所有规则。对于Java而言,应该很少有冲突——除了字段和局部变量、方法和构造函数、内部和封闭类型,我认为你不会有问题。但你没有指定可能输入的确切“解决方案”。 - predi
@predi,我不确定你最后一句话的意思。我喜欢你的方法,但它存在一个问题:如果用户在代码的多行语句中忘记了分号等语法错误,那么ANTLR将无法匹配任何替代方案(即使有些替代方案显然比其他替代方案更合适)。 - James Ko
确保您的新规则将EOF标记指定为最后一个预期的标记 - 如果没有匹配任何替代方案(甚至部分匹配),那可能是原因。通过“分辨率”,我指的是用户被允许向下钻取的程度,例如,如果输入<T,V>,您是否希望它被识别?仅仅是Foo呢?这两个都是有效的Java片段。即使只是片段,代码生成器也有望要求语法正确的输入。 - predi
@predi哦,你是在说ANTLR会匹配一种替代方案,即使存在非零数量的语法错误?我没有亲自尝试过,我只是认为即使有一个错误也会完全排除另一种替代方案。如果它设法不这样做,那就太令人印象深刻了。 - James Ko
我不喜欢新的顶级解析规则,因为它会污染语法。 - Ivan Kochurkin
2个回答

1
不要尝试猜测一个语言片段的有效语法规则入口来解析它,而是逐步向源文本添加作用域包装器,直到实现有效的顶级规则解析。也就是说,每次解析失败时,都逐步添加虚拟的包、类和方法语句作为源文本包装器。无论哪个包装器被添加以实现成功解析,都将成为已知量。因此,可以轻松地识别表示原始源文本的解析树节点。可能需要使用快速失败的解析器;使用BailErrorStrategy构造解析器以获得这种行为。

1

我们在 Swiftify 中的算法尝试从定义的规则集中选择最适合的解析规则。该 Web 服务将 Objective-C 代码片段转换为 Swift,并且您可以立即通过自己来评估转换质量。

算法

我们使用开源的 ObjectiveC grammar。算法的详细步骤如下:

  1. 使用以下规则解析输入的 Objective-C 代码片段
    • translationUnit
    • implementationDefinitionList
    • interfaceDeclarationList
    • expression
    • compoundStatement
  2. 如果某个规则的解析结果不包含任何错误,则立即返回此规则。
  3. 选择距离结尾最近的解析错误的规则。
  4. 如果有两个或更多规则具有相同的最接近结尾错误位置,请选择语法错误数量最少的规则。

演示

有一些使用不同解析规则解析的测试代码示例:

即使输入不正确,我们的算法也能够检测到适当的解析规则:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接