-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add characterization tests for context-free rule application order
Signed-off-by: Nathan Rebours <nathan.p.rebours@gmail.com>
- Loading branch information
Showing
3 changed files
with
181 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
(rule | ||
(alias runtest) | ||
(enabled_if | ||
(>= %{ocaml_version} "4.10.0")) | ||
(deps | ||
(:test test.ml) | ||
(package ppxlib)) | ||
(action | ||
(chdir | ||
%{project_root} | ||
(progn | ||
(run expect-test %{test}) | ||
(diff? %{test} %{test}.corrected))))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
open Ppxlib | ||
|
||
(* Generates a [let derived_<type_name> = "ok"] or a | ||
[let derived_<type_name> = "uninterpreted extension in input"] if | ||
the type manifest is an uninterpreted extension. *) | ||
let deriver = | ||
let binding ~loc type_name expr = | ||
let var_name = "derived_" ^ type_name in | ||
let pat = Ast_builder.Default.ppat_var ~loc {txt = var_name; loc} in | ||
let vb = Ast_builder.Default.value_binding ~loc ~pat ~expr in | ||
[Ast_builder.Default.pstr_value ~loc Nonrecursive [vb]] | ||
in | ||
let str_type_decl = | ||
Deriving.Generator.V2.make_noarg | ||
(fun ~ctxt (_rec_flag, type_decls) -> | ||
let loc = Expansion_context.Deriver.derived_item_loc ctxt in | ||
match type_decls with | ||
| { ptype_manifest = Some {ptyp_desc = Ptyp_extension _; _} | ||
; ptype_name = {txt; _}; _}::_ -> | ||
binding ~loc txt [%expr "uninterpreted extension in input"] | ||
| {ptype_name = {txt; _}; _}::_ -> | ||
binding ~loc txt [%expr "ok"] | ||
| [] -> assert false) | ||
in | ||
Deriving.add ~str_type_decl "derived" | ||
|
||
[%%expect{| | ||
val deriver : Deriving.t = <abstr> | ||
|}] | ||
|
||
(* Generates a [type t = int] *) | ||
let gen_type_decl = | ||
Extension.V3.declare | ||
"gen_type_decl" | ||
Extension.Context.structure_item | ||
Ast_pattern.(pstr nil) | ||
(fun ~ctxt -> | ||
let loc = Expansion_context.Extension.extension_point_loc ctxt in | ||
[%stri type t = int]) | ||
|> Context_free.Rule.extension | ||
|
||
let () = Driver.register_transformation ~rules:[gen_type_decl] "gen_type_decl" | ||
|
||
[%%expect{| | ||
val gen_type_decl : Context_free.Rule.t = <abstr> | ||
|}] | ||
|
||
(* You cannot attach attributes to structure item extension points *) | ||
[%%gen_type_decl] | ||
[@@deriving derived] | ||
|
||
[%%expect{| | ||
Line _, characters 3-19: | ||
Error: Attributes not allowed here | ||
|}] | ||
|
||
(* Generates a [type t = int[@@deriving derived]] *) | ||
let gen_type_decl_with_derived = | ||
Extension.V3.declare | ||
"gen_type_decl_with_derived" | ||
Extension.Context.structure_item | ||
Ast_pattern.(pstr nil) | ||
(fun ~ctxt -> | ||
let loc = Expansion_context.Extension.extension_point_loc ctxt in | ||
[%stri type t = int[@@deriving derived]]) | ||
|> Context_free.Rule.extension | ||
|
||
let () = | ||
Driver.register_transformation | ||
~rules:[gen_type_decl_with_derived] | ||
"gen_type_decl_with_derived" | ||
|
||
[%%expect{| | ||
val gen_type_decl_with_derived : Context_free.Rule.t = <abstr> | ||
|}] | ||
|
||
(* Attributes rule must be applied in code generated by a structure item | ||
extension *) | ||
[%%gen_type_decl_with_derived] | ||
|
||
[%%expect{| | ||
type t = int | ||
val derived_t : string = "ok" | ||
|}] | ||
|
||
let gen_inline_type_decls_with_derived = | ||
Extension.V3.declare_inline | ||
"gen_inline_type_decls_with_derived" | ||
Extension.Context.structure_item | ||
Ast_pattern.(pstr nil) | ||
(fun ~ctxt -> | ||
let loc = Expansion_context.Extension.extension_point_loc ctxt in | ||
[%str | ||
type t = int[@@deriving derived] | ||
type u = float[@@deriving derived]]) | ||
|> Context_free.Rule.extension | ||
|
||
let () = | ||
Driver.register_transformation | ||
~rules:[gen_inline_type_decls_with_derived] | ||
"gen_inline_type_decls_with_derived" | ||
|
||
[%%expect{| | ||
val gen_inline_type_decls_with_derived : Context_free.Rule.t = <abstr> | ||
|}] | ||
|
||
(* That also stands for inline extension rules *) | ||
[%%gen_inline_type_decls_with_derived] | ||
|
||
[%%expect{| | ||
type t = int | ||
val derived_t : string = "ok" | ||
type u = float | ||
val derived_u : string = "ok" | ||
|}] | ||
|
||
let id = | ||
Extension.V3.declare | ||
"id" | ||
Extension.Context.core_type | ||
Ast_pattern.(ptyp __) | ||
(fun ~ctxt:_ core_type -> core_type) | ||
|> Context_free.Rule.extension | ||
|
||
let () = Driver.register_transformation ~rules:[id] "id" | ||
|
||
[%%expect{| | ||
val id : Context_free.Rule.t = <abstr> | ||
|}] | ||
|
||
(* At the time of writing this test, attribute rules are applied before | ||
expanding the node they're attached to. That seems slightly wrong as | ||
it's likely that expander function doesn't expect to find uninterpreted | ||
extensions in its input. *) | ||
type t = [%id: int] | ||
[@@deriving derived] | ||
|
||
[%%expect{| | ||
type t = int | ||
val derived_t : string = "uninterpreted extension in input" | ||
|}] |