F#fslex fsyacc是否适合生产代码?

时间:2011-03-22 15:48:00

标签: f# fslex fsyacc maturity

在阅读了一个2年历史的网页后,真的扯掉fslex / fsyacc,越野车,慢,愚蠢等等与他们的OCamel同行相比,我想知道什么是最适合lexing解析的需求?

我之前使用过ANTLR和C#绑定,但我目前正在学习F#,当我看到它附带一个解析器生成器时很兴奋。由于F#现在正式发布,似乎微软真的希望支持和​​发展。你会说fslex和fsyacc对于生产代码是否值得?

3 个答案:

答案 0 :(得分:10)

Fslex和fsyacc当然可以用于生产。毕竟,它们在Microsoft Visual Studio 2010中使用,因为F#lexer和解析器是使用它们编写的(F# compiler source code也是演示如何有效使用它们的一个很好的例子。)

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

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

答案 1 :(得分:10)

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

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

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

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

答案 2 :(得分:5)

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集成,因为它只是vanilla F#代码。我发现它易于阅读和维护。在我的生产代码中,性能已经足够了。