github.com/HertzDevil/pattern.cr
main / published Feb 27, 2021 / repository
Runtime structural pattern matching macros
pattern.cr
Structural pattern matching proof-of-concept library
Usage
Due to syntax restrictions, macro arguments must be valid Crystal expressions and cannot declare new variables, and features like splats in container literals are not supported (but see crystal-lang/crystal#3718). The following conventions are applied in this library:
exp ~>? patbecomesPattern.matches?(exp, pat). Flow typing will not work properly because the bound variables inpatmust be declared prior to this point; usePattern.try_match(exp, pat)in those cases instead.exp ~> patbecomesPattern.match!(exp, pat). Flow typing works here due to the exception thrown in the failing branch.*xin array patterns are replaced with__splat(x). The__splatdoesn't need to be an existing method.**xin hash patterns are replaced with__double_splat => x. Note thatNamedTuplepatterns cannot have a double splat in this way.
Refer to the same examples in SPEC.md to see how compiler
support for structural pattern matching would look like. See also the
known limitations.
# Before
ary = [1, 2, 3, 4, 5, 6, 7]
if ary.size >= 2
a, b = ary
rest = ary[2..]
else
raise "not enough elements"
end
# After
require "pattern"
a, b, rest = nil, nil, nil
Pattern.match! [1, 2, 3, 4, 5, 6, 7], [a, b, __splat(rest)]
# Before
if ary.size == 3
first, x, y = ary
end
if first.is_a?(Circle) && x.is_a?(Int32) && y.is_a?(Int32)
first.move_to(x, y)
end
# After
require "pattern"
first = nil
x = nil
y = nil
Pattern.try_match(ary, {first = Circle, x = Int32, y = Int32}) do
first.move_to(x, y)
end
# Before
anniversary = events.find do |ev|
ev.has_key?("start") && ev["start"].in?("2020-01-01".."2020-12-31") &&
ev.has_key?("id") && ev["id"] =~ /^ANNIV.*/
end["start"]
events.select! &.[]?("start").== anniversary
# After
require "pattern"
anniversary = nil
Pattern.match!(events, {__splat(_), {
"start" => anniversary = "2020-01-01".."2020-12-31",
"id" => /^ANNIV.*/,
}, __splat(_)})
events.select! do |ev|
Pattern.matches?(ev, {"start" => __pin(anniversary)})
end
Installation
-
Add the dependency to your
shard.yml:dependencies: pattern: github: HertzDevil/pattern.cr -
Run
shards install
Contributing
- Fork it (https://github.com/HertzDevil/pattern.cr/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
Contributors
- Quinton Miller - creator and maintainer