【问题标题】:Type checking of infix operators in compiler编译器中中缀运算符的类型检查
【发布时间】:2015-07-03 11:32:44
【问题描述】:

我正在编写一个编译器(在 Haskell 中)并且在语言的语法中有添加中缀运算符的规则(以添加为例):

EAdd . Expr ::= Expr "+" Expr

表示EAdd是一个表达式,它由表达式、字符串"+"和另一个表达式组成。

解析器返回抽象语法树(AST):

data Expr = ... | EAdd Expr Expr

如果检查函数调用的参数类型是否正确,我想做一个类型检查器。

注意,“+”是一个接受两个整数并返回一个整数的函数。其他运算符类似。

目前我想出了三种检查EAdd的方法,它们都包括在初始符号表中添加“+”作为函数:

  1. 声明中缀加号是用两个参数调用函数“+”的语法糖。在解析器和类型检查器之间放置将 AST 从解析器转换为另一种数据类型(不带EAdd)的“desugarizer”。

  2. (与第一个类似)声明中缀加号是语法糖,但 desugarizer 使用相同的 AST 数据类型。 Typechecker 在给定 EAdd 时返回错误。

  3. 将“脱糖器”内联到类型检查器中。类似这样:

    ...
    typecheck (EAdd a b) = typecheck (ECall infixPlus [a, b])
    ...
    

请注意,所有二进制中缀运算符都受此约束(其他算术、布尔运算、比较运算符)。

似乎第一种方法是正确的。但这意味着在编译器管道的后期,特别是在代码生成器中,那些ECalls 应该作为特殊情况处理,因为在编译器输出(在我的情况下 - llvm)中,这些函数是假定的被内联(与通常的函数调用不同)。 这意味着 codegen 有一个函数列表,其调用的处理方式与其他函数调用不同。

解决这个问题的最佳方法是什么?

UPD

在 Haskell 中如何处理类似的问题(来自 https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/Renamer):

... renamer 做了以下事情:

  • 整理固定装置。解析器将所有中缀应用程序解析为左关联,而不考虑固定性。例如“a + b * c”被解析为“(a + b) * c”。重命名器使用模块中声明的固定性重新关联这些嵌套的运算符应用程序。

【问题讨论】:

  • 这是对您当前想法的很好且可读的描述。你想回答什么问题?
  • 在我看来,您的困惑源于您将加法视为一种特殊情况。我倾向于将 中缀运算符 解析为一些“中缀函数调用”AST 元素,并使用其他一些(更明确和语法导向的)机制来表示是否应该内联函数.我不是一个编译器的人,所以用一点盐来思考我的想法;)
  • @BenjaminHodgson 随着对 AST 的这些更改,如何处理操作优先级和关联性?
  • @andrybak 优先级/关联性从根本上来说不是解析问题吗?解析器根据它对运算符优先级的了解有效地为您插入括号,但生成的 AST 与您自己编写的括号相同。 (再说一次,不是专家,可能有人会来告诉我我所说的一切都是错误的)

标签: haskell compiler-construction


【解决方案1】:

LLVM 支持inline attributes 例如

define void @f() alwaysinline { ... }

因此,一种选择是将+ 视为普通函数调用,并让 LLVM 完成其优化工作。

【讨论】:

  • 这意味着每个使用二元运算符的程序的 llvm 代码都将包含几个 alwaysinline 函数,这些函数通常(我假设)在编译的上一步中内联。
猜你喜欢
  • 2015-06-10
  • 1970-01-01
  • 2016-06-25
  • 2017-01-13
  • 1970-01-01
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 2014-10-04
相关资源
最近更新 更多