ocaml-containers/examples/ccparse_sexp.ml
2021-06-06 18:50:28 -04:00

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_map `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