case UNLANG_TYPE_FOREACH:
case UNLANG_TYPE_ELSE:
case UNLANG_TYPE_ELSIF:
- case UNLANG_TYPE_FILTER:
case UNLANG_TYPE_GROUP:
case UNLANG_TYPE_IF:
case UNLANG_TYPE_LOAD_BALANCE:
}
-/** Validate and fixup a map that's part of a filter section.
- *
- * @param map to validate.
- * @param ctx data to pass to fixup function (currently unused).
- * @return
- * - 0 if valid.
- * - -1 not valid.
- */
-static int unlang_fixup_filter(map_t *map, UNUSED void *ctx)
-{
- CONF_PAIR *cp = cf_item_to_pair(map->ci);
-
- /*
- * Anal-retentive checks.
- */
- if (DEBUG_ENABLED3) {
- if (tmpl_is_attr(map->lhs) && (map->lhs->name[0] != '&')) {
- cf_log_warn(cp, "Please change attribute reference to '&%s %s ...'",
- map->lhs->name, fr_table_str_by_value(fr_tokens_table, map->op, "<INVALID>"));
- }
-
- if (tmpl_is_attr(map->rhs) && (map->rhs->name[0] != '&')) {
- cf_log_warn(cp, "Please change attribute reference to '... %s &%s'",
- fr_table_str_by_value(fr_tokens_table, map->op, "<INVALID>"), map->rhs->name);
- }
- }
-
- /*
- * We only allow attributes on the LHS.
- */
- if (map->lhs->type != TMPL_TYPE_ATTR) {
- cf_log_err(cp, "Filter sections can only operate on attributes");
- return -1;
- }
-
- if (map->rhs->type == TMPL_TYPE_LIST) {
- cf_log_err(map->ci, "Cannot filter an attribute using a list.");
- return -1;
- }
-
- /*
- * Fixup LHS attribute references to change NUM_UNSPEC to NUM_ALL.
- */
- if (tmpl_is_attr(map->lhs)) tmpl_attr_rewrite_leaf_num(map->lhs, NUM_UNSPEC, NUM_ALL);
-
- /*
- * Fixup RHS attribute references to change NUM_UNSPEC to NUM_ALL.
- */
- if (tmpl_is_attr(map->rhs)) tmpl_attr_rewrite_leaf_num(map->rhs, NUM_UNSPEC, NUM_ALL);
-
- /*
- * Values used by unary operators should be literal ANY
- *
- * We then free the template and alloc a NULL one instead.
- */
- if (map->op == T_OP_CMP_FALSE) {
- if (!tmpl_is_unresolved(map->rhs) || (strcmp(map->rhs->name, "ANY") != 0)) {
- WARN("%s[%d] Wildcard deletion MUST use '!* ANY'",
- cf_filename(cp), cf_lineno(cp));
- }
-
- TALLOC_FREE(map->rhs);
-
- map->rhs = tmpl_alloc(map, TMPL_TYPE_NULL, T_INVALID, NULL, 0);
- }
-
- /*
- * Lots of sanity checks for insane people...
- */
-
- /*
- * Filtering only allows for filtering operators.
- */
- if (tmpl_is_attr(map->lhs) && !fr_equality_op[map->op]) {
- cf_log_err(map->ci, "Invalid operator \"%s\" in update section. "
- "Only assignment or filter operators are allowed",
- fr_table_str_by_value(fr_tokens_table, map->op, "<INVALID>"));
- return -1;
- }
-
- /*
- * If the map has a unary operator there's no further
- * processing we need to, as RHS is unused.
- */
- if (map->op == T_OP_CMP_FALSE) return 0;
-
- /*
- * If LHS is an attribute, and RHS is a literal, we can
- * preparse the information into a TMPL_TYPE_DATA.
- *
- * Unless it's a unary operator in which case we
- * ignore map->rhs.
- */
- if (tmpl_is_attr(map->lhs) && tmpl_is_unresolved(map->rhs)) {
- fr_type_t type = tmpl_da(map->lhs)->type;
-
- /*
- * @todo - allow passing octets to
- * FR_TYPE_STRUCT, which can then decode them as
- * data? That would be rather powerful.
- */
- if (fr_type_is_structural(type)) type = FR_TYPE_STRING;
-
- /*
- * It's a literal string, just copy it.
- * Don't escape anything.
- */
- if (tmpl_cast_in_place(map->rhs, type, tmpl_da(map->lhs)) < 0) {
- cf_log_perr(map->ci, "Cannot convert RHS value (%s) to LHS attribute type (%s)",
- fr_type_to_str(FR_TYPE_STRING),
- fr_type_to_str(tmpl_da(map->lhs)->type));
- return -1;
- }
- } /* else we can't precompile the data */
-
- return 0;
-}
-
static unlang_group_t *group_allocate(unlang_t *parent, CONF_SECTION *cs, unlang_ext_t const *ext)
{
unlang_group_t *g;
return c;
}
-static unlang_t *compile_filter(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs)
-{
- int rcode;
-
- unlang_group_t *g;
- unlang_map_t *gext;
-
- unlang_t *c;
- char const *name2 = cf_section_name2(cs);
-
- tmpl_rules_t t_rules;
-
- static unlang_ext_t const filter_ext = {
- .type = UNLANG_TYPE_FILTER,
- .len = sizeof(unlang_map_t),
- .type_name = "unlang_map_t"
- };
-
- /*
- * We allow unknown attributes here.
- */
- t_rules = *(unlang_ctx->rules);
- t_rules.attr.allow_unknown = true;
- RULES_VERIFY(&t_rules);
-
- g = group_allocate(parent, cs, &filter_ext);
- if (!g) return NULL;
-
- gext = unlang_group_to_map(g);
-
- /*
- * This looks at cs->name2 to determine which list to update
- */
- map_list_init(&gext->map);
- rcode = map_afrom_cs(gext, &gext->map, cs, &t_rules, &t_rules, unlang_fixup_filter, NULL, 128);
- if (rcode < 0) return NULL; /* message already printed */
- if (map_list_empty(&gext->map)) {
- cf_log_err(cs, "'filter' sections cannot be empty");
- return NULL;
- }
-
- c = unlang_group_to_generic(g);
-
- if (name2) {
- c->name = name2;
- c->debug_name = talloc_typed_asprintf(c, "filter %s", name2);
- } else {
- c->name = "filter";
- c->debug_name = c->name;
- }
-
- /*
- * The fixups here occur whether or not it's UPDATE or FILTER
- */
- if (!pass2_fixup_update(g, unlang_ctx->rules)) {
- talloc_free(g);
- return NULL;
- }
-
- compile_action_defaults(c, unlang_ctx);
-
- return c;
-}
-
#define T(_x) [T_OP_ ## _x] = true
static const bool edit_list_sub_op[T_TOKEN_LAST] = {
{ L("case"), (void *) compile_case },
{ L("else"), (void *) compile_else },
{ L("elsif"), (void *) compile_elsif },
- { L("filter"), (void *) compile_filter },
{ L("foreach"), (void *) compile_foreach },
{ L("group"), (void *) compile_group },
{ L("if"), (void *) compile_if },
}
/*
- * Do local optimizations.
+ * Do some optimizations.
*
* @todo - check for tail of LHS
*
* lhs->call.args->flags.can_purify |= rhs->flags.can_purify | rhs->flags.pure;
* lhs->flags.can_purify = lhs->call.args->flags.can_purify;
*/
-static xlat_exp_t *logical_purify(xlat_exp_t *lhs, fr_token_t op, xlat_exp_t *rhs)
+static xlat_exp_t *logical_peephole_optimize(xlat_exp_t *lhs, fr_token_t op, xlat_exp_t *rhs)
{
bool value;
* FOO && BAR --> FOO && BAR
*/
-
/*
* 1 || FOO --> 1
* 0 || FOO --> FOO
/*
- * Purify static values.
+ * Do some optimizations
*/
-static int binary_purify(TALLOC_CTX *ctx, xlat_exp_t **out, xlat_exp_t *lhs, fr_token_t op, xlat_exp_t *rhs)
+static int binary_peephole_optimize(TALLOC_CTX *ctx, xlat_exp_t **out, xlat_exp_t *lhs, fr_token_t op, xlat_exp_t *rhs)
{
fr_value_box_t *lhs_box, *rhs_box;
fr_value_box_t box;
*/
if (((c == '!') || (c == '~')) && (op != T_LAND) && (op != T_LOR)) {
fr_strerror_printf("Operator '%c' is only applied to the left hand side of the '%s' operation, add (..) to evaluate the operation first", c, fr_tokens[op]);
+ fail_lhs:
fr_sbuff_set(&our_in, &m_lhs);
return -fr_sbuff_used(&our_in);
}
*/
if (logical_ops[op]) {
if (reparse_rcode(head, &rhs, true) < 0) {
+ fail_rhs:
fr_sbuff_set(&our_in, &m_rhs);
return -fr_sbuff_used(&our_in);
}
goto redo;
}
- if (reparse_rcode(head, &lhs, true) < 0) {
- fr_sbuff_set(&our_in, &m_lhs);
- return -fr_sbuff_used(&our_in);
- }
+ if (reparse_rcode(head, &lhs, true) < 0) goto fail_lhs;
/*
* Peephole optimizer.
* FOO || 0 --> FOO
* FOO && 1 --> 1
*/
- node = logical_purify(lhs, op, rhs);
+ node = logical_peephole_optimize(lhs, op, rhs);
if (node) {
+ replace:
lhs = node;
rhs = node = NULL;
goto redo;
* as special cases, so we can check lists for emptiness.
*/
if (fr_equality_op[op]) {
- if (!valid_type(lhs)) {
- fail_lhs:
- fr_sbuff_set(&our_in, &m_lhs);
- return -fr_sbuff_used(&our_in);
- }
+ if (!valid_type(lhs)) goto fail_lhs;
- if (!valid_type(rhs)) {
- fr_sbuff_set(&our_in, &m_rhs);
- return -fr_sbuff_used(&our_in);
- }
+ if (!valid_type(rhs)) goto fail_rhs;
/*
* Peephole optimization. If both LHS
if (cond) {
int rcode;
- rcode = binary_purify(head, &node, lhs, op, rhs);
+ rcode = binary_peephole_optimize(head, &node, lhs, op, rhs);
if (rcode < 0) goto fail_lhs;
- if (rcode) {
- lhs = node;
- rhs = node = NULL;
- goto redo;
- }
+ if (rcode) goto replace;
}
}