(** 
This module main use is to create a parser that can log its execution and is able to signal several errors (with the right option) from a parser generated with menhir.

The functor Make produces this parser provided it is given the modules generated by menhir, along the one obtained from reading the .cmly file generated by menhir.

A typical instantiation of this module should look like :
{[module ParserSign :
  Cairn.Parsing.parser_decorated with type value_parsed = Program.program =
struct
  type value_parsed = Program.program

  let error_strategy = Cairn.Parsing.PopFirst

  module Lexer = Lexer
  module Parser = Parser
  module ParserMessages = ParserMessages
end

module Grammar = MenhirSdk.Cmly_read.Read (struct let filename = "Parser.cmly" end)

module P = Cairn.Parsing.Make (ParserSign) (Grammar)
]}
assuming [Lexer], [Parser] and [ParserMessages] are the modules produced by menhir (with the right options), and that "Parser.cmly" is the name (with path) to the cmly file produced by menhir. Namely, type [value_parsed] should be rendered visible for the result of the parser to be usable.

For the cmly file, it might not straightforward to use its direct name (especially if the executable is destined to be installed or executed from somewhere else than its own directory).
  
In that case, it might be worth to bundle it in the executable with, for example, ocaml-crunch (see examples provided to see how). It is then needed to use the [Lift] functor of {!MenhirSdk.Cmly_read} rather than the [Read] one as follows:
{[module Grammar = MenhirSdk.Cmly_read.Lift (struct
  let file_content = Option.get (<Module_generated_by_ocaml_crunch>.read "<name_of_cmly_file>")
  let prefix = "CMLY" ^ MenhirSdk.Version.version
  let grammar = Marshal.from_string file_content (String.length prefix)
end)]}
This is adapted from the [Read] functor of {!MenhirSdk.Cmly_read}.

@author Vincent Penelle <vincent.penelle@u-bordeaux.fr>. 
*)

(** type for error-recovery strategy*)
type error_strategy =
  | Stop  (** The parser will stop after the first error encountered.*)
  | PopFirst
      (** After an error, the parser will pop the stack until either the top element is a terminal or non-terminal with {b backup} attribute set (in the grammar) or the stack is empty, and then ignore tokens until the first that can be shifted.*)

(** Signature to provide to the Make functor. The module Parser and Lexer should be those generated by menhir, with options {i --table --inspection --cmly}.*)
module type parser_decorated = sig
  type value_parsed
  (** The type of value that is produced by the parser. For the generated parser to be usable, it is advised to render this type visible.*)

  val error_strategy : error_strategy
  (** If [Stop], the parser will stop at the first error encountered. If [PopFirst], it will instead pop the stack until a terminal or non-terminal with attributes backup set in the grammar, and then ignores all tokens until the first that can be shifted and resume parser there -- not ideal, but the only way it seems to be possible.*)

  (** Module generated with menhir. Must be generated with options {i --table --inspection --cmly} to work properly, as the inspection API is used, and the .cmly file is analysed.*)
  module Parser : sig
    type token

    exception Error

    module MenhirInterpreter : sig
      include
        MenhirLib.IncrementalEngine.INCREMENTAL_ENGINE with type token = token

      type 'a terminal
      type _ nonterminal

      include
        MenhirLib.IncrementalEngine.INSPECTION
          with type 'a lr1state := 'a lr1state
          with type production := production
          with type 'a terminal := 'a terminal
          with type 'a nonterminal := 'a nonterminal
          with type 'a env := 'a env
    end

    module Incremental : sig
      val main : Lexing.position -> value_parsed MenhirInterpreter.checkpoint
    end
  end

  (** Lexer generated by menhir.*)
  module Lexer : sig
    val token : Lexing.lexbuf -> Parser.token
  end
end

(** Signature matching the module generated by menhir with option {i --compile-errors}. Used for error displaying. Will work if provided with a dummy [message] function instead (but less informative).*)
module type parser_messages = sig
  val message : int -> string
end

(** Signature of the modules produced by this library. Common to {!Make} and {!MakeWithDefaultMessage}.
    Contains functions to get a log of the execution of the parser, as long with a graphical explorer of this log.*)
module type parser_logger = sig
  module Parser : parser_decorated

  val state_to_lr0_list : int -> string list
  (** Function that associates to a lr1 state number a list of string representing the lr0 items it contains.*)

  val parse :
    string ->
    Lexing.lexbuf ->
    Parser.value_parsed option
    * ParserLog.configuration list
    * (string * string * string) list
  (** [parse text lexbuf] parses an input pointed by [lexbuf] whose content is [text]. [text] and [lexbuf] might be obtained with MenhirLib.LexerUtil. It returns [(value,log,errors)], where: 
    {ul {- [value] is either [Some value] if the parser produced a semantical value or [None].}
      {- [log] is a configuration list that represents the execution of the parser (to be used with functions from [ParserLog] alongside [state_to_lr0_list]).}
      {- [errors] is a list of error messages encountered along the execution, in order to which they appeared. If this list is not empty, [value] should probably not be trusted.
      The first string is the position of the error, the second the two tokens between which the error occured, and the last an explanation (from the [ParserMessages] provided).}}*)

  val parse_interactive : string -> Lexing.lexbuf -> Parser.value_parsed option
  (** [parse_interactive text lexbuf] parses an input pointed by [lexbuf], whose content is [text]. Displays a terminal user interface allowing to navigate the log of the parser. Then returns the parsed value (if no error was encountered, [None] otherwise).*)

  val parse_log :
    string -> Lexing.lexbuf -> string -> string -> Parser.value_parsed option
  (** [parse_log text lexbuf log_file error_file] parses an input pointed by [lexbuf], whose content is [text]. Writes a log of the parser execution in the file of name [log_file] and an error log in the file of name [error_file]. Returns the parsed value (if no error was encountered, [None] otherwise)*)

  val parse_interactive_or_log :
    string ->
    Lexing.lexbuf ->
    bool ->
    string option ->
    string option ->
    Parser.value_parsed option
  (** [parse_log text lexbuf interactive log_file error_file] parses an input pointed by [lexbuf], whose content is [text]. Displays a terminal user interface allowing to navigate the log of the parser if [interactive is true]. Writes a log of the parser execution in the file of name [log_file] if it is not [None] and an error log in the file of name [error_file] if it is not [None]. Returns the parsed value (if no error was encountered, [None] otherwise)*)

  val parse_string_interactive : string -> Parser.value_parsed option
  (** [parse_string_interactive string] parses the string [string]. Displays a terminal user interface allowing to navigate the log of the parser. Then returns the parsed value (if no error was encountered, [None] otherwise).*)

  val parse_file_interactive : string -> Parser.value_parsed option
  (** [parse_file_interactive file] parses the content of the file [file]. Displays a terminal user interface allowing to navigate the log of the parser. Then returns the parsed value (if no error was encountered, [None] otherwise).*)
end

(** Main functor of this module. It generates a module that can parse a text with the parser provided as an argument, and generates a log of the partial derivations produced along the run of the parser, a log of errors encountered (several errors supported if generated with PopFirst strategy), and can display a tui explorer of the sequence of partial derivations produced by the parser.
    - [Parser] is the {!parser_decorated} obtained from the modules generated by menhir invocation.
    - [ParserMessages] is the module of type {!parser_messages} obtained by menhir with option {i --compile-errors} and filled in by the user.
    - [Grammar] is the module of type {!MenhirSdk.Cmly_api.GRAMMAR} that has been read from a cmly file produced by menhir with option {i --cmly}.*)
module Make : functor
  (Parser : parser_decorated)
  (ParserMessages : parser_messages)
  (Grammar : MenhirSdk.Cmly_api.GRAMMAR)
  -> parser_logger with module Parser = Parser

(** Same as Make, but provides defaults error messages of the form "Error on state x" where x is the state where the parser encountered the message. Useful if you do not want to generate messages with menhir (yet).*)
module MakeWithDefaultMessage : functor
  (Parser : parser_decorated)
  (Grammar : MenhirSdk.Cmly_api.GRAMMAR)
  -> parser_logger with module Parser = Parser
