let (>>) x f = f x
let fail fmt = Printf.ksprintf failwith fmt
open Printf
open ExtLib
module type Number = sig
type t
val neg : t -> t
val of_int : int -> t
val to_int : t -> int
val of_string : string -> t
val to_string : t -> string
val zero : t
val add : t -> t -> t
val mul : t -> t -> t
end
module Make(N : Number) = struct
type t =
| S of string
| I of N.t
| L of t list
| D of (string * t) list
let decode_stream ?(hints=[]) chars =
let ten = N.of_int 10 in
let digit c = N.of_int (Char.code c - Char.code '0') in
let rec loop acc = parser
| [< x=parse_one; t; >] -> loop (x::acc) t
| [< >] -> List.rev acc
and parse_one = parser
| [< s=parse_string >] -> S s
| [< ''i'; n=parse_int_num; ''e' >] -> I n
| [< ''l'; l=loop []; ''e' >] -> L l
| [< ''d'; d=loop_d []; ''e' >] -> D d
and loop_d acc = parser
| [< k=parse_string; v=parse_one; t >] -> loop_d ((k,v) :: acc) t
| [< >] -> List.rev acc
and parse_string = parser
| [< n = parse_pos_num; '':'; s = take (N.to_int n) >] -> s
and parse_int_num = parser
| [< ''-'; x = parse_pos_num >] -> N.neg x
| [< x = parse_pos_num >] -> x
and parse_pos_num = parser
| [< ''0' >] -> N.zero
| [< ''1'..'9' as c; n = parse_digits (digit c) >] -> n
and parse_digits n = parser
| [< ''0'..'9' as c; t >] -> parse_digits (N.add (N.mul n ten) (digit c)) t
| [< >] -> n
and take n chars =
let s = String.make n '\000' in
for i = 0 to n-1 do s.[i] <- Stream.next chars done; s
in
let main () =
let r = parse_one chars in
if not (List.mem `IgnoreTail hints) then Stream.empty chars;
r
in
let show () =
let tail = Stream.npeek 10 chars >> List.map (String.make 1) >> String.concat "" in
sprintf "Position %u : %s" (Stream.count chars) tail
in
try
main ()
with
| Stream.Error _ | Stream.Failure -> failwith (show ())
let rec print out =
let pr fmt = IO.printf out fmt in
function
| I n -> pr "%s " (N.to_string n)
| S s -> pr "\"%s\" " (String.slice ~last:10 s)
| L l -> pr "( "; List.iter (print out) l; pr ") "
| D d -> pr "{ "; List.iter (fun (k,v) -> pr "%s: " k; print out v) d; pr "} "
let to_string t =
let module B = Buffer in
let b = B.create 100 in
let puts s = bprintf b "%u:%s" (String.length s) s in
let rec put = function
| I n -> bprintf b "i%se" (N.to_string n)
| S s -> puts s
| L l -> B.add_char b 'l'; List.iter put l; B.add_char b 'e'
| D d ->
B.add_char b 'd';
List.iter (fun (s,x) -> puts s; put x) (List.sort ~cmp:(fun (s1,_) (s2,_) -> compare s1 s2) d);
B.add_char b 'e'
in
put t; B.contents b
let decode s =
Stream.of_string s >> decode_stream
let key s k v =
match v with
| D l -> k (try List.assoc s l with Not_found -> fail "no key '%s'" s)
| _ -> fail "not a dictionary"
let int = function I n -> n | _ -> fail "int"
let str = function S s -> s | _ -> fail "str"
let list k v = match v with L l -> k l | _ -> fail "list"
let listof k v = match v with L l -> List.map k l | _ -> fail "listof"
let dict k v = match v with D l -> k l | _ -> fail "dict"
end
include Make(Int64)