我想在F#中编写一个解析器,因为我必须使用Antlr。这意味着我必须为每个要解析的AST节点定义一个Visitor
类。现在我遇到了一些问题,即存在一些循环依赖的规则,如:
boolExpr : boolTerm 'or' boolTerm ;
boolTerm : boolAtom 'and' boolAtom ;
boolAtom : '(' boolExpr ')'
| ... ;
这意味着我需要3个具有相同循环依赖关系的访问者类,我希望将每个访问者类放在他们自己的文件中
//BoolExprVisitor.fs
let boolExprVisitor = { new BaseVisitor<AST.BoolExpr>() with
override __.VisitBoolExpr(context: BoolExprContext) =
context.boolTerm() |> mapAccept boolTermVisitor |> AST.BoolExpr
}
//BoolTermVisitor.fs
let boolTermVisitor = { new BaseVisitor<AST.BoolTerm>() with
override __.VisitBoolTerm(context: BoolTermContext) =
context.boolAtom() |> mapAccept boolAtomVisitor |> AST.BoolTerm
}
//BoolAtomVisitor.fs
let boolAtomVisitor = { new BaseVisitor<AST.BoolAtom>() with
override __.VisitBoolAtom(context: BoolAtomContext) =
context.boolExpr() |> accept boolExprVisitor |> AST.BoolAtom
}
但是F#不喜欢这些循环依赖。如何使F#接受它们或重构我的访问者以不需要cyclid依赖?
答案 0 :(得分:2)
对于以后遇到此问题的任何人:
正如rmunn所说,我想要不同文件中的类的事实根本不是一个好设计。另外,对于BoolTerm
,BoolAtom
和BoolExpr
,我不需要不同的AST节点,因为它们都可以描述为同一节点BoolExpr
。
我的解决方案是将所有布尔表达式访问者合并到同一类中(并将表达式访问者的所有文件合并到一个文件中):
//AST.fs
type BoolExpr =
| BoolConjunctionExpr of BoolOp * BoolExpr list
| ...
//ExpressionVisitors.fs
let boolExprVisitor = { new BaseVisitor<AST.BoolExpr>() with
override this.VisitBoolExpr(context: BoolExprContext) =
context.boolTerm() |> mapAccept this |> AST.BoolConjunctionExpr AST.Or
override this.VisitBoolTerm(context: BoolTermContext) =
context.boolAtom() |> mapAccept this |> AST.BoolConjunctionExpr AST.And
override this.VisitBoolAtom(context: BoolAtomContext) =
context.boolExpr() |> accept this
}
答案 1 :(得分:0)
我认为如果您不想使用and
同时创建访问者实例,则必须使用可变变量。为其中一个访问者创建一个可变变量,其值为未选中的默认类型。然后,当您将实际实例分配给变量时。
//BoolExprVisitor.fs
module BoolExprVisitor =
let mutable boolTermVisitor = Unchecked.defaultof<BaseVisitor<AST.BoolTerm>>
let boolExprVisitor = { new BaseVisitor<AST.BoolExpr>() with
override __.VisitBoolExpr(context: BoolExprContext) =
context.boolTerm() |> mapAccept boolTermVisitor |> AST.BoolExpr
}
//BoolAtomVisitor.fs
module BoolAtomVisitor =
open BoolExprVisitor
let boolAtomVisitor = { new BaseVisitor<AST.BoolAtom>() with
override __.VisitBoolAtom(context: BoolAtomContext) =
context.boolExpr() |> accept boolExprVisitor |> AST.BoolAtom
}
//BoolTermVisitor.fs
module BoolTermVisitor
open BoolAtomVisitor
let boolTermVisitor = { new BaseVisitor<AST.BoolTerm>() with
override __.VisitBoolTerm(context: BoolTermContext) =
context.boolAtom() |> mapAccept boolAtomVisitor |> AST.BoolTerm
}
BoolExprVisitor.boolTermVisitor <- boolTermVisitor
请注意,这与在FParsec中使用createParserForwardedToRef
基本相同。