matcher
A simple expression matcher and evaluator for Erlang.
Evaluate tuple-based expressions for comparisons, boolean logic, and substring matching — with optional case-insensitive variants and pluggable value providers.
Installation
Add matcher_erl to your rebar.config dependencies:
{deps, [
{matcher_erl, "~> 1.0"}
]}.Or directly from GitHub:
{deps, [
{matcher, {git, "https://github.com/ratopi/matcher.git", {tag, "1.1.0"}}}
]}.Then run:
rebar3 compileQuick Start
%% Simple equality
true = matcher:eval({'=', <<"Albert">>, <<"Albert">>}).
%% Case-insensitive equality
true = matcher:eval({'=~', <<"Albert">>, <<"albert">>}).
%% Boolean logic
true = matcher:eval({'|', [
{'=', <<"A">>, <<"B">>},
{'=', <<"A">>, <<"A">>}
]}).
%% Substring matching
true = matcher:eval({'?', <<"bert">>, <<"Albert">>}).Expressions
Expressions are tuples in one of the following forms:
{Op, Arg} %% Unary (e.g. not)
{Op, Arg1, Arg2} %% Binary (e.g. eq, lt, part_of)
{Op, [Arg, ...]} %% N-ary (e.g. and, or)Expressions can be nested — any argument can be an expression again:
{'&', [{'=', <<"Albert">>, <<"Albert">>}, {'!', {'=', <<"A">>, <<"B">>}}]}Providers
eval/2 and match/2 accept a Provider as the first argument. A provider is a fun/1 that resolves values that cannot be evaluated further.
Map as Provider
The most common use case — look up atom keys in a map:
Map = #{name => <<"Albert">>, age => 42}.
true = matcher:eval(Map, {'=', name, <<"Albert">>}).
true = matcher:eval(Map, {'>', age, 18}).
false = matcher:eval(Map, {'=', name, <<"Bob">>}).matcher:eval(Map, Expr) is shorthand for matcher:eval(map_provider(Map), Expr).
Custom Provider
You can use any fun/1:
Provider = fun
(temperature) -> 23.5;
(unit) -> <<"°C">>;
(X) -> X
end.
true = matcher:eval(Provider, {'>', temperature, 20}).
You can even implement custom operators in your provider by handling tuples like {my_op, A, B}.
eval vs match
| Function | Returns | On unknown operator |
|---|---|---|
eval/1, eval/2 | Any value | Returns unevaluated expression as-is |
match/1, match/2 | true, false, or {error, ...} | {error, {unevaluated_expression, Expr}} |
Operators
Unary (one operand)
| Short | Long | Description |
|---|---|---|
! | not | Logical negation |
N-ary (list of operands)
| Short | Long | Description |
|---|---|---|
& | and | Logical AND (short-circuit) |
| | or | Logical OR (short-circuit) |
Binary (two operands)
| Short | Long | Case-insensitive | Description |
|---|---|---|---|
< | lt | <~ | Less than |
> | gt | >~ | Greater than |
>=, => | ge | >=~, =>~ | Greater than or equal |
<=, =< | le | <=~, =<~ | Less than or equal |
=, == | eq | =~ | Equal |
? | part_of | ?~ | First string is part of second |
Case-insensitive operators can also be written in long form, e.g. {eq, case_insensitive}.
Types
The library exports the following types:
-type provider() :: fun((term()) -> term()).
-type expression() :: {unary_op(), expression()}
| {binary_op(), term(), term()}
| {nary_op(), [expression()]}
| term().See the module documentation for full type definitions.
Testing
rebar3 ctLicense
MIT — see LICENSE for details.
I'd love to hear from you if you find this library useful. :-)