F# fslex fsyacc成熟稳定可用于生产代码吗?

10

阅读了一篇两年前的网页,对fslex/fsyacc进行了批评,称其有缺陷、速度慢、愚蠢等等,与OCamel相比。我想知道在词法和语法分析方面什么是最好的选择?

我以前用过ANTLR,并使用C#绑定,但现在正在学习F#,当我看到它带有一个解析器生成器时感到兴奋。由于F#现在正式发布,并且似乎是微软真正想要支持和开发的东西。您认为fslex和fsyacc是否值得用于生产代码吗?


页面在哪里?一直在寻找有关fLex/fsyacc的更多信息。目前为止相当稀少... - gjvdkamp
1
也许在这里:http://flyingfrogblog.blogspot.com/2009/07/problems-with-fslex-and-fsyacc.html - R. Martinho Fernandes
Harrop博士的需求可能比您自己的更高级(他开发相对复杂的商业软件)。根据您的需求,fslex/fsyacc可能已经足够了。 - Daniel
3个回答

11

Fslex和fsyacc被F#编译器使用,因此它们在某种程度上可用。我几年前使用过它们,在我的需求方面表现良好。

然而,我的经验是,在F#中,lex/yacc比OCaml不成熟得多。许多OCaml社区的人多年来一直在使用它们,包括许多学生(似乎使用它们编写小型解释器/编译器是一项常见练习)。我认为很少有F#开发人员使用它们,也不认为F#团队最近在这些工具上做了很多工作(例如,VS集成不是重点)。如果您不是非常苛求,Fslex和fsyacc可能就足够了。

一个解决方案可以是将Menhir(camlyacc替代品,具有几个不错的功能)适应于F#的使用。我不知道这需要多少工作。

个人而言,每当我需要编写解析器时,我现在都使用FParsec。它的使用方式非常不同,但也更加灵活,并且生成良好的解析错误消息。我一直对它感到非常满意,其作者在我有问题时总是非常乐于助人。


10

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

我不确定fslex/fsyacc与它们的OCaml等效物或ANTLR相比如何。然而,Frederik Holmstrom有一篇文章将ANTLR与用F#手写的解析器在IronJS中使用进行了比较。不幸的是,他没有fslex/fsyacc版本,因此没有直接比较。

回答一些具体问题-您可以获取MSBUILD任务以便在构建过程中运行fslex/fsyacc,因此它集成得非常好。您不会获得语法高亮,但我认为这并不是什么大问题。它可能比OCaml版本慢,但这只影响您更改解析器时的编译-我对F#解析器进行了一些修改,并没有发现编译时间是个问题。


1
你无法获得语法高亮。更重要的是,你不会得到任何错误反馈,因此你必须手动搜索源代码以找到每个错误的行和列位置。此外,我发现Visual Studio在多阶段构建中无法正确处理依赖关系,因此它总是重新编译所有内容,而这些工具非常缓慢。 - J D
FParsec是一个解析器组合器,非常快速、内存高效,并且提供完整的F#语法支持和能力(您只需使用F#编写所有内容,无需任何预编译)。 - Abel

6
fslex和fsyacc工具是专门为F#编译器编写的,不适合广泛使用。尽管如此,由于F#方面完全缺乏VS集成(OCaml具有出色的语法高亮显示、跳转到定义和错误回溯),我成功地将大量代码从OCaml移植到F#,但这是费力的。特别是,我尽可能多地将F#代码从词法分析器和解析器中移动出来。
我们经常需要编写解析器,并要求Microsoft官方支持fslex和fsyacc,但我认为这不会发生。
我的建议是,如果您需要翻译一个使用ocamllex和ocamlyacc的大型遗留OCaml代码库,请使用fslex和fsyacc。否则,请从头开始编写解析器。
我个人不喜欢解析器组合器库,而更喜欢使用类似于此s表达式解析器的主动模式编写解析器。
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#代码。我发现它易于阅读和可维护性。在我的生产代码中,性能已经超过了足够的水平。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接