编译器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;
这样的语句实际上是合法的,但会在大多数编译器中引起警告。在其他语言中有一个明确的规则,禁止将某些类型的表达式用作语句表达式。
¹ 当然,只考虑那些完全区分语句和表达式的语言。
目前正在开发一个小玩具编译器,请考虑以下代码:
// 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;
这样的语句实际上是合法的,但会在大多数编译器中引起警告。在其他语言中有一个明确的规则,禁止将某些类型的表达式用作语句表达式。
¹ 当然,只考虑那些完全区分语句和表达式的语言。