mirror of
https://github.com/CatalaLang/catala.git
synced 2024-11-08 07:51:43 +03:00
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:
parent
c96c6e187f
commit
94f8eac858
@ -165,7 +165,7 @@ module Content = struct
|
||||
| MainMessage msg -> msg ppf
|
||||
| Result msg -> msg ppf
|
||||
| Suggestion suggestions_list ->
|
||||
Suggestions.display ppf suggestions_list)
|
||||
Suggestions.format ppf suggestions_list)
|
||||
ppf message_elements)
|
||||
content
|
||||
| Cli.GNU ->
|
||||
@ -177,35 +177,40 @@ module Content = struct
|
||||
let ppf = get_ppf target in
|
||||
Format.pp_print_list ~pp_sep:Format.pp_print_newline
|
||||
(fun ppf elt ->
|
||||
let pos, message = match elt with
|
||||
| MainMessage m ->
|
||||
let pos =
|
||||
List.find_map (function
|
||||
| Position {pos_message = None; pos} -> Some pos
|
||||
| _ -> None) content
|
||||
|> function
|
||||
| None ->
|
||||
List.find_map (function
|
||||
| Position {pos_message = _; pos} -> Some pos
|
||||
| _ -> None) content
|
||||
| some -> some
|
||||
in
|
||||
pos, m
|
||||
| Position { pos_message; pos } ->
|
||||
let message = match pos_message with
|
||||
| Some m -> m
|
||||
| None -> fun _ -> ()
|
||||
in
|
||||
(Some pos), message
|
||||
| Result m -> None, m
|
||||
| Suggestion sl -> None, fun ppf -> Suggestions.display ppf sl
|
||||
in
|
||||
Option.iter (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))
|
||||
let pos, message =
|
||||
match elt with
|
||||
| MainMessage m ->
|
||||
let pos =
|
||||
List.find_map
|
||||
(function
|
||||
| Position { pos_message = None; pos } -> Some pos
|
||||
| _ -> None)
|
||||
content
|
||||
|> function
|
||||
| None ->
|
||||
List.find_map
|
||||
(function
|
||||
| Position { pos_message = _; pos } -> Some pos
|
||||
| _ -> None)
|
||||
content
|
||||
| some -> some
|
||||
in
|
||||
pos, m
|
||||
| Position { pos_message; pos } ->
|
||||
let message =
|
||||
match pos_message with Some m -> m | None -> fun _ -> ()
|
||||
in
|
||||
Some pos, message
|
||||
| Result m -> None, m
|
||||
| Suggestion sl -> None, fun ppf -> Suggestions.format ppf sl
|
||||
in
|
||||
Option.iter
|
||||
(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;
|
||||
Format.pp_print_newline ppf ()
|
||||
end
|
||||
@ -220,19 +225,19 @@ exception CompilerError of Content.t
|
||||
|
||||
let raise_spanned_error
|
||||
?(span_msg : Content.message option)
|
||||
?(suggestion : string list option)
|
||||
?(suggestion = ([] : string list))
|
||||
(span : Pos.t)
|
||||
format =
|
||||
let continuation (message : Format.formatter -> unit) =
|
||||
raise
|
||||
(CompilerError
|
||||
([MainMessage message; Position { pos_message = span_msg; pos = span }]
|
||||
@ match suggestion with None -> [] | Some sugg -> [Suggestion sugg]))
|
||||
@ match suggestion with [] -> [] | sugg -> [Suggestion sugg]))
|
||||
in
|
||||
Format.kdprintf continuation format
|
||||
|
||||
let raise_multispanned_error_full
|
||||
?(suggestion : string list option)
|
||||
?(suggestion = ([] : string list))
|
||||
(spans : (Content.message option * Pos.t) list)
|
||||
format =
|
||||
Format.kdprintf
|
||||
@ -243,14 +248,14 @@ let raise_multispanned_error_full
|
||||
:: List.map
|
||||
(fun (pos_message, pos) -> Position { pos_message; pos })
|
||||
spans
|
||||
@ match suggestion with None -> [] | Some sugg -> [Suggestion sugg])))
|
||||
@ match suggestion with [] -> [] | sugg -> [Suggestion sugg])))
|
||||
format
|
||||
|
||||
let raise_multispanned_error
|
||||
?(suggestion : string list option)
|
||||
?(suggestion = ([] : string list))
|
||||
(spans : (string option * Pos.t) list)
|
||||
format =
|
||||
raise_multispanned_error_full ?suggestion
|
||||
raise_multispanned_error_full ~suggestion
|
||||
(List.map
|
||||
(fun (msg, pos) ->
|
||||
Option.map (fun s ppf -> Format.pp_print_string ppf s) msg, pos)
|
||||
|
@ -55,7 +55,7 @@ let levenshtein_distance (s : string) (t : string) : int =
|
||||
of keyword + 1, in order to get suggestions close to "keyword")*)
|
||||
let suggestion_minimum_levenshtein_distance_association
|
||||
(candidates : string list)
|
||||
(keyword : string) : string list option =
|
||||
(keyword : string) : string list =
|
||||
let rec strings_minimum_levenshtein_distance
|
||||
(minimum : int)
|
||||
(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.*)
|
||||
| [] -> result
|
||||
in
|
||||
let suggestions =
|
||||
strings_minimum_levenshtein_distance
|
||||
(1 + (String.length keyword / 3))
|
||||
(*In order to select suggestions that are not too far away from the
|
||||
keyword*)
|
||||
[] candidates
|
||||
in
|
||||
match suggestions with [] -> None | _ :: _ -> Some suggestions
|
||||
strings_minimum_levenshtein_distance
|
||||
(1 + (String.length keyword / 3))
|
||||
(*In order to select suggestions that are not too far away from the
|
||||
keyword*)
|
||||
[] candidates
|
||||
|
||||
let display (ppf : Format.formatter) (suggestions_list : string list) =
|
||||
let format (ppf : Format.formatter) (suggestions_list : string list) =
|
||||
match suggestions_list with
|
||||
| [] -> ()
|
||||
| _ :: _ ->
|
||||
|
23
compiler/catala_utils/suggestions.mli
Normal file
23
compiler/catala_utils/suggestions.mli
Normal 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
|
@ -130,7 +130,7 @@ let raise_error_cons_not_found
|
||||
in
|
||||
Message.raise_spanned_error
|
||||
~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 \
|
||||
a typographical error)."
|
||||
|
||||
|
@ -138,14 +138,14 @@ module ParserAux (LocalisedLexer : Lexer_common.LocalisedLexer) = struct
|
||||
Format.fprintf ppf "Message: @{<yellow>%s@}@,%t"
|
||||
(String.trim (String.uncapitalize_ascii msg)))
|
||||
(fun (ppf : Format.formatter) ->
|
||||
Format.fprintf ppf "You can only write : ";
|
||||
Format.fprintf ppf "You could have written : ";
|
||||
Format.pp_print_list
|
||||
~pp_sep:(fun ppf () -> Format.fprintf ppf ",@ or ")
|
||||
(fun ppf string -> Format.fprintf ppf "@{<yellow>\"%s\"@}" string)
|
||||
ppf
|
||||
(List.map (fun (s, _) -> s) acceptable_tokens))
|
||||
in
|
||||
raise_parser_error ?suggestion:similar_acceptable_tokens
|
||||
raise_parser_error ~suggestion:similar_acceptable_tokens
|
||||
(Pos.from_lpos (lexing_positions lexbuf))
|
||||
(Option.map Pos.from_lpos last_positions)
|
||||
(Utf8.lexeme lexbuf) custom_menhir_message
|
||||
|
@ -31,7 +31,7 @@ $ catala Interpret -s Test1
|
||||
[ERROR]
|
||||
Syntax error at token "scope"
|
||||
Message: expected either 'condition', or 'content' followed by the expected variable type
|
||||
You can only write : "condition",
|
||||
You could have written : "condition",
|
||||
or "content"
|
||||
|
||||
Error token:
|
||||
@ -75,7 +75,7 @@ $ catala Interpret -s Test2
|
||||
[ERROR]
|
||||
Syntax error at token "scope"
|
||||
Message: expected either 'condition', or 'content' followed by the expected variable type
|
||||
You can only write : "condition",
|
||||
You could have written : "condition",
|
||||
or "content"
|
||||
|
||||
Error token:
|
||||
@ -119,7 +119,7 @@ $ catala Interpret -s Test3
|
||||
[ERROR]
|
||||
Syntax error at token "scope"
|
||||
Message: expected either 'condition', or 'content' followed by the expected variable type
|
||||
You can only write : "condition",
|
||||
You could have written : "condition",
|
||||
or "content"
|
||||
|
||||
Error token:
|
||||
@ -165,7 +165,7 @@ $ catala Interpret -s Test4
|
||||
[ERROR]
|
||||
Syntax error at token "scope"
|
||||
Message: expected either 'condition', or 'content' followed by the expected variable type
|
||||
You can only write : "condition",
|
||||
You could have written : "condition",
|
||||
or "content"
|
||||
|
||||
Error token:
|
||||
|
37
tests/test_default/bad/typing_or_logical_error.catala_en
Normal file
37
tests/test_default/bad/typing_or_logical_error.catala_en
Normal 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#
|
||||
```
|
Loading…
Reference in New Issue
Block a user