在阅读了一个2年历史的网页后,真的扯掉fslex / fsyacc,越野车,慢,愚蠢等等与他们的OCamel同行相比,我想知道什么是最适合lexing解析的需求?
我之前使用过ANTLR和C#绑定,但我目前正在学习F#,当我看到它附带一个解析器生成器时很兴奋。由于F#现在正式发布,似乎微软真的希望支持和发展。你会说fslex和fsyacc对于生产代码是否值得?
答案 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#代码。我发现它易于阅读和维护。在我的生产代码中,性能已经足够了。