Design patterns 使用表达式AST
使用AST时是否有最佳实践? 我有一个解析表达式AST。恒常表达式、二进制表达式等。 我想用AST中的信息填充一个GUI对话框,在这里我有点困惑,因为我的代码变得非常混乱 例如:Design patterns 使用表达式AST,design-patterns,expression,abstract-syntax-tree,Design Patterns,Expression,Abstract Syntax Tree,使用AST时是否有最佳实践? 我有一个解析表达式AST。恒常表达式、二进制表达式等。 我想用AST中的信息填充一个GUI对话框,在这里我有点困惑,因为我的代码变得非常混乱 例如: expression = "Var1 > 10 AND Var2 < 20" expression=“Var1>10和Var2
expression = "Var1 > 10 AND Var2 < 20"
expression=“Var1>10和Var2<20”
我想用值10 resp填充两个文本框。来自AST的20个。
我现在做的是一个递归方法,它检查正确的子表达式类型(使用.Net is运算符),并相应地执行操作,代码真的很“难闻”:
是否有任何设计模式,比如Visitor之类的模式,使它更容易/更可读/更易于维护?大多数编译器通过使用
- 方法重写
- 访客
列表中。然后,调用代码可以使用此列表中的值填充文本框
方法重写
最顶层的AST类定义了一个抽象方法,该方法在子类中被重写
class AstNode {
.. // Some stuff
public abstract void collectValues(List<Integer> ints);
}
class ConstantExpression : AstNode {
private int value;
.. // Some stuff
public override void collectValues(List<Integer> ints) { ints.Add(value); }
}
class BinaryExpression : AstNode {
private AstNode left;
private AstNode right;
.. // Some stuff
public override void collectValues(List<Integer> ints) {
left.collectValues(ints);
right.collectValues(ints);
}
}
class Identifier : AstNode {
.. // Some stuff
public override void collectValues(List<Integer> ints) {
// do nothing!
}
}
类节点{
../一些东西
公共抽象值(列表整数);
}
类ConstantPression:AstNode{
私有int值;
../一些东西
公共覆盖无效集合值(列表整数){ints.Add(值);}
}
类二进制表达式:AstNode{
私有节点左;
私有节点权;
../一些东西
公共覆盖无效集合值(列表整数){
左。收集值(整数);
对。收集值(整数);
}
}
类标识符:AstNode{
../一些东西
公共覆盖无效集合值(列表整数){
//什么都不要做!
}
}
访客
相同的程序,但使用访问者编写
class Visitor {
public abstract void visit(ConstantExpression e);
public abstract void visit(BinaryExpression e);
public abstract void visit(Identifier e);
}
class AstNode {
.. // Some stuff
public abstract void accept(Visitor v);
}
class ConstantExpression : AstNode {
public int value;
.. // Some stuff
public override void accept(Visitor v) { v.visit(this); }
}
class BinaryExpression : AstNode {
private AstNode left;
private AstNode right;
.. // Some stuff
public override void accept(Visitor v) {
left.accept(v);
right.accept(v);
v.visit(this);
}
}
class Identifier : AstNode {
.. // Some stuff
public override void accept(Visitor v) { v.visit(this); }
}
class ValueCollector : Visitor {
public List<Integer> ints = ...;
public void visit(ConstantExpression e) { ints.Add(e.value); }
public void visit(BinaryExpression e) { }
public void visit(Identifier e) { }
}
类访问者{
公开摘要无效访问(ConstantExpression e);
公开摘要无效访问(二进制表达);
公开摘要无效访问(标识符e);
}
类AstNode{
../一些东西
公开摘要无效接受(访客v);
}
类ConstantPression:AstNode{
公共价值观;
../一些东西
公共覆盖无效接受(访问者){v.visit(this);}
}
类二进制表达式:AstNode{
私有节点左;
私有节点权;
../一些东西
公共覆盖无效接受(访客v){
左。接受(v);
对,接受(五),;
v、 访问(本);
}
}
类标识符:AstNode{
../一些东西
公共覆盖无效接受(访问者){v.visit(this);}
}
类ValueCollector:访问者{
公共列表整数=。。。;
公共无效访问(ConstantExpression e){ints.Add(e.value);}
公共无效访问(二进制表达式){}
公共无效访问(标识符e){}
}
大多数编译器通过使用
- 方法重写
- 访客
下面是如何将表达式中出现的所有常量值(文字)收集到整数的列表中。然后,调用代码可以使用此列表中的值填充文本框
方法重写
最顶层的AST类定义了一个抽象方法,该方法在子类中被重写
class AstNode {
.. // Some stuff
public abstract void collectValues(List<Integer> ints);
}
class ConstantExpression : AstNode {
private int value;
.. // Some stuff
public override void collectValues(List<Integer> ints) { ints.Add(value); }
}
class BinaryExpression : AstNode {
private AstNode left;
private AstNode right;
.. // Some stuff
public override void collectValues(List<Integer> ints) {
left.collectValues(ints);
right.collectValues(ints);
}
}
class Identifier : AstNode {
.. // Some stuff
public override void collectValues(List<Integer> ints) {
// do nothing!
}
}
类节点{
../一些东西
公共抽象值(列表整数);
}
类ConstantPression:AstNode{
私有int值;
../一些东西
公共覆盖无效集合值(列表整数){ints.Add(值);}
}
类二进制表达式:AstNode{
私有节点左;
私有节点权;
../一些东西
公共覆盖无效集合值(列表整数){
左。收集值(整数);
对。收集值(整数);
}
}
类标识符:AstNode{
../一些东西
公共覆盖无效集合值(列表整数){
//什么都不要做!
}
}
访客
相同的程序,但使用访问者编写
class Visitor {
public abstract void visit(ConstantExpression e);
public abstract void visit(BinaryExpression e);
public abstract void visit(Identifier e);
}
class AstNode {
.. // Some stuff
public abstract void accept(Visitor v);
}
class ConstantExpression : AstNode {
public int value;
.. // Some stuff
public override void accept(Visitor v) { v.visit(this); }
}
class BinaryExpression : AstNode {
private AstNode left;
private AstNode right;
.. // Some stuff
public override void accept(Visitor v) {
left.accept(v);
right.accept(v);
v.visit(this);
}
}
class Identifier : AstNode {
.. // Some stuff
public override void accept(Visitor v) { v.visit(this); }
}
class ValueCollector : Visitor {
public List<Integer> ints = ...;
public void visit(ConstantExpression e) { ints.Add(e.value); }
public void visit(BinaryExpression e) { }
public void visit(Identifier e) { }
}
类访问者{
公开摘要无效访问(ConstantExpression e);
公开摘要无效访问(二进制表达);
公开摘要无效访问(标识符e);
}
类AstNode{
../一些东西
公开摘要无效接受(访客v);
}
类ConstantPression:AstNode{
公共价值观;
../一些东西
公共覆盖无效接受(访问者){v.visit(this);}
}
类二进制表达式:AstNode{
私有节点左;
私有节点权;
../一些东西
公共覆盖无效接受(访客v){
左。接受(v);
对,接受(五),;
v、 访问(本);
}
}
类标识符:AstNode{
../一些东西
公共覆盖无效接受(访问者){v.visit(this);}
}
类ValueCollector:访问者{
公共列表整数=。。。;
公共无效访问(ConstantExpression e){ints.Add(e.value);}
公共无效访问(二进制表达式){}
公共无效访问(标识符e){}
}
使用AST的最佳实践包括:
- 获取好的解析器来帮助您从某种语法描述构建AST
- 获取一个好的库来帮助您浏览AST以收集/修改AST
对于使用AST的严肃工作,最好使用设计用于生成和操作AST的包。通常,这样的包包括很多额外的支持,例如模式匹配和/或使用源到源转换的AST重写
以下是几点:
- (我的产品)
演示如何仅使用BNF、模式和源代码到源代码转换来构建和操作AST。使用AST的最佳实践包括:
- 获取好的解析器来帮助您从某种语法描述构建AST
- 获取一个好的库来帮助您浏览AST以收集/修改AST
对于使用AST的严肃工作,最好使用设计用于生成和操作AST的包。这类软件包通常包括很多额外的支持,例如pa