planck / lib / pbuffer.ml

open Spotlib.Spot

module type X = sig

  type +'a t (** parser monad type *)

  val prefix : int -> string t
  (** fast retrieval of the prefix string of the given length *)

  val takeWhile : (char -> bool) -> string t
  (** [takeWhile p] matches the longest prefix string where all the chars hold [p] *)

  val ( ??** ) : (char -> bool) -> string t
  val ( ??* ) : (char -> bool) -> unit t
  (** Same as [Planck_intf.( ?** )] and [Planck_intf.( ?* )] but specialized for char stream *)   

  val string : string -> unit t
  (** Efficient version of [Pchar.string] using Sbuffer. 
      [string s] succeeds if the prefix of the stream is [s]. *)

  val chars_to_string : char list t -> string t
  (** Type conversion from [char list t] to [string t].
      For efficiency reason, users is not encouraged to use this function. Use [matched] instead.
  *)

  val matched : unit t -> string t
  (** [matched t] returns the matched string part of [t].
      It is far more efficient than manual concatenation of chars using [chars_to_string].
  *)

  val with_matched : 'a t -> ('a * string) t
  (** [with_matched t] returns the matched string part of [t] 
      along with the original result of [t]. *)

  val ( </> ) : 'a t -> 'a t -> 'a t
  (** Longest match. [a </> b] succeeds if either [a] or [b] succeeds.
      If the both of [a] and [b] succeeds, the longer match is used as the result of [a </> b].

      Note: [a </> b] runs both [a] and [b]. Too many uses of [</>] affects the parsing performance badly.
  *)
end

module Extend(Sbuffer : sig
  include Stream_intf.S with type Elem.t = char
                        and  type Pos.t  = Position.File.t
  val substr : t -> int -> int -> string * t
  val takeWhile : (char -> bool) -> t -> string * t
  val bytes : t -> int
end)(Base : Planck_intf.S
     with type Str.desc   = Sbuffer.desc (* I want to say Str.t = Sbuffer.t but not possible *)
     and  type Str.Elem.t = char
     and  type Str.Pos.t  = Position.File.t
     and  type Str.Attr.t = Sbuffer.Attr.t
    ) = struct

  open Base

  (* No reason not having Pchar extensions *)

  include Pchar.Extend(Sbuffer)(Base)

  let bytes = stream >>= fun stream -> return (Sbuffer.bytes stream)

  let prefix n = 
    stream >>= fun str ->
    let pos = Sbuffer.bytes str in
    try
      let s, str' = Sbuffer.substr str pos n in
      set_stream str' >>= fun () -> 
      return s
    with
    | _ -> error "unexpected end of stream"

  let takeWhile p =
    stream >>= fun str ->
    let s, str' = Sbuffer.takeWhile p str in
    set_stream str' >>= fun () ->
    return s

  let ( ??** ) = takeWhile
  let ( ??* ) p = void (takeWhile p)

  let string s =
    position >>= fun pos0 ->
    result (prefix (String.length s)) >>= function
      | `Ok s' when s = s' -> return ()
      | _ -> throw (pos0, Printf.sprintf "expected %S" s)

  let matched : unit t -> string t = fun t ->
    stream >>= fun stream ->
    bytes >>= fun pos_start ->
    t >>= fun () -> 
    bytes >>= fun pos_end ->
    return (fst (Sbuffer.substr stream pos_start (pos_end - pos_start)))

  let with_matched : 'a t -> ('a * string) t = fun t ->
    stream >>= fun stream ->
    bytes >>= fun pos_start ->
    t >>= fun res -> 
    bytes >>= fun pos_end ->
    return (res, fst (Sbuffer.substr stream pos_start (pos_end - pos_start)))

  (** longest match *)
  let (</>) : 'a t -> 'a t -> 'a t = fun t1 t2 ->
    position >>= fun pos0 ->
    (result (t1 >>= fun res -> stream >>= fun str -> return (res, str)))
      <&> (fun res1 ->
        result (t2 >>= fun res -> stream >>= fun str -> return (res, str)) >>= fun res2 ->
        match res1, res2 with
        | `Ok (res, str), `Error _ 
        | `Error _, `Ok (res, str) -> set_stream str >>= fun () -> return res
        | `Ok (res1, str1), `Ok (res2, str2) -> 
            if Sbuffer.bytes str1 >= Sbuffer.bytes str2 then set_stream str1 >>= fun () -> return res1
            else  set_stream str2 >>= fun () -> return res2
        | `Error (_,err1), `Error (_,err2) ->
            error (err1 ^ " or " ^ err2) <?@> pos0)

end

module Make(Sbuffer : sig
  include Stream_intf.S
  with type Elem.t = char
  and  type Pos.t  = Position.File.t
  val substr : t -> int -> int -> string * t
  val takeWhile : (char -> bool) -> t -> string * t
  val bytes : t -> int
end) = struct
  module Base = Pbase.Make(Sbuffer)
  include Base
  include Extend(Sbuffer)(Base)
end
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.