我正在尝试实现具有扩展解析功能的库。我决定使用fsyacc,因为我从大学就知道了。不幸的是我遇到了以下问题
我为我的语法( Head )定义了一个类,并将其实现放在一个文件中。然后我将解析器定义为:
...
%start head
%type <Head> head
...
Fsyacc生成seeparated模块( Parser )。为了成功,必须按以下顺序进行编译:{{1}} Head.fs
为了使这个库与您在.NET中可以找到的类似,我想在 Head 中添加静态 Parse 方法。不幸的是,我需要使用 Parser 模块中的方法。
我知道可以使用“和”运算符解决此类类型依赖项,但它仅适用于在一个文件中定义的类型。
有没有其他方法可以创建依赖于彼此的类型,即使它们位于不同的文件中?我正在寻找声明/实现分离机制,就像在C / C ++中那样,但我不能找不到任何东西。
答案 0 :(得分:7)
简答:不。在F#2.0中,无法跨多个文件执行相互递归的实体。 (这是我们计划在下一版语言中解决的问题。)
您可以通过各种方式解决此问题,通常使用间接点和突变点。例如,你的Head类型可以有一个静态的'InitializeParser'方法,它将一个函数值发送到一个可变的全局变量中,然后在Head中定义的静态Parse方法可以通过那个可变的全局调用,并且在解析器实际定义之后,它可以去调用InitializeParser来调用值。(如果这没有意义,我可以更详细地说出来。)
答案 1 :(得分:3)
我希望有可能。在我阅读Brian的回复后,我开始寻找合适的解决方法。我不想强制库用户调用任何初始化方法。因此我想出了一些不同的东西。
如果编译器无法在编译时解析依赖项,我可以在运行时自己完成。 这是我的DepencendciesResolver的定义
module DependenciesResolver =
let GetMethod(_type, _method) =
lazy (
let a = System.Reflection.Assembly.GetExecutingAssembly()
let t = a.GetType(_type)
t.GetMethod(_method)
)
在分隔文件中定义的类的示例:
A.fs
namespace MutualRecursion
type A() =
static member _b = DependenciesResolver.GetMethod("MutualRecursion.B", "b")
static member b() = A._b.Value.Invoke(null, [||])
B.fs
nameespace MutualRecursion
type B =
static member b() =
printf "Called b()"
编译顺序为:A.fs B.fs
答案 2 :(得分:1)
你不能使用第三个文件解决这个问题,这个文件在这两个文件之后进行编译并使用新方法扩展Head吗?
答案 3 :(得分:1)
我会做类似以下的事情(我怀疑这正是Brian提出的建议)。请注意,用户不必进行任何棘手的初始化 - 类型本身知道如何“打结”。
<强> Head.fs 强>
type IParser =
abstract Parse : string -> int // or whatever
...
type Head() =
static let mutable parser = Unchecked.defaultof<IParser>
static member internal SetParser p = parser <- p
member x.DoSomethingCool() =
let i = parser.Parse("something to parse")
...
<强> Parser.fs 强>
type Parser private () =
static do
Head.SetParser (Parser())
interface IParser with
member x.Parse s = 0
...