diff --git a/src/interpret.ml b/src/interpret.ml index 33dd73f9537b785446ef52336cc1d4ac2a4f4f5b..c27bce60cf26043b2adad9fc37d63cfd06f2cb03 100644 --- a/src/interpret.ml +++ b/src/interpret.ml @@ -1,19 +1,33 @@ (* This module is in charge of handling the [--interpret] option, if it is present. *) -open Grammar module I = Invariant (* artificial dependency; ensures that [Invariant] runs first *) (* --------------------------------------------------------------------------- *) -(* A sentence is a pair of an optional non-terminal start symbol and a - list of terminal symbols. *) +(* The following definitions are in sync with [SentenceParser]. *) + +open Grammar +type terminals = Terminal.t list +type sentence = Nonterminal.t option * terminals +type located_sentence = Positions.positions * sentence +type message = string + +(* A run is a series of sentences together with an error message. *) + +type run = located_sentence list * message -type sentence = - Nonterminal.t option * Terminal.t list +(* A targeted sentence is a located sentence together with the state + into which it leads. *) -type located_sentence = - Positions.positions * sentence +type targeted_sentence = located_sentence * Lr1.node + +(* A targeted run is a series of targeted sentences together with an error + message. *) + +type targeted_run = targeted_sentence list * message + +(* --------------------------------------------------------------------------- *) (* Debugging. @@ -202,22 +216,32 @@ let interpret_error sentence = (* --------------------------------------------------------------------------- *) -(* [convert_located_sentence] interprets a (located) sentence, expecting it to - end in an error, and returns the state in which the error is obtained. This - is used by [--compile-errors]. *) - -let convert_located_sentence (poss, sentence) = - let fail msg = - Error.signal poss (Printf.sprintf - "This sentence does not end with a syntax error, as desired.\n%s" - msg - ); - [] (* dummy result *) - in - interpret_error_aux poss sentence fail (fun s -> [ (poss, sentence), s ]) - -let convert_entry (sentences, message) = - List.flatten (List.map convert_located_sentence sentences), message +(* [target_sentence] interprets a (located) sentence, expecting it to end in + an error, computes the state in which the error is obtained, and constructs + a targeted sentence. *) + +let fail poss msg = + Error.signal poss (Printf.sprintf + "This sentence does not end with a syntax error, as desired.\n%s" + msg + ); + [] (* dummy result *) + +let target_sentence : located_sentence -> targeted_sentence list = + fun (poss, sentence) -> + interpret_error_aux poss sentence + (fail poss) + (fun s -> [ (poss, sentence), s ]) + +let target_run : run -> targeted_run = + fun (sentences, message) -> + List.flatten (List.map target_sentence sentences), message + +let target_runs : run list -> targeted_run list = + fun runs -> + let runs = List.map target_run runs in + if Error.errors() then exit 1; + runs (* --------------------------------------------------------------------------- *) @@ -277,50 +301,57 @@ let () = (* --------------------------------------------------------------------------- *) +(* Reading a [.messages] file. *) + +let read_messages filename : run list = + (* Read and segment the file. *) + let segments : (string * Lexing.lexbuf) list = Segment.segment filename in + (* Process the segments, two by two. We expect one segment to contain + a non-empty series of sentences, and the next segment to contain + free-form text. *) + let rec loop accu segments = + match segments with + | [] -> + List.rev accu + | (_, lexbuf) :: [] -> + (* Oops, we are desynchronized. *) + Error.signal + (Positions.one (Lexing.lexeme_end_p lexbuf)) + "Syntax error: missing a final message. I may be desynchronized."; + List.rev accu + | (_, lexbuf) :: (text, _) :: segments -> + (* Read a non-empty series of located sentences. *) + match SentenceParser.entry SentenceLexer.lex lexbuf with + | exception Parsing.Parse_error -> + (* Report an error. *) + Error.signal + (Positions.one (Lexing.lexeme_start_p lexbuf)) + "Syntax error: ill-formed sentence."; + (* Continue anyway. *) + loop accu segments + | sentences -> + loop ((sentences, text) :: accu) segments + in + let runs = loop [] segments in + if Error.errors() then exit 1; + (* Although we try to report several errors, [SentenceLexer.lex] may + abort the whole process after just one error. This could be improved. *) + runs + +(* --------------------------------------------------------------------------- *) + (* If [--compile-errors <filename>] is set, compile the error message descriptions found in file [filename] down to OCaml code, then stop. *) let () = Settings.compile_errors |> Option.iter (fun filename -> - (* Read and segment the file. Each segment is a pair of a string and a - lexbuf. *) - let segments = Segment.segment filename in - (* Process the segments, two by two. We expect one segment to contain - a non-empty series of sentences, and the next segment to contain - free-form text. *) - let rec loop accu segments = - match segments with - | [] -> - List.rev accu - | (_, lexbuf) :: [] -> - (* Oops, we are desynchronized. *) - Error.signal - (Positions.one (Lexing.lexeme_end_p lexbuf)) - "Syntax error: missing a final message. I may be desynchronized."; - List.rev accu - | (_, lexbuf) :: (text, _) :: segments -> - (* Read a non-empty series of located sentences. *) - match SentenceParser.entry SentenceLexer.lex lexbuf with - | exception Parsing.Parse_error -> - (* Report an error. *) - Error.signal - (Positions.one (Lexing.lexeme_start_p lexbuf)) - "Syntax error: ill-formed sentence."; - (* Continue anyway. *) - loop accu segments - | sentences -> - loop ((sentences, text) :: accu) segments - in - let entries = loop [] segments in - if Error.errors() then exit 1; - (* Although we try to report several errors, [SentenceLexer.lex] may - abort the whole process after just one error. This could be improved. *) + (* Read the file. *) + let runs = read_messages filename in (* Convert every sentence to a state number. We signal an error if a sentence does not end in an error, as expected. *) - let entries = List.map convert_entry entries in - if Error.errors() then exit 1; + let runs = target_runs runs in (* Build a mapping of states to located sentences. This allows us to detect if two sentences lead to the same state. *) @@ -337,7 +368,7 @@ let () = | exception Not_found -> Lr1.NodeMap.add s sentence2 mapping ) mapping sentences_and_states - ) Lr1.NodeMap.empty entries + ) Lr1.NodeMap.empty runs in if Error.errors() then exit 1; @@ -371,7 +402,7 @@ let () = (* Map all these states to this message. *) { branchpat = POr states; branchbody = EStringConst message } :: branches - ) [ default ] entries + ) [ default ] runs in let messagedef = { valpublic = true;