Dans le contexte d'une erreur, vous pouvez extraire un emplacement de lexème défaillant dans un format à deux positions, en utilisant les fonctions Parsing.symbol_start_pos
et Parsing.symbol_end_pos
. Malheureusement, le module Parsing
ne fournit pas vraiment un accès au lexème sous forme de chaîne, mais si l'entrée a été stockée dans un fichier, il est possible de l'extraire manuellement ou d'imprimer une erreur dans un compilateur. manuellement. Un module Parser_error
est ci-dessous. Il définit la fonction Parser_error.throw
qui déclenchera une exception Parser_error.T
. L'exception porte sur un message de diagnostic et une position d'un lexème défectueux. Plusieurs fonctions pratiques sont fournies pour extraire ce lexème d'un fichier ou pour générer un message de fichier. Si votre entrée n'est pas stockée dans un fichier, vous pouvez utiliser la fonction string_of_exn
qui accepte l'entrée en tant que chaîne et l'exception Parser_error.T
, et en extrait la sous-chaîne incriminée. Ceci est un example d'un analyseur qui utilise cette exception pour les rapports d'erreurs.
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
Voici un exemple qui montre comment utiliser au cas où il n'y a pas de fichier, et l'entrée est à partir d'un flux ou interactif (coquille) source:
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)
Compte tenu d'une ligne de chaîne en entrée, vos fonctions renvoient la ** sous-chaîne ** précise qui soulève des erreurs, alors que je demandais comment montrer toute la chaîne d'entrée. Mais je pense que ma question initiale est assez simple: on pourrait juste enrouler une erreur de manipulation autour de 'Parser_e.main' ou' Parse.statement' et toujours appeler le wrapper ... Bon à savoir votre exemple et module qui est plus précis .. – SoftTimur
Cela ne serait pas possible, car l'analyseur ne le sait pas tout seul. Au moment de l'échec, il se trouve dans un état où il n'y a plus de transitions. L'historique de la façon dont il a fini dans cet état n'est pas stocké. Vous pouvez activer le mode de débogage et imprimer cet historique, mais cela est différent de la création d'une erreur d'analyse pour un utilisateur final. – ivg