10

在阅读了一个 2 年历史的网页后,与他们的 OCamel 同行相比,它确实撕裂了 fslex/fsyacc、错误、缓慢、愚蠢等,我想知道什么是词法解析需求的最佳选择?

我以前用 C# 绑定使用过 ANTLR,但目前正在学习 F#,当我看到它带有解析器生成器时很兴奋。由于 F# 现已正式发布,微软似乎真正致力于支持和开发。你会说 fslex 和 fsyacc 对于生产代码来说值得吗?

4

3 回答 3

11

F# 编译器使用 Fslex 和 fsyacc,因此它们可以工作。几年前我用过它们,它足以满足我的需求。

然而,我的经验是 lex/yacc 在 F# 中的成熟度远低于在 OCaml 中的成熟度。OCaml 社区中的许多人已经使用它们多年,包括许多学生(似乎用它们编写一个小型解释器/编译器是一种常见的练习)。我认为没有多少 F# 开发人员使用过它们,而且我认为 F# 团队最近没有在这些工具上做很多工作(例如,VS 集成不是优先事项)。如果你不是很紧急,Fslex 和 fsyacc 对你来说就足够了。

一个解决方案可能是调整 Menhir(具有几个不错功能的 camlyacc 替代品)以将其与 F# 一起使用。我不知道会做多少工作。

就个人而言,我现在每次需要编写解析器时都使用FParsec 。使用起来完全不同,但它也更加灵活,并且可以生成良好的解析错误消息。我对它非常满意,当我有问题时,它的作者总是非常乐于助人。

于 2011-03-23T01:08:23.490 回答
10

Fslex 和 fsyacc 肯定已准备好用于生产。毕竟,它们在 Microsoft Visual Studio 2010 中使用,因为 F# 词法分析器和解析器是使用它们编写的(F# 编译器源代码也是一个很好的示例,它演示了如何有效地使用它们)。

我不确定 fslex/fsyacc 与他们的 OCaml 等价物或 ANTLR 相比如何。然而,Frederik Holmstrom 有一篇文章将 ANTLR 与IronJS 中使用的用F# 编写的手写解析器进行了比较。可惜他没有fslex/fsyacc版本,所以没有直接对比。

要回答一些具体问题 - 您可以获得用于运行 fslex/fsyacc 的 MSBUILD 任务作为构建的一部分,因此它集成得很好。你没有得到语法高亮,但我认为这没什么大不了的。它可能比 OCaml 版本慢,但仅当您更改解析器时才会影响编译 - 我对 F# 解析器进行了一些修改,但没有发现编译时间有问题。

于 2011-03-22T17:06:40.677 回答
6

fslex 和 fsyacc 工具是专门为 F# 编译器编写的,不适合更广泛的使用。也就是说,由于这些工具,我已经设法将重要的代码库从 OCaml 移植到 F#,但由于在 F# 端完全缺乏 VS 集成,这很费力(OCaml与语法突出显示、跳转到定义和错误有很好的集成回归)。特别是,我将尽可能多的 F# 代码移出词法分析器和解析器。

我们经常需要编写解析器,并要求微软添加对 fslex 和 fsyacc 的官方支持,但我不相信这会发生。

我的建议是仅当您面临翻译使用 ocamllex 和 ocamlyacc 的大型遗留 OCaml 代码库时才使用 fslex 和 fsyacc。否则,从头开始编写解析器。

我个人不是解析器组合库的粉丝,而是更喜欢使用看起来像这个 s-expression 解析器的活动模式来编写解析器:

let alpha = set['A'..'Z'] + set['a'..'z']
let numeric = set['0'..'9']
let alphanumeric = alpha + numeric

let (|Empty|Next|) (s: string, i) =
  if i < s.Length then Next(s.[i], (s, i+1)) else Empty

let (|Char|_|) alphabet = function
  | Empty -> None
  | s, i when Set.contains s.[i] alphabet -> Some(s, i+1)
  | _ -> None

let rec (|Chars|) alphabet = function
  | Char alphabet (Chars alphabet it)
  | it -> it

let sub (s: string, i0) (_, i1) =
  s.Substring(i0, i1-i0)

let rec (|SExpr|_|) = function
  | Next ((' ' | '\n' | '\t'), SExpr(f, it)) -> Some(f, it)
  | Char alpha (Chars alphanumeric it1) as it0 -> Some(box(sub it0 it1), it1)
  | Next ('(', SExprs(fs, Next(')', it))) -> Some(fs, it)
  | _ -> None
and (|SExprs|) = function
  | SExpr(f, SExprs(fs, it)) -> box(f, fs), it
  | it -> null, it

这种方法不需要任何 VS 集成,因为它只是普通的 F# 代码。我发现它易于阅读和维护。在我的生产代码中,性能已经绰绰有余。

于 2013-10-05T02:48:25.947 回答