open Fix
open Sigs

module Make (G : CNF) = struct

  open G

  let equal (t1 : terminal) (t2 : terminal) =
    (t1 : terminal :> int) = (t2 : terminal :> int)

  let rec exists_in_interval i j (p : int -> bool) : bool =
    i <= j && (p i || exists_in_interval (i+1) j p)

  module V = struct
    (* A variable is a triple of a nonterminal [A] and two integer indices [i]
       and [j]. Its Boolean interpretation is [true] iff the input substring
       comprised between [i] and [j] belongs to the language generated by [A]. *)
    type t =
      nonterminal * int * int
  end

  module F =
    Fix.ForType(V)(Prop.Boolean)

  let eval input (nt, i, j) var =
    productions nt |> List.exists (fun production ->
      match production with
      | E ->
          i = j
      | T t ->
          i + 1 = j && equal input.(i) t
      | NN (nt1, nt2) ->
         exists_in_interval i j (fun k ->
           var (nt1, i, k) && var (nt2, k, j)
         )
    )

  let parse (input : terminal array) =
    F.lfp (eval input) (start, 0, Array.length input)

end
