diff --git a/compiler/catala_utils/message.ml b/compiler/catala_utils/message.ml index 93ff8149..9c13a13f 100644 --- a/compiler/catala_utils/message.ml +++ b/compiler/catala_utils/message.ml @@ -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 "@{%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 "@{%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) diff --git a/compiler/catala_utils/suggestions.ml b/compiler/catala_utils/suggestions.ml index 83c60170..0428e7b1 100644 --- a/compiler/catala_utils/suggestions.ml +++ b/compiler/catala_utils/suggestions.ml @@ -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 | [] -> () | _ :: _ -> diff --git a/compiler/catala_utils/suggestions.mli b/compiler/catala_utils/suggestions.mli new file mode 100644 index 00000000..66527c5a --- /dev/null +++ b/compiler/catala_utils/suggestions.mli @@ -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é , Emile + Rolley + + 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 diff --git a/compiler/desugared/from_surface.ml b/compiler/desugared/from_surface.ml index 4782cd30..eab2d0a5 100644 --- a/compiler/desugared/from_surface.ml +++ b/compiler/desugared/from_surface.ml @@ -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)." diff --git a/compiler/surface/parser_driver.ml b/compiler/surface/parser_driver.ml index 737cfd0a..db8bb25a 100644 --- a/compiler/surface/parser_driver.ml +++ b/compiler/surface/parser_driver.ml @@ -138,14 +138,14 @@ module ParserAux (LocalisedLexer : Lexer_common.LocalisedLexer) = struct Format.fprintf ppf "Message: @{%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 "@{\"%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 diff --git a/examples/NSW_community_gaming/tests/test_nsw_social_housie.catala_en b/examples/NSW_community_gaming/tests/test_nsw_social_housie.catala_en index cbc72deb..dc2c2c27 100644 --- a/examples/NSW_community_gaming/tests/test_nsw_social_housie.catala_en +++ b/examples/NSW_community_gaming/tests/test_nsw_social_housie.catala_en @@ -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: diff --git a/tests/test_default/bad/typing_or_logical_error.catala_en b/tests/test_default/bad/typing_or_logical_error.catala_en new file mode 100644 index 00000000..b22a00ea --- /dev/null +++ b/tests/test_default/bad/typing_or_logical_error.catala_en @@ -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# +```