I’ve done that in the past with ppx_sexp_conv
. You describe the shape for the data and directly use the generated functions. The main trick is that you don’t have to map precisely everything: if you declare a field as sexp
, it will parse as the sexp itself. And you can use a any
type to put a unit
hole instead of the sexp if you don’t need it.
type any = unit [@@deriving sexp_of]
let any_of_sexp _ = ()
type executables_stanza = {
names : string list;
requires : string list;
modules : any;
include_dirs : any;
}
[@@deriving sexp]
type library_stanza = {
name : string;
uid : string;
local : bool;
requires : string list;
source_dir : any;
modules : any;
include_dirs : any;
}
[@@deriving sexp]
type stanza = Executables of executables_stanza | Library of library_stanza
[@@deriving sexp_of]
let stanza_of_sexp = function
| Sexp.List [ Atom "executables"; s ] ->
Executables (executables_stanza_of_sexp s)
| Sexp.List [ Atom "library"; s ] -> Library (library_stanza_of_sexp s)
| Sexp.List [ Atom atom; _ ] -> raise_s [%message "stanza_of_sexp" atom]
| sexp -> raise_s [%message "stanza_of_sexp" (sexp : Sexp.t)]
type t = stanza list [@@deriving sexp]