+++ /dev/null
-(******************************************************************************
- * Sexplib *
- * *
- * Copyright (C) 2005- Jane Street Holding, LLC *
- * Contact: opensource@janestreet.com *
- * WWW: http://www.janestreet.com/ocaml *
- * Author: Markus Mottl *
- * *
- * This file is derived from file "pa_tywith.ml" of version 0.45 of the *
- * library "Tywith". *
- * *
- * Tywith is Copyright (C) 2004, 2005 by *
- * *
- * Martin Sandin <msandin@hotmail.com> *
- * *
- * *
- * This library is free software; you can redistribute it and/or *
- * modify it under the terms of the GNU Lesser General Public *
- * License as published by the Free Software Foundation; either *
- * version 2 of the License, or (at your option) any later version. *
- * *
- * This library is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
- * Lesser General Public License for more details. *
- * *
- * You should have received a copy of the GNU Lesser General Public *
- * License along with this library; if not, write to the Free Software *
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
- * *
- ******************************************************************************)
-
-(* Pa_sexp_conv: Preprocessing Module for Automated S-expression Conversions *)
-
-open StdLabels
-open MoreLabels
-
-open Printf
-
-open Camlp4
-open PreCast
-
-module Gen = Pa_type_conv.Gen
-
-(* Utility functions *)
-
-let mk_rev_bindings loc fps =
- let coll (i, bindings, patts, vars) fp =
- let name = "v" ^ string_of_int i in
- let var_expr = <:expr@loc< $lid:name$ >> in
- let expr =
- match fp with
- | `Fun fun_expr -> <:expr@loc< $fun_expr$ $var_expr$ >>
- | `Match matchings -> <:expr@loc< match $var_expr$ with [ $matchings$ ] >>
- in
- let patt = <:patt@loc< $lid:name$ >> in
- let bindings = <:binding@loc< $patt$ = $expr$ and $bindings$ >> in
- i - 1, bindings, patt :: patts, var_expr :: vars
- in
- let n = List.length fps in
- let _, bindings, patts, expr =
- List.fold_left ~f:coll ~init:(n, Ast.BiNil loc, [], []) fps
- in
- bindings, patts, expr
-
-let mk_bindings loc fps = mk_rev_bindings loc (List.rev fps)
-
-let unroll_cnv_fp loc var = function
- | `Fun fun_expr -> <:expr@loc< $fun_expr$ $var$ >>
- | `Match matchings -> <:expr@loc< match $var$ with [ $matchings$ ] >>
-
-let unroll_fun_matches loc fp1 fp2 =
- match fp1, fp2 with
- | `Fun fun_expr1, `Fun fun_expr2 ->
- <:expr@loc< $fun_expr1$ $fun_expr2$ >>
- | `Fun fun_expr, `Match matching ->
- <:expr@loc< $fun_expr$ (fun [ $matching$ ]) >>
- | _ -> assert false (* impossible *)
-
-let rec sig_of_tds cnv = function
- | Ast.TyDcl (loc, type_name, tps, rhs, cl) -> cnv loc type_name tps rhs cl
- | Ast.TyAnd (loc, tp1, tp2) ->
- <:sig_item@loc< $sig_of_tds cnv tp1$; $sig_of_tds cnv tp2$ >>
- | _ -> assert false (* impossible *)
-
-
-(* Generators for S-expressions *)
-
-(* Generates the signature for type conversion to S-expressions *)
-module Sig_generate_sexp_of = struct
-
- let rec sig_of_td__loop acc = function
- | [] ->
- let loc = Ast.loc_of_ctyp acc in
- <:ctyp@loc< $acc$ -> Sexplib.Sexp.t >>
- | tp :: tps ->
- let tp = Gen.drop_variance_annotations tp in
- let loc = Ast.loc_of_ctyp tp in
- let sexp_of = sig_of_td__loop <:ctyp@loc< $acc$ $tp$ >> tps in
- <:ctyp@loc< ( $tp$ -> Sexplib.Sexp.t ) -> $sexp_of$ >>
-
- let sig_of_td loc type_name tps _rhs _cl =
- let sexp_of = sig_of_td__loop <:ctyp@loc< $lid:type_name$ >> tps in
- <:sig_item@loc< value $lid: "sexp_of_" ^ type_name$ : $sexp_of$ >>
-
- let mk_sig tds = <:sig_item< $sig_of_tds sig_of_td tds$ >>
-
- let () = Pa_type_conv.add_sig_generator "sexp_of" mk_sig
-
- let mk_sig_exn = function
- | <:ctyp@loc< $uid:_$ >> | <:ctyp@loc< $uid:_$ of $_$ >> ->
- <:sig_item@loc< >>
- | tp -> Gen.error tp ~fn:"mk_sig_exn" ~msg:"unknown type"
-
- let () = Pa_type_conv.add_sig_generator ~is_exn:true "sexp" mk_sig_exn
-end
-
-
-(* Generates the signature for type conversion from S-expressions *)
-module Sig_generate_of_sexp = struct
-
- let rec is_polymorphic_variant = function
- | <:ctyp< private $tp$ >> -> is_polymorphic_variant tp
- | <:ctyp< ( $tup:_$ ) >>
- | <:ctyp< $_$ -> $_$ >>
- | <:ctyp< { $_$ } >>
- | <:ctyp< [ $_$ ] >> -> `Surely_not
- | <:ctyp< [< $_$ ] >> | <:ctyp< [> $_$ ] >>
- | <:ctyp< [= $_$ ] >> -> `Definitely
- | <:ctyp< '$_$ >>
- | <:ctyp< $_$ $_$ >>
- | <:ctyp< $id:_$ >>
- | <:ctyp< >> -> `Maybe
- | <:ctyp< $tp1$ == $tp2$ >> ->
- begin match is_polymorphic_variant tp1 with
- | (`Surely_not | `Definitely) as res -> res
- | `Maybe -> is_polymorphic_variant tp2 end
- | tp -> Gen.unknown_type tp "Sig_generate_of_sexp.is_polymorphic_variant"
-
- let rec sig_of_td__loop acc = function
- | [] ->
- let loc = Ast.loc_of_ctyp acc in
- <:ctyp@loc< Sexplib.Sexp.t -> $acc$ >>
- | tp :: tps ->
- let tp = Gen.drop_variance_annotations tp in
- let loc = Ast.loc_of_ctyp tp in
- let of_sexp = sig_of_td__loop <:ctyp@loc< $acc$ $tp$ >> tps in
- <:ctyp@loc< ( Sexplib.Sexp.t -> $tp$ ) -> $of_sexp$ >>
-
- let sig_of_td with_poly loc type_name tps rhs _cl =
- let of_sexp = sig_of_td__loop <:ctyp@loc< $lid:type_name$ >> tps in
- let of_sexp_item =
- <:sig_item@loc< value $lid: type_name ^ "_of_sexp"$ : $of_sexp$; >>
- in
- match with_poly, is_polymorphic_variant rhs with
- | true, `Surely_not ->
- Gen.error rhs ~fn:"Sig_generate_of_sexp.sig_of_td"
- ~msg:"sexp_poly annotation \
- but type is surely not a polymorphic variant"
- | false, (`Surely_not | `Maybe) -> of_sexp_item
- | (true | false), `Definitely | true, `Maybe ->
- <:sig_item@loc<
- $of_sexp_item$;
- value $lid: type_name ^ "_of_sexp__"$ : $of_sexp$;
- >>
-
- let mk_sig with_poly tds =
- <:sig_item< $sig_of_tds (sig_of_td with_poly) tds$ >>
-
- let () = Pa_type_conv.add_sig_generator "of_sexp" (mk_sig false)
- let () = Pa_type_conv.add_sig_generator "of_sexp_poly" (mk_sig true)
-end
-
-
-(* Generates the signature for type conversion to S-expressions *)
-module Sig_generate = struct
- let () =
- Pa_type_conv.add_sig_generator "sexp" (fun tds ->
- let loc = Ast.loc_of_ctyp tds in
- <:sig_item@loc<
- $Sig_generate_sexp_of.mk_sig tds$;
- $Sig_generate_of_sexp.mk_sig false tds$
- >>)
-
- let () =
- Pa_type_conv.add_sig_generator "sexp_poly" (fun tds ->
- let loc = Ast.loc_of_ctyp tds in
- <:sig_item@loc<
- $Sig_generate_sexp_of.mk_sig tds$;
- $Sig_generate_of_sexp.mk_sig true tds$
- >>)
-end
-
-
-(* Generator for converters of OCaml-values to S-expressions *)
-module Generate_sexp_of = struct
- let mk_abst_call loc tn rev_path =
- <:expr@loc<
- $id:Gen.ident_of_rev_path loc (("sexp_of_" ^ tn) :: rev_path)$
- >>
-
- (* Conversion of type paths *)
- let sexp_of_path_fun loc id =
- match Gen.get_rev_id_path id [] with
- | tn :: rev_path -> mk_abst_call loc tn rev_path
- | [] -> assert false (* impossible *)
-
- (* Conversion of types *)
- let rec sexp_of_type = function
- | <:ctyp@loc< sexp_opaque $_$ >> ->
- `Fun <:expr@loc< Sexplib.Conv.sexp_of_opaque >>
- | <:ctyp@loc< $tp1$ $tp2$ >> -> `Fun (sexp_of_appl_fun loc tp1 tp2)
- | <:ctyp< ( $tup:tp$ ) >> -> sexp_of_tuple tp
- | <:ctyp@loc< '$parm$ >> -> `Fun <:expr@loc< $lid:"_of_" ^ parm$ >>
- | <:ctyp@loc< $id:id$ >> -> `Fun (sexp_of_path_fun loc id)
- | <:ctyp@loc< $_$ -> $_$ >> as arrow ->
- `Fun <:expr@loc< fun (_f : $arrow$) ->
- Sexplib.Conv.sexp_of_fun Pervasives.ignore >>
- | <:ctyp< [< $row_fields$ ] >> | <:ctyp< [> $row_fields$ ] >>
- | <:ctyp< [= $row_fields$ ] >> -> sexp_of_variant row_fields
- | <:ctyp< ! $parms$ . $poly_tp$ >> -> sexp_of_poly parms poly_tp
- | tp -> Gen.unknown_type tp "sexp_of_type"
-
- (* Conversion of polymorphic types *)
- and sexp_of_appl_fun loc tp1 tp2 =
- match sexp_of_type tp1, sexp_of_type tp2 with
- | `Fun fun_expr1, `Fun fun_expr2 -> <:expr@loc< $fun_expr1$ $fun_expr2$ >>
- | `Fun fun_expr, `Match matching ->
- <:expr@loc< $fun_expr$ (fun [ $matching$ ]) >>
- | _ -> assert false (* impossible *)
-
-
- (* Conversion of tuples *)
- and sexp_of_tuple tp =
- let loc = Ast.loc_of_ctyp tp in
- let fps = List.map ~f:sexp_of_type (Ast.list_of_ctyp tp []) in
- let bindings, patts, vars = mk_bindings loc fps in
- let in_expr = <:expr@loc< Sexplib.Sexp.List $Gen.mk_expr_lst loc vars$ >> in
- let expr = <:expr@loc< let $bindings$ in $in_expr$ >> in
- `Match <:match_case@loc< ( $tup:Ast.paCom_of_list patts$ ) -> $expr$ >>
-
-
- (* Conversion of variant types *)
-
- and mk_cnv_expr tp =
- let loc = Ast.loc_of_ctyp tp in
- match sexp_of_type tp with
- | `Fun fun_expr -> <:expr@loc< $fun_expr$ >>
- | `Match matchings -> <:expr@loc< fun [ $matchings$ ] >>
-
- and sexp_of_variant row_fields =
- let rec loop = function
- | <:ctyp@loc< $tp1$ | $tp2$ >> ->
- <:match_case@loc< $loop tp1$ | $loop tp2$ >>
- | <:ctyp@loc< `$cnstr$ >> ->
- <:match_case@loc< `$cnstr$ -> Sexplib.Sexp.Atom $str:cnstr$ >>
- | <:ctyp@loc< `$cnstr$ of sexp_list $tp$>> ->
- let cnv_expr =
- match sexp_of_type tp with
- | `Fun fun_expr -> <:expr@loc< $fun_expr$ >>
- | `Match matchings ->
- <:expr@loc< fun el -> match el with [ $matchings$ ] >>
- in
- <:match_case@loc<
- `$cnstr$ l ->
- Sexplib.Sexp.List
- [ Sexplib.Sexp.Atom $str:cnstr$ ::
- Sexplib.Conv.list_map $cnv_expr$ l]
- >>
- | <:ctyp@loc< `$cnstr$ of $tps$ >> ->
- let fps = List.map ~f:sexp_of_type (Ast.list_of_ctyp tps []) in
- let bindings, patts, vars = mk_bindings loc fps in
- let cnstr_expr = <:expr@loc< Sexplib.Sexp.Atom $str:cnstr$ >> in
- let expr =
- <:expr@loc<
- let $bindings$ in
- Sexplib.Sexp.List $Gen.mk_expr_lst loc (cnstr_expr :: vars)$
- >>
- in
- <:match_case@loc< `$cnstr$ $Ast.paSem_of_list patts$ -> $expr$ >>
- | <:ctyp< [< $row_fields$ ] >> | <:ctyp< [> $row_fields$ ] >>
- | <:ctyp< [= $row_fields$ ] >> -> loop row_fields
- | <:ctyp@loc< $tp1$ $tp2$ >> ->
- let id_path = Gen.get_appl_path loc tp1 in
- let call = sexp_of_appl_fun loc tp1 tp2 in
- <:match_case@loc< #$id_path$ as v -> $call$ v >>
- | <:ctyp@loc< $id:id$ >> | <:ctyp@loc< #$id:id$ >> ->
- let call =
- match Gen.get_rev_id_path id [] with
- | tn :: rev_path -> mk_abst_call loc tn rev_path
- | [] -> assert false (* impossible *)
- in
- <:match_case@loc< #$id$ as v -> $call$ v >>
- | tp -> Gen.unknown_type tp "sexp_of_variant"
- in
- `Match (loop row_fields)
-
-
- (* Polymorphic record fields *)
-
- and sexp_of_poly parms tp =
- let loc = Ast.loc_of_ctyp tp in
- let bindings =
- let mk_binding parm =
- <:binding@loc< $lid:"_of_" ^ parm$ = Sexplib.Conv.sexp_of_opaque >>
- in
- List.map ~f:mk_binding (Gen.ty_var_list_of_ctyp parms [])
- in
- match sexp_of_type tp with
- | `Fun fun_expr -> `Fun <:expr@loc< let $list:bindings$ in $fun_expr$ >>
- | `Match matchings ->
- `Match
- <:match_case@loc<
- arg ->
- let $list:bindings$ in
- match arg with
- [ $matchings$ ]
- >>
-
-
- (* Conversion of sum types *)
-
- let rec branch_sum = function
- | <:ctyp@loc< $tp1$ | $tp2$ >> ->
- <:match_case@loc< $branch_sum tp1$ | $branch_sum tp2$ >>
- | <:ctyp@loc< $uid:cnstr$ >> ->
- <:match_case@loc< $uid:cnstr$ -> Sexplib.Sexp.Atom $str:cnstr$ >>
- | <:ctyp@loc< $uid:cnstr$ of sexp_list $tp$>> ->
- let cnv_expr =
- match sexp_of_type tp with
- | `Fun fun_expr -> <:expr@loc< $fun_expr$ >>
- | `Match matchings ->
- <:expr@loc< fun el -> match el with [ $matchings$ ] >>
- in
- <:match_case@loc<
- $uid:cnstr$ l ->
- Sexplib.Sexp.List
- [Sexplib.Sexp.Atom $str:cnstr$ ::
- Sexplib.Conv.list_map $cnv_expr$ l]
- >>
- | <:ctyp@loc< $uid:cnstr$ of $tps$ >> ->
- let fps = List.map ~f:sexp_of_type (Ast.list_of_ctyp tps []) in
- let cnstr_expr = <:expr@loc< Sexplib.Sexp.Atom $str:cnstr$ >> in
- let bindings, patts, vars = mk_bindings loc fps in
- let patt =
- match patts with
- | [patt] -> patt
- | _ -> <:patt@loc< ( $tup:Ast.paCom_of_list patts$ ) >>
- in
- <:match_case@loc<
- $uid:cnstr$ $patt$ ->
- let $bindings$ in
- Sexplib.Sexp.List $Gen.mk_expr_lst loc (cnstr_expr :: vars)$
- >>
- | tp -> Gen.unknown_type tp "branch_sum"
-
- let sexp_of_sum alts = `Match (branch_sum alts)
-
-
- (* Conversion of record types *)
-
- let mk_rec_patt loc patt name =
- let p = <:patt@loc< $lid:name$ = $lid:"v_" ^ name$ >> in
- <:patt@loc< $patt$; $p$ >>
-
- let sexp_of_default_field patt expr name tp sexp_of empty =
- let loc = Ast.loc_of_ctyp tp in
- let patt = mk_rec_patt loc patt name in
- let cnv_expr =
- match sexp_of_type tp with
- | `Fun fun_expr -> <:expr@loc< $fun_expr$ >>
- | `Match matchings ->
- <:expr@loc< fun el -> match el with [ $matchings$ ] >>
- in
- let expr =
- let v_name = <:expr@loc< $lid: "v_" ^ name$ >> in
- <:expr@loc<
- let bnds =
- if $v_name$ = $empty$ then bnds
- else
- let arg = $sexp_of$ $cnv_expr$ $v_name$ in
- let bnd =
- Sexplib.Sexp.List [Sexplib.Sexp.Atom $str:name$; arg]
- in
- [ bnd :: bnds ]
- in
- $expr$
- >>
- in
- patt, expr
-
- let sexp_of_record flds_ctyp =
- let flds = Ast.list_of_ctyp flds_ctyp [] in
- let rec coll (patt, expr) = function
- | <:ctyp@loc< $lid:name$ : mutable sexp_option $tp$ >>
- | <:ctyp@loc< $lid:name$ : sexp_option $tp$ >> ->
- let patt = mk_rec_patt loc patt name in
- let vname = <:expr@loc< v >> in
- let cnv_expr = unroll_cnv_fp loc vname (sexp_of_type tp) in
- let expr =
- <:expr@loc<
- let bnds =
- match $lid:"v_" ^ name$ with
- [ None -> bnds
- | Some v ->
- let arg = $cnv_expr$ in
- let bnd =
- Sexplib.Sexp.List [Sexplib.Sexp.Atom $str:name$; arg]
- in
- [ bnd :: bnds ] ]
- in
- $expr$
- >>
- in
- patt, expr
- | <:ctyp@loc< $lid:name$ : mutable sexp_bool >>
- | <:ctyp@loc< $lid:name$ : sexp_bool >> ->
- let patt = mk_rec_patt loc patt name in
- let expr =
- <:expr@loc<
- let bnds =
- if $lid:"v_" ^ name$ then
- let bnd = Sexplib.Sexp.List [Sexplib.Sexp.Atom $str:name$] in
- [ bnd :: bnds ]
- else bnds
- in
- $expr$
- >>
- in
- patt, expr
- | <:ctyp@loc< $lid:name$ : mutable sexp_list $tp$ >>
- | <:ctyp@loc< $lid:name$ : sexp_list $tp$ >> ->
- sexp_of_default_field
- patt expr name tp <:expr@loc< sexp_of_list >> <:expr@loc< [] >>
- | <:ctyp@loc< $lid:name$ : mutable sexp_array $tp$ >>
- | <:ctyp@loc< $lid:name$ : sexp_array $tp$ >> ->
- sexp_of_default_field
- patt expr name tp <:expr@loc< sexp_of_array >> <:expr@loc< [||] >>
- | <:ctyp@loc< $lid:name$ : mutable $tp$ >>
- | <:ctyp@loc< $lid:name$ : $tp$ >> ->
- let patt = mk_rec_patt loc patt name in
- let vname = <:expr@loc< $lid:"v_" ^ name$ >> in
- let cnv_expr = unroll_cnv_fp loc vname (sexp_of_type tp) in
- let expr =
- <:expr@loc<
- let arg = $cnv_expr$ in
- let bnd = Sexplib.Sexp.List [Sexplib.Sexp.Atom $str:name$; arg] in
- let bnds = [ bnd :: bnds ] in
- $expr$
- >>
- in
- patt, expr
- | _ -> assert false (* impossible *)
- in
- let loc = Ast.loc_of_ctyp flds_ctyp in
- let init_expr = <:expr@loc< Sexplib.Sexp.List bnds >> in
- let patt, expr =
- List.fold_left ~f:coll ~init:(<:patt@loc<>>, init_expr) flds
- in
- `Match
- <:match_case@loc<
- { $patt$ } ->
- let bnds = [] in
- $expr$
- >>
-
-
- (* Empty type *)
- let sexp_of_nil loc = `Fun <:expr@loc< fun _v -> assert False >>
-
-
- (* Generate code from type definitions *)
-
- let sexp_of_td loc type_name tps rhs =
- let body =
- let rec loop tp =
- Gen.switch_tp_def tp
- ~alias:(fun (_ : Loc.t) tp -> sexp_of_type tp)
- ~sum:(fun (_ : Loc.t) tp -> sexp_of_sum tp)
- ~record:(fun (_ : Loc.t) tp -> sexp_of_record tp)
- ~variants:(fun (_ : Loc.t) tp -> sexp_of_variant tp)
- ~mani:(fun (_ : Loc.t) _tp1 tp2 -> loop tp2)
- ~nil:sexp_of_nil
- in
- match loop rhs with
- | `Fun fun_expr ->
- (* Prevent violation of value restriction and problems with
- recursive types by eta-expanding function definitions *)
- <:expr@loc< fun [ v -> $fun_expr$ v ] >>
- | `Match matchings -> <:expr@loc< fun [ $matchings$ ] >>
- in
- let mk_pat id = <:patt@loc< $lid:id$ >> in
- let patts =
- List.map tps
- ~f:(fun ty -> <:patt@loc< $lid:"_of_" ^ Gen.get_tparam_id ty$>>)
- in
- let bnd = mk_pat ("sexp_of_" ^ type_name) in
- <:binding@loc< $bnd$ = $Gen.abstract loc patts body$ >>
-
- let rec sexp_of_tds = function
- | Ast.TyDcl (loc, type_name, tps, rhs, _cl) ->
- sexp_of_td loc type_name tps rhs
- | Ast.TyAnd (loc, tp1, tp2) ->
- <:binding@loc< $sexp_of_tds tp1$ and $sexp_of_tds tp2$ >>
- | _ -> assert false (* impossible *)
-
- let sexp_of tds =
- let binding, recursive, loc =
- match tds with
- | Ast.TyDcl (loc, type_name, tps, rhs, _cl) ->
- sexp_of_td loc type_name tps rhs,
- Gen.type_is_recursive type_name rhs, loc
- | Ast.TyAnd (loc, _, _) as tds -> sexp_of_tds tds, true, loc
- | _ -> assert false (* impossible *)
- in
- if recursive then <:str_item@loc< value rec $binding$ >>
- else <:str_item@loc< value $binding$ >>
-
- (* Add code generator to the set of known generators *)
- let () = Pa_type_conv.add_generator "sexp_of" sexp_of
-
- let string_of_ident id =
- let str_lst = Gen.get_rev_id_path id [] in
- String.concat ~sep:"." str_lst
-
- let sexp_of_exn tp =
- let get_full_cnstr cnstr = Pa_type_conv.get_conv_path () ^ "." ^ cnstr in
- let expr =
- match tp with
- | <:ctyp@loc< $uid:cnstr$ >> ->
- <:expr@loc<
- Sexplib.Exn_magic.register $uid:cnstr$ $str:get_full_cnstr cnstr$
- >>
- | <:ctyp@loc< $uid:cnstr$ of $tps$ >> ->
- let ctyps = Ast.list_of_ctyp tps [] in
- let fps = List.map ~f:sexp_of_type ctyps in
- let sexp_converters =
- List.map fps ~f:(function
- | `Fun fun_expr -> <:expr@loc< $fun_expr$ >>
- | `Match matchings -> <:expr@loc< fun [ $matchings$ ] >>)
- in
- let _, patts, vars = mk_bindings loc fps in
- let register_name = sprintf "register%d" (List.length fps) in
- let make_exc =
- let var_args =
- match vars with
- | [var] -> var
- | _ -> <:expr@loc< $tup:Ast.exCom_of_list vars$ >>
- in
- Gen.abstract loc patts <:expr@loc< $uid:cnstr$ $var_args$ >>
- in
- let call =
- let partial =
- <:expr@loc<
- Sexplib.Exn_magic.$lid:register_name$
- $make_exc$ $str:get_full_cnstr cnstr$
- >>
- in
- Gen.apply loc partial sexp_converters
- in
- <:expr@loc< $call$ >>
- | tp -> Gen.unknown_type tp "sexp_of_exn"
- in
- let loc = Ast.loc_of_ctyp tp in
- <:str_item@loc< value () = $expr$ >>
-
- let () = Pa_type_conv.add_generator ~is_exn:true "sexp" sexp_of_exn
-end
-
-
-(* Generator for converters of S-expressions to OCaml-values *)
-module Generate_of_sexp = struct
- let mk_abst_call loc tn ?(internal = false) rev_path =
- let tns = tn ^ "_of_sexp" in
- let tns_suff = if internal then tns ^ "__" else tns in
- <:expr@loc< $id:Gen.ident_of_rev_path loc (tns_suff :: rev_path)$ >>
-
- (* Utility functions for polymorphic variants *)
-
- (* Handle backtracking when variants do not match *)
- let handle_no_variant_match loc expr =
- <:match_case@loc< Sexplib.Conv_error.No_variant_match _ -> $expr$ >>
-
- let is_wildcard = function [_] -> true | _ -> false
-
- (* Generate code depending on whether to generate a match for the last
- case of matching a variant *)
- let handle_variant_match_last loc match_last matches =
- if match_last || is_wildcard matches then
- match matches with
- | <:match_case< $_$ -> $expr$ >> :: _ -> expr
- | _ -> assert false (* impossible *)
- else <:expr@loc< match atom with [ $list:matches$ ] >>
-
- (* Generate code for matching malformed S-expressions *)
- let mk_variant_other_matches loc rev_els call =
- let coll_structs acc (loc, cnstr) =
- <:match_case@loc<
- $str:cnstr$ -> Sexplib.Conv_error.$lid:call$ _tp_loc _sexp
- >> :: acc
- in
- let exc_no_variant_match =
- <:match_case@loc<
- _ -> Sexplib.Conv_error.no_variant_match _tp_loc _sexp
- >>
- in
- List.fold_left ~f:coll_structs ~init:[exc_no_variant_match] rev_els
-
- (* Split the row fields of a variant type into lists of atomic variants,
- structured variants, atomic variants + included variant types,
- and structured variants + included variant types. *)
- let rec split_row_field (atoms, structs, ainhs, sinhs as acc) = function
- | <:ctyp@loc< `$cnstr$ >> ->
- let tpl = loc, cnstr in
- (
- tpl :: atoms,
- structs,
- `A tpl :: ainhs,
- sinhs
- )
- | <:ctyp@loc< `$cnstr$ of $tps$ >> ->
- (
- atoms,
- (loc, cnstr) :: structs,
- ainhs,
- `S (loc, cnstr, tps) :: sinhs
- )
- | <:ctyp< [< $row_fields$ ] >> | <:ctyp< [> $row_fields$ ] >>
- | <:ctyp< [= $row_fields$ ] >> ->
- List.fold_left ~f:split_row_field
- ~init:acc (Ast.list_of_ctyp row_fields [])
- | <:ctyp< $_$ $_$ >>
- | (<:ctyp< $id:_$ >> | <:ctyp< #$id:_$ >>) as inh ->
- let iinh = `I inh in
- (
- atoms,
- structs,
- iinh :: ainhs,
- iinh :: sinhs
- )
- | tp -> Gen.unknown_type tp "split_row_field"
-
- (* Conversion of type paths *)
- let path_of_sexp_fun loc id =
- match Gen.get_rev_id_path id [] with
- | tn :: rev_path -> mk_abst_call loc tn rev_path
- | [] -> assert false (* no empty paths *)
-
- (* Conversion of types *)
- let rec type_of_sexp = function
- | <:ctyp@loc< sexp_opaque $_$ >> ->
- `Fun <:expr@loc< Sexplib.Conv.opaque_of_sexp >>
- | <:ctyp@loc< sexp_option >> ->
- `Fun <:expr@loc< fun a_of_sexp v -> Some (a_of_sexp v) >>
- | <:ctyp@loc< sexp_list >> ->
- `Fun <:expr@loc< fun a_of_sexp v ->
- Sexplib.Conv.list_of_sexp a_of_sexp v >>
- | <:ctyp@loc< sexp_array >> ->
- `Fun <:expr@loc< fun a_of_sexp v ->
- Sexplib.Conv.array_of_sexp a_of_sexp v >>
- | <:ctyp@loc< $tp1$ $tp2$ >> ->
- let fp1 = type_of_sexp tp1 in
- let fp2 = type_of_sexp tp2 in
- `Fun (unroll_fun_matches loc fp1 fp2)
- | <:ctyp< ( $tup:tp$ ) >> -> tuple_of_sexp tp
- | <:ctyp@loc< '$parm$ >> -> `Fun <:expr@loc< $lid:"_of_" ^ parm$ >>
- | <:ctyp@loc< $id:id$ >> -> `Fun (path_of_sexp_fun loc id)
- | <:ctyp@loc< $_$ -> $_$ >> as arrow ->
- `Fun <:expr@loc< fun sexp ->
- (Sexplib.Conv.fun_of_sexp sexp : $arrow$) >>
- | <:ctyp< [< $row_fields$ ] >> | <:ctyp< [> $row_fields$ ] >>
- | <:ctyp< [= $row_fields$ ] >> ->
- variant_of_sexp ?full_type:None row_fields
- | <:ctyp< ! $parms$ . $poly_tp$ >> -> poly_of_sexp parms poly_tp
- | tp -> Gen.unknown_type tp "type_of_sexp"
-
- (* Conversion of tuples *)
- and tuple_of_sexp tps =
- let fps = List.map ~f:type_of_sexp (Ast.list_of_ctyp tps []) in
- let loc = Ast.loc_of_ctyp tps in
- let bindings, patts, vars = mk_bindings loc fps in
- let n = string_of_int (List.length fps) in
- `Match
- <:match_case@loc<
- Sexplib.Sexp.List $Gen.mk_patt_lst loc patts$ ->
- let $bindings$ in
- ( $tup:Ast.exCom_of_list vars$ )
- | sexp ->
- Sexplib.Conv_error.tuple_of_size_n_expected _tp_loc $int:n$ sexp
- >>
-
- (* Generate internal call *)
- and mk_internal_call = function
- | <:ctyp@loc< $id:id$ >> | <:ctyp@loc< #$id:id$ >> ->
- let call =
- match Gen.get_rev_id_path id [] with
- | tn :: rev_path -> mk_abst_call loc tn ~internal:true rev_path
- | [] -> assert false (* impossible *)
- in
- call
- | <:ctyp@loc< $tp1$ $tp2$ >> ->
- let fp1 = `Fun (mk_internal_call tp1) in
- let fp2 = type_of_sexp tp2 in
- unroll_fun_matches loc fp1 fp2
- | _ -> assert false (* impossible *)
-
- (* Generate code for matching included variant types *)
- and handle_variant_inh full_type match_last other_matches inh =
- let loc = Ast.loc_of_ctyp inh in
- let fun_expr = mk_internal_call inh in
- let match_exc =
- handle_no_variant_match loc (
- handle_variant_match_last loc match_last other_matches) in
- let new_other_matches =
- [
- <:match_case@loc<
- _ -> try ($fun_expr$ _sexp :> $full_type$) with [ $match_exc$ ]
- >>
- ]
- in
- new_other_matches, true
-
- (* Generate code for matching atomic variants *)
- and mk_variant_match_atom loc full_type rev_atoms_inhs rev_structs =
- let coll (other_matches, match_last) = function
- | `A (loc, cnstr) ->
- let new_match = <:match_case@loc< $str:cnstr$ -> `$cnstr$ >> in
- new_match :: other_matches, false
- | `I inh ->
- handle_variant_inh full_type match_last other_matches inh
- in
- let other_matches =
- mk_variant_other_matches loc rev_structs "ptag_no_args"
- in
- let match_atoms_inhs, match_last =
- List.fold_left ~f:coll ~init:(other_matches, false) rev_atoms_inhs in
- handle_variant_match_last loc match_last match_atoms_inhs
-
-
- (* Variant conversions *)
-
- (* Match arguments of constructors (variants or sum types) *)
- and mk_cnstr_args_match ~is_variant cnstr tps =
- let loc = Ast.loc_of_ctyp tps in
- let cnstr vars_expr =
- if is_variant then <:expr@loc< `$cnstr$ $vars_expr$ >>
- else <:expr@loc< $uid:cnstr$ $vars_expr$ >>
- in
- match tps with
- | <:ctyp@loc< sexp_list $tp$ >> ->
- let cnv =
- match type_of_sexp tp with
- | `Fun fun_expr -> <:expr@loc< $fun_expr$ >>
- | `Match matchings ->
- <:expr@loc< fun el -> match el with [ $matchings$ ] >>
- in
- cnstr <:expr@loc< Sexplib.Conv.list_map ($cnv$) sexp_args >>
- | _ ->
- let fps = List.map ~f:type_of_sexp (Ast.list_of_ctyp tps []) in
- let bindings, patts, vars = mk_bindings loc fps in
- let good_arg_match =
- let vars_expr =
- match vars with
- | [var_expr] -> var_expr
- | _ -> <:expr@loc< ( $tup:Ast.exCom_of_list vars$ ) >>
- in
- cnstr vars_expr
- in
- let handle_exc =
- if is_variant then "ptag_incorrect_n_args"
- else "stag_incorrect_n_args"
- in
- <:expr@loc<
- match sexp_args with
- [ $Gen.mk_patt_lst loc patts$ -> let $bindings$ in $good_arg_match$
- | _ -> Sexplib.Conv_error.$lid:handle_exc$ _tp_loc _tag _sexp ]
- >>
-
- (* Generate code for matching structured variants *)
- and mk_variant_match_struct loc full_type rev_structs_inhs rev_atoms =
- let has_structs_ref = ref false in
- let coll (other_matches, match_last) = function
- | `S (loc, cnstr, tps) ->
- has_structs_ref := true;
- let expr = mk_cnstr_args_match ~is_variant:true cnstr tps in
- let new_match =
- <:match_case@loc< ($str:cnstr$ as _tag) -> $expr$ >>
- in
- new_match :: other_matches, false
- | `I inh ->
- handle_variant_inh full_type match_last other_matches inh
- in
- let other_matches =
- mk_variant_other_matches loc rev_atoms "ptag_no_args"
- in
- let match_structs_inhs, match_last =
- List.fold_left ~f:coll ~init:(other_matches, false) rev_structs_inhs
- in
- (
- handle_variant_match_last loc match_last match_structs_inhs,
- !has_structs_ref
- )
-
- (* Generate code for handling atomic and structured variants (i.e. not
- included variant types) *)
- and handle_variant_tag loc full_type row_fields =
- let rev_atoms, rev_structs, rev_atoms_inhs, rev_structs_inhs =
- List.fold_left ~f:split_row_field ~init:([], [], [], []) row_fields
- in
- let match_struct, has_structs =
- mk_variant_match_struct loc full_type rev_structs_inhs rev_atoms in
- let maybe_sexp_args_patt =
- if has_structs then <:patt@loc< sexp_args >>
- else <:patt@loc< _ >>
- in
- <:match_case@loc<
- Sexplib.Sexp.Atom atom as _sexp ->
- $mk_variant_match_atom loc full_type rev_atoms_inhs rev_structs$
- | Sexplib.Sexp.List
- [Sexplib.Sexp.Atom atom :: $maybe_sexp_args_patt$] as _sexp ->
- $match_struct$
- | Sexplib.Sexp.List [Sexplib.Sexp.List _ :: _] as sexp ->
- Sexplib.Conv_error.nested_list_invalid_poly_var _tp_loc sexp
- | Sexplib.Sexp.List [] as sexp ->
- Sexplib.Conv_error.empty_list_invalid_poly_var _tp_loc sexp
- >>
-
- (* Generate matching code for variants *)
- and variant_of_sexp ?full_type row_tp =
- let loc = Ast.loc_of_ctyp row_tp in
- let row_fields = Ast.list_of_ctyp row_tp [] in
- let is_contained, full_type =
- match full_type with
- | None -> true, <:ctyp@loc< [= $row_tp$ ] >>
- | Some full_type -> false, full_type
- in
- let top_match =
- match row_fields with
- | (<:ctyp< $id:_$ >> | <:ctyp< $_$ $_$ >>) as inh :: rest ->
- let rec loop inh row_fields =
- let call =
- <:expr@loc< ( $mk_internal_call inh$ sexp :> $full_type$ ) >>
- in
- match row_fields with
- | [] -> call
- | h :: t ->
- let expr =
- match h with
- | <:ctyp< $id:_$ >> | <:ctyp< $_$ $_$ >> -> loop h t
- | _ ->
- let rftag_matches =
- handle_variant_tag loc full_type row_fields
- in
- <:expr@loc< match sexp with [ $rftag_matches$ ] >>
- in
- <:expr@loc<
- try $call$ with
- [ $handle_no_variant_match loc expr$ ]
- >>
- in
- <:match_case@loc< sexp -> $loop inh rest$ >>
- | _ :: _ -> handle_variant_tag loc full_type row_fields
- | [] -> assert false (* impossible *)
- in
- if is_contained then
- `Fun
- <:expr@loc<
- fun sexp ->
- try match sexp with [ $top_match$ ]
- with
- [ Sexplib.Conv_error.No_variant_match (_tp_loc, sexp) ->
- Sexplib.Conv_error.no_matching_variant_found _tp_loc sexp
- ]
- >>
- else `Match top_match
-
- and poly_of_sexp parms tp =
- let loc = Ast.loc_of_ctyp tp in
- let bindings =
- let mk_binding parm =
- <:binding@loc<
- $lid:"_of_" ^ parm$ =
- fun sexp -> Sexplib.Conv_error.record_poly_field_value _tp_loc sexp
- >>
- in
- List.map ~f:mk_binding (Gen.ty_var_list_of_ctyp parms [])
- in
- match type_of_sexp tp with
- | `Fun fun_expr -> `Fun <:expr@loc< let $list:bindings$ in $fun_expr$ >>
- | `Match matchings ->
- `Match
- <:match_case@loc<
- arg ->
- let $list:bindings$ in
- match arg with
- [ $matchings$ ]
- >>
-
-
- (* Sum type conversions *)
-
- (* Generate matching code for well-formed S-expressions wrt. sum types *)
- let rec mk_good_sum_matches = function
- | <:ctyp@loc< $uid:cnstr$ >> ->
- let lccnstr = String.uncapitalize cnstr in
- <:match_case@loc<
- Sexplib.Sexp.Atom ($str:lccnstr$ | $str:cnstr$) -> $uid:cnstr$
- >>
- | <:ctyp@loc< $uid:cnstr$ of $tps$ >> ->
- let lccnstr = String.uncapitalize cnstr in
- <:match_case@loc<
- (Sexplib.Sexp.List
- [Sexplib.Sexp.Atom ($str:lccnstr$ | $str:cnstr$ as _tag) ::
- sexp_args] as _sexp) ->
- $mk_cnstr_args_match ~is_variant:false cnstr tps$
- >>
- | <:ctyp@loc< $tp1$ | $tp2$ >> ->
- <:match_case@loc<
- $mk_good_sum_matches tp1$
- | $mk_good_sum_matches tp2$
- >>
- | _ -> assert false (* impossible *)
-
- (* Generate matching code for malformed S-expressions with good tags
- wrt. sum types *)
- let rec mk_bad_sum_matches = function
- | <:ctyp@loc< $uid:cnstr$ >> ->
- let lccnstr = String.uncapitalize cnstr in
- <:match_case@loc<
- Sexplib.Sexp.List
- [Sexplib.Sexp.Atom ($str:lccnstr$ | $str:cnstr$) :: _] as sexp ->
- Sexplib.Conv_error.stag_no_args _tp_loc sexp
- >>
- | <:ctyp@loc< $uid:cnstr$ of $_$ >> ->
- let lccnstr = String.uncapitalize cnstr in
- <:match_case@loc<
- Sexplib.Sexp.Atom ($str:lccnstr$ | $str:cnstr$) as sexp ->
- Sexplib.Conv_error.stag_takes_args _tp_loc sexp
- >>
- | <:ctyp@loc< $tp1$ | $tp2$ >> ->
- <:match_case@loc<
- $mk_bad_sum_matches tp1$
- | $mk_bad_sum_matches tp2$
- >>
- | _ -> assert false (* impossible *)
-
- (* Generate matching code for sum types *)
- let sum_of_sexp alts =
- let loc = Ast.loc_of_ctyp alts in
- `Match
- <:match_case@loc<
- $mk_good_sum_matches alts$
- | $mk_bad_sum_matches alts$
- | Sexplib.Sexp.List [Sexplib.Sexp.List _ :: _] as sexp ->
- Sexplib.Conv_error.nested_list_invalid_sum _tp_loc sexp
- | Sexplib.Sexp.List [] as sexp ->
- Sexplib.Conv_error.empty_list_invalid_sum _tp_loc sexp
- | sexp -> Sexplib.Conv_error.unexpected_stag _tp_loc sexp
- >>
-
-
- (* Record conversions *)
-
- (* Generate code for extracting record fields *)
- let mk_extract_fields tp =
- let rec loop no_args args = function
- | <:ctyp< $tp1$; $tp2$ >> ->
- let no_args, args = loop no_args args tp2 in
- loop no_args args tp1
- | <:ctyp@loc< $lid:nm$ : mutable sexp_bool >>
- | <:ctyp@loc< $lid:nm$ : sexp_bool>> ->
- let no_args =
- <:match_case@loc<
- $str:nm$ ->
- if $lid:nm ^ "_field"$.val then
- duplicates.val := [ field_name :: duplicates.val ]
- else $lid:nm ^ "_field"$.val := True
- | $no_args$
- >>
- in
- no_args, args
- | <:ctyp@loc< $lid:nm$ : mutable sexp_option $tp$ >>
- | <:ctyp@loc< $lid:nm$ : sexp_option $tp$ >>
- | <:ctyp@loc< $lid:nm$ : mutable $tp$ >>
- | <:ctyp@loc< $lid:nm$ : $tp$ >> ->
- let unrolled =
- unroll_cnv_fp loc <:expr@loc< _field_sexp >> (type_of_sexp tp)
- in
- let args =
- <:match_case@loc<
- $str:nm$ ->
- match $lid:nm ^ "_field"$.val with
- [ None ->
- let fvalue = $unrolled$ in
- $lid:nm ^ "_field"$.val := Some fvalue
- | Some _ ->
- duplicates.val := [ field_name :: duplicates.val ] ]
- | $args$
- >>
- in
- no_args, args
- | _ -> assert false (* impossible *)
- in
- let handle_extra =
- let loc = Ast.loc_of_ctyp tp in
- <:match_case@loc<
- _ ->
- if Sexplib.Conv.record_check_extra_fields.val then
- extra.val := [ field_name :: extra.val ]
- else ()
- >>
- in
- loop handle_extra handle_extra tp
-
- (* Generate code for handling the result of matching record fields *)
- let mk_handle_record_match_result has_poly flds =
- let has_nonopt_fields = ref false in
- let res_tpls, bi_lst, good_patts =
- let rec loop (res_tpls, bi_lst, good_patts as acc) = function
- | <:ctyp@loc< $lid:nm$ : $tp$ >> ->
- let fld = <:expr@loc< $lid:nm ^ "_field"$.val >> in
- let new_bi_lst, new_good_patts =
- match tp with
- | <:ctyp@loc< sexp_bool >> | <:ctyp@loc< mutable sexp_bool >>
- | <:ctyp@loc< sexp_option $_$ >>
- | <:ctyp@loc< mutable sexp_option $_$ >>
- | <:ctyp@loc< sexp_list $_$ >>
- | <:ctyp@loc< mutable sexp_list $_$ >>
- | <:ctyp@loc< sexp_array $_$ >>
- | <:ctyp@loc< mutable sexp_array $_$ >> ->
- bi_lst, <:patt@loc< $lid:nm ^ "_value"$ >> :: good_patts
- | _ ->
- let loc = Ast.loc_of_ctyp tp in
- has_nonopt_fields := true;
- (
- <:expr@loc<
- (Pervasives.(=) $fld$ None, $str:nm$) >> :: bi_lst,
- <:patt@loc< Some $lid:nm ^ "_value"$ >> :: good_patts
- )
- in
- (
- <:expr@loc< $fld$ >> :: res_tpls,
- new_bi_lst,
- new_good_patts
- )
- | <:ctyp< $tp1$; $tp2$ >> -> loop (loop acc tp2) tp1
- | _ -> assert false (* impossible *)
- in
- loop ([], [], []) flds
- in
- let loc = Ast.loc_of_ctyp flds in
- let match_good_expr =
- if has_poly then
- let rec loop acc = function
- | <:ctyp< $tp1$; $tp2$ >> -> loop (loop acc tp2) tp1
- | <:ctyp@loc< $lid:nm$ : $_$ >> ->
- <:expr@loc< $lid:nm ^ "_value"$ >> :: acc
- | _ -> assert false (* impossible *)
- in
- match loop [] flds with
- | [match_good_expr] -> match_good_expr
- | match_good_exprs ->
- <:expr@loc< $tup:Ast.exCom_of_list match_good_exprs$ >>
- else
- let rec loop = function
- | <:ctyp@loc< $tp1$; $tp2$ >> ->
- <:rec_binding@loc< $loop tp1$; $loop tp2$ >>
- | <:ctyp@loc< $lid:nm$ : sexp_list $_$ >> ->
- <:rec_binding@loc<
- $lid:nm$ =
- match $lid:nm ^ "_value"$ with
- [ None -> [] | Some v -> v ]
- >>
- | <:ctyp@loc< $lid:nm$ : sexp_array $_$ >> ->
- <:rec_binding@loc<
- $lid:nm$ =
- match $lid:nm ^ "_value"$ with
- [ None -> [||] | Some v -> v ]
- >>
- | <:ctyp@loc< $lid:nm$ : $_$ >> ->
- <:rec_binding@loc< $lid:nm$ = $lid:nm ^ "_value"$ >>
- | _ -> assert false (* impossible *)
- in
- <:expr@loc< { $loop flds$ } >>
- in
- let expr, patt =
- match res_tpls, good_patts with
- | [res_expr], [res_patt] -> res_expr, res_patt
- | _ ->
- <:expr@loc< $tup:Ast.exCom_of_list res_tpls$ >>,
- <:patt@loc< $tup:Ast.paCom_of_list good_patts$ >>
- in
- if !has_nonopt_fields then
- <:expr@loc<
- match $expr$ with
- [ $patt$ -> $match_good_expr$
- | _ ->
- Sexplib.Conv_error.record_undefined_elements _tp_loc sexp
- $Gen.mk_expr_lst loc bi_lst$
- ]
- >>
- else <:expr@loc< match $expr$ with [ $patt$ -> $match_good_expr$ ] >>
-
- (* Generate code for converting record fields *)
- let mk_cnv_fields has_poly flds =
- let field_refs =
- let rec loop = function
- | <:ctyp@loc< $tp1$; $tp2$ >> ->
- <:binding@loc< $loop tp1$ and $loop tp2$ >>
- | <:ctyp@loc< $lid:nm$ : sexp_bool >> ->
- <:binding@loc< $lid:nm ^ "_field"$ = ref False >>
- | <:ctyp@loc< $lid:nm$ : $_$ >> ->
- <:binding@loc< $lid:nm ^ "_field"$ = ref None >>
- | _ -> assert false (* impossible *)
- in
- loop flds
- in
- let mc_no_args_fields, mc_fields_with_args = mk_extract_fields flds in
- let loc = Ast.loc_of_ctyp flds in
- <:expr@loc<
- let $field_refs$ and duplicates = ref [] and extra = ref [] in
- let rec iter = fun
- [ [
- Sexplib.Sexp.List
- [(Sexplib.Sexp.Atom field_name); _field_sexp] ::
- tail
- ] ->
- do {
- match field_name with
- [ $mc_fields_with_args$ ];
- iter tail }
- | [Sexplib.Sexp.List [(Sexplib.Sexp.Atom field_name)] :: tail] ->
- do {
- match field_name with
- [ $mc_no_args_fields$ ];
- iter tail }
- | [sexp :: _] ->
- Sexplib.Conv_error.record_only_pairs_expected _tp_loc sexp
- | [] -> () ]
- in
- do {
- iter field_sexps;
- if Pervasives.(<>) duplicates.val [] then
- Sexplib.Conv_error.record_duplicate_fields
- _tp_loc duplicates.val sexp
- else if Pervasives.(<>) extra.val [] then
- Sexplib.Conv_error.record_extra_fields _tp_loc extra.val sexp
- else $mk_handle_record_match_result has_poly flds$
- }
- >>
-
- let rec is_poly = function
- | <:ctyp< $_$ : ! $_$ . $_$ >> -> true
- | <:ctyp< $flds1$; $flds2$ >> -> is_poly flds1 || is_poly flds2
- | _ -> false
-
- (* Generate matching code for records *)
- let record_of_sexp flds =
- let loc = Ast.loc_of_ctyp flds in
- let handle_fields =
- let has_poly = is_poly flds in
- let cnv_fields = mk_cnv_fields has_poly flds in
- if has_poly then
- let is_singleton_ref = ref true in
- let patt =
- let rec loop = function
- | <:ctyp@loc< $tp1$; $tp2$ >> ->
- is_singleton_ref := false;
- <:patt@loc< $loop tp1$, $loop tp2$ >>
- | <:ctyp@loc< $lid:nm$ : $_$ >> -> <:patt@loc< $lid:nm$ >>
- | _ -> assert false (* impossible *)
- in
- let patt = loop flds in
- if !is_singleton_ref then patt
- else <:patt@loc< $tup:patt$ >>
- in
- let record_def =
- let rec loop = function
- | <:ctyp@loc< $tp1$; $tp2$ >> ->
- <:rec_binding@loc< $loop tp1$; $loop tp2$ >>
- | <:ctyp@loc< $lid:nm$ : $_$ >> ->
- <:rec_binding@loc< $lid:nm$ = $lid:nm$ >>
- | _ -> assert false (* impossible *)
- in
- loop flds
- in
- <:expr@loc<
- let $patt$ = $cnv_fields$ in
- { $record_def$ }
- >>
- else cnv_fields
- in
- `Match
- <:match_case@loc<
- Sexplib.Sexp.List field_sexps as sexp -> $handle_fields$
- | Sexplib.Sexp.Atom _ as sexp ->
- Sexplib.Conv_error.record_list_instead_atom _tp_loc sexp
- >>
-
-
- (* Empty type *)
- let nil_of_sexp loc =
- `Fun <:expr@loc< fun sexp -> Sexplib.Conv_error.empty_type _tp_loc sexp >>
-
-
- (* Generate code from type definitions *)
-
- let rec is_poly_call = function
- | <:expr< $f$ $_$ >> -> is_poly_call f
- | <:expr< $lid:name$ >> -> name.[0] = '_' && name.[1] = 'o'
- | _ -> false
-
- let td_of_sexp loc type_name tps rhs =
- let is_alias_ref = ref false in
- let handle_alias tp =
- is_alias_ref := true;
- type_of_sexp tp
- in
- let coll_args tp param =
- <:ctyp@loc< $tp$ $Gen.drop_variance_annotations param$ >>
- in
- let full_type =
- List.fold_left ~f:coll_args ~init:<:ctyp@loc< $lid:type_name$ >> tps
- in
- let is_variant_ref = ref false in
- let handle_variant row_fields =
- is_variant_ref := true;
- variant_of_sexp ~full_type row_fields
- in
- let body =
- let rec loop tp =
- Gen.switch_tp_def tp
- ~alias:(fun (_ : Loc.t) tp -> handle_alias tp)
- ~sum:(fun (_ : Loc.t) tp -> sum_of_sexp tp)
- ~record:(fun (_ : Loc.t) tp -> record_of_sexp tp)
- ~variants:(fun (_ : Loc.t) tp -> handle_variant tp)
- ~mani:(fun (_ : Loc.t) _tp1 tp2 -> loop tp2)
- ~nil:nil_of_sexp
- in
- match loop rhs with
- | `Fun fun_expr ->
- (* Prevent violation of value restriction and problems with
- recursive types by eta-expanding function definitions *)
- <:expr@loc< fun [ t -> $fun_expr$ t ] >>
- | `Match matchings -> <:expr@loc< fun [ $matchings$ ] >>
- in
- let internal_name = type_name ^ "_of_sexp__" in
- let arg_patts, arg_exprs =
- List.split (
- List.map ~f:(function tp ->
- let name = "_of_" ^ Gen.get_tparam_id tp in
- <:patt@loc< $lid:name$ >>, <:expr@loc< $lid:name$ >>
- )
- tps)
- in
- let with_poly_call = !is_alias_ref && is_poly_call body in
- let internal_fun_body =
- let full_type_name =
- sprintf "%s.%s" (Pa_type_conv.get_conv_path ()) type_name
- in
- if with_poly_call then
- Gen.abstract loc arg_patts
- <:expr@loc<
- fun sexp ->
- Sexplib.Conv_error.silly_type $str:full_type_name$ sexp
- >>
- else
- <:expr@loc<
- let _tp_loc = $str:full_type_name$ in
- $Gen.abstract loc arg_patts body$
- >>
- in
- let pre_external_fun_body =
- let internal_call =
- let internal_expr = <:expr@loc< $lid:internal_name$ >> in
- <:expr@loc< $Gen.apply loc internal_expr arg_exprs$ sexp >>
- in
- let no_variant_match_mc =
- <:match_case@loc<
- Sexplib.Conv_error.No_variant_match (_tp_loc, sexp) ->
- Sexplib.Conv_error.no_matching_variant_found _tp_loc sexp
- >>
- in
- if with_poly_call then
- <:expr@loc< try $body$ sexp with [ $no_variant_match_mc$ ] >>
- (* Type alias may refer to variant, therefore same handling here! *)
- else if !is_variant_ref || !is_alias_ref then
- <:expr@loc< try $internal_call$ with [ $no_variant_match_mc$ ] >>
- else internal_call
- in
- let internal_binding =
- <:binding@loc< $lid:internal_name$ = $internal_fun_body$ >>
- in
- let external_fun_patt = <:patt@loc< $lid:type_name ^ "_of_sexp"$ >> in
- let external_fun_body =
- Gen.abstract loc arg_patts
- <:expr@loc< fun sexp -> $pre_external_fun_body$ >>
- in
- let external_binding =
- <:binding@loc< $external_fun_patt$ = $external_fun_body$ >>
- in
- internal_binding, external_binding
-
- let rec tds_of_sexp acc = function
- | Ast.TyDcl (loc, type_name, tps, rhs, _cl) ->
- td_of_sexp loc type_name tps rhs :: acc
- | Ast.TyAnd (_, tp1, tp2) -> tds_of_sexp (tds_of_sexp acc tp2) tp1
- | _ -> assert false (* impossible *)
-
- (* Generate code from type definitions *)
- let of_sexp = function
- | Ast.TyDcl (loc, type_name, tps, rhs, _cl) ->
- let internal_binding, external_binding =
- td_of_sexp loc type_name tps rhs
- in
- let recursive = Gen.type_is_recursive type_name rhs in
- if recursive then
- <:str_item@loc<
- value rec $internal_binding$
- and $external_binding$
- >>
- else
- <:str_item@loc<
- value $internal_binding$;
- value $external_binding$
- >>
- | Ast.TyAnd (loc, _, _) as tds ->
- let two_bindings = tds_of_sexp [] tds in
- let bindings =
- List.map ~f:(fun (b1, b2) -> <:binding@loc< $b1$ and $b2$ >>)
- two_bindings
- in
- <:str_item@loc< value rec $list:bindings$ >>
- | _ -> assert false (* impossible *)
-
- (* Add code generator to the set of known generators *)
- let () = Pa_type_conv.add_generator "of_sexp" of_sexp
-end
-
-module Quotations = struct
- let of_sexp_quote loc _loc_name_opt cnt_str =
- Pa_type_conv.set_conv_path_if_not_set loc;
- let ctyp = Gram.parse_string Syntax.ctyp_quot loc cnt_str in
- let fp = Generate_of_sexp.type_of_sexp ctyp in
- let body =
- match fp with
- | `Fun fun_expr -> <:expr@loc< $fun_expr$ sexp >>
- | `Match matchings -> <:expr@loc< match sexp with [$matchings$] >>
- in
- let full_type_name =
- sprintf "%s line %i: %s"
- (Pa_type_conv.get_conv_path ()) (Loc.start_line loc) cnt_str
- in
- <:expr@loc<
- fun [ sexp ->
- let _tp_loc = $str:full_type_name$ in
- $body$ ]
- >>
-
- let () =
- Syntax.Quotation.add "of_sexp" Syntax.Quotation.DynAst.expr_tag
- of_sexp_quote
-
- let sexp_of_quote loc _loc_name_opt cnt_str =
- Pa_type_conv.set_conv_path_if_not_set loc;
- let ctyp = Gram.parse_string Syntax.ctyp_quot loc cnt_str in
- Generate_sexp_of.mk_cnv_expr ctyp
-
- let () =
- Syntax.Quotation.add "sexp_of" Syntax.Quotation.DynAst.expr_tag
- sexp_of_quote
-end
-
-(* Add "of_sexp" and "sexp_of" as "sexp" to the set of generators *)
-let () =
- Pa_type_conv.add_generator
- "sexp"
- (fun tds ->
- let loc = Ast.loc_of_ctyp tds in
- <:str_item@loc<
- $Generate_of_sexp.of_sexp tds$; $Generate_sexp_of.sexp_of tds$
- >>
- )