1

menhir文字列から式ASTへの式を解析しようとするフロントエンドが書かれています。フロントエンドのエントリ ポイントは、Parser_e.main私の OCaml コードのいくつかの異なる場所で呼び出されます。そのため、外部ではなくフロントエンド内で発生する可能性のあるエラーをキャッチできるようにしたいと考えています。エラーをキャッチするときに表示したい特定の重要な情報は、フロントエンドが解析できない入力文字列全体です。(フロントエンドはほとんどすべてを読み取ることができるため、レクサーからのエラーは非常にまれです)。

そこで、このスレッドをたどって、エラーが発生したときに詳細情報を出力しようとしました。にparser_e.mly、追加しました

exception LexErr of string
exception ParseErr of string

let error msg start finish  = 
  Printf.sprintf "(line %d: char %d..%d): %s" start.pos_lnum 
       (start.pos_cnum - start.pos_bol) (finish.pos_cnum - finish.pos_bol) msg

let parse_error msg nterm =
  raise (ParseErr (error msg (rhs_start_pos nterm) (rhs_end_pos nterm)))

e_expression:
/* empty */ { EE_empty }
| INTEGER { EE_integer $1 }
| DOUBLE { EE_double $1 }
...
| error { parse_error "e_expression" 1; ERR "" }

しかし、まだ入力文字列を情報として持っていません。それを得るために私が欠けている機能がある場合、誰かがいますか?

4

1 に答える 1

2

Parsing.symbol_start_posエラーのコンテキストでは、関数と関数を使用して、失敗した語彙素の場所を 2 つの位置の形式で抽出できParsing.symbol_end_posます。残念ながら、Parsingモジュールは語彙素への文字列としてのアクセスを実際には提供しませんが、入力がファイルに保存されている場合は、手動で抽出するか、コンパイラ スタイルでエラーを出力することができます。これにより、派生した IDE がそれを理解して強調表示します。手動で。モジュールParser_errorは以下です。例外を発生Parser_error.throwさせる関数を定義します。Parser_error.T例外は、診断メッセージと失敗した語彙素の位置を伝えます。この語彙素をファイルから抽出したり、ファイル位置メッセージを生成したりするために、いくつかの便利な関数が提供されています。入力がファイルに保存されていない場合string_of_exnは、入力を文字列として受け入れる関数を使用できます。Parser_error.T例外であり、そこから問題のある部分文字列を抽出します。これは、エラー報告にこの例外を使用するパーサーの例です。

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-21T14:24:37.217 に答える