0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-09-16 18:35:19 +00:00

Filter: Implement multiple dispatch for methods

- Extend method descriptors with type signature
 - Daisy chain method descriptors for the same symbol
 - Dispatch methods for same symbol based on type signature
 - Split add/delete/filter operations to multiple methods
 - Replace ad-hoc dispatch of old-style syntax with scope-based dispatch
 - Also change method->arg_num to count initial arg

It still needs some improvements, like better handling of untyped
expressions and better error reporting when no dispatch can be done.

The multiple dispatch could also be extended to dispatch regular
function-like expressions in a uniform way.
This commit is contained in:
Ondrej Zajicek 2023-07-03 17:00:58 +02:00
parent c696e3cb8e
commit cc1099a041
7 changed files with 207 additions and 117 deletions

View File

@ -687,7 +687,7 @@ cf_lex_symbol(const char *data)
return ENUM;
}
case SYM_METHOD:
return sym->method->arg_num ? CF_SYM_METHOD_ARGS : CF_SYM_METHOD_BARE;
return (sym->method->arg_num > 1) ? CF_SYM_METHOD_ARGS : CF_SYM_METHOD_BARE;
case SYM_VOID:
return CF_SYM_UNDEFINED;
default:

View File

@ -869,12 +869,12 @@ static_attr:
term_dot_method: term '.' { f_method_call_start($1); } method_name_cont { f_method_call_end(); $$ = $4; };
method_name_cont:
CF_SYM_METHOD_BARE {
$$ = $1->method->new_inst(FM.object, NULL);
$$ = f_dispatch_method($1, FM.object, NULL);
}
| CF_SYM_METHOD_ARGS {
f_method_call_args();
} '(' var_list ')' {
$$ = $1->method->new_inst(FM.object, $4);
$$ = f_dispatch_method($1, FM.object, $4);
}
;
@ -913,32 +913,15 @@ term:
| '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_const_empty(T_LCLIST); }
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5); }
| ADD '(' term ',' term ')' {
switch ($3->type) {
case T_CLIST: $$ = f_new_inst(FI_CLIST_ADD, $3, $5); break;
case T_ECLIST: $$ = f_new_inst(FI_ECLIST_ADD, $3, $5); break;
case T_LCLIST: $$ = f_new_inst(FI_LCLIST_ADD, $3, $5); break;
default: cf_error("Can't add to type %s", f_type_name($3->type));
}
$$ = f_dispatch_method_x("add", $3->type, $3, $5);
cf_warn("add(x,y) function is deprecated, please use x.add(y)");
}
| DELETE '(' term ',' term ')' {
switch ($3->type) {
case T_PATH: $$ = f_new_inst(FI_PATH_DEL, $3, $5); break;
case T_CLIST: $$ = f_new_inst(FI_CLIST_DEL, $3, $5); break;
case T_ECLIST: $$ = f_new_inst(FI_ECLIST_DEL, $3, $5); break;
case T_LCLIST: $$ = f_new_inst(FI_LCLIST_DEL, $3, $5); break;
default: cf_error("Can't delete from type %s", f_type_name($3->type));
}
$$ = f_dispatch_method_x("delete", $3->type, $3, $5);
cf_warn("delete(x,y) function is deprecated, please use x.delete(y)");
}
| FILTER '(' term ',' term ')' {
switch ($3->type) {
case T_PATH: $$ = f_new_inst(FI_PATH_FILTER, $3, $5); break;
case T_CLIST: $$ = f_new_inst(FI_CLIST_FILTER, $3, $5); break;
case T_ECLIST: $$ = f_new_inst(FI_ECLIST_FILTER, $3, $5); break;
case T_LCLIST: $$ = f_new_inst(FI_LCLIST_FILTER, $3, $5); break;
default: cf_error("Can't filter type %s", f_type_name($3->type));
}
$$ = f_dispatch_method_x("filter", $3->type, $3, $5);
cf_warn("filter(x,y) function is deprecated, please use x.filter(y)");
}

View File

@ -67,7 +67,9 @@ enum f_type {
struct f_method {
struct symbol *sym;
struct f_inst *(*new_inst)(struct f_inst *obj, struct f_inst *args);
const struct f_method *next;
uint arg_num;
enum f_type args_type[];
};
/* Filter value; size of this affects filter memory consumption */

View File

@ -53,6 +53,7 @@ m4_define(FID_NEW_ATTRIBUTES, `m4_divert(110)')
m4_define(FID_NEW_BODY, `m4_divert(103)')
m4_define(FID_NEW_METHOD, `m4_divert(111)')
m4_define(FID_METHOD_CALL, `m4_divert(112)')
m4_define(FID_TYPE_SIGNATURE, `m4_divert(113)')
m4_define(FID_DUMP_BODY, `m4_divert(104)m4_define([[FID_DUMP_BODY_EXISTS]])')
m4_define(FID_LINEARIZE_BODY, `m4_divert(105)')
m4_define(FID_SAME_BODY, `m4_divert(106)')
@ -125,7 +126,7 @@ FID_IFCONST([[
constargs = 0;
]])
} while (child$1 = child$1->next);
m4_define([[INST_METHOD_NUM_ARGS]],m4_eval($1-1))m4_dnl
m4_define([[INST_METHOD_NUM_ARGS]],$1)m4_dnl
m4_ifelse($1,1,,[[FID_NEW_METHOD()m4_dnl
struct f_inst *arg$1 = args;
if (args == NULL) cf_error("Not enough arguments"); /* INST_NAME */
@ -183,6 +184,8 @@ m4_define(ARG_TYPE, `ARG_TYPE_STATIC($1,$2) ARG_TYPE_DYNAMIC($1,$2)')
m4_define(ARG_TYPE_STATIC, `m4_dnl
m4_ifelse($1,1,[[m4_define([[INST_METHOD_OBJECT_TYPE]],$2)]],)m4_dnl
FID_TYPE_SIGNATURE()m4_dnl
method->args_type[m4_eval($1-1)] = $2;
FID_NEW_BODY()m4_dnl
if (f$1->type && (f$1->type != ($2)) && !f_const_promotion(f$1, ($2)))
cf_error("Argument $1 of %s must be of type %s, got type %s",
@ -230,7 +233,7 @@ FID_NEW_ARGS()m4_dnl
, struct f_inst * f$1
FID_NEW_BODY()m4_dnl
whati->f$1 = f$1;
m4_define([[INST_METHOD_NUM_ARGS]],m4_eval($1-1))m4_dnl
m4_define([[INST_METHOD_NUM_ARGS]],$1)m4_dnl
FID_NEW_METHOD()m4_dnl
struct f_inst *arg$1 = args;
if (args == NULL) cf_error("Not enough arguments"); /* INST_NAME */
@ -421,17 +424,11 @@ m4_undivert(112)
FID_METHOD_SCOPE_INIT()m4_dnl
[INST_METHOD_OBJECT_TYPE] = {},
FID_METHOD_REGISTER()m4_dnl
sym = cf_new_symbol(&f_type_method_scopes[INST_METHOD_OBJECT_TYPE],
global_root_scope_pool, global_root_scope_linpool,
INST_METHOD_NAME);
sym->class = SYM_METHOD;
sym->method = method = lp_allocz(global_root_scope_linpool, sizeof(struct f_method));
*method = (struct f_method) {
.sym = sym,
.new_inst = f_new_method_]]INST_NAME()[[,
.arg_num = INST_METHOD_NUM_ARGS,
};
method = lp_allocz(global_root_scope_linpool, sizeof(struct f_method) + INST_METHOD_NUM_ARGS * sizeof(enum f_type));
method->new_inst = f_new_method_]]INST_NAME()[[;
method->arg_num = INST_METHOD_NUM_ARGS;
m4_undivert(113)
f_register_method(INST_METHOD_OBJECT_TYPE, INST_METHOD_NAME, method);
]])m4_dnl
@ -634,10 +631,27 @@ struct sym_scope *f_type_method_scope(enum f_type t)
return (t < ARRAY_SIZE(f_type_method_scopes)) ? &f_type_method_scopes[t] : NULL;
}
static void
f_register_method(enum f_type t, const byte *name, struct f_method *dsc)
{
struct sym_scope *scope = &f_type_method_scopes[t];
struct symbol *sym = cf_find_symbol_scope(scope, name);
if (!sym)
{
sym = cf_new_symbol(scope, global_root_scope_pool, global_root_scope_linpool, name);
sym->class = SYM_METHOD;
}
dsc->sym = sym;
dsc->next = sym->method;
sym->method = dsc;
}
void f_type_methods_register(void)
{
struct symbol *sym;
struct f_method *method;
FID_WR_PUT(13)
for (uint i = 0; i < ARRAY_SIZE(f_type_method_scopes); i++)

View File

@ -1246,7 +1246,8 @@
RESULT(T_PATH, ad, [[ as_path_prepend(fpool, v1.val.ad, v2.val.i) ]]);
}
INST(FI_CLIST_ADD, 2, 1) { /* (Extended) Community list add */
/* Community list add */
INST(FI_CLIST_ADD, 2, 1) {
ARG(1, T_CLIST);
ARG_ANY(2);
METHOD_CONSTRUCTOR("add");
@ -1264,47 +1265,54 @@
runtime("Can't add non-pair");
}
INST(FI_ECLIST_ADD, 2, 1) {
INST(FI_ECLIST_ADD_EC, 2, 1) {
ARG(1, T_ECLIST);
ARG_ANY(2);
ARG(2, T_EC);
METHOD_CONSTRUCTOR("add");
/* v2.val is either EC or EC-set */
if ((v2.type == T_SET) && eclist_set_type(v2.val.t))
runtime("Can't add set");
else if (v2.type == T_ECLIST)
RESULT(T_ECLIST, ad, [[ ec_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
else if (v2.type != T_EC)
runtime("Can't add non-ec");
else
RESULT(T_ECLIST, ad, [[ ec_set_add(fpool, v1.val.ad, v2.val.ec) ]]);
RESULT(T_ECLIST, ad, [[ ec_set_add(fpool, v1.val.ad, v2.val.ec) ]]);
}
INST(FI_LCLIST_ADD, 2, 1) {
INST(FI_ECLIST_ADD_ECLIST, 2, 1) {
ARG(1, T_ECLIST);
ARG(2, T_ECLIST);
METHOD_CONSTRUCTOR("add");
RESULT(T_ECLIST, ad, [[ ec_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
}
INST(FI_LCLIST_ADD_LC, 2, 1) {
ARG(1, T_LCLIST);
ARG_ANY(2);
ARG(2, T_LC);
METHOD_CONSTRUCTOR("add");
/* v2.val is either LC or LC-set */
if ((v2.type == T_SET) && lclist_set_type(v2.val.t))
runtime("Can't add set");
else if (v2.type == T_LCLIST)
RESULT(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
else if (v2.type != T_LC)
runtime("Can't add non-lc");
else
RESULT(T_LCLIST, ad, [[ lc_set_add(fpool, v1.val.ad, v2.val.lc) ]]);
RESULT(T_LCLIST, ad, [[ lc_set_add(fpool, v1.val.ad, v2.val.lc) ]]);
}
INST(FI_PATH_DEL, 2, 1) { /* Path delete */
INST(FI_LCLIST_ADD_LCLIST, 2, 1) {
ARG(1, T_LCLIST);
ARG(2, T_LCLIST);
METHOD_CONSTRUCTOR("add");
RESULT(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
}
INST(FI_PATH_DELETE_INT, 2, 1) {
ARG(1, T_PATH);
ARG_ANY(2);
ARG(2, T_INT);
METHOD_CONSTRUCTOR("delete");
if ((v2.type == T_SET) && path_set_type(v2.val.t) || (v2.type == T_INT))
RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]);
else
runtime("Can't delete non-integer (set)");
RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]);
}
INST(FI_CLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
INST(FI_PATH_DELETE_SET, 2, 1) {
ARG(1, T_PATH);
ARG(2, T_SET);
METHOD_CONSTRUCTOR("delete");
if (!path_set_type(v2.val.t))
runtime("Mismatched set type");
RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]);
}
/* Community list delete */
INST(FI_CLIST_DELETE, 2, 1) {
ARG(1, T_CLIST);
ARG_ANY(2);
METHOD_CONSTRUCTOR("delete");
@ -1322,76 +1330,119 @@
runtime("Can't delete non-pair");
}
INST(FI_ECLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
INST(FI_ECLIST_DELETE_EC, 2, 1) {
ARG(1, T_ECLIST);
ARG_ANY(2);
ARG(2, T_EC);
METHOD_CONSTRUCTOR("delete");
/* v2.val is either EC or EC-set */
if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST))
RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
else if (v2.type != T_EC)
runtime("Can't delete non-ec");
else
RESULT(T_ECLIST, ad, [[ ec_set_del(fpool, v1.val.ad, v2.val.ec) ]]);
RESULT(T_ECLIST, ad, [[ ec_set_del(fpool, v1.val.ad, v2.val.ec) ]]);
}
INST(FI_LCLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
INST(FI_ECLIST_DELETE_ECLIST, 2, 1) {
ARG(1, T_ECLIST);
ARG(2, T_ECLIST);
METHOD_CONSTRUCTOR("delete");
RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
}
INST(FI_ECLIST_DELETE_SET, 2, 1) {
ARG(1, T_ECLIST);
ARG(2, T_SET);
METHOD_CONSTRUCTOR("delete");
if (!eclist_set_type(v2.val.t))
runtime("Mismatched set type");
RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
}
INST(FI_LCLIST_DELETE_LC, 2, 1) {
ARG(1, T_LCLIST);
ARG_ANY(2);
ARG(2, T_LC);
METHOD_CONSTRUCTOR("delete");
/* v2.val is either LC or LC-set */
if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST))
RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
else if (v2.type != T_LC)
runtime("Can't delete non-lc");
else
RESULT(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]);
RESULT(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]);
}
INST(FI_PATH_FILTER, 2, 1) { /* (Extended) Community list add or delete */
INST(FI_LCLIST_DELETE_LCLIST, 2, 1) {
ARG(1, T_LCLIST);
ARG(2, T_LCLIST);
METHOD_CONSTRUCTOR("delete");
RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
}
INST(FI_LCLIST_DELETE_SET, 2, 1) {
ARG(1, T_LCLIST);
ARG(2, T_SET);
METHOD_CONSTRUCTOR("delete");
if (!lclist_set_type(v2.val.t))
runtime("Mismatched set type");
RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
}
INST(FI_PATH_FILTER_SET, 2, 1) {
ARG(1, T_PATH);
ARG_ANY(2);
ARG(2, T_SET);
METHOD_CONSTRUCTOR("filter");
if ((v2.type == T_SET) && path_set_type(v2.val.t))
RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter integer");
}
if (!path_set_type(v2.val.t))
runtime("Mismatched set type");
INST(FI_CLIST_FILTER, 2, 1) {
RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 1) ]]);
}
INST(FI_CLIST_FILTER_CLIST, 2, 1) {
ARG(1, T_CLIST);
ARG_ANY(2);
ARG(2, T_CLIST);
METHOD_CONSTRUCTOR("filter");
/* Community (or cluster) list */
struct f_val dummy;
if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST))
RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter pair");
RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
}
INST(FI_ECLIST_FILTER, 2, 1) {
INST(FI_CLIST_FILTER_SET, 2, 1) {
ARG(1, T_CLIST);
ARG(2, T_SET);
METHOD_CONSTRUCTOR("filter");
if (!clist_set_type(v2.val.t, &(struct f_val){}))
runtime("Mismatched set type");
RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
}
INST(FI_ECLIST_FILTER_ECLIST, 2, 1) {
ARG(1, T_ECLIST);
ARG_ANY(2);
ARG(2, T_ECLIST);
METHOD_CONSTRUCTOR("filter");
/* v2.val is either EC or EC-set */
if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST))
RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter ec");
RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
}
INST(FI_LCLIST_FILTER, 2, 1) {
ARG(1, T_LCLIST);
ARG_ANY(2);
INST(FI_ECLIST_FILTER_SET, 2, 1) {
ARG(1, T_ECLIST);
ARG(2, T_SET);
METHOD_CONSTRUCTOR("filter");
/* v2.val is either LC or LC-set */
if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST))
RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter lc");
if (!eclist_set_type(v2.val.t))
runtime("Mismatched set type");
RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
}
INST(FI_LCLIST_FILTER_LCLIST, 2, 1) {
ARG(1, T_LCLIST);
ARG(2, T_LCLIST);
METHOD_CONSTRUCTOR("filter");
RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
}
INST(FI_LCLIST_FILTER_SET, 2, 1) {
ARG(1, T_LCLIST);
ARG(2, T_SET);
METHOD_CONSTRUCTOR("filter");
if (!lclist_set_type(v2.val.t))
runtime("Mismatched set type");
RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
}
INST(FI_ROA_CHECK_IMPLICIT, 0, 1) { /* ROA Check */

View File

@ -96,6 +96,8 @@ void f_add_lines(const struct f_line_item *what, struct filter_iterator *fit);
struct filter *f_new_where(struct f_inst *);
struct f_inst *f_dispatch_method(struct symbol *sym, struct f_inst *obj, struct f_inst *args);
struct f_inst *f_dispatch_method_x(const char *name, enum f_type t, struct f_inst *obj, struct f_inst *args);
struct f_inst *f_for_cycle(struct symbol *var, struct f_inst *term, struct f_inst *block);
struct f_inst *f_print(struct f_inst *vars, int flush, enum filter_return fret);

View File

@ -30,7 +30,8 @@ filter_name(const struct filter *filter)
return filter->sym->name;
}
struct filter *f_new_where(struct f_inst *where)
struct filter *
f_new_where(struct f_inst *where)
{
struct f_inst *cond = f_new_inst(FI_CONDITION, where,
f_new_inst(FI_DIE, F_ACCEPT),
@ -41,6 +42,43 @@ struct filter *f_new_where(struct f_inst *where)
return f;
}
static inline int
f_match_signature(const struct f_method *dsc, struct f_inst *args)
{
uint i;
for (i = 1; args && (i < dsc->arg_num); args = args->next, i++)
if (dsc->args_type[i] && (args->type != dsc->args_type[i]))
return 0;
return !args && !(i < dsc->arg_num);
}
struct f_inst *
f_dispatch_method(struct symbol *sym, struct f_inst *obj, struct f_inst *args)
{
/* Note! We should revert args */
for (const struct f_method *dsc = sym->method; dsc; dsc = dsc->next)
if (f_match_signature(dsc, args))
return dsc->new_inst(obj, args);
cf_error("Cannot dispatch method '%s'", sym->name);
}
struct f_inst *
f_dispatch_method_x(const char *name, enum f_type t, struct f_inst *obj, struct f_inst *args)
{
struct sym_scope *scope = f_type_method_scope(t);
struct symbol *sym = cf_find_symbol_scope(scope, name);
if (!sym)
cf_error("Cannot dispatch method '%s'", name);
return f_dispatch_method(sym, obj, args);
}
struct f_inst *
f_for_cycle(struct symbol *var, struct f_inst *term, struct f_inst *block)
{