Skip to content

Add a ppx#4

Closed
Drup wants to merge 14 commits intoocaml:masterfrom
Drup:ppx
Closed

Add a ppx#4
Drup wants to merge 14 commits intoocaml:masterfrom
Drup:ppx

Conversation

@Drup
Copy link

@Drup Drup commented Feb 15, 2017

I added a little ppx that recognizes two new suffix for integers and floating pointer numbers: 'z' and 'q' for the Z and Q modules respectively.

Here is a bunch of things you can do with it:

# 1z ;;
- : Z.t = 1
# 12345z ;;
- : Z.t = 12345
# -0x12a2fq ;;
- : Q.t = -76335
# 1.2q ;;
- : Q.t = 6/5
# 2.45e10q ;;
- : Q.t = 24500000000
# 2.45e-5q ;;
- : Q.t = 49/2000000
# 1.34e3 ;;
- : float = 1340.
# 1.34e3z ;;
- : Z.t = 1340
# 1.34e50z ;;
- : Z.t = 134000000000000000000000000000000000000000000000000
# -84.3654q ;;
- : Q.t = -421827/5000
# 1.2z ;;
This literal does not fit in an integer.

The ppx does not have any dependency and should be cross-compilation proof (the ppx does not link with zarith itself and do not make assumption about the wordsize of the target). At the moment, it does not handle the hexa notation of floats, although that could be added.

The ppx is only compiled when ocaml >= 4.03. I tried to make it all fit in the configure/makefile stuff. I hope it's correct, my makefile-fu is not extremly strong. I also added some tests.

The code is not particularly elegant, especially the float part, in particular because we do not use zarith itself.
The transformation is strictly local. We could lift the constant definition to be globally bound, but I feel it's better to let the compiler do it's job.

Merlin works perfectly well with it.

@Drup
Copy link
Author

Drup commented Feb 15, 2017

Note that there is the question of making this resilient to changes in OCaml itself. using either ppx_tools or ocaml-migrate-parsetree could eliminate the issue.

ppx.ml Outdated
(List.map (fun e -> Asttypes.Nolabel, e) l))

let try_int s =
try `Int32 (Int32.of_string s)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is it on purpose that there is no attempt to fit the integer in a native (I mean 31- or 63-bit) int here? On 32-bit platforms, it makes a difference because int values are unboxed whereas Int32.t values are allocated.

Copy link
Author

Choose a reason for hiding this comment

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

Yes, I though about that. We could definitely check if they are small enough to fit. I don't want to emit integers literals that are bigger than 31 bits though, as it's going to annoy people that do cross-compilation greatly.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah yes, good point.

Copy link
Author

@Drup Drup Feb 18, 2017

Choose a reason for hiding this comment

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

While implementing this, I realized there is a bug in the implementation:

# 0x8fffffffl ;;
- : int32 = -1879048193l
# 0x8fffffffz ;;
- : Z.t = -1879048193

Which is a bit unfortunate. I'm not sure yet what's the best way to solve it. Until now, I tried to avoid using zarith inside the ppx itself but maybe It would be best, to avoid this kind of issues ...

ppx.ml Outdated
let integer_z = integer "Z"
let integer_q = integer "Q"

(* float_of_string doesn't error on floats that are not representable so
Copy link
Collaborator

@pascal-cuoq pascal-cuoq Feb 16, 2017

Choose a reason for hiding this comment

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

Sorry to comment on a comment, but as phrased this confused me more than it helped me.

How about replacing the sentence “float_of_string doesn't error on floats …” by:

When the programmer writes 9007199254740993.0q, they want the rational for 9007199254740993, not for the closest double-precision number to that 9007199254740992.
Similarly, when the programmer writes 0.1q, they want the rational for 1/10, not for 1000000000000000055511151231257827021181583404541015625/10000000000000000000000000000000000000000000000000000000.
For all these reasons float_of_string must not be used here.

Copy link
Author

@Drup Drup Feb 18, 2017

Choose a reason for hiding this comment

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

I changed the comment using your version, thanks!

@xavierleroy
Copy link
Contributor

Concerning your surprises with Int32.of_string et al: It is intentional that hexadecimal or octal strings in the [2^31, 2^32[ range are converted to negative integers. But that's definitely not what you want here.

One possible solution is to compare the sign of the string (does the string start with a minus sign?) with that of the integer returned by the "of_string" function. If they differ, you are in the 2^31 to 2^32 range and an overflow occurred.

Note that Int32.of_string raises an exception if the result is < -2^31 or >= 2^32. So, by combining the exception with the explicit sign check, you're safe.

@Drup
Copy link
Author

Drup commented Feb 18, 2017

@xavierleroy The behavior absolutely makes sense in the context of integers with a limited size. I'm just not used to writing large constants in hexa. :)
The question is more largely: Do we keep using this kind of checks or should we just use zarith itself in the ppx ?

@xavierleroy
Copy link
Contributor

It appears that the ppx preprocessor is a standalone executable. In which case, I see no reason not to use ZArith inside the ppx.

@xavierleroy
Copy link
Contributor

One year later, where are we with this pull request?

@Drup
Copy link
Author

Drup commented Feb 10, 2018

Oh, I completely forgot about that one.
@xavierleroy The only reason I avoided using zarith inside the ppx was because I assume it would cause issues for cross compilation (which is often annoying for ppxs). If you feel like this is not a problem, then I will change it. Not sure when I'll have time yet though.

@Drup
Copy link
Author

Drup commented Feb 18, 2018

Ok, I now use Zarith, exports to integer when it fits, and added a test for the bug found in the discussion above.

The only remaining annoying point is the portability across OCaml versions. The best way to solve it is clearly to use ocaml-migrate-parsetree, but I'm not sure how you feel about adding an optional dependencies to Zarith ...

@gasche
Copy link
Member

gasche commented Feb 22, 2018

Wouldn't it make sense to distribute a separate ppx_zarith package that depends on ocaml-migrate-parsetree (and Zarith), instead of using optional dependencies? The two packages can be maintained in the same upstream repository.

@xavierleroy
Copy link
Contributor

I like the idea of having two distinct OPAM packages so that the PPX package can depend on ocaml-migrate-parsetree.

@Drup
Copy link
Author

Drup commented Feb 28, 2018

Right, I also agree, I just didn't have the courage to wrestle with the build system... Let me see what I can do after the ICFP deadline. :)

@Drup
Copy link
Author

Drup commented Apr 7, 2018

I tried to make the makefile build different packages. I really have no idea how to do it, so instead I wrote a completely independent package in a subdirectory. Since we're building a ppx, it uses jbuilder, as that makes building driverified ppxs much easier.

If/When someone jbuilderify the rest of the library, it will be possible to build zarith and the ppx either separately or together easily. For now, it can only be done separately.

It's now using omp and is compatible with ppx drivers. I also added a zarith.opam file on the way.

@Drup Drup force-pushed the ppx branch 3 times, most recently from 562c2e6 to 72365b5 Compare April 7, 2018 16:17
@Drup
Copy link
Author

Drup commented Jun 28, 2018

So, feedback on the last version of the patch? :)

@Drup
Copy link
Author

Drup commented Mar 17, 2019

This is now hosted in https://github.com/Drup/zarith-ppx and released.

@Drup Drup closed this Mar 17, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants