编译器AST如何实现语句和表达式

Compiler AST how to implement statements and expressions

目前正在开发一个小玩具编译器,请考虑以下代码:

// AST base class
abstract class AST { /* codegen methods */}

// abstract classes for Statements and Expressions
abstract class Statement : AST {}
abstract class Expression : AST {}

// usage of the abstract classes
class CodeBlock : AST {
    public List<Statement> BlockStatements;
}
class BinOp : AST {
    public Expression LHS, RHS;
    public char Operator;
}

// a constant value is always an expression
class ConstantInt : Expression {
    public int Value;
}

现在问题来了,我将如何实施 FunctionCall class?如果它用在表达式中,它将是表达式的一部分,例如 min(4, 5) + 3 因此 FunctionCall : Expression 是有意义的。但是我不能在这样的块中进行函数调用 { writeToConsole("Hello World"); } 所以 FunctionCall : Statement 听起来很合理,但这不适用于表达式语法。使 Statement 继承自 Expression 也不起作用,因为它允许像这样的 AST min(4, 5) + int a.

我想获得有关如何将语句和表达式分开的建议,但可以同时分开的内容除外。

实现此目的的一种简单方法是使用接口。使用 IStatement 接口和 IExpression 并使 FunctionCall 实现两者。此外,让你的 BinOp 实现 IExpression

您应该意识到您有 "functions returning values"(应该是表达式)和“没有 return 值的函数(应该是语句)。

因此,如果您将 ExpressionFunction 和 StatementFunction 定义为单独的 类。

,您的问题就会消失

另一种方法:您可以将 Function 定义为表达式,并将 StatementFunction 定义为 AST,并将 Function 作为字段。

使语句成为表达式确实不是一个好主意(出于您指出的原因)。然而,使表达式成为语句具有更多优点。

事实上,大多数¹ 语言的语法都有类似这样的条目:

statement ::= expression ';'

也就是说一个表达式后面跟一个分号就是一个语句,即所谓的表达式语句。在您的 AST 中,您可以通过使 Expression 继承 Statement 或通过创建 class ExpressionStatement 来表示它,它只是包装 Expression.

除了允许函数调用作为语句外,它还允许其他表达式作为语句。对于像赋值、复合赋值或增量表达式这样的副作用表达式,这很有意义。

对于没有副作用的表达式,比如简单的算术,意义不大。在 C 和 C++ 中,像 a + b; 这样的语句实际上是合法的,但会在大多数编译器中引起警告。在其他语言中有一个明确的规则,禁止将某些类型的表达式用作语句表达式。


¹ 当然,只考虑那些完全区分语句和表达式的语言。