open ExtLib
let (>>) x f = f x
let ($) f g = function x -> f (g x)
type php = AI of (int * php) list | AS of (string * php) list | S of string | I of int | B of bool | F of float | N
let check x y = if x <> y then failwith (Printf.sprintf "Php_serialize failed : %u <> %u" x y)
let rec parse_one = parser
| [< ''a'; '':'; n=number; '':'; ''{'; a=parse_array; ''}' >] -> ignore n; a
| [< ''b'; '':'; n=number; '';' >] -> B (0 <> n)
| [< ''d'; '':'; f=parse_float_semi; >] -> F f
| [< n=parse_int >] -> I n
| [< s=parse_str >] -> S s
| [< ''N'; '';' >] -> N
and number t = parse_nat 0 t
and parse_nat n = parser
| [< ''0'..'9' as c; t >] -> let digit = Char.code c - Char.code '0' in parse_nat (n * 10 + digit) t
| [< >] -> n
and integer = parser
| [< ''-'; t >] -> - (number t)
| [< t >] -> number t
and parse_int = parser
| [< ''i'; '':'; n=integer; '';' >] -> n
and parse_float_semi t = FIXME
let buf = Scanf.Scanning.from_function (fun () -> Stream.next t) in
Scanf.bscanf buf "%f;" (fun f -> f)
and parse_str = parser
| [< ''s'; '':'; n=number; '':'; ''"'; s=take_string n; ''"'; '';' >] -> s
and take_string n t = String.init n (fun _ -> Stream.next t)
and parse_array = parser
| [< k=parse_int; v=parse_one; a=parse_int_array [k,v] >] -> AI a
| [< k=parse_str; v=parse_one; a=parse_str_array [k,v] >] -> AS a
| [< >] -> AI []
and parse_int_array acc = parser
| [< k=parse_int; v=parse_one; t >] -> parse_int_array ((k,v)::acc) t
| [< >] -> List.rev acc
and parse_str_array acc = parser
| [< k=parse_str; v=parse_one; t >] -> parse_str_array ((k,v)::acc) t
| [< >] -> List.rev acc
let parse stream =
let show () =
let tail = Stream.npeek 10 stream >> List.map (String.make 1) >> String.concat "" in
Printf.sprintf "Position %u : %s" (Stream.count stream) tail
in
try
let r = parse_one stream in
Stream.empty stream; r
with
| Stream.Error _ | Stream.Failure -> failwith (show ())
let parse_string = parse $ Stream.of_string
exception Error of string
let fail v str = raise (Error (Printf.sprintf "%s : %s" str (Std.dump v)))
let int = function I n -> n | x -> fail x "int"
let str = function S s -> s | x -> fail x "str"
let opt k x = try Some (k x) with Error _ -> None
let values f = function
| AS a -> List.map (f $ snd) a
| AI a -> List.map (f $ snd) a
| x -> fail x "values"
let array f = function
| AS a -> List.map (fun (k,v) -> k, f v) a
| x -> fail x "array"
let assoc php name =
match php with
| AS a -> List.assoc name a
| _ -> fail php "assoc"
module Out = struct
let str s = S s
let int n = I n
let array f e = AI (e >> Enum.mapi (fun i x -> i, f x) >> List.of_enum)
let iarray f e = AI (e >> Enum.map (fun (k,v) -> k, f v) >> List.of_enum)
let sarray f e = AS (e >> Enum.map (fun (k,v) -> k, f v) >> List.of_enum)
let output out v =
let put_arr f a = IO.printf out "a:%u:{" (List.length a); List.iter f a; IO.write out '}' in
let rec put = function
| AS a -> put_arr (fun (k,v) -> put (S k); put v) a
| AI a -> put_arr (fun (k,v) -> put (I k); put v) a
| I n -> IO.printf out "i:%i;" n
| B b -> IO.printf out "b:%u;" (if b then 1 else 0)
| F f -> IO.printf out "d:%f;" f
| N -> IO.nwrite out "N;"
| S s -> IO.printf out "s:%u:\"%s\";" (String.length s) s
in
put v
end
let to_string v =
let out = IO.output_string () in
Out.output out v;
IO.close_out out