From 899679b86cc0ed269db85dbcb8ba61a85415bc87 Mon Sep 17 00:00:00 2001 From: Denis Merigoux Date: Mon, 5 Oct 2020 00:39:29 +0200 Subject: [PATCH] Much better syntax error messages --- src/catala/catala_surface/parser.messages | 10 +++--- src/catala/catala_surface/parser_driver.ml | 41 +++++++++++++++------- src/catala/catala_surface/parser_errors.ml | 10 +++--- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/catala/catala_surface/parser.messages b/src/catala/catala_surface/parser.messages index 31f83c20..564cbc8b 100644 --- a/src/catala/catala_surface/parser.messages +++ b/src/catala/catala_surface/parser.messages @@ -1191,7 +1191,7 @@ source_file_or_master: LAW_ARTICLE BEGIN_CODE SCOPE CONSTRUCTOR UNDER_CONDITION ## In state 109, spurious reduction of production expression -> logical_expression ## -unbalanced parenthesis +unmatched parenthesis that should have been closed by here source_file_or_master: LAW_ARTICLE BEGIN_CODE SCOPE CONSTRUCTOR UNDER_CONDITION LPAREN YEAR ## @@ -1538,7 +1538,7 @@ source_file_or_master: LAW_ARTICLE BEGIN_CODE SCOPE CONSTRUCTOR UNDER_CONDITION ## base_expression mult_op ## -expected an expression on the right side of the multiplication operator +expected an expression on the right side of the multiplication or division operator source_file_or_master: LAW_ARTICLE BEGIN_CODE SCOPE CONSTRUCTOR UNDER_CONDITION TRUE NOT_EQUAL YEAR ## @@ -1586,7 +1586,7 @@ source_file_or_master: LAW_ARTICLE BEGIN_CODE SCOPE CONSTRUCTOR UNDER_CONDITION ## mult_expression sum_op ## -expected an expression on the right side of the sum operator +expected an expression on the right side of the sum or minus operator source_file_or_master: LAW_ARTICLE BEGIN_CODE SCOPE CONSTRUCTOR UNDER_CONDITION TRUE WE_HAVE ## @@ -1607,7 +1607,7 @@ source_file_or_master: LAW_ARTICLE BEGIN_CODE SCOPE CONSTRUCTOR UNDER_CONDITION ## In state 45, spurious reduction of production primitive_expression -> small_expression ## -expected an operator to compose the expression with +expected an operator to compose the expression on the left with source_file_or_master: LAW_ARTICLE BEGIN_CODE SCOPE CONSTRUCTOR UNDER_CONDITION TRUE WITH YEAR ## @@ -1633,7 +1633,7 @@ source_file_or_master: LAW_ARTICLE BEGIN_CODE SCOPE CONSTRUCTOR UNDER_CONDITION ## small_expression ## -expected an operator to compose the expression with +expected an operator to compose the expression on the left with source_file_or_master: LAW_ARTICLE BEGIN_CODE SCOPE CONSTRUCTOR UNDER_CONDITION VERTICAL INT_LITERAL DIV INT_LITERAL DIV INT_LITERAL YEAR ## diff --git a/src/catala/catala_surface/parser_driver.ml b/src/catala/catala_surface/parser_driver.ml index 03be9029..31064e33 100644 --- a/src/catala/catala_surface/parser_driver.ml +++ b/src/catala/catala_surface/parser_driver.ml @@ -50,20 +50,31 @@ let levenshtein_distance (s : string) (t : string) : int = d.(m).(n) -let raise_parser_error (loc : Pos.t) (token : string) (msg : string) = - Errors.raise_spanned_error (Printf.sprintf "Syntax error at token \"%s\"\n%s" token msg) loc +let syntax_hints_style = [ ANSITerminal.yellow ] + +let raise_parser_error (error_loc : Pos.t) (last_good_loc : Pos.t option) (token : string) + (msg : string) : 'a = + Errors.raise_multispanned_error + (Printf.sprintf "Syntax error at token %s\n%s" + (Cli.print_with_style syntax_hints_style "\"%s\"" token) + msg) + ( ( match last_good_loc with + | None -> [] + | Some last_good_loc -> [ (Some "Last good token:", last_good_loc) ] ) + @ [ (Some "Error token:", error_loc) ] ) let fail (lexbuf : lexbuf) (env : 'semantic_value I.env) (token_list : (string * Parser.token) list) (last_input_needed : 'semantic_value I.env option) : 'a = let wrong_token = Utf8.lexeme lexbuf in - let acceptable_tokens = + let acceptable_tokens, last_positions = match last_input_needed with | Some last_input_needed -> - List.filter - (fun (_, t) -> - I.acceptable (I.input_needed last_input_needed) t (fst (lexing_positions lexbuf))) - token_list - | None -> token_list + ( List.filter + (fun (_, t) -> + I.acceptable (I.input_needed last_input_needed) t (fst (lexing_positions lexbuf))) + token_list, + Some (I.positions last_input_needed) ) + | None -> (token_list, None) in let similar_acceptable_tokens = List.sort @@ -83,19 +94,25 @@ let fail (lexbuf : lexbuf) (env : 'semantic_value I.env) (token_list : (string * if levx = levy then String.length x - String.length y else levx - levy) acceptable_tokens in + let similar_token_msg = if List.length similar_acceptable_tokens = 0 then None else Some (Printf.sprintf "did you mean %s?" (String.concat ", or maybe " - (List.map (fun (ts, _) -> Printf.sprintf "\"%s\"" ts) similar_acceptable_tokens))) + (List.map + (fun (ts, _) -> Cli.print_with_style syntax_hints_style "\"%s\"" ts) + similar_acceptable_tokens))) in (* The parser has suspended itself because of a syntax error. Stop. *) let custom_menhir_message = match Parser_errors.message (state env) with - | exception Not_found -> "Message: unexpected token" - | msg -> "Message: " ^ String.trim (String.uncapitalize_ascii msg) + | exception Not_found -> + "Message: " ^ Cli.print_with_style syntax_hints_style "%s" "unexpected token" + | msg -> + "Message: " + ^ Cli.print_with_style syntax_hints_style "%s" (String.trim (String.uncapitalize_ascii msg)) in let msg = match similar_token_msg with @@ -103,7 +120,7 @@ let fail (lexbuf : lexbuf) (env : 'semantic_value I.env) (token_list : (string * | Some similar_token_msg -> Printf.sprintf "%s\nAutosuggestion: %s" custom_menhir_message similar_token_msg in - raise_parser_error (lexing_positions lexbuf) (Utf8.lexeme lexbuf) msg + raise_parser_error (lexing_positions lexbuf) last_positions (Utf8.lexeme lexbuf) msg let rec loop (next_token : unit -> Parser.token * Lexing.position * Lexing.position) (token_list : (string * Parser.token) list) (lexbuf : lexbuf) diff --git a/src/catala/catala_surface/parser_errors.ml b/src/catala/catala_surface/parser_errors.ml index b5c7f5eb..95620684 100644 --- a/src/catala/catala_surface/parser_errors.ml +++ b/src/catala/catala_surface/parser_errors.ml @@ -24,14 +24,14 @@ let message s = | 21 -> "expected a \"/\"\n" | 22 -> "expected the third component of the date literal\n" | 23 -> "expected a delimiter to finish the date literal\n" - | 45 -> "expected an operator to compose the expression with\n" + | 45 -> "expected an operator to compose the expression on the left with\n" | 51 -> "expected an enum constructor to test if the expression on the left\n" - | 50 -> "expected an operator to compose the expression with\n" - | 81 -> "expected an expression on the right side of the sum operator\n" + | 50 -> "expected an operator to compose the expression on the left with\n" + | 81 -> "expected an expression on the right side of the sum or minus operator\n" | 105 -> "expected an expression on the right side of the logical operator\n" | 53 -> "expected an expression for the argument of this function call\n" | 77 -> "expected an expression on the right side of the comparison operator\n" - | 86 -> "expected an expression on the right side of the multiplication operator\n" + | 86 -> "expected an expression on the right side of the multiplication or division operator\n" | 83 -> "expected an operator to compose the expression on the left\n" | 132 -> "expected an expression standing for the set you want to test for membership\n" | 46 -> "expected an identifier standing for a struct field or a subscope name\n" @@ -60,7 +60,7 @@ let message s = complete\n" | 139 -> "expected the \"with patter\" keyword to complete the pattern matching expression\n" | 32 -> "expected an expression inside the parenthesis\n" - | 125 -> "unbalanced parenthesis\n" + | 125 -> "unmatched parenthesis that should have been closed by here\n" | 54 -> "expected a unit for this literal, or a valid operator to complete the expression \n" | 34 -> "expected an expression for the test of the conditional\n" | 135 -> "expected an expression the for the \"then\" branch of the conditiona\n"