analyzer.ml 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. type 'a result = { content : 'a; report : Qsp_syntax.Report.t list }
  2. (**
  3. Run the QSP parser and apply the analyzer over it.
  4. See [syntax/S]
  5. *)
  6. let rec parse :
  7. type a context.
  8. (module Qsp_syntax.S.Analyzer
  9. with type Location.t = a
  10. and type context = context) ->
  11. Lexbuf.t ->
  12. context ->
  13. (a result, Qsp_syntax.Report.t) Result.t =
  14. fun (module S : Qsp_syntax.S.Analyzer
  15. with type Location.t = a
  16. and type context = context) ->
  17. let module Parser = Parser.Make (S) in
  18. let module IncrementalParser =
  19. Interpreter.Interpreter (Parser.MenhirInterpreter) in
  20. fun l context ->
  21. let lexer = Lexbuf.tokenize Lexer.main l in
  22. let init = Parser.Incremental.main (fst (Lexbuf.positions l)) in
  23. (* Firslty, check if we are able to read the whole syntax from the source *)
  24. let evaluation =
  25. try IncrementalParser.of_lexbuf lexer l init with
  26. | Lexer.LexError message ->
  27. let start_pos, end_pos = Lexbuf.positions l in
  28. let err =
  29. IncrementalParser.
  30. { code = Interpreter.Custom message; start_pos; end_pos }
  31. in
  32. Error err
  33. | Lexer.UnclosedQuote | Lex_state.Out_of_context ->
  34. let start_pos, end_pos = Lexbuf.positions l in
  35. let err =
  36. IncrementalParser.
  37. {
  38. code = Interpreter.Custom "Unclosed string";
  39. start_pos;
  40. end_pos;
  41. }
  42. in
  43. Error err
  44. in
  45. (* Then evaluate the result *)
  46. match (evaluation, Lexbuf.is_recovery l) with
  47. | Ok r, _ ->
  48. (* We have been able to read the syntax, apply the checkers over the
  49. Tree *)
  50. let content = r context in
  51. Ok { content; report = S.Location.v content }
  52. | _, true ->
  53. (* This pattern can occur after recovering from an error. The
  54. application attempt to start from a clean state in the next
  55. location, but may fail to detect the correct position. If so, we
  56. just start again until we hook the next location *)
  57. parse (module S) l context
  58. | Error e, _ ->
  59. let message =
  60. match e.IncrementalParser.code with
  61. | Interpreter.UnrecoverableError -> "UnrecoverableError"
  62. | Interpreter.InvalidSyntax -> "Invalid Syntax"
  63. | Interpreter.Custom msg -> msg
  64. | Interpreter.MenhirCode c ->
  65. let message_content =
  66. try Parser_messages.message c
  67. with Not_found ->
  68. String.concat "" [ "(Error code "; string_of_int c; ")" ]
  69. in
  70. String.concat "" [ String.trim message_content ]
  71. in
  72. let report = Qsp_syntax.Report.error (e.start_pos, e.end_pos) message in
  73. (* Rollback the buffer from the latest errror before discarding until
  74. the end of the location. This ensure we will read the marker
  75. for the end location in the case the error was actually in
  76. this line itsef.
  77. Example :
  78. # location
  79. <ERROR HERE>
  80. ! ------- a
  81. --- location ---------------------------------
  82. *)
  83. Lexbuf.rollback l;
  84. (* Discard the remaining file to read. The parser is now in a blank
  85. state, it does not make sense to keep feeding it with the new
  86. tokens. *)
  87. let () = try Lexer.discard l with _ -> () in
  88. Error report