2

Genlex+camlp4ストリーム パーサーが処理できるほど単純な言語用のパーサーを作成しています。ただし、解析エラーが発生した場合に備えて、多かれ少なかれ正確な場所(つまり、少なくとも行番号)を取得することにまだ興味があります。

char Stream私の考えは、以下のコードのように、行数を処理するオリジナルとtoken Streamof の間の中間ストリームを使用するGenlexことですが、より簡単な解決策があるかどうか疑問に思っていますか?

let parse_file s =
  let num_lines = ref 1 in
  let bol = ref 0 in
  let print_pos fmt i =
    (* Emacs-friendly location *)
    Printf.fprintf fmt "File %S, line %d, characters %d-%d:" 
      s !num_lines (i - !bol) (i - !bol)
  in
  (* Normal stream *)
  let chan = 
    try open_in s
    with
      Sys_error e -> Printf.eprintf "Cannot open %s: %s\n%!" s e; exit 1
  in
  let chrs = Stream.of_channel chan in
  (* Capture newlines and move num_lines and bol accordingly *)
  let next i =
    try
      match Stream.next chrs with
       | '\n' -> bol := i; incr num_lines; Some '\n'
       | c -> Some c
   with Stream.Failure -> None
  in
  let chrs = Stream.from next in
  (* Pass that to the Genlex's lexer *)
  let toks = lexer chrs in
  let error s =
    Printf.eprintf "%a\n%s %a\n%!"
      print_pos (Stream.count chrs) s print_top toks;
    exit 1
  in
  try
    parse toks
  with
    | Stream.Failure -> error "Failure"
    | Stream.Error e -> error ("Error " ^ e)
    | Parsing.Parse_error -> error "Unexpected symbol"
4

1 に答える 1

2

より簡単な解決策は、Camlp4 文法を使用することです。

このように構築されたパーサーを使用すると、(低レベルのツールである) ストリーム パーサーの場合とは異なり、適切なエラー メッセージを「無料で」取得できます。

OCaml のレクサーはすでにニーズに合っているため、独自のレクサーを定義する必要がない可能性があります。ただし、独自のレクサーが本当に必要な場合は、カスタム レクサーを簡単にプラグインできます。

module Camlp4Loc = Camlp4.Struct.Loc
module Lexer = MyLexer.Make(Camlp4Loc)
module Gram = Camlp4.Struct.Grammar.Static.Make(Lexer)

open Lexer

let entry = Gram.Entry.mk "entry"

EXTEND Gram
  entry: [ [ ... ] ];
END

let parse str =
   Gram.parse rule (Loc.mk file) (Stream.of_string str)

OCaml を初めて使用する場合、このモジュール システムのトリッキーはすべて、最初は黒いブードゥー教の魔法のように見えるかもしれません :-) Camlp4 が非常に文書化されていない獣であるという事実も、経験の非現実性に寄与する可能性があります。

そのため、メーリング リストで質問することを躊躇しないでください (愚かな質問でも構いません) 。

于 2012-10-30T19:25:34.463 に答える