如何检查语句中操作的类型?

How to check the type of an operation in a statement?

我希望能够检查 return 值的类型是否与 ANTLR 中方法的类型相同。 (即 int processOperation() 应该 return 像 return (3-1*4) 这样的 int)

我的语法如下:https://github.com/RodrigoZea/Lab00DDC/blob/fda787998e5ed1cc5e5d94e6506ed6ca08dbd955/Decaf/Decaf.g4

我正在使用 ANTLR4 的 python 实现,但我不确定如何检查 return 语句中的操作类型,例如 (1+3* 4) 应该 return 一个整数。我使用的是监听器,所以我的逻辑如下:

  1. 首先检查值是否为原始值(即 return“随机”,return 1)
  2. 检查该值是操作还是单个变量。

对于单个变量,在符号 table 中搜索它就足够了,但对于一个操作,我不确定如何处理它。我读过有关使用 ParseTreeProperty<> 的内容,但我认为在 Python 版本的 ANTLR4 中没有实现它,这似乎是我在 ANTLR4 权威参考中阅读的最佳方法因为它将保存节点(和操作子树)的数据类型,我可以轻松检查其类型并将其与我的方法类型进行比较。我猜我在输入运算符规则时需要检查,但我不确定如何处理该数据,或者是否有办法在 Python 中实现 ParseTreeProperty。谢谢

ParseTreeProperty 便于将属性“附加”到解析树的节点,并且可能是跟踪树中每个节点的 type 的有用方法。但是,正如评论中提到的,您可以使用其他数据结构来跟踪每个节点的类型并映射回它。 (注意:如果您将这种方法与侦听器一起使用,正如您的问题所暗示的那样,您需要在 *Exit() 方法中实现它,因为您希望所有 children 都被“侦听” to”及其分配的类型,以便您可以确定 parent 表达式的类型。)

使用侦听器,您也可以只拥有一堆类型。当您退出每个表达式时,它会弹出其所有 children 的类型,为自己计算表达式类型,并将该类型压入堆栈。当然,您必须注意正确管理推送和弹出(注意异常),但它可以是一个相当干净的实现。

您还可以实现表达式类型验证访问者。使用这种方法,您可以编写一个 return 类型的表达式访问者。对于每个重写的 visit*(),您只需在每个 child 上调用 visit() 来获取它的类型,然后决定您想要的结果类型是什么(甚至可能它是否是一个有效的表达式) .请注意,“访问”节点 return 是访问者的结果,这是访问者和听众之间的主要区别之一(另一个是,对于访问者,您必须明确选择如何导航你的 child 个节点)。

就“如何处理这些数据”而言,此时您正在就您希望语言的行为方式、什么是有效的等做出设计决策。

例如:

7 * "string"

也许您决定 7Int 类型,而“字符串”是 String 类型。在你的 listener/visitor for for 乘法表达式中,由你来决定这是否是一个错误(结果“type”可能是 InvalidType),或者可能像 Ruby,这是获取“stringstringstringstringstringstringstring”的一种可爱方式,在这种情况下,您会 return 一种 String。对于函数,您可以决定函数的 return 类型。您是否要求明确定义它们?必须在它们被引用之前定义(如果没有,你需要通过你的解析树创建一个符号 table 函数和 return 类型来引用,然后你才能导航你的树评估表达式类型)。也许,你有一种动态语言,其中不同的输入类型(甚至值)可能会导致你的函数有不同的 return 类型。

很明显,这对语言设计选择有很深的影响,并且语言已经就如何处理它们做出了许多不同的决定。 ANTLR 只是您的解析技术(除了像听众和访问者一样提供便利 类)与您如何做出这些决定或如何实施它们无关。而且,没有办法将它们编入语法中,因为它们是语义问题,对解析或解析树的构造没有影响。

因此,根据 Mike 和 kaby 的回答,我想出了一个解决方案。它非常简单,但非常实用。 在 Python 中复制 ParseTreeProperty 的最佳方法是创建字典,ctx 对象将是键,值是手动设置的,具体取决于您想要的值(这就是 Mike 的答案所在)便利)。要更新字典值,您将在 *Exit() 方法上执行此操作,正如 Mike 所说。

例如,如果您要退出 int 文字、char 文字或其他任何内容(您可以参考我的语法),您可以按如下方式在字典中添加条目:

    def exitType_literal(self, ctx: DecafParser.Type_literalContext):
        self.nodeTypes[ctx] = 'type'

例如,如果我想将一个节点保存为一个 int 值,我会做类似...

    def exitInt_literal(self, ctx: DecafParser.Int_literalContext):
        self.parseTreePropertyDictionary[ctx] = 'int'

但是,如果您想获取变量的值,则必须在符号 Table 的实现中进行搜索。那是我获取类型值的方法。

因此,一旦设置了每个节点,您就可以简单地设置您希望如何处理您的操作。例如,如果您希望“+”运算符与整数一起使用,您将检查字典中第一个和第二个运算符的类型,检查两者是否都是整数,如果是这样,则将其保存在字典中'int' 类型的节点,您正在其中处理“+”运算符。

然后,要获取操作的类型,您只需访问该节点上的字典,它将 return 'int' 或您设置的任何类型。