feature: pattern guards - #13
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
endIf 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
elseAnd, 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
elseAfterwards, 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.0has breaking changes in the Parsetree AST.