2016-07-21 9 views
0

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

私はthis threadに従い、エラーがあった場合に詳細情報を印刷しようとしました。 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 "" } 

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

答えて

1

エラーのコンテキストでは、Parsing.symbol_start_posParsing.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) 
+0

入力として、あなたの関数はエラーを発生させる正確な**部分文字列**を返しますが、入力文字列全体を表示する方法を尋ねていました。しかし私は私の最初の質問は非常に簡単だと思う:私たちは 'Parser_e.main'または' Parse.statement'の周りにエラー処理をラップし、常にラッパーを呼び出すことができます...もっと正確なあなたの例とモジュールを知りたい。 – SoftTimur

+0

これは不可能です。パーザはそれ自体を知りません。故障の瞬間に、それはもはや遷移が存在しない状態にあることがわかります。この状態でどのようになったのかの歴史は保存されていません。デバッグモードを有効にしてこの履歴を印刷することはできますが、これはエンドユーザにとって素晴らしいパーサーエラーを作成することとは異なります。 – ivg

関連する問題