* { !  } provides a very basic way of declaring configuration options using combinators . 

     Config files are built from s - expressions , and so the parsing combinators reflect the underlying 
     structure of the file .

    Config files are built from s-expressions, and so the parsing combinators reflect the underlying
    structure of the file. *)

open IlluaminateCore

(** Represents a configured term.

    This effectively acts as a representation of the underlying configuration file, merged together
    using combinators to build nested data structures. *)
module Kind : sig
  type term
  type fields
end

(** An arbitrary parser for terms. This allows some combinators to span over fields and base terms. *)
type ('a, 'kind) parser

(** The main term parser. This consumes a list of sexprs and yields an arbitrary value. *)
type 'a t = ('a, Kind.term) parser

(** An axillary parser for key-value pairs. *)
type 'a fields = ('a, Kind.fields) parser

(* (\** Get the default value of this term. *\)
 * val default : ('a, 'kind) parser -> 'a *)

* { 3 Basic combinators }

(** An empty parser which does nothing. *)
val unit : (unit, 'kind) parser

(** A parser which always yields a value. *)
val const : 'a -> ('a, 'kind) parser

(** Functor-style for parsers, allowing you to apply a function over a parser's result. *)
val ( let+ ) : ('a, 'kind) parser -> ('a -> 'b) -> ('b, 'kind) parser

(** Applicative functor-style syntax for parsers, allowing you to combine parsers. *)
val ( and+ ) : ('a, 'kind) parser -> ('b, 'kind) parser -> ('a * 'b, 'kind) parser

* { 3 Value parsers }

*  a boolean , only accepting the atoms " true " and " false "
val bool : bool t

*  some arbitrary atom .
val atom : ty:string -> (string -> 'a option) -> 'a t

*  some arbitrary atom , allowing returning custom error messages .
val atom_res : ty:string -> (string -> ('a, string) result) -> 'a t

*  a string , either as a raw atom or a constant value .
val string : string t

*  a float .
val float : float t

*  an integer .
val int : int t

*  all remaining items using this parser .
val many : 'a t -> 'a list t

*  all remaining items using this parser . 

     This requires there to be at least one item present - ` some term ` is equivalent to ` term ` then 
     ` many term ` .

    This requires there to be at least one item present - `some term` is equivalent to `term` then
    `many term`. *)
val some : 'a t -> 'a list t

(** Enter a list, and parse the body using this parser. *)
val in_list : 'a t -> 'a t

*  a list of items . Equivalent to ` in_list ( many term ) ` .
val list : 'a t -> 'a list t

(** If the next term is a list, parse a list of items. Otherwise call the parser directly.

    It is recommended your parser only accepts atoms - otherwise you may get rather unexpected
    behaviour. *)
val list_or_one : 'a t -> 'a list t

*  any remaining values as fields .
val fields : 'a fields -> 'a t

* { 3 Field parsers }

*  a required field .
val field : name:string -> 'a t -> 'a fields

*  an optional field , with some default value .
val field_opt : name:string -> 'a t -> 'a option fields

*  a field which may occur multiple times .
val field_repeated : name:string -> 'a t -> 'a list fields

(** {4 Running the parser} *)

(** Run this parser against a buffer. *)
val parse_buf : Span.filename -> Lexing.lexbuf -> 'a t -> ('a, Span.t * string) result

* Represents a configured term.

    This effectively acts as a representation of the underlying configuration file, merged together
    using combinators to build nested data structures. 
* An arbitrary parser for terms. This allows some combinators to span over fields and base terms. 
* The main term parser. This consumes a list of sexprs and yields an arbitrary value. 
* An axillary parser for key-value pairs. 
 (\** Get the default value of this term. *\)
 * val default : ('a, 'kind) parser -> 'a 
* An empty parser which does nothing. 
* A parser which always yields a value. 
* Functor-style for parsers, allowing you to apply a function over a parser's result. 
* Applicative functor-style syntax for parsers, allowing you to combine parsers. 
* Enter a list, and parse the body using this parser. 
* If the next term is a list, parse a list of items. Otherwise call the parser directly.

    It is recommended your parser only accepts atoms - otherwise you may get rather unexpected
    behaviour. 
* {4 Running the parser} 
* Run this parser against a buffer. 
* { !  } provides a very basic way of declaring configuration options using combinators . 

     Config files are built from s - expressions , and so the parsing combinators reflect the underlying 
     structure of the file .

    Config files are built from s-expressions, and so the parsing combinators reflect the underlying
    structure of the file. *)

open IlluaminateCore

module Kind : sig
  type term
  type fields
end

type ('a, 'kind) parser

type 'a t = ('a, Kind.term) parser

type 'a fields = ('a, Kind.fields) parser


* { 3 Basic combinators }

val unit : (unit, 'kind) parser

val const : 'a -> ('a, 'kind) parser

val ( let+ ) : ('a, 'kind) parser -> ('a -> 'b) -> ('b, 'kind) parser

val ( and+ ) : ('a, 'kind) parser -> ('b, 'kind) parser -> ('a * 'b, 'kind) parser

* { 3 Value parsers }

*  a boolean , only accepting the atoms " true " and " false "
val bool : bool t

*  some arbitrary atom .
val atom : ty:string -> (string -> 'a option) -> 'a t

*  some arbitrary atom , allowing returning custom error messages .
val atom_res : ty:string -> (string -> ('a, string) result) -> 'a t

*  a string , either as a raw atom or a constant value .
val string : string t

*  a float .
val float : float t

*  an integer .
val int : int t

*  all remaining items using this parser .
val many : 'a t -> 'a list t

*  all remaining items using this parser . 

     This requires there to be at least one item present - ` some term ` is equivalent to ` term ` then 
     ` many term ` .

    This requires there to be at least one item present - `some term` is equivalent to `term` then
    `many term`. *)
val some : 'a t -> 'a list t

val in_list : 'a t -> 'a t

*  a list of items . Equivalent to ` in_list ( many term ) ` .
val list : 'a t -> 'a list t

val list_or_one : 'a t -> 'a list t

*  any remaining values as fields .
val fields : 'a fields -> 'a t

* { 3 Field parsers }

*  a required field .
val field : name:string -> 'a t -> 'a fields

*  an optional field , with some default value .
val field_opt : name:string -> 'a t -> 'a option fields

*  a field which may occur multiple times .
val field_repeated : name:string -> 'a t -> 'a list fields


val parse_buf : Span.filename -> Lexing.lexbuf -> 'a t -> ('a, Span.t * string) result
