Restricting what a pattern parameter binds
A parameter of a function annotated with BRONTO_BEFORE() binds any expression
of the appropriate type. BRONTO_BINDS(predicate) written after the parameter
name narrows this, so that the parameter binds only expressions in the set
described by predicate. Today those sets are categories of literals.
struct RewriteLiteralArgument : bronto::rewrite_expr {
BRONTO_BEFORE()
void find(int n BRONTO_BINDS(bronto::literal)) { foo(n); }
BRONTO_AFTER()
void repl(int n) { rewritten(n); }
};This rule rewrites foo(1), but leaves foo(x) alone.
BRONTO_BINDS
Requirements
BRONTO_BINDS may only be applied to a parameter of a function annotated with
BRONTO_BEFORE(), and a parameter may carry at most one such annotation.
predicate must be an integral constant expression built from the bronto::
constants described below.
Semantics
BRONTO_BINDS filters what the parameter would otherwise bind. The parameter
still binds only expressions whose type it accepts. The annotation rejects any
expression whose category is absent from predicate. A pattern with such a
parameter simply does not match at sites where the expression falls outside the
set, and nothing is rewritten there.
Categories
Each constant names one category of expression.
| Constant | Matches | Does not match |
|---|---|---|
bronto::integer_literal | 42, 42ull, +42, -42, ~42 | x, 3.14, !42 |
bronto::floating_literal | 1.f, 1e2, 3.L, +2. | a double variable, !2. |
bronto::character_literal | 'a', '\0', L'β' | 0, +'a', ~'a' |
bronto::boolean_literal | true, false, !false | 0, -false, ~true |
bronto::string_literal | "hello", L"world" | NULL, a char const* variable |
bronto::pointer_literal | nullptr, NULL | 0, 0L, an int* variable |
bronto::user_defined_literal | 1.5_km, 42_bits, "name"_tag, +42_bits | 42, 1.5, "name", ~42_bits |
bronto::non_literal | anything in none of the *_literal categories | 7 |
bronto::literal names the union of every *_literal category except
bronto::non_literal, so it matches any literal expression.
NULL is a pointer literal wherever it expands to __null, which is how most
implementations spell it. A literal 0 is not a pointer literal, even in a
context where it is a null pointer constant.
Unary operators
An expression stays in its operand's category when a unary operator is meaningful for that category.
+and-preserve integer, floating-point, and user-defined literals.~preserves integer literals.!preserves boolean literals.
Every other combination is a bronto::non_literal. This is why !42 does not
bind a parameter restricted to integer literals, and +'a' does not bind one
restricted to character literals.
Composing predicates
Categories are bitmasks, so predicates compose with the bitwise operators. Write
| for the union of two categories, & for their intersection, and ~ for the
complement of a category.
struct LogEverythingElse : bronto::rewrite_expr {
template <typename T>
BRONTO_BEFORE()
void find(T x BRONTO_BINDS(~(bronto::integer_literal |
bronto::string_literal))) {
return sink(x);
}
template <typename T>
BRONTO_AFTER()
void replace(T x) {
return logged(x);
}
};This rule rewrites sink(3.14) and sink(v), but not sink(7) or
sink("hi"). Writing bronto::non_literal is equivalent to writing
~bronto::literal.
Restricting by value instead
If you want to restrict a match based on its constant-evaluated value, you can
use bronto::where, which is the value-level companion to
BRONTO_BINDS. A predicate on what a literal equals, such as being positive or
even, can use bronto::where. The two compose, so a parameter can carry a
BRONTO_BINDS annotation while the match pattern contains a bronto::where
clause.