4

我正在尝试实现一个具有扩展解析功能的库。我决定使用 fsyacc,因为我从大学就知道它。不幸的是我遇到了以下问题。

我为我的语法头( Head )定义了一个类,并将其实现放在一个文件中。然后我将解析器定义为:

...
%start head
%type <Head> head
...

Fsyacc 生成分离模块(Parser)。为了成功,它必须按以下顺序编译:Head.fs Parser.fs

为了使这个库类似于您在 .NET 中可以找到的库,我想向Head添加一个静态Parse方法。不幸的是,我需要使用Parser模块中的方法。

我知道这种类型的依赖可以用''运算符来解决,但它只适用于在一个文件中定义的类型。

有没有其他方法可以创建相互依赖的类型,即使它们位于单独的文件中?我一直在寻找像 C/C++ 中的声明/实现分离机制,但我找不到任何东西。

4

4 回答 4

7

简短的回答:没有。在 F# 2.0 中,无法跨多个文件执行相互递归的实体。(这是我们计划在下一版本的语言中解决的问题。)

您可以通过多种方式解决此问题,通常使用间接点和突变。例如,您的 Head 类型可能有一个静态的“InitializeParser”方法,该方法将函数值插入可变全局变量,然后 Head 中定义的静态 Parse 方法可以通过该可变全局调用,并且在实际定义解析器之后,它可以去调用 InitializeParser 来插入值。(如果这没有意义,我可以更详细地拼出来。)

于 2010-10-21T01:25:07.513 回答
3

我希望这是可能的。在我阅读了布赖恩的回复后,我开始寻找合适的解决方法。我不想强迫图书馆用户调用任何初始化方法。因此,我想出了一些不同的东西。

如果编译器无法在编译时解决依赖关系,我可以在运行时自行解决。这是我的 DepenendciesResolver 的定义

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

于 2010-10-21T10:18:29.917 回答
1

您不能使用在这两个文件之后编译并使用新方法扩展 Head 的第三个文件来解决这个问题吗?

于 2010-10-21T08:26:18.573 回答
1

我会做类似以下的事情(我怀疑这大致是布赖恩的提议)。请注意,用户不必进行任何棘手的初始化 - 类型本身知道如何“打结”。

头文件

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")
    ...

解析器.fs

type Parser private () =
  static do
    Head.SetParser (Parser())
  interface IParser with
    member x.Parse s = 0
    ...
于 2010-10-21T15:50:33.723 回答