Skip to content

Commit

Permalink
Add support for ML-style guards (#110)
Browse files Browse the repository at this point in the history
which are patterns of the form `if e end`
  • Loading branch information
gafter authored Nov 11, 2024
1 parent 1785df4 commit 8bcb71f
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "Match"
uuid = "7eb4fadd-790c-5f42-8a69-bfa0b872bfbf"
version = "2.1.2"
version = "2.2.0"
authors = ["Neal Gafter <[email protected]>", "Kevin Squire <[email protected]>"]

[deps]
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ for examples of this and other features.
* `x && y` matches values which match both patterns `x` and `y`
* `x, if condition end` matches only if `condition` is true (`condition` may use any variables that occur earlier in the pattern eg `(x, y, z where x + y > z)`)
* `x where condition` An alternative form for `x, if condition end`
* `if condition end` A boolean computed pattern. `x && if condition end` is another way of writing `x where condition`.
* Anything else is treated as a constant and tested for equality
* Expressions can be interpolated in as constants via standard interpolation syntax `\$(x)`. Interpolations may use previously bound variables.

Expand Down
8 changes: 8 additions & 0 deletions src/binding.jl
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,14 @@ function bind_pattern!(
pattern0, assigned = bind_pattern!(location, subpattern, input, binder, assigned)
pattern1 = shred_where_clause(guard, false, location, binder, assigned)
pattern = BoundAndPattern(location, source, BoundPattern[pattern0, pattern1])

elseif is_expr(source, :if, 2)
# if expr end
if !is_empty_block(source.args[2])
error("$(location.file):$(location.line): Unrecognized @match guard syntax: `$source`.")
end
guard = source.args[1]
pattern = shred_where_clause(guard, false, location, binder, assigned)

elseif is_expr(source, :call) && source.args[1] == :(:) && length(source.args) in 3:4
# A range pattern. We depend on the Range API to make sense of it.
Expand Down
37 changes: 36 additions & 1 deletion test/matchtests.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@testset "tests from Match.jl" begin
@testset "tests for Match.jl" begin

@testset "Type matching" begin
# Type matching
Expand Down Expand Up @@ -410,4 +410,39 @@ end

end

@testset "Test support for ML-style guards, which are patterns of the form `if expr end`" begin
t = true
f = false
@test @match 1 begin
if f end => false
if t end => true
end
# The original feature request was for ML-style guards of the form
# pattern && if condition end => result
# as an alternative to guards of the forms
# pattern, if condition end => result
# pattern where condition => result
@test @match 1 begin
1 && if !t end => false
1 && if !f end => true
end
end

@testset "Test an error when ML-style guards are not used correctly" begin
let line = 0, file = Symbol(@__FILE__)
try
line = (@__LINE__) + 2
@eval @match Foo(1, 2) begin
if t; nothing; end => true
end
@test false
catch ex
@test ex isa LoadError
e = ex.error
@test e isa ErrorException
@test startswith(e.msg, "$file:$line: Unrecognized @match guard syntax:")
end
end
end

end

0 comments on commit 8bcb71f

Please sign in to comment.