10

fslex/fsyacc、バギー、遅い、愚かなどを実際にリッピングしている 2 年前の Web ページを読んだ後、OCamel の対応するものと比較して、構文解析の必要性をレクシングするための最善の策は何だろうか?

以前は C# バインディングで ANTLR を使用していましたが、現在 F# を学習中であり、パーサー ジェネレーターが付属しているのを見て興奮しました。F# は現在正式にリリースされており、Microsoft が実際にサポートと開発を目指しているようです。fslex と fsyacc は製品コードにとって価値があると思いますか?

4

3 に答える 3

11

Fslex と fsyacc は F# コンパイラで使用されるため、ある程度機能します。私は数年前にそれらを使用しましたが、私のニーズには十分でした。

しかし、私の経験では、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# コードを移動しました。

私たちはしばしばパーサーを書く必要があり、Microsoft に fslex と fsyacc の公式サポートを追加するように依頼しましたが、これが実現するとは思いません。

私のアドバイスは、ocamllex と ocamllyacc を使用する大規模なレガシー 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

このアプローチは単なるバニラ F# コードであるため、VS 統合は必要ありません。読みやすく、保守しやすいと思います。私の製品コードでは、パフォーマンスは十分以上でした。

于 2013-10-05T02:48:25.947 に答える