ocaml-containers/examples/ccparse_sexp.ml
2024-05-13 20:57:53 -04:00

75 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