0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-11-09 20:58:44 +00:00

Merge commit 'a3dc2645' into thread-next

This commit is contained in:
Maria Matejka 2023-10-29 00:50:38 +02:00
commit de8288c679
12 changed files with 272 additions and 111 deletions

View File

@ -391,6 +391,7 @@ else: {
\>\= return GEQ; \>\= return GEQ;
\&\& return AND; \&\& return AND;
\|\| return OR; \|\| return OR;
\-\> return IMP;
\[\= return PO; \[\= return PO;
\=\] return PC; \=\] return PC;

View File

@ -100,7 +100,7 @@ CF_DECLS
} }
%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT %token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT
%token GEQ LEQ NEQ AND OR %token GEQ LEQ NEQ AND OR IMP
%token PO PC %token PO PC
%token <i> NUM ENUM %token <i> NUM ENUM
%token <ip4> IP4 %token <ip4> IP4
@ -128,7 +128,7 @@ CF_DECLS
%nonassoc PREFIX_DUMMY %nonassoc PREFIX_DUMMY
%left AND OR %left AND OR
%nonassoc '=' '<' '>' '~' GEQ LEQ NEQ NMA PO PC %nonassoc '=' '<' '>' '~' GEQ LEQ NEQ NMA IMP PO PC
%left '|' '&' %left '|' '&'
%left '+' '-' %left '+' '-'
%left '*' '/' '%' %left '*' '/' '%'

View File

@ -551,7 +551,7 @@ include "tablename.conf";;
Define a filter. You can learn more about filters in the following Define a filter. You can learn more about filters in the following
chapter. chapter.
<tag><label id="opt-function">function <m/type/ <m/name/ (<m/parameters/) <m/local variables/ { <m/commands/ }</tag> <tag><label id="opt-function">function <m/name/ (<m/parameters/) [ -&gt; <m/return type/ ] <m/local variables/ { <m/commands/ }</tag>
Define a function. You can learn more about functions in the following chapter. Define a function. You can learn more about functions in the following chapter.
<tag><label id="opt-protocol">protocol rip|ospf|bgp|<m/.../ [<m/name/ [from <m/name2/]] { <m>protocol options</m> }</tag> <tag><label id="opt-protocol">protocol rip|ospf|bgp|<m/.../ [<m/name/ [from <m/name2/]] { <m>protocol options</m> }</tag>
@ -1344,21 +1344,21 @@ can group several statements to a single compound statement by using braces
(<cf>{ <M>statements</M> }</cf>) which is useful if you want to make a bigger (<cf>{ <M>statements</M> }</cf>) which is useful if you want to make a bigger
block of code conditional. block of code conditional.
<p>BIRD supports functions, so that you don't have to repeat the same blocks of <p>BIRD supports functions, so that you don not have to repeat the same blocks
code over and over. Functions can have zero or more parameters and they can have of code over and over. Functions can have zero or more parameters and they can
local variables. You should always specify the function return type and always have local variables. If the function returns value, then you should always
return it. No-return functions and multiple-type returning functions are deprecated. specify its return type. Direct recursion is possible. Function definitions look
Direct recursion is possible. Function definitions look like this: like this:
<code> <code>
function int name () function name() -> int
{ {
int local_variable; int local_variable;
int another_variable = 5; int another_variable = 5;
return 42; return 42;
} }
function pair with_parameters (int parameter) function with_parameters(int parameter) -> pair
{ {
print parameter; print parameter;
return (1, 2); return (1, 2);

View File

@ -374,12 +374,12 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%nonassoc ELSE %nonassoc ELSE
%type <xp> cmds_int cmd_prep %type <xp> cmds_int cmd_prep
%type <x> term term_bs cmd cmd_var cmds cmds_scoped constant constructor var var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail term_dot_method method_name_cont %type <x> term term_bs cmd cmd_var cmds cmds_scoped constant constructor var var_list var_list_r function_call symbol_value bgp_path_expr bgp_path bgp_path_tail term_dot_method method_name_cont
%type <fsa> static_attr %type <fsa> static_attr
%type <f> filter where_filter %type <f> filter where_filter
%type <fl> filter_body function_body %type <fl> filter_body function_body
%type <flv> lvalue %type <flv> lvalue
%type <i> type maybe_type function_vars %type <i> type function_vars function_type
%type <fa> function_argsn function_args %type <fa> function_argsn function_args
%type <ecs> ec_kind %type <ecs> ec_kind
%type <fret> break_command %type <fret> break_command
@ -527,6 +527,11 @@ function_vars:
} }
; ;
function_type:
/* EMPTY */ { $$ = T_VOID; }
| IMP type { $$ = $2; }
;
filter_body: function_body ; filter_body: function_body ;
filter: filter:
@ -566,29 +571,24 @@ function_body:
; ;
conf: function_def ; conf: function_def ;
maybe_type:
/* EMPTY */ { $$ = T_VOID; }
| type { $$ = $1; }
;
function_def: function_def:
FUNCTION maybe_type symbol { FUNCTION symbol {
DBG( "Beginning of function %s\n", $3->name ); DBG( "Beginning of function %s\n", $2->name );
this_function = cf_define_symbol(new_config, $3, SYM_FUNCTION, function, NULL); this_function = cf_define_symbol(new_config, $2, SYM_FUNCTION, function, NULL);
/* if ($2 == T_VOID) cf_warn("Support for functions without explicit return type will be removed soon" ); */
cf_enter_filters(); cf_enter_filters();
cf_push_scope(new_config, this_function); cf_push_scope(new_config, this_function);
} function_args { } function_args function_type {
/* Make dummy f_line for storing function prototype */ /* Make dummy f_line for storing function prototype */
struct f_line *dummy = cfg_allocz(sizeof(struct f_line)); struct f_line *dummy = cfg_allocz(sizeof(struct f_line));
this_function->function = dummy; this_function->function = dummy;
dummy->return_type = $2; dummy->return_type = $5;
/* Revert the args */ /* Revert the args */
while ($5) { while ($4) {
struct f_arg *tmp = $5; struct f_arg *tmp = $4;
$5 = $5->next; $4 = $4->next;
tmp->next = dummy->arg_list; tmp->next = dummy->arg_list;
dummy->arg_list = tmp; dummy->arg_list = tmp;
@ -598,7 +598,7 @@ function_def:
$7->args = this_function->function->args; $7->args = this_function->function->args;
$7->arg_list = this_function->function->arg_list; $7->arg_list = this_function->function->arg_list;
$7->return_type = this_function->function->return_type; $7->return_type = this_function->function->return_type;
$3->function = $7; $2->function = $7;
cf_pop_scope(new_config); cf_pop_scope(new_config);
cf_exit_filters(); cf_exit_filters();
} }
@ -828,10 +828,27 @@ constructor:
; ;
/* This generates the function_call variable list backwards. */ /* This generates the function_call variable list backwards */
var_list: /* EMPTY */ { $$ = NULL; } var_list_r:
/* EMPTY */ { $$ = NULL; }
| term { $$ = $1; } | term { $$ = $1; }
| var_list ',' term { $$ = $3; $$->next = $1; } | var_list_r ',' term { $$ = $3; $$->next = $1; }
;
var_list: var_list_r
{
$$ = NULL;
/* Revert the var_list_r */
while ($1) {
struct f_inst *tmp = $1;
$1 = $1->next;
tmp->next = $$;
$$ = tmp;
}
}
;
function_call: function_call:
CF_SYM_KNOWN '(' var_list ')' CF_SYM_KNOWN '(' var_list ')'
@ -839,17 +856,7 @@ function_call:
if ($1->class != SYM_FUNCTION) if ($1->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really."); cf_error("You can't call something which is not a function. Really.");
/* Revert the var_list */ $$ = f_new_inst(FI_CALL, $3, $1);
struct f_inst *args = NULL;
while ($3) {
struct f_inst *tmp = $3;
$3 = $3->next;
tmp->next = args;
args = tmp;
}
$$ = f_new_inst(FI_CALL, args, $1);
} }
; ;
@ -885,12 +892,12 @@ static_attr:
term_dot_method: term '.' { f_method_call_start($1); } method_name_cont { f_method_call_end(); $$ = $4; }; term_dot_method: term '.' { f_method_call_start($1); } method_name_cont { f_method_call_end(); $$ = $4; };
method_name_cont: method_name_cont:
CF_SYM_METHOD_BARE { CF_SYM_METHOD_BARE {
$$ = f_dispatch_method($1, FM.object, NULL); $$ = f_dispatch_method($1, FM.object, NULL, 1);
} }
| CF_SYM_METHOD_ARGS { | CF_SYM_METHOD_ARGS {
f_method_call_args(); f_method_call_args();
} '(' var_list ')' { } '(' var_list ')' {
$$ = f_dispatch_method($1, FM.object, $4); $$ = f_dispatch_method($1, FM.object, $4, 1);
} }
; ;
@ -927,7 +934,11 @@ term:
| '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT, f_get_empty(T_CLIST)); } | '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT, f_get_empty(T_CLIST)); }
| '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_get_empty(T_ECLIST)); } | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_get_empty(T_ECLIST)); }
| '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_get_empty(T_LCLIST)); } | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_get_empty(T_LCLIST)); }
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5); }
| PREPEND '(' term ',' term ')' {
$$ = f_dispatch_method_x("prepend", $3->type, $3, $5);
cf_warn("prepend(x,y) function is deprecated, please use x.prepend(y)");
}
| ADD '(' term ',' term ')' { | ADD '(' term ',' term ')' {
$$ = f_dispatch_method_x("add", $3->type, $3, $5); $$ = f_dispatch_method_x("add", $3->type, $3, $5);
cf_warn("add(x,y) function is deprecated, please use x.add(y)"); cf_warn("add(x,y) function is deprecated, please use x.add(y)");
@ -1039,13 +1050,13 @@ cmd:
cf_error("Attribute %s is read-only", $3->attribute->name); cf_error("Attribute %s is read-only", $3->attribute->name);
$$ = f_new_inst(FI_EA_UNSET, $3->attribute); $$ = f_new_inst(FI_EA_UNSET, $3->attribute);
} }
| break_command var_list ';' { | break_command var_list_r ';' {
$$ = f_print($2, !!$2, $1); $$ = f_print($2, !!$2, $1);
} }
| PRINT var_list ';' { | PRINT var_list_r ';' {
$$ = f_print($2, 1, F_NOP); $$ = f_print($2, 1, F_NOP);
} }
| PRINTN var_list ';' { | PRINTN var_list_r ';' {
$$ = f_print($2, 0, F_NOP); $$ = f_print($2, 0, F_NOP);
} }
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); } | function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }

View File

@ -27,6 +27,8 @@
static const char * const f_type_str[] = { static const char * const f_type_str[] = {
[T_VOID] = "void", [T_VOID] = "void",
[T_NONE] = "none",
[T_OPAQUE] = "opaque byte string", [T_OPAQUE] = "opaque byte string",
[T_IFACE] = "interface", [T_IFACE] = "interface",
@ -62,10 +64,12 @@ static const char * const f_type_str[] = {
[T_PREFIX_SET] = "prefix set", [T_PREFIX_SET] = "prefix set",
}; };
STATIC_ASSERT((1 << (8 * sizeof(btype))) == ARRAY_SIZE(f_type_str));
const char * const char *
f_type_name(btype t) f_type_name(btype t)
{ {
return (t < ARRAY_SIZE(f_type_str)) ? (f_type_str[t] ?: "?") : "?"; return f_type_str[t] ?: "?";
} }
btype btype

View File

@ -251,8 +251,9 @@ const struct adata *lclist_filter(struct linpool *pool, const struct adata *list
/* Special undef value for paths and clists */ /* Special undef value for paths and clists */
static inline int static inline int
undef_value(struct f_val v) val_is_undefined(struct f_val v)
{ {
return ((v.type == T_PATH) || (v.type == T_CLIST) || return ((v.type == T_PATH) || (v.type == T_CLIST) ||
(v.type == T_ECLIST) || (v.type == T_LCLIST)) && (v.type == T_ECLIST) || (v.type == T_LCLIST)) &&

View File

@ -380,7 +380,8 @@ m4_undivert(102)m4_dnl
[[m4_dnl The one case in The Big Switch inside interpreter [[m4_dnl The one case in The Big Switch inside interpreter
case INST_NAME(): case INST_NAME():
#define whati (&(what->i_]]INST_NAME()[[)) #define whati (&(what->i_]]INST_NAME()[[))
m4_ifelse(m4_eval(INST_INVAL() > 0), 1, [[if (fstk->vcnt < INST_INVAL()) runtime("Stack underflow"); fstk->vcnt -= INST_INVAL(); ]]) m4_ifelse(m4_eval(INST_INVAL() > 0), 1, [[if (fstk->vcnt < INST_INVAL()) runtime("Stack underflow");
fstk->vcnt -= INST_INVAL();]])
m4_undivert(108)m4_dnl m4_undivert(108)m4_dnl
#undef whati #undef whati
break; break;
@ -573,8 +574,8 @@ fi_constant(struct f_inst *what, struct f_val val)
return what; return what;
} }
static int int
f_const_promotion(struct f_inst *arg, btype want) f_const_promotion_(struct f_inst *arg, btype want, int update)
{ {
if (arg->fi_code != FI_CONSTANT) if (arg->fi_code != FI_CONSTANT)
return 0; return 0;
@ -582,15 +583,17 @@ f_const_promotion(struct f_inst *arg, btype want)
struct f_val *c = &arg->i_FI_CONSTANT.val; struct f_val *c = &arg->i_FI_CONSTANT.val;
if ((c->type == T_IP) && ipa_is_ip4(c->val.ip) && (want == T_QUAD)) { if ((c->type == T_IP) && ipa_is_ip4(c->val.ip) && (want == T_QUAD)) {
*c = (struct f_val) { if (update)
.type = T_QUAD, *c = (struct f_val) {
.val.i = ipa_to_u32(c->val.ip), .type = T_QUAD,
}; .val.i = ipa_to_u32(c->val.ip),
};
return 1; return 1;
} }
else if ((c->type == T_SET) && (!c->val.t) && (want == T_PREFIX_SET)) { else if ((c->type == T_SET) && (!c->val.t) && (want == T_PREFIX_SET)) {
*c = f_const_empty_prefix_set; if (update)
*c = f_const_empty_prefix_set;
return 1; return 1;
} }

View File

@ -500,7 +500,7 @@
INST(FI_DEFINED, 1, 1) { INST(FI_DEFINED, 1, 1) {
ARG_ANY(1); ARG_ANY(1);
RESULT(T_BOOL, i, (v1.type != T_VOID) && !undef_value(v1)); RESULT(T_BOOL, i, (v1.type != T_VOID) && !val_is_undefined(v1));
} }
METHOD_R(T_NET, type, T_ENUM_NETTYPE, i, v1.val.net->type); METHOD_R(T_NET, type, T_ENUM_NETTYPE, i, v1.val.net->type);
@ -1101,8 +1101,6 @@
NEVER_CONSTANT; NEVER_CONSTANT;
VARARG; VARARG;
SYMBOL; SYMBOL;
/* Fake result type declaration */
RESULT_TYPE(sym->function->return_type); RESULT_TYPE(sym->function->return_type);
FID_NEW_BODY() FID_NEW_BODY()
@ -1222,22 +1220,39 @@
} }
/* Community list add */ /* Community list add */
INST(FI_CLIST_ADD, 2, 1) { INST(FI_CLIST_ADD_PAIR, 2, 1) {
ARG(1, T_CLIST); ARG(1, T_CLIST);
ARG_ANY(2); ARG(2, T_PAIR);
METHOD_CONSTRUCTOR("add"); METHOD_CONSTRUCTOR("add");
struct f_val dummy; RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]);
if ((v2.type == T_PAIR) || (v2.type == T_QUAD)) }
RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]);
/* IP->Quad implicit conversion */ INST(FI_CLIST_ADD_IP, 2, 1) {
else if (val_is_ip4(&v2)) ARG(1, T_CLIST);
RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]); ARG(2, T_IP);
else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy)) METHOD_CONSTRUCTOR("add");
runtime("Can't add set");
else if (v2.type == T_CLIST) FID_NEW_BODY();
RESULT(T_CLIST, ad, [[ int_set_union(fpool, v1.val.ad, v2.val.ad) ]]); /* IP->Quad implicit conversion, must be before FI_CLIST_ADD_QUAD */
else cf_warn("Method add(clist, ip) is deprecated, please use add(clist, quad)");
runtime("Can't add non-pair");
FID_INTERPRET_BODY();
if (!val_is_ip4(&v2)) runtime("Mismatched IP type");
RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
}
INST(FI_CLIST_ADD_QUAD, 2, 1) {
ARG(1, T_CLIST);
ARG(2, T_QUAD);
METHOD_CONSTRUCTOR("add");
RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]);
}
INST(FI_CLIST_ADD_CLIST, 2, 1) {
ARG(1, T_CLIST);
ARG(2, T_CLIST);
METHOD_CONSTRUCTOR("add");
RESULT(T_CLIST, ad, [[ int_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
} }
INST(FI_ECLIST_ADD_EC, 2, 1) { INST(FI_ECLIST_ADD_EC, 2, 1) {
@ -1287,22 +1302,50 @@
} }
/* Community list delete */ /* Community list delete */
INST(FI_CLIST_DELETE, 2, 1) { INST(FI_CLIST_DELETE_PAIR, 2, 1) {
ARG(1, T_CLIST); ARG(1, T_CLIST);
ARG_ANY(2); ARG(2, T_PAIR);
METHOD_CONSTRUCTOR("delete"); METHOD_CONSTRUCTOR("delete");
/* Community (or cluster) list */ RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]);
struct f_val dummy; }
if ((v2.type == T_PAIR) || (v2.type == T_QUAD)) INST(FI_CLIST_DELETE_IP, 2, 1) {
RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]); ARG(1, T_CLIST);
/* IP->Quad implicit conversion */ ARG(2, T_IP);
else if (val_is_ip4(&v2)) METHOD_CONSTRUCTOR("delete");
RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST)) FID_NEW_BODY();
RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 0) ]]); /* IP->Quad implicit conversion, must be before FI_CLIST_DELETE_QUAD */
else cf_warn("Method delete(clist, ip) is deprecated, please use delete(clist, quad)");
runtime("Can't delete non-pair");
FID_INTERPRET_BODY();
if (!val_is_ip4(&v2)) runtime("Mismatched IP type");
RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
}
INST(FI_CLIST_DELETE_QUAD, 2, 1) {
ARG(1, T_CLIST);
ARG(2, T_QUAD);
METHOD_CONSTRUCTOR("delete");
RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]);
}
INST(FI_CLIST_DELETE_CLIST, 2, 1) {
ARG(1, T_CLIST);
ARG(2, T_CLIST);
METHOD_CONSTRUCTOR("delete");
RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 0) ]]);
}
INST(FI_CLIST_DELETE_SET, 2, 1) {
ARG(1, T_CLIST);
ARG(2, T_SET);
METHOD_CONSTRUCTOR("delete");
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, 0) ]]);
} }
INST(FI_ECLIST_DELETE_EC, 2, 1) { INST(FI_ECLIST_DELETE_EC, 2, 1) {

View File

@ -36,6 +36,16 @@ const char *f_instruction_name_(enum f_instruction_code fi);
static inline const char *f_instruction_name(enum f_instruction_code fi) static inline const char *f_instruction_name(enum f_instruction_code fi)
{ return f_instruction_name_(fi) + 3; } { return f_instruction_name_(fi) + 3; }
int f_const_promotion_(struct f_inst *arg, enum btype want, int update);
static inline int f_const_promotion(struct f_inst *arg, enum btype want)
{ return f_const_promotion_(arg, want, 1); }
static inline int f_try_const_promotion(struct f_inst *arg, enum btype want)
{ return f_const_promotion_(arg, want, 0); }
struct f_arg { struct f_arg {
struct symbol *arg; struct symbol *arg;
struct f_arg *next; struct f_arg *next;
@ -96,7 +106,7 @@ void f_add_lines(const struct f_line_item *what, struct filter_iterator *fit);
struct filter *f_new_where(struct f_inst *); 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(struct symbol *sym, struct f_inst *obj, struct f_inst *args, int skip);
struct f_inst *f_dispatch_method_x(const char *name, enum btype t, struct f_inst *obj, struct f_inst *args); struct f_inst *f_dispatch_method_x(const char *name, enum btype 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_for_cycle(struct symbol *var, struct f_inst *term, struct f_inst *block);
struct f_inst *f_implicit_roa_check(struct rtable_config *tab); struct f_inst *f_implicit_roa_check(struct rtable_config *tab);

View File

@ -45,25 +45,100 @@ f_new_where(struct f_inst *where)
static inline int static inline int
f_match_signature(const struct f_method *dsc, struct f_inst *args) f_match_signature(const struct f_method *dsc, struct f_inst *args)
{ {
uint i; int i, arg_num = (int) dsc->arg_num;
for (i = 1; args && (i < dsc->arg_num); args = args->next, i++) for (i = 1; args && (i < arg_num); args = args->next, i++)
if (dsc->args_type[i] && (args->type != dsc->args_type[i])) if (dsc->args_type[i] && (args->type != dsc->args_type[i]) &&
!f_try_const_promotion(args, dsc->args_type[i]))
return 0; return 0;
return !args && !(i < dsc->arg_num); return !args && !(i < arg_num);
}
/* Variant of f_match_signature(), optimized for error reporting */
static inline void
f_match_signature_err(const struct f_method *dsc, struct f_inst *args, int *pos, int *want, int *got)
{
int i, arg_num = (int) dsc->arg_num;
for (i = 1; args && (i < arg_num); args = args->next, i++)
if (dsc->args_type[i] && (args->type != dsc->args_type[i]) &&
!f_try_const_promotion(args, dsc->args_type[i]))
break;
*pos = i;
*want = (i < arg_num) ? dsc->args_type[i] : T_NONE;
*got = args ? args->type : T_NONE;
} }
struct f_inst * struct f_inst *
f_dispatch_method(struct symbol *sym, struct f_inst *obj, struct f_inst *args) f_dispatch_method(struct symbol *sym, struct f_inst *obj, struct f_inst *args, int skip)
{ {
/* Note! We should revert args */ /* Find match */
for (const struct f_method *dsc = sym->method; dsc; dsc = dsc->next) for (const struct f_method *dsc = sym->method; dsc; dsc = dsc->next)
if (f_match_signature(dsc, args)) if (f_match_signature(dsc, args))
return dsc->new_inst(obj, args); return dsc->new_inst(obj, args);
cf_error("Cannot dispatch method '%s'", sym->name);
/* No valid match - format error message */
int best_pos = -1; /* Longest argument position with partial match */
int best_got = 0; /* Received type at best partial match position */
int best_count = 0; /* Number of partial matches at best position */
const int best_max = 8; /* Max number of reported types */
int best_want[best_max]; /* Expected types at best position */
for (const struct f_method *dsc = sym->method; dsc; dsc = dsc->next)
{
int pos, want, got;
f_match_signature_err(dsc, args, &pos, &want, &got);
/* Ignore shorter match */
if (pos < best_pos)
continue;
/* Found longer match, reset existing results */
if (pos > best_pos)
{
best_pos = pos;
best_got = got;
best_count = 0;
}
/* Skip duplicates */
for (int i = 0; i < best_count; i++)
if (best_want[i] == want)
goto next;
/* Skip if we have enough types */
if (best_count >= best_max)
continue;
/* Add new expected type */
best_want[best_count] = want;
best_count++;
next:;
}
/* There is at least one method */
ASSERT(best_pos >= 0 && best_count > 0);
/* Update best_pos for printing */
best_pos = best_pos - skip + 1;
if (!best_got)
cf_error("Cannot infer type of argument %d of '%s', please assign it to a variable", best_pos, sym->name);
/* Format list of expected types */
buffer tbuf;
STACK_BUFFER_INIT(tbuf, 128);
for (int i = 0; i < best_count; i++)
buffer_print(&tbuf, " / %s", best_want[i] ? f_type_name(best_want[i]) : "any");
char *types = tbuf.start + 3;
char *dots = (best_count >= best_max) || (tbuf.pos == tbuf.end) ? " / ..." : "";
cf_error("Argument %d of '%s' expected %s%s, got %s",
best_pos, sym->name, types, dots, f_type_name(best_got));
} }
struct f_inst * struct f_inst *
@ -75,7 +150,7 @@ f_dispatch_method_x(const char *name, enum btype t, struct f_inst *obj, struct f
if (!sym) if (!sym)
cf_error("Cannot dispatch method '%s'", name); cf_error("Cannot dispatch method '%s'", name);
return f_dispatch_method(sym, obj, args); return f_dispatch_method(sym, obj, args, 0);
} }
@ -87,7 +162,7 @@ f_for_cycle(struct symbol *var, struct f_inst *term, struct f_inst *block)
/* Static type check */ /* Static type check */
if (term->type == T_VOID) if (term->type == T_VOID)
cf_error("Couldn't infer the type of FOR expression, please assign it to a variable."); cf_error("Cannot infer type of FOR expression, please assign it to a variable");
enum btype el_type = f_type_element_type(term->type); enum btype el_type = f_type_element_type(term->type);
struct sym_scope *scope = el_type ? f_type_method_scope(term->type) : NULL; struct sym_scope *scope = el_type ? f_type_method_scope(term->type) : NULL;
@ -131,7 +206,7 @@ f_implicit_roa_check(struct rtable_config *tab)
return f_new_inst(FI_ROA_CHECK, return f_new_inst(FI_ROA_CHECK,
f_new_inst(FI_RTA_GET, fsa), f_new_inst(FI_RTA_GET, fsa),
f_dispatch_method(ms, path_getter, NULL), ms->method->new_inst(path_getter, NULL),
tab); tab);
} }

View File

@ -124,17 +124,17 @@ attribute lclist test_ca_lclist_max21;
define one = 1; define one = 1;
define ten = 10; define ten = 10;
function int onef(int a) function onef(int a) -> int
{ {
return 1; return 1;
} }
function int twof(int a) function twof(int a) -> int
{ {
return 2; return 2;
} }
function int oneg(int a) function oneg(int a) -> int
{ {
return 1; return 1;
} }
@ -385,7 +385,7 @@ bt_test_suite(t_bytestring, "Testing bytestrings");
* ------------- * -------------
*/ */
function pair 'mkpair-a'(int a) function 'mkpair-a'(int a) -> pair
{ {
return (1, a); return (1, a);
} }
@ -860,7 +860,7 @@ bt_test_suite(t_flowspec, "Testing flowspec routes");
* ------------- * -------------
*/ */
function bgpmask mkpath(int a; int b) function mkpath(int a; int b) -> bgpmask
{ {
return [= a b 3 2 1 =]; return [= a b 3 2 1 =];
} }
@ -1121,6 +1121,12 @@ clist r;
l = filter(l2, [(3,1..4)]); l = filter(l2, [(3,1..4)]);
l2 = filter(l2, [(3,3..6)]); l2 = filter(l2, [(3,3..6)]);
quad q = 2.0.1.0;
clist ql = add(add(add(-empty-, 1.0.0.1), q), 3.1.0.0);
bt_assert(delete(ql, 1.0.0.1) = add(add(-empty-, 2.0.1.0), 3.1.0.0));
bt_assert(delete(ql, [2.0.0.0 .. 4.0.0.0]) = add(-empty-, 1.0.0.1));
bt_assert(filter(ql, [3.0.0.0 .. 4.0.0.0]) = add(-empty-, 3.1.0.0));
# clist A (10,20,30) # clist A (10,20,30)
bt_assert(l = add(add(add(add(-empty-, (3,1)), (3,2)), (3,3)), (3,4))); bt_assert(l = add(add(add(add(-empty-, (3,1)), (3,2)), (3,3)), (3,4)));
bt_assert(format(l) = "(clist (3,1) (3,2) (3,3) (3,4))"); bt_assert(format(l) = "(clist (3,1) (3,2) (3,3) (3,4))");
@ -1243,6 +1249,12 @@ function t_clist_new()
l = l2.filter([(3,1..4)]); l = l2.filter([(3,1..4)]);
l2.filter([(3,3..6)]); l2.filter([(3,3..6)]);
quad q = 2.0.1.0;
clist ql = -empty-.add(1.0.0.1).add(q).add(3.1.0.0);
bt_assert(delete(ql, 1.0.0.1) = -empty-.add(2.0.1.0).add(3.1.0.0));
bt_assert(delete(ql, [2.0.0.0 .. 4.0.0.0]) = -empty-.add(1.0.0.1));
bt_assert(filter(ql, [3.0.0.0 .. 4.0.0.0]) = -empty-.add(3.1.0.0));
# clist A (10,20,30) # clist A (10,20,30)
bt_assert(l = -empty-.add((3,1)).add((3,2)).add((3,3)).add((3,4))); bt_assert(l = -empty-.add((3,1)).add((3,2)).add((3,3)).add((3,4)));
bt_assert(format(l) = "(clist (3,1) (3,2) (3,3) (3,4))"); bt_assert(format(l) = "(clist (3,1) (3,2) (3,3) (3,4))");
@ -1543,7 +1555,7 @@ bt_test_suite(t_ec_set, "Testing sets of extended communities");
* ------------------------- * -------------------------
*/ */
function lc mktrip(int a) function mktrip(int a) -> lc
{ {
return (a, 2*a, 3*a); return (a, 2*a, 3*a);
} }
@ -1858,7 +1870,7 @@ bt_test_suite(t_define, "Testing defined() function");
* ------------------------- * -------------------------
*/ */
function int callme(int arg1; int arg2) function callme(int arg1; int arg2) -> int
int i; int i;
{ {
case arg1 { case arg1 {
@ -1869,12 +1881,12 @@ int i;
return 0; return 0;
} }
function int callmeagain(int a; int b; int c) function callmeagain(int a; int b; int c) -> int
{ {
return a + b + c; return a + b + c;
} }
function int fifteen() function fifteen() -> int
{ {
return 15; return 15;
} }
@ -1907,28 +1919,28 @@ function local_vars(int j)
bt_assert(j = 35 && k = 20 && m = 100); bt_assert(j = 35 && k = 20 && m = 100);
} }
function int factorial(int x) function factorial(int x) -> int
{ {
if x = 0 then return 0; if x = 0 then return 0;
if x = 1 then return 1; if x = 1 then return 1;
else return x * factorial(x - 1); else return x * factorial(x - 1);
} }
function int fibonacci(int x) function fibonacci(int x) -> int
{ {
if x = 0 then return 0; if x = 0 then return 0;
if x = 1 then return 1; if x = 1 then return 1;
else return fibonacci(x - 1) + fibonacci(x - 2); else return fibonacci(x - 1) + fibonacci(x - 2);
} }
function bgppath hanoi_init(int a; int b) function hanoi_init(int a; int b) -> bgppath
{ {
if b = 0 if b = 0
then return +empty+; then return +empty+;
else return prepend(hanoi_init(a + 1, b - 1), a); else return prepend(hanoi_init(a + 1, b - 1), a);
} }
function bgppath hanoi_solve(int n; bgppath h_src; bgppath h_dst; bgppath h_aux; bool x; bool y) function hanoi_solve(int n; bgppath h_src; bgppath h_dst; bgppath h_aux; bool x; bool y) -> bgppath
{ {
# x -> return src or dst # x -> return src or dst
# y -> print state # y -> print state

View File

@ -49,6 +49,7 @@ union bval_long {
enum btype { enum btype {
/* Nothing. Simply nothing. */ /* Nothing. Simply nothing. */
T_VOID = 0, T_VOID = 0,
T_NONE = 0xff,
/* Something but inaccessible. */ /* Something but inaccessible. */
T_OPAQUE = 0x02, /* Opaque byte string (not filterable) */ T_OPAQUE = 0x02, /* Opaque byte string (not filterable) */