-
Notifications
You must be signed in to change notification settings - Fork 3
feature: pattern guards #13
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
base: master
Are you sure you want to change the base?
Conversation
30496fa
to
eca58e3
Compare
How does this work precisely ? In particular, how do you compute cases on which to "fall through" in case of guard failure ? |
This snippet is what controls the fall-through semantics: let rec mk_guard_chains = function
| [] -> [%expr None]
| (_, bs, rhs, guard) :: rest ->
let bs = List.rev bs in
begin
match guard with
| None -> [%expr Some [%e wrap_group_bindings ~captured_acc:[] ~loc rhs offG bs]]
| Some guard_expr ->
let guarded = [%expr if [%e guard_expr] then Some [%e rhs] else [%e mk_guard_chains rest]] in
wrap_group_bindings ~captured_acc:[] ~loc guarded offG bs
end If there is no guard, then simply return the rhs with the bindings Effectively, if one writes: match str with
| {|regex1|} when x > 0 -> rhs1
| {|regex1|} when x < 0 -> rhs2
| {|regex1|} -> rhs3
| _ -> ... It will generate: if Re.Mark.test _g regex1_mark then
let x = Re.Group.get _g 1 in
if x > 0 then Some rhs1
else if x < 0 then Some rhs2
else Some rhs3
else And, if one writes a similar match pattern but where there is no case without a guard match str with
| {|regex1|} when x > 0 -> rhs1
| {|regex1|} when x < 0 -> rhs2
| _ -> ... Then it generates: if Re.Mark.test _g regex1_mark then
let x = Re.Group.get _g 1 in
if x > 0 then Some rhs1
else if x < 0 then Some rhs2
else None
else Afterwards, the last |
What if you write
Such that |
Before the last match, there is a sequence of |
The question is : what if Additionally, I implemented this kind of dispatch for I would definitely want some additional test cases here. (Some benchmarks also wouldn't hurt, but that's for the whole library, really ...) |
You're totally right! Big oversight on my part. open struct
...
let rec __ppx_regexp_dispatch marks handlers _g i =
if i >= Array.length marks then None
else if Re.Mark.test _g marks.(i) then
match handlers.(i) _g with
| Some result -> Some result
| None -> __ppx_regexp_dispatch (i + 1)
else __ppx_regexp_dispatch (i + 1)
...
end
...
let handlers = [| _case_0; _case_1; ... |] in
match __ppx_regexp_dispatch marks handlers _g 0 with
| Some result -> result
| None -> default_rhs (basically a But there's still the problem of the Marks, it refuses to mark until the end, only until it finds the first match... And I'm guessing it isn't a trivial feature to add to the Marking machinery of |
I was able to get around this, unfortunately resorting to not compiling to a single group. If the regexes in the current group:
Unfortunately the cases where a new group is started for guarded cases is the most common, but for this not to be the case we would need to have a way to understand if the RE's are intersecting or not, which isn't easy with I will update this PR to use this system as well, unless of course there's better ideas. |
It seems reasonable to split up into multiple groups to handle guards. The only alternatives I can think of have other inefficiencies. I think the grouping can be slightly simplified/optimised by allowing a pattern with a guard to be the final case of each group, i.e.:
|
Related to #12
This PR adds support for pattern guards. The scope for the guards contains the variables bound by the regexes.
Details
This feature changes the generated code the most.
Before: Each pattern case generated inline matching code:
After: Pattern cases are compiled into handler functions that return option values:
Cases with the same pattern but different guards are grouped together.
Note
This updates the opam package to use
ppxlib <= "0.35.0"
only, asppxlib.0.36.0
has breaking changes in the Parsetree AST.