エラーのコンテキストでは、Parsing.symbol_start_pos
とParsing.symbol_end_pos
の関数を使用して、2つの位置の形式で失敗した語彙素の場所を抽出することができます。残念ながらParsing
モジュールは文字列としての字句へのアクセスを実際に提供しませんが、入力がファイルに格納されていれば、それを手動で抽出することも、コンパイラスタイルでエラーを出力することもできます。手動でモジュールParser_error
は下記の通りです。 Parser_error.T
例外を発生させる関数Parser_error.throw
が定義されています。この例外は、診断メッセージと失敗した語彙素の位置をうなずきます。ファイルからこの字句を抽出するため、またはファイル配置メッセージを生成するための便利な関数がいくつか用意されています。入力がファイルに格納されていない場合は、入力を文字列として受け取り、Parser_error.T
例外を受け入れる関数string_of_exn
を使用して、そこから問題の部分文字列を抽出することができます。これはエラー報告にこの例外を使用するパーサのexampleです。ここ
open Lexing
(** T(message,start,finish) parser failed with a [message] on an
input specified by [start] and [finish] position.*)
exception T of (string * position * position)
(** [throw msg] raise a [Parser_error.T] exception with corresponding
message. Must be called in a semantic action of a production rule *)
let throw my_unique_msg =
let check_pos f = try f() with _ -> dummy_pos in
Printexc.(print_raw_backtrace stderr (get_raw_backtrace()));
let sp = check_pos Parsing.symbol_start_pos in
let ep = check_pos Parsing.symbol_end_pos in
raise (T (my_unique_msg,sp,ep))
(** [fileposition start finish] creates a string describing a position
of an lexeme specified by [start] and [finish] file positions. The
message has the same format as OCaml and GNU compilers, so it is
recognized by most IDE, e.g., Emacs. *)
let fileposition err_s err_e =
Printf.sprintf
"\nFile \"%s\", line %d, at character %d-%d\n"
err_s.pos_fname err_s.pos_lnum err_s.pos_cnum err_e.pos_cnum
(** [string_of_exn line exn] given a [line] in a file, extract a failed
lexeme form the exception [exn] and create a string denoting the
parsing error in a format similar to the format used by OCaml
compiler, i.e., with fancy underlying. *)
let string_of_exn line (msg,err_s,err_e) =
let b = Buffer.create 42 in
if err_s.pos_fname <> "" then
Buffer.add_string b (fileposition err_s err_e);
Buffer.add_string b
(Printf.sprintf "Parse error: %s\n%s\n" msg line);
let start = max 0 (err_s.pos_cnum - err_s.pos_bol) in
for i=1 to start do
Buffer.add_char b ' '
done;
let diff = max 1 (err_e.pos_cnum - err_s.pos_cnum) in
for i=1 to diff do
Buffer.add_char b '^'
done;
Buffer.contents b
(** [extract_line err] a helper function that will extract a line from
a file designated by the parsing error exception *)
let extract_line err =
let line = ref "" in
try
let ic = open_in err.pos_fname in
for i=0 to max 0 (err.pos_lnum - 1) do
line := input_line ic
done;
close_in ic;
!line
with exn -> !line
(** [to_string exn] converts an exception to a string *)
let to_string ((msg,err,_) as exn) =
let line = extract_line err in
string_of_exn line exn
にはファイルが存在しない場合場合に使用する方法を示している、一例であり、入力ストリームまたは対話型(シェル状)ソースからのものである:文字列ラインを考える
let parse_command line =
try
let lbuf = Lexing.from_string line in
`Ok Parser.statement Lexer.tokens lbuf
with
| Parsing.Parse_error -> `Fail "Parse error"
| Parser_error.T exn -> `Fail (Parser_error.string_of_exn line exn)
出典
2016-07-21 14:24:37
ivg
入力として、あなたの関数はエラーを発生させる正確な**部分文字列**を返しますが、入力文字列全体を表示する方法を尋ねていました。しかし私は私の最初の質問は非常に簡単だと思う:私たちは 'Parser_e.main'または' Parse.statement'の周りにエラー処理をラップし、常にラッパーを呼び出すことができます...もっと正確なあなたの例とモジュールを知りたい。 – SoftTimur
これは不可能です。パーザはそれ自体を知りません。故障の瞬間に、それはもはや遷移が存在しない状態にあることがわかります。この状態でどのようになったのかの歴史は保存されていません。デバッグモードを有効にしてこの履歴を印刷することはできますが、これはエンドユーザにとって素晴らしいパーサーエラーを作成することとは異なります。 – ivg