2016-07-21 28 views
0

Ich habe ein Frontend in menhir geschrieben, die versucht, einen Ausdruck zu analysieren: von einer Zeichenfolge zu einem Ausdruck AST. Der Einstiegspunkt des Frontends Parser_e.main wird an verschiedenen Stellen in meinem OCaml-Code aufgerufen. Ich möchte also mögliche Fehler innerhalb des Frontends eher als außerhalb wahrnehmen können. Wenn ich einen Fehler erhalte, ist eine bestimmte wichtige Information, die ich zeigen möchte, die gesamte Eingabezeichenfolge, die das Frontend nicht analysieren kann. (Fehler vom Lexer sind sehr selten, weil das Frontend fast alles lesen kann).Erhalten Sie die Eingabezeichenfolge, die Parsing-Fehler im Parser löst

Also habe ich versucht, this thread zu folgen, und um mehr Informationen zu drucken, wenn ein Fehler vorliegt. In parser_e.mly habe ich

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 "" } 

hinzugefügt Aber es hat noch nicht die Eingabestring als Information. Hat jemand, wenn es irgendeine Funktion gibt, die ich vermisse, um das zu bekommen?

Antwort

1

Im Kontext eines Fehlers können Sie einen Speicherort für fehlgeschlagene Lexeme in einem Format von zwei Positionen extrahieren, indem Sie die Funktionen Parsing.symbol_start_pos und Parsing.symbol_end_pos verwenden. Leider bietet Parsing Modul nicht wirklich einen Zugriff auf das Lexem als String, aber wenn die Eingabe in Datei gespeichert wurde, ist es möglich, es manuell zu extrahieren oder einen Fehler in einem Compiler-Stil zu drucken, den eine Descent IDE verstehen und hervorheben wird es manuell. Ein Modul Parser_error ist unten. Es definiert die Funktion Parser_error.throw, die eine Parser_error.T Ausnahme auslösen wird. Die Ausnahme verursacht eine Diagnosemeldung und eine Position eines fehlgeschlagenen Lexems. Um dieses Lexem aus einer Datei zu extrahieren oder um eine Dateipositionsnachricht zu erzeugen, sind einige nützliche Funktionen vorgesehen. Wenn Ihre Eingabe nicht in einer Datei gespeichert ist, können Sie die string_of_exn-Funktion verwenden, die die Eingabe als Zeichenfolge und die Ausnahme Parser_error.T akzeptiert, und die betreffende Teilzeichenfolge daraus extrahieren. Dies ist ein example eines Parsers, der diese Ausnahme für die Fehlerberichterstattung verwendet. Hier

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 

ist ein Beispiel, das zeigt, wie in dem Fall verwendet werden, wenn es keine Datei ist, und Eingang ist von einem Strom oder interaktiv (schalenartige) Quelle:

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

eine Zeichenfolge Linie Gegeben Als Eingabe geben Ihre Funktionen die genaue ** Teilzeichenfolge ** zurück, die Fehler verursacht, während ich gefragt habe, wie die gesamte Eingabezeichenfolge angezeigt werden soll. Aber ich denke, meine anfängliche Frage ist ziemlich einfach: Wir könnten einfach eine Fehlerbehandlung um 'Parser_e.main' oder' Parse.statement' wickeln und immer den Wrapper aufrufen ... Gut zu wissen, welches Beispiel und Modul genauer ist .. – SoftTimur

+0

Dies ist nicht möglich, da der Parser es nicht selbst kennt. Im Moment des Scheiterns befindet es sich in einem Zustand, in dem es keine Übergänge mehr gibt. Die Geschichte, wie es in diesem Zustand gelandet ist, wird nicht gespeichert. Sie können den Debugging-Modus aktivieren und dieses Protokoll drucken, aber das unterscheidet sich von einem netten Parser-Fehler für einen Endbenutzer. – ivg