Better indications for the user :

- We're able to say from the parser what the user could have written. It may not be complete due to the acceptable function of Menhir : it is only an indication given to the user (and not intended to be an adaptive documentation)
- .mli file added : module interface for suggestions
- Add a test that provides a typographical or a logical error.
- Documentation and type / name changes for suggestions
This commit is contained in:
Aminata-Dev 2023-07-12 16:32:55 +02:00
parent c96c6e187f
commit 94f8eac858
7 changed files with 115 additions and 53 deletions

View File

@ -165,7 +165,7 @@ module Content = struct
| MainMessage msg -> msg ppf | MainMessage msg -> msg ppf
| Result msg -> msg ppf | Result msg -> msg ppf
| Suggestion suggestions_list -> | Suggestion suggestions_list ->
Suggestions.display ppf suggestions_list) Suggestions.format ppf suggestions_list)
ppf message_elements) ppf message_elements)
content content
| Cli.GNU -> | Cli.GNU ->
@ -177,35 +177,40 @@ module Content = struct
let ppf = get_ppf target in let ppf = get_ppf target in
Format.pp_print_list ~pp_sep:Format.pp_print_newline Format.pp_print_list ~pp_sep:Format.pp_print_newline
(fun ppf elt -> (fun ppf elt ->
let pos, message = match elt with let pos, message =
| MainMessage m -> match elt with
let pos = | MainMessage m ->
List.find_map (function let pos =
| Position {pos_message = None; pos} -> Some pos List.find_map
| _ -> None) content (function
|> function | Position { pos_message = None; pos } -> Some pos
| None -> | _ -> None)
List.find_map (function content
| Position {pos_message = _; pos} -> Some pos |> function
| _ -> None) content | None ->
| some -> some List.find_map
in (function
pos, m | Position { pos_message = _; pos } -> Some pos
| Position { pos_message; pos } -> | _ -> None)
let message = match pos_message with content
| Some m -> m | some -> some
| None -> fun _ -> () in
in pos, m
(Some pos), message | Position { pos_message; pos } ->
| Result m -> None, m let message =
| Suggestion sl -> None, fun ppf -> Suggestions.display ppf sl match pos_message with Some m -> m | None -> fun _ -> ()
in in
Option.iter (fun pos -> Format.fprintf ppf "@{<blue>%s@}: " Some pos, message
(Pos.to_string_short pos)) | Result m -> None, m
pos; | Suggestion sl -> None, fun ppf -> Suggestions.format ppf sl
pp_marker target ppf; in
Format.pp_print_char ppf ' '; Option.iter
Format.pp_print_string ppf (unformat message)) (fun pos ->
Format.fprintf ppf "@{<blue>%s@}: " (Pos.to_string_short pos))
pos;
pp_marker target ppf;
Format.pp_print_char ppf ' ';
Format.pp_print_string ppf (unformat message))
ppf content; ppf content;
Format.pp_print_newline ppf () Format.pp_print_newline ppf ()
end end
@ -220,19 +225,19 @@ exception CompilerError of Content.t
let raise_spanned_error let raise_spanned_error
?(span_msg : Content.message option) ?(span_msg : Content.message option)
?(suggestion : string list option) ?(suggestion = ([] : string list))
(span : Pos.t) (span : Pos.t)
format = format =
let continuation (message : Format.formatter -> unit) = let continuation (message : Format.formatter -> unit) =
raise raise
(CompilerError (CompilerError
([MainMessage message; Position { pos_message = span_msg; pos = span }] ([MainMessage message; Position { pos_message = span_msg; pos = span }]
@ match suggestion with None -> [] | Some sugg -> [Suggestion sugg])) @ match suggestion with [] -> [] | sugg -> [Suggestion sugg]))
in in
Format.kdprintf continuation format Format.kdprintf continuation format
let raise_multispanned_error_full let raise_multispanned_error_full
?(suggestion : string list option) ?(suggestion = ([] : string list))
(spans : (Content.message option * Pos.t) list) (spans : (Content.message option * Pos.t) list)
format = format =
Format.kdprintf Format.kdprintf
@ -243,14 +248,14 @@ let raise_multispanned_error_full
:: List.map :: List.map
(fun (pos_message, pos) -> Position { pos_message; pos }) (fun (pos_message, pos) -> Position { pos_message; pos })
spans spans
@ match suggestion with None -> [] | Some sugg -> [Suggestion sugg]))) @ match suggestion with [] -> [] | sugg -> [Suggestion sugg])))
format format
let raise_multispanned_error let raise_multispanned_error
?(suggestion : string list option) ?(suggestion = ([] : string list))
(spans : (string option * Pos.t) list) (spans : (string option * Pos.t) list)
format = format =
raise_multispanned_error_full ?suggestion raise_multispanned_error_full ~suggestion
(List.map (List.map
(fun (msg, pos) -> (fun (msg, pos) ->
Option.map (fun s ppf -> Format.pp_print_string ppf s) msg, pos) Option.map (fun s ppf -> Format.pp_print_string ppf s) msg, pos)

View File

@ -55,7 +55,7 @@ let levenshtein_distance (s : string) (t : string) : int =
of keyword + 1, in order to get suggestions close to "keyword")*) of keyword + 1, in order to get suggestions close to "keyword")*)
let suggestion_minimum_levenshtein_distance_association let suggestion_minimum_levenshtein_distance_association
(candidates : string list) (candidates : string list)
(keyword : string) : string list option = (keyword : string) : string list =
let rec strings_minimum_levenshtein_distance let rec strings_minimum_levenshtein_distance
(minimum : int) (minimum : int)
(result : string list) (result : string list)
@ -88,16 +88,13 @@ let suggestion_minimum_levenshtein_distance_association
(*The "result" list is returned at the end of the "candidates'" list.*) (*The "result" list is returned at the end of the "candidates'" list.*)
| [] -> result | [] -> result
in in
let suggestions = strings_minimum_levenshtein_distance
strings_minimum_levenshtein_distance (1 + (String.length keyword / 3))
(1 + (String.length keyword / 3)) (*In order to select suggestions that are not too far away from the
(*In order to select suggestions that are not too far away from the keyword*)
keyword*) [] candidates
[] candidates
in
match suggestions with [] -> None | _ :: _ -> Some suggestions
let display (ppf : Format.formatter) (suggestions_list : string list) = let format (ppf : Format.formatter) (suggestions_list : string list) =
match suggestions_list with match suggestions_list with
| [] -> () | [] -> ()
| _ :: _ -> | _ :: _ ->

View File

@ -0,0 +1,23 @@
(* This file is part of the Catala compiler, a specification language for tax
and social benefits computation rules. Copyright (C) 2023 Inria, contributor:
Aminata Boiguillé <aminata.boiguille@etu.sorbonne-universite.fr>, Emile
Rolley <emile.rolley@tuta.io>
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License. *)
val suggestion_minimum_levenshtein_distance_association :
string list -> string -> string list
(**Returns a list of the closest words into {!name:candidates} to the keyword
{!name:keyword}*)
val format : Format.formatter -> string list -> unit

View File

@ -130,7 +130,7 @@ let raise_error_cons_not_found
in in
Message.raise_spanned_error Message.raise_spanned_error
~span_msg:(fun ppf -> Format.fprintf ppf "Here is your code :") ~span_msg:(fun ppf -> Format.fprintf ppf "Here is your code :")
?suggestion:closest_constructors (Mark.get constructor) ~suggestion:closest_constructors (Mark.get constructor)
"The name of this constructor has not been defined before@ (it's probably \ "The name of this constructor has not been defined before@ (it's probably \
a typographical error)." a typographical error)."

View File

@ -138,14 +138,14 @@ module ParserAux (LocalisedLexer : Lexer_common.LocalisedLexer) = struct
Format.fprintf ppf "Message: @{<yellow>%s@}@,%t" Format.fprintf ppf "Message: @{<yellow>%s@}@,%t"
(String.trim (String.uncapitalize_ascii msg))) (String.trim (String.uncapitalize_ascii msg)))
(fun (ppf : Format.formatter) -> (fun (ppf : Format.formatter) ->
Format.fprintf ppf "You can only write : "; Format.fprintf ppf "You could have written : ";
Format.pp_print_list Format.pp_print_list
~pp_sep:(fun ppf () -> Format.fprintf ppf ",@ or ") ~pp_sep:(fun ppf () -> Format.fprintf ppf ",@ or ")
(fun ppf string -> Format.fprintf ppf "@{<yellow>\"%s\"@}" string) (fun ppf string -> Format.fprintf ppf "@{<yellow>\"%s\"@}" string)
ppf ppf
(List.map (fun (s, _) -> s) acceptable_tokens)) (List.map (fun (s, _) -> s) acceptable_tokens))
in in
raise_parser_error ?suggestion:similar_acceptable_tokens raise_parser_error ~suggestion:similar_acceptable_tokens
(Pos.from_lpos (lexing_positions lexbuf)) (Pos.from_lpos (lexing_positions lexbuf))
(Option.map Pos.from_lpos last_positions) (Option.map Pos.from_lpos last_positions)
(Utf8.lexeme lexbuf) custom_menhir_message (Utf8.lexeme lexbuf) custom_menhir_message

View File

@ -31,7 +31,7 @@ $ catala Interpret -s Test1
[ERROR] [ERROR]
Syntax error at token "scope" Syntax error at token "scope"
Message: expected either 'condition', or 'content' followed by the expected variable type Message: expected either 'condition', or 'content' followed by the expected variable type
You can only write : "condition", You could have written : "condition",
or "content" or "content"
Error token: Error token:
@ -75,7 +75,7 @@ $ catala Interpret -s Test2
[ERROR] [ERROR]
Syntax error at token "scope" Syntax error at token "scope"
Message: expected either 'condition', or 'content' followed by the expected variable type Message: expected either 'condition', or 'content' followed by the expected variable type
You can only write : "condition", You could have written : "condition",
or "content" or "content"
Error token: Error token:
@ -119,7 +119,7 @@ $ catala Interpret -s Test3
[ERROR] [ERROR]
Syntax error at token "scope" Syntax error at token "scope"
Message: expected either 'condition', or 'content' followed by the expected variable type Message: expected either 'condition', or 'content' followed by the expected variable type
You can only write : "condition", You could have written : "condition",
or "content" or "content"
Error token: Error token:
@ -165,7 +165,7 @@ $ catala Interpret -s Test4
[ERROR] [ERROR]
Syntax error at token "scope" Syntax error at token "scope"
Message: expected either 'condition', or 'content' followed by the expected variable type Message: expected either 'condition', or 'content' followed by the expected variable type
You can only write : "condition", You could have written : "condition",
or "content" or "content"
Error token: Error token:

View File

@ -0,0 +1,37 @@
###Article
```catala
declaration scope A:
output wrong_definition content integer
scope A:
definition wrong_definition = 1
```
```catala-test-inline
$ catala Interpret -s A
[ERROR]
Syntax error at token "="
Message: expected 'under condition' followed by a condition, 'equals' followed by the definition body, or the rest of the variable qualified name
You could have written : "of",
or "state",
or "equals",
or "under condition",
or "."
Error token:
┌─⯈ tests/test_default/bad/typing_or_logical_error.catala_en:8.30-8.31:
└─┐
8 │ definition wrong_definition = 1
│ ‾
Last good token:
┌─⯈ tests/test_default/bad/typing_or_logical_error.catala_en:8.13-8.29:
└─┐
8 │ definition wrong_definition = 1
│ ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
Maybe you wanted to write : "."
#return code 123#
```