Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OCaml 4.11 support #118

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ install: wget https://raw.githubusercontent.com/ocaml/ocaml-travisci-skeleton/ma
script: bash -ex ./.travis-docker.sh
env:
global:
- PINS="ppx_deriving_yojson:."
- PINS="ppx_deriving_yojson:. ppx_deriving:git://github.com/ocaml-ppx/ppx_deriving.git ocaml-migrate-parsetree:git://github.com/ocaml-ppx/ocaml-migrate-parsetree.git"
matrix:
- PACKAGE="ppx_deriving_yojson" DISTRO="ubuntu-16.04" OCAML_VERSION="4.10.0+rc2" OCAML_BETA="enable"
- PACKAGE="ppx_deriving_yojson" DISTRO="ubuntu-16.04" OCAML_VERSION="4.11.0+trunk" OCAML_BETA="enable"
- PACKAGE="ppx_deriving_yojson" DISTRO="ubuntu-16.04" OCAML_VERSION="4.10"
- PACKAGE="ppx_deriving_yojson" DISTRO="ubuntu-16.04" OCAML_VERSION="4.09"
- PACKAGE="ppx_deriving_yojson" DISTRO="ubuntu-16.04" OCAML_VERSION="4.08"
- PACKAGE="ppx_deriving_yojson" DISTRO="ubuntu-16.04" OCAML_VERSION="4.07"
- PACKAGE="ppx_deriving_yojson" DISTRO="ubuntu-16.04" OCAML_VERSION="4.06"
- PACKAGE="ppx_deriving_yojson" DISTRO="ubuntu-16.04" OCAML_VERSION="4.05"
- PACKAGE="ppx_deriving_yojson" DISTRO="ubuntu-16.04" OCAML_VERSION="4.04"
4 changes: 1 addition & 3 deletions ppx_deriving_yojson.opam
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@ depends: [
"yojson" {>= "1.6.0" & < "2.0.0"}
"result"
"ppx_deriving" {>= "4.0" & < "5.0"}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think also the new version restriction for ppx_deriving is required, since older versions wouldn't work.

"ppx_tools"
"ppxfind"
"ppxlib" {>= "0.9.0"}
"dune"
"cppo" {build}
"ounit" {with-test & >= "2.0.0"}
]
conflicts: [
Expand Down
8 changes: 1 addition & 7 deletions src/dune
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
(rule
(deps ppx_deriving_yojson.cppo.ml)
(targets ppx_deriving_yojson.ml)
(action (run %{bin:cppo} -V OCAML:%{ocaml_version} %{deps} -o %{targets})))

(library
(name ppx_deriving_yojson_runtime)
(public_name ppx_deriving_yojson.runtime)
Expand All @@ -15,8 +10,7 @@
(public_name ppx_deriving_yojson)
(synopsis "[@@deriving yojson]")
(libraries ppx_deriving.api)
(preprocess
(action (run ppxfind -legacy ppx_tools.metaquot --as-pp %{input-file})))
(preprocess (pps ppxlib.metaquot))
(ppx_runtime_libraries ppx_deriving_yojson_runtime yojson)
(modules ppx_deriving_yojson)
(kind ppx_deriver)
Expand Down
123 changes: 55 additions & 68 deletions src/ppx_deriving_yojson.cppo.ml → src/ppx_deriving_yojson.ml
Original file line number Diff line number Diff line change
@@ -1,33 +1,29 @@
#if OCAML_VERSION >= (4, 08, 0)
#define Rtag(label, attrs, has_empty, args) \
Rtag({ txt = label }, has_empty, args)
#elif OCAML_VERSION >= (4, 06, 0)
#define Rtag(label, attrs, has_empty, args) \
Rtag({ txt = label }, attrs, has_empty, args)
#endif

#if OCAML_VERSION >= (4, 08, 0)
#define Attribute_expr(loc_, txt_, payload) { attr_name = \
{ txt = txt_; loc = loc_ }; \
attr_payload = payload; \
attr_loc = loc_ }
#else
#define Attribute_expr(loc_, txt_, payload) ({txt = txt_; loc = loc_}, payload)
#endif

open Longident
open Location
open Asttypes
open Parsetree
open Ppxlib
open Ast_helper
open Ast_convenience

module Ast_builder_default_loc = struct
include Ppx_deriving.Ast_convenience

let gen_def_loc f x =
let loc = !Ast_helper.default_loc in
f ~loc x

let lid = gen_def_loc Ast_builder.Default.Located.lident
let list = gen_def_loc Ast_builder.Default.elist
let pstr = gen_def_loc Ast_builder.Default.pstring
let plist = gen_def_loc Ast_builder.Default.plist
let lam = gen_def_loc Ast_builder.Default.pexp_fun Nolabel None
end

open Ast_builder_default_loc

let warning_39 () =
let loc = !Ast_helper.default_loc in
let name = { txt = "ocaml.warning"; loc } in
Ast_helper.Attr.mk ~loc name (PStr [%str "-39"])


#if OCAML_VERSION >= (4, 10, 0)
let mod_mknoloc x = mknoloc (Some x)
#else
let mod_mknoloc x = mknoloc x
#endif

let deriver = "yojson"
let raise_errorf = Ppx_deriving.raise_errorf
Expand Down Expand Up @@ -83,20 +79,20 @@ let parse_options options =

let poly_fun names expr =
List.fold_right (fun name expr ->
#if OCAML_VERSION >= (4, 05, 0)
let loc = name.Location.loc in
let name = name.Location.txt in
#endif
[%expr fun [%p pvar ("poly_"^name)] -> [%e expr]]
) names expr

let type_add_attrs typ attributes =
let type_add_attrs typ attributes =
{ typ with ptyp_attributes = typ.ptyp_attributes @ attributes }

let rec ser_expr_of_typ typ =
match attr_ser typ.ptyp_attributes with
| Some e -> e
| None -> ser_expr_of_only_typ typ
and ser_expr_of_only_typ typ =
let loc = typ.ptyp_loc in
let attr_int_encoding typ =
match attr_int_encoding typ with `String -> "String" | `Int -> "Intlit"
in
Expand Down Expand Up @@ -138,29 +134,22 @@ and ser_expr_of_only_typ typ =
| { ptyp_desc = Ptyp_variant (fields, _, _); ptyp_loc } ->
let cases =
fields |> List.map (fun (field: row_field) ->
#if OCAML_VERSION >= (4, 08, 0)
match field.prf_desc with
#else
match field with
#endif
| Rtag(label, attrs, true (*empty*), []) ->
#if OCAML_VERSION >= (4, 08, 0)
| Rtag(label, true (*empty*), []) ->
let label = label.txt in
let attrs = field.prf_attributes in
#endif
Exp.case (Pat.variant label None)
[%expr `List [`String [%e str (attr_name label attrs)]]]
| Rtag(label, attrs, false, [{ ptyp_desc = Ptyp_tuple typs }]) ->
#if OCAML_VERSION >= (4, 08, 0)
| Rtag(label, false, [{ ptyp_desc = Ptyp_tuple typs }]) ->
let label = label.txt in
let attrs = field.prf_attributes in
#endif
Exp.case (Pat.variant label (Some (ptuple (List.mapi (fun i _ -> pvar (argn i)) typs))))
[%expr `List ((`String [%e str (attr_name label attrs)]) :: [%e
list (List.mapi
(fun i typ -> app (ser_expr_of_typ typ) [evar (argn i)]) typs)])]
| Rtag(label, attrs, false, [typ]) ->
#if OCAML_VERSION >= (4, 08, 0)
| Rtag(label, false, [typ]) ->
let label = label.txt in
let attrs = field.prf_attributes in
#endif
Exp.case (Pat.variant label (Some [%pat? x]))
[%expr `List [`String [%e str (attr_name label attrs)];
[%e ser_expr_of_typ typ] x]]
Expand All @@ -183,6 +172,7 @@ and ser_expr_of_only_typ typ =

(* http://desuchan.net/desu/src/1284751839295.jpg *)
let rec desu_fold ~path f typs =
let loc = !Ast_helper.default_loc in
typs |>
List.mapi (fun i typ -> i, app (desu_expr_of_typ ~path typ) [evar (argn i)]) |>
List.fold_left (fun x (i, y) ->
Expand All @@ -193,6 +183,7 @@ and desu_expr_of_typ ~path typ =
| Some e -> e
| None -> desu_expr_of_only_typ ~path typ
and desu_expr_of_only_typ ~path typ =
let loc = typ.ptyp_loc in
let error = [%expr Result.Error [%e str (String.concat "." path)]] in
let decode' cases =
Exp.function_ (
Expand Down Expand Up @@ -250,37 +241,26 @@ and desu_expr_of_only_typ ~path typ =
(desu_fold ~path tuple typs)
| { ptyp_desc = Ptyp_variant (fields, _, _); ptyp_loc } ->
let inherits, tags = List.partition (fun field ->
#if OCAML_VERSION >= (4, 08, 0)
match field.prf_desc with
#else
match field with
#endif
Rinherit _ -> true
| _ -> false) fields
in
let tag_cases = tags |> List.map (fun field ->
#if OCAML_VERSION >= (4, 08, 0)
match field.prf_desc with
#else
match field with
#endif
| Rtag(label, attrs, true (*empty*), []) ->
#if OCAML_VERSION >= (4, 08, 0)
| Rtag(label, true (*empty*), []) ->
let label = label.txt in
let attrs = field.prf_attributes in
#endif
Exp.case [%pat? `List [`String [%p pstr (attr_name label attrs)]]]
[%expr Result.Ok [%e Exp.variant label None]]
| Rtag(label, attrs, false, [{ ptyp_desc = Ptyp_tuple typs }]) ->
#if OCAML_VERSION >= (4, 08, 0)
| Rtag(label, false, [{ ptyp_desc = Ptyp_tuple typs }]) ->
let label = label.txt in
let attrs = field.prf_attributes in
#endif
Exp.case [%pat? `List ((`String [%p pstr (attr_name label attrs)]) :: [%p
plist (List.mapi (fun i _ -> pvar (argn i)) typs)])]
(desu_fold ~path (fun x -> (Exp.variant label (Some (tuple x)))) typs)
| Rtag(label, attrs, false, [typ]) ->
#if OCAML_VERSION >= (4, 08, 0)
| Rtag(label, false, [typ]) ->
let label = label.txt in
let attrs = field.prf_attributes in
#endif
Exp.case [%pat? `List [`String [%p pstr (attr_name label attrs)]; x]]
[%expr [%e desu_expr_of_typ ~path typ] x >>= fun x ->
Result.Ok [%e Exp.variant label (Some [%expr x])]]
Expand All @@ -294,11 +274,7 @@ and desu_expr_of_only_typ ~path typ =
let toplevel_typ = typ in
inherits
|> List.map (fun field ->
#if OCAML_VERSION >= (4, 08, 0)
match field.prf_desc with
#else
match field with
#endif
| Rinherit typ -> typ
| _ -> assert false)
|> List.fold_left (fun expr typ -> [%expr
Expand Down Expand Up @@ -329,12 +305,14 @@ let wrap_runtime decls =

let ser_type_of_decl ~options ~path:_ type_decl =
ignore (parse_options options);
let loc = !Ast_helper.default_loc in
let typ = Ppx_deriving.core_type_of_type_decl type_decl in
let polymorphize = Ppx_deriving.poly_arrow_of_type_decl
(fun var -> [%type: [%t var] -> Yojson.Safe.t]) type_decl in
polymorphize [%type: [%t typ] -> Yojson.Safe.t]

let ser_str_of_record varname labels =
let loc = !Ast_helper.default_loc in
let fields =
labels |> List.mapi (fun _i { pld_name = { txt = name }; pld_type; pld_attributes } ->
let field = Exp.field (evar varname) (mknoloc (Lident name)) in
Expand Down Expand Up @@ -452,7 +430,7 @@ let ser_str_of_type ~options ~path ({ ptype_loc = loc } as type_decl) =
let var = pvar var_s in
([],
[Vb.mk
~attrs:[Attribute_expr(!Ast_helper.default_loc, "ocaml.warning", PStr [%str "-39"])]
~attrs:[warning_39 ()]
(Pat.constraint_ var poly_type)
(polymorphize [%expr ([%e wrap_runtime serializer])])],
[Str.value Nonrecursive [Vb.mk [%expr [%e pvar "_"]] [%expr [%e evar var_s]]] ]
Expand Down Expand Up @@ -499,7 +477,7 @@ let ser_str_of_type_ext ~options ~path:_ ({ ptyext_path = { loc }} as type_ext)
Ppx_deriving.mangle_lid
(`PrefixSuffix ("M", "to_yojson")) type_ext.ptyext_path.txt
in
String.concat "." (Longident.flatten mod_lid)
String.concat "." (Longident.flatten_exn mod_lid)
in
let polymorphize = Ppx_deriving.poly_fun_of_type_ext type_ext in
let serializer = polymorphize (wrap_runtime serializer) in
Expand All @@ -509,20 +487,25 @@ let ser_str_of_type_ext ~options ~path:_ ({ ptyext_path = { loc }} as type_ext)
let body = [%expr let fallback = [%e field] in [%e set_field]] in
[Str.value ?loc:None Nonrecursive [Vb.mk (Pat.construct (lid "()") None) body]]

let error_or typ = [%type: [%t typ] Ppx_deriving_yojson_runtime.error_or]
let error_or typ =
let loc = typ.ptyp_loc in
[%type: [%t typ] Ppx_deriving_yojson_runtime.error_or]

let desu_type_of_decl_poly ~options ~path:_ type_decl type_ =
ignore (parse_options options);
let loc = !Ast_helper.default_loc in
let polymorphize = Ppx_deriving.poly_arrow_of_type_decl
(fun var -> [%type: Yojson.Safe.t -> [%t error_or var]]) type_decl in
polymorphize type_

let desu_type_of_decl ~options ~path type_decl =
let typ = Ppx_deriving.core_type_of_type_decl type_decl in
let loc = typ.ptyp_loc in
desu_type_of_decl_poly ~options ~path type_decl [%type: Yojson.Safe.t -> [%t error_or typ]]


let desu_str_of_record ~is_strict ~error ~path wrap_record labels =
let loc = !Ast_helper.default_loc in
let top_error = error path in
let record =
List.fold_left
Expand Down Expand Up @@ -665,7 +648,7 @@ let desu_str_of_type ~options ~path ({ ptype_loc = loc } as type_decl) =
loop ((List.mapi (fun i _ -> argn i) ptype_params) @ ["x"])
in
([],
[Vb.mk ~attrs:[Attribute_expr(!Ast_helper.default_loc, "ocaml.warning", PStr [%str "-39"])]
[Vb.mk ~attrs:[warning_39 ()]
(Pat.constraint_ var poly_type)
(polymorphize [%expr ([%e wrap_runtime desurializer])]) ],
[Str.value Nonrecursive [Vb.mk [%expr [%e pvar "_"]] [%expr [%e evar var_s]]]]
Expand Down Expand Up @@ -711,7 +694,7 @@ let desu_str_of_type_ext ~options ~path ({ ptyext_path = { loc } } as type_ext)
Ppx_deriving.mangle_lid
(`PrefixSuffix ("M", "of_yojson")) type_ext.ptyext_path.txt
in
String.concat "." (Longident.flatten mod_lid)
String.concat "." (Longident.flatten_exn mod_lid)
in
let polymorphize = Ppx_deriving.poly_fun_of_type_ext type_ext in
let desurializer = wrap_runtime (polymorphize desurializer) in
Expand All @@ -735,6 +718,7 @@ let ser_sig_of_type ~options ~path type_decl =
(Ppx_deriving.fold_left_type_decl (fun acc name -> name :: acc) [] type_decl)
in
let typ = Ppx_deriving.core_type_of_type_decl type_decl in
let loc = typ.ptyp_loc in
let polymorphize_ser = Ppx_deriving.poly_arrow_of_type_decl
(fun var -> [%type: [%t var] -> Yojson.Safe.t]) type_decl
in
Expand Down Expand Up @@ -763,6 +747,7 @@ let desu_sig_of_type ~options ~path type_decl =
(desu_type_of_decl ~options ~path type_decl))
in
let typ = Ppx_deriving.core_type_of_type_decl type_decl in
let loc = typ.ptyp_loc in
let of_yojson_exn =
Sig.value (Val.mk (mknoloc (Ppx_deriving.mangle_type_decl (`Suffix "of_yojson_exn") type_decl))
(desu_type_of_decl_poly ~options ~path type_decl [%type: Yojson.Safe.t -> [%t typ]]))
Expand Down Expand Up @@ -806,6 +791,7 @@ let yojson_str_fields ~options ~path:_ type_decl =
| true, kind ->
match kind, type_decl.ptype_manifest with
| Ptype_record labels, _ ->
let loc = !Ast_helper.default_loc in
let fields =
labels |> List.map (fun { pld_name = { txt = name }; pld_attributes } ->
[%expr [%e str (attr_key name pld_attributes)]])
Expand All @@ -829,6 +815,7 @@ let yojson_sig_fields ~options ~path:_ type_decl =
| true, kind ->
match kind, type_decl.ptype_manifest with
| Ptype_record _, _ ->
let loc = !Ast_helper.default_loc in
[
Sig.module_ (Md.mk (mod_mknoloc (Ppx_deriving.mangle_type_decl (`Prefix "Yojson_meta") type_decl))
(Mty.signature [
Expand Down
7 changes: 1 addition & 6 deletions src_test/dune
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
(rule
(deps test_ppx_yojson.cppo.ml)
(targets test_ppx_yojson.ml)
(action (run %{bin:cppo} -V OCAML:%{ocaml_version} %{deps} -o %{targets})))

(executable
(name test_ppx_yojson)
(libraries oUnit result)
Expand All @@ -12,4 +7,4 @@
(alias
(name runtest)
(deps test_ppx_yojson.exe)
(action (run %{deps})))
(action (run %{deps})))
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,8 @@ type v = A | B of int | C of int * string
[@@deriving show, yojson]
type r = { x : int; y : string }
[@@deriving show, yojson]
#if OCAML_VERSION >= (4, 03, 0)
type rv = RA | RB of int | RC of int * string | RD of { z : string }
[@@deriving show, yojson]
#endif

let test_unit _ctxt =
assert_roundtrip pp_u u_to_yojson u_of_yojson
Expand Down Expand Up @@ -208,7 +206,6 @@ let test_rec _ctxt =
assert_roundtrip pp_r r_to_yojson r_of_yojson
{x=42; y="foo"} "{\"x\":42,\"y\":\"foo\"}"

#if OCAML_VERSION >= (4, 03, 0)
let test_recvar _ctxt =
assert_roundtrip pp_rv rv_to_yojson rv_of_yojson
RA "[\"RA\"]";
Expand All @@ -218,7 +215,6 @@ let test_recvar _ctxt =
(RC(42, "foo")) "[\"RC\", 42, \"foo\"]";
assert_roundtrip pp_rv rv_to_yojson rv_of_yojson
(RD{z="foo"}) "[\"RD\", {\"z\": \"foo\"}]"
#endif

type geo = {
lat : float [@key "Latitude"] ;
Expand Down Expand Up @@ -527,9 +523,7 @@ let suite = "Test ppx_yojson" >::: [
"test_pvar" >:: test_pvar;
"test_var" >:: test_var;
"test_rec" >:: test_rec;
#if OCAML_VERSION >= (4, 03, 0)
"test_recvar" >:: test_recvar;
#endif
"test_key" >:: test_key;
"test_id" >:: test_id;
"test_custvar" >:: test_custvar;
Expand Down