-
Notifications
You must be signed in to change notification settings - Fork 99
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
Add json mapper for pp_ast #526
base: main
Are you sure you want to change the base?
Conversation
b73cfdd
to
a60d33c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a quick comment from me, I don't think we would want to add yojson as a dependency of ppxlib for this feature (I might be mistaken).
The implementation uses the lifter to lift values to JSON values (not actually print them, like pp_simple_val
does). So I wonder if this couldn't be its own little tool that depends on ppxlib?
One workaround if this were to be merged here would be to add yojson as a dependency for ppxlib-pp-ast and then ppxlib could just return the polymorphic variants that happen to correspond to the Yojson.Safe.t
variants?
Yes, as Patrick said, ppxlib should not depend on external libraries as it otherwise prevents them from being able to depend on ppxlib. The We could add a json lifter to ppxlib but given it's fairly easy to write one this could be done directly in Did you try to load the resulting JSON into AST explorer? It would be nice to have a working usecase for this before we merge this feature! |
@NathanReb Hey ya!
I've just pushed a PR on astexplorer refmt to add the ppxlib pp_ast working with this json feature: jchavarri/astexplorer-refmt#1 About the yojson, I'm working on removing it |
That's awesome! Let me know when this is ready for review! |
03a46a1
to
01ffa5b
Compare
src/pp_ast.ml
Outdated
let pp_simple_val_to_json fmt simple_val = | ||
let rec aux indent fmt simple_val = | ||
match simple_val with | ||
| Unit -> Format.fprintf fmt {|"null"|} | ||
| Int i -> Format.fprintf fmt "%d" i | ||
| String s -> Format.fprintf fmt {|"%s"|} s | ||
| Special s -> Format.fprintf fmt {|"%s"|} s | ||
| Bool b -> Format.fprintf fmt "%b" b | ||
| Char c -> Format.fprintf fmt {|"%c"|} c | ||
| Float f -> Format.fprintf fmt "%f" f | ||
| Int32 i32 -> Format.fprintf fmt "%ld" i32 | ||
| Int64 i64 -> Format.fprintf fmt "%Ld" i64 | ||
| Nativeint ni -> Format.fprintf fmt "%nd" ni | ||
| Array l | Tuple l | List l -> | ||
Format.fprintf fmt "[\n"; | ||
List.iteri | ||
~f:(fun i sv -> | ||
if i > 0 then Format.fprintf fmt ",\n"; | ||
Format.fprintf fmt "%s" (String.make (indent + 2) ' '); | ||
aux (indent + 2) fmt sv) | ||
l; | ||
Format.fprintf fmt "\n%s]" (String.make indent ' ') | ||
| Record fields -> | ||
Format.fprintf fmt "{\n"; | ||
List.iteri | ||
~f:(fun i (k, v) -> | ||
if i > 0 then Format.fprintf fmt ",\n"; | ||
Format.fprintf fmt "%s\"%s\": " (String.make (indent + 2) ' ') k; | ||
aux (indent + 2) fmt v) | ||
fields; | ||
Format.fprintf fmt "\n%s}" (String.make indent ' ') | ||
| Constr (cname, []) -> Format.fprintf fmt {|"%s"|} cname | ||
| Constr (cname, [ (Constr (_, _ :: _) as x) ]) -> | ||
Format.fprintf fmt "{\n%s\"%s\": " (String.make (indent + 2) ' ') cname; | ||
aux (indent + 2) fmt x; | ||
Format.fprintf fmt "\n%s}" (String.make indent ' ') | ||
| Constr (cname, [ x ]) -> | ||
Format.fprintf fmt "{\n%s\"%s\": " (String.make (indent + 2) ' ') cname; | ||
aux (indent + 2) fmt x; | ||
Format.fprintf fmt "\n%s}" (String.make indent ' ') | ||
| Constr (cname, l) -> | ||
Format.fprintf fmt "{\n%s\"%s\": [\n" (String.make (indent + 2) ' ') cname; | ||
List.iteri | ||
~f:(fun i sv -> | ||
if i > 0 then Format.fprintf fmt ",\n"; | ||
Format.fprintf fmt "%s" (String.make (indent + 4) ' '); | ||
aux (indent + 4) fmt sv) | ||
l; | ||
Format.fprintf fmt "\n%s]\n%s}" (String.make (indent + 2) ' ') (String.make indent ' ') | ||
in | ||
aux 0 fmt simple_val |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@patricoferris @NathanReb I've just pushed this simple pretty-printing json function to remove the yojson.
One workaround if this were to be merged here would be to add yojson as a dependency for ppxlib-pp-ast and then ppxlib could return the polymorphic variants that happen to correspond to the Yojson.Safe.t variants?
I could let yojson as a ppxlib_pp_ast
dep and return the poly-vars to print jsons only at ppxlib_pp_ast
or on the usage of Ppxlib.pp_ast
, but it looked hacky as we would have the "pretty-printing" not printing the json but return those poly vars.
Another alternative would be ppxlib-tools
to have this dependency and pp_ast
to use it.
But as it was not hard to create a simple pp for json, I created it. It isn't as pretty as yojson cause we break every array/list/tuple/record line, but it does not feel like a problem right now.
BTW, I tried to follow pp_simple_val
way to print, but it had a strange indent structure, and I created this way. If any issue, just let me now.
@NathanReb BTW, I think now it's ready for review |
My impression is that we could simplify this quite a bit by having a lifter exposed that returns a Yojson compatible value. It could be built on top of the That way, we can have How does that sound? |
Sorry for the delay by the way, the end of year holidays + the OCaml 5.03 release took quite a lot of my time! |
4889a55
to
a3210f5
Compare
I tried to keep any json syntax away from pp_ast. What I didI exposed the Lines 368 to 370 in 4889a55
For example, on Lines 21 to 43 in a3210f5
Lines 179 to 182 in a3210f5
So, anyone wanting to print ASTs can choose to map simple_val and print it. WDYT? That way, pp_ast can be more opened without any json type contract dependency. |
5dd552d
to
b12844d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good! I think you're right, that's the simplest and most flexible solution here.
Could you add a couple tests in test/ppxlib-pp-ast/
, a changelog entry and update ppxlib-tools deps to reflect the changes?
src/pp_ast.mli
Outdated
@@ -60,28 +60,46 @@ module Config : sig | |||
be. *) | |||
end | |||
|
|||
type simple_val = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're not responsible for this as I'm the one who came up with that name originally but since we're going to expose it, I think we should come up with something better.
I think some commonly used name for this kind of type is repr
as in representation. If you have a suggestion I'd gladly hear it as I'm not super inspired here as the original name suggests 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
repr
looks like a good name, though.
src/pp_ast.mli
Outdated
| List of simple_val list | ||
| Special of string | ||
|
||
type printer = Format.formatter -> simple_val -> unit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we could remove this alias in favor of a simpler repr pp
don't you think?
src/pp_ast.mli
Outdated
type 'a pp = Format.formatter -> 'a -> unit | ||
type 'a configurable = ?config:Config.t -> 'a pp | ||
type 'a configurable = ?config:Config.t -> ?printer:printer -> 'a pp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For future reference: this could be considered a breaking change here.
Do you think it would make sense to make the ?printer
a part of the config directly? That way this functions API does not change and we just add a new optional argument to Config.make
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could eventually split the full config into a lifter config and a printing config.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Config.make
will pretty much always be applied fully as opposed to the printer which could be partially applied with the ?config
argument so it can then be used as a regular 'a pp
.
You are right that strictly speaking it is the same kind of change but given the nature of the two functions, I think it's far less likely to break user code that way.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense to me
Ops, I rebased it and forgot to add back the tests. Sorry |
b12844d
to
2e69e20
Compare
c721653
to
434b564
Compare
When you said, "update ppxlib-tools deps to reflect the changes?", what did you mean by that? I didn't get it. Besides that, I think I updated the remaining content. LMK if anything is missing |
434b564
to
0342632
Compare
Now that ppxlib-tools depends on The CI's green because we happen to also depend on it via the benchmark package but installing ppxlib-tools via opam would fail. |
0342632
to
f4180c2
Compare
Signed-off-by: Pedro B S Lisboa <[email protected]>
f4180c2
to
11c4ba2
Compare
I'm trying to wrap up the 5.3 compat thing and I'll review and merge right after, sorry for the delay! |
Description
Add
json
mapper onpp_ast
to improve usage in tools such as AST Explorer.The idea is to increment the @NathanReb PR #517
@NathanReb, your opinion on how to structure it better is very welcome.
@jchavarri @davesnx thank you for the help.
How
To use it, we just need to pass
--json
toppxlib-pp-ast.
It works with all other flags, such as--show-attrs
and--show-loc
.How to test
ppxlib-pp-ast test.ml
and check the resultppxlib-pp-ast --json test.ml
and check the result--json