Expression pattern matching

To find and replace an expression pattern, create a struct that inherits publicly from bronto::rewrite_expr (either directly or indirectly) and use the BRONTO_BEFORE() and BRONTO_AFTER() annotations to identify expressions and define their replacements.

Example
struct OperatorPlusToStrCat : bronto::rewrite_expr {
  BRONTO_BEFORE()
  auto operator_plus(std::string lhs, std::string rhs) {
    return lhs + rhs;
  }

  BRONTO_AFTER()
  auto use_strcat(std::string lhs, std::string rhs) {
    return absl::StrCat(lhs, rhs);
  }
};

BRONTO_BEFORE

Requirements

Each struct inheriting from bronto::rewrite_expr must have one member function or template member function marked BRONTO_BEFORE(). This function must have a single return statement (and no other statements).

Semantics

The returned expression from a function annotated with BRONTO_BEFORE() defines the pattern to be matched. The pattern is syntactic in the sense that any implicit casts/conversions are ignored. However there are some syntactic constructs that are also ignored. In particular, parentheses that do not change the meaning of an expression are ignored in the pattern. For example, a pattern such as (a + b) + c will match 1 + 2 + 3, even though it does not have parentheses.

Parameters

Each function parameter represents a "hole" that can bind to any expression of the appropriate type ignoring qualifiers. If the parameter's type is dependent on a template parameter, the parameter represents any expression whose type can be represented by appropriate choices for the template parameters. In other words,

template <typename T>
BRONTO_BEFORE()
auto f(T *ptr) {
  return ptr->something();
}

will match calls to something through operator-> on a pointer type but will not match calls to something on a std::unique_ptr. Even though operator-> is defined, std::unique_ptr<U> will not bind to a function template parameter T*.

For each matched pattern, the expressions and types that correspond to a parameter and template parameter "holes" are said to be captured (the same terminology as regular expression captures). These expressions and types can be used in the replacement pattern annotated with BRONTO_AFTER.

BRONTO_AFTER

Requirements

Each struct inheriting from bronto::rewrite_expr must have one member function or template member function marked BRONTO_AFTER(). The function must have a single return statement and must be able to accept any function parameters or template parameters from any function marked BRONTO_BEFORE() in the struct.

Semantics

The returned expression represents the pattern to replace things found by BRONTO_BEFORE with. Each captured expression or type is used in place of the parameter or template parameter of the same name.

struct StrCatPerformanceExample : bronto::rewrite_expr {
  BRONTO_BEFORE()
  auto find(auto x, auto y) {
    return absl::StrCat(x, std::to_string(y));
  }

  // NOTE: Even though the parameters are in reverse order (`y`
  // before `x`), they still bind to the variables of the same name
  // in `find`. Their positioning here is not relevant.
  BRONTO_BEFORE()
  auto replace(auto y, auto x) {
    return absl::StrCat(x, y);
  }
};