如果你的语言是命令式的/类 c 语言,常见的场景是顶层结构被分成 2 个超类型:
程序是一个语句列表,它本身就是一个语句。
您可能希望拥有一个扩展语句基类的语句类型类。
一个典型的场景是这样的:
- 语句块(语句列表)
- ite (if then else)
- for(一个 for 循环及其初始化语句列表、检查表达式、增量语句和块
- while(类似,但只检查表达式
- 变量声明
- 赋值(包括 += -= ++ --,您可以使用运算符字段、lval 和 rval 将所有内容包装在一个类中)
- 函数调用(无效一)
对于表达式:
- Bop(二元运算,任何有 2 个操作数和 1 个运算符,即 + - * / % | & && || ==
- Uop(一元运算,任何有 1 个操作数和 1 个运算符,即 ~ !)
- 函数调用(非空函数)
- 条件表达式(exp ? true val : false val)
拥有这 2 个抽象(表达式和语句)的好处是,在您的所有类中,您将拥有抽象类型,并且将能够使用访问者模式访问 AST。
例如,一些类看起来像这样(伪代码):
class Ite extends Statement {
Expression condition;
Statement ifBranch;
Statement elseBranch;
}
class Bop extends Expression {
BOperator operator; // +, -. * or whatever
Expression left; // Left operand
Expression right; // Right operand
}
class StatementBlock extends Statement {
List<Statement> statements;
}
class Assignment extends Statement {
AOperator assignOp; // = += -= etc.
LVal lvalue; // The lvalue cannot be an arbitrary expression, you will usually have a specific type for it
Expression rvalue; // Right value
}
此外,您将需要某种方式来表示 类型(对于 AST,仅静态类型就足够了,如果您的项目还需要实现一些后端,那么您还需要一些动态类型)。
如果您不打算支持需要大小信息的固定大小数组,通常可以使用一些枚举指定静态类型。如果你想要固定大小的数组,大小,你可以实现一个类型的类,并让数组类型保存额外的大小信息。
enum Type {
CHAR,
SHORT,
INT,
LONG,
FLOAT,
DOUBLE,
ARRAY
}
class Float extends StaticType {
final Type type = Type.FLOAT;
}
class Array extends StaticArray {
final Type type = Type.ARRAY;
int size;
}
然后,您将为 AST 中的每种类型实例化一个 StaticType 实例,例如当用户声明一个变量时。如果您计划在将来进行静态类型检查,您也可以使用相同的层次结构。
至于以 AST 形式运行/解释代码,您将需要一个 Memory 来保存包含有关运行时内存信息的堆栈/堆。此时,您需要将值与其类型信息一起存储。