mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 11:15:31 -05:00
also add a sample of IRC logs of #ocaml on libera.chat, to make sure they parse properly.
66 lines
2.1 KiB
OCaml
66 lines
2.1 KiB
OCaml
open CCParse
|
|
|
|
type sexp = Atom of string | List of sexp list
|
|
|
|
let rec pp_sexpr out (s:sexp) : unit = match s with
|
|
| Atom s -> Format.fprintf out "%S" s
|
|
| List l ->
|
|
Format.fprintf out "(@[";
|
|
List.iteri (fun i s -> if i>0 then Format.fprintf out "@ "; pp_sexpr out s) l;
|
|
Format.fprintf out "@])"
|
|
|
|
let str_of_sexp = CCFormat.to_string pp_sexpr
|
|
|
|
let skip_white_and_comments =
|
|
fix @@ fun self ->
|
|
skip_white *>
|
|
( try_or (char ';')
|
|
~f:(fun _ -> skip_chars (function '\n' -> false | _ -> true) *> self)
|
|
~else_:(return ())
|
|
)
|
|
|
|
let atom =
|
|
chars_fold_transduce `Start
|
|
~f:(fun acc c ->
|
|
match acc, c with
|
|
| `Start, '"' -> `Continue `In_quote
|
|
| `Start, (' ' | '\t' | '\n' | '(' | ')' | ';') -> `Fail "atom"
|
|
| `Normal, (' ' | '\t' | '\n' | '(' | ')' | ';') -> `Stop
|
|
| `Done, _ -> `Stop
|
|
| `In_quote, '"' -> `Continue `Done (* consume *)
|
|
| `In_quote, '\\' -> `Continue `Escape
|
|
| `In_quote, c -> `Yield (`In_quote, c)
|
|
| `Escape, 'n' -> `Yield (`In_quote, '\n')
|
|
| `Escape, 't' -> `Yield (`In_quote, '\t')
|
|
| `Escape, '"' -> `Yield (`In_quote, '"')
|
|
| `Escape, '\\' -> `Yield (`In_quote, '\\')
|
|
| `Escape, c -> `Fail (Printf.sprintf "unknown escape code \\%c" c)
|
|
| (`Start | `Normal), c -> `Yield (`Normal, c)
|
|
| _ -> `Fail "invalid atom"
|
|
)
|
|
>>= function
|
|
| `In_quote, _ -> fail "unclosed \""
|
|
| `Escape, _ -> fail "unfinished escape sequence"
|
|
| _, "" -> fail "expected non-empty atom"
|
|
| _, s -> return (Atom s)
|
|
|
|
let psexp =
|
|
fix @@ fun self ->
|
|
skip_white_and_comments *>
|
|
try_or (char '(')
|
|
~f:(fun _ ->
|
|
(sep ~by:skip_white_and_comments self
|
|
<* skip_white_and_comments <* char ')') >|= fun l -> List l)
|
|
~else_:atom
|
|
|
|
let psexp_l =
|
|
many_until ~until:(skip_white_and_comments *> eoi) psexp
|
|
|
|
let () =
|
|
let s = CCIO.File.read_exn Sys.argv.(1) in
|
|
match parse_string psexp_l s with
|
|
| Ok l ->
|
|
Format.printf "parsed:@.";
|
|
List.iter (Format.printf "%a@." pp_sexpr) l
|
|
| Error e ->
|
|
Format.printf "parse error: %s@." e; exit 1
|