mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-11-08 20:28:43 +00:00
Filter: functions can and should have typed return values
This commit is contained in:
parent
27ac8cb2ca
commit
c836bd388d
@ -539,7 +539,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/name/ (<m/parameters/) <m/local variables/ { <m/commands/ }</tag>
|
<tag><label id="opt-function">function <m/type/ <m/name/ (<m/parameters/) <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>
|
||||||
@ -1298,18 +1298,22 @@ 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't have to repeat the same blocks of
|
||||||
code over and over. Functions can have zero or more parameters and they can have
|
code over and over. Functions can have zero or more parameters and they can have
|
||||||
local variables. Recursion is not allowed. Function definitions look like this:
|
local variables. You should always specify the function return type and always
|
||||||
|
return it. No-return functions and multiple-type returning functions are deprecated.
|
||||||
|
Direct recursion is possible. Function definitions look like this:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
function name ()
|
function int name ()
|
||||||
{
|
{
|
||||||
int local_variable;
|
int local_variable;
|
||||||
int another_variable = 5;
|
int another_variable = 5;
|
||||||
|
return 42;
|
||||||
}
|
}
|
||||||
|
|
||||||
function with_parameters (int parameter)
|
function pair with_parameters (int parameter)
|
||||||
{
|
{
|
||||||
print parameter;
|
print parameter;
|
||||||
|
return (1, 2);
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
@ -1323,7 +1327,7 @@ may return values using the <cf>return <m/[expr]/</cf> command. Returning a
|
|||||||
value exits from current function (this is similar to C).
|
value exits from current function (this is similar to C).
|
||||||
|
|
||||||
<p>Filters are defined in a way similar to functions except they cannot have
|
<p>Filters are defined in a way similar to functions except they cannot have
|
||||||
explicit parameters. They get a route table entry as an implicit parameter, it
|
explicit parameters and cannot return. They get a route table entry as an implicit parameter, it
|
||||||
is also passed automatically to any functions called. The filter must terminate
|
is also passed automatically to any functions called. The filter must terminate
|
||||||
with either <cf/accept/ or <cf/reject/ statement. If there is a runtime error in
|
with either <cf/accept/ or <cf/reject/ statement. If there is a runtime error in
|
||||||
filter, the route is rejected.
|
filter, the route is rejected.
|
||||||
|
@ -19,6 +19,8 @@ static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; }
|
|||||||
static inline u32 pair_a(u32 p) { return p >> 16; }
|
static inline u32 pair_a(u32 p) { return p >> 16; }
|
||||||
static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
|
static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
|
||||||
|
|
||||||
|
static struct symbol *this_function;
|
||||||
|
|
||||||
static struct f_method_scope {
|
static struct f_method_scope {
|
||||||
struct f_inst *object;
|
struct f_inst *object;
|
||||||
struct sym_scope scope;
|
struct sym_scope scope;
|
||||||
@ -372,7 +374,7 @@ CF_METHODS(IS_V4, TYPE, IP, RD, LEN, MAXLEN, ASN, SRC, DST, MASK,
|
|||||||
%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 function_vars
|
%type <i> type maybe_type function_vars
|
||||||
%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
|
||||||
@ -388,8 +390,11 @@ CF_GRAMMAR
|
|||||||
|
|
||||||
conf: filter_def ;
|
conf: filter_def ;
|
||||||
filter_def:
|
filter_def:
|
||||||
FILTER symbol { $2 = cf_define_symbol(new_config, $2, SYM_FILTER, filter, NULL); cf_push_scope( new_config, $2 ); }
|
FILTER symbol {
|
||||||
filter_body {
|
$2 = cf_define_symbol(new_config, $2, SYM_FILTER, filter, NULL);
|
||||||
|
cf_push_scope( new_config, $2 );
|
||||||
|
this_function = NULL;
|
||||||
|
} filter_body {
|
||||||
struct filter *f = cfg_alloc(sizeof(struct filter));
|
struct filter *f = cfg_alloc(sizeof(struct filter));
|
||||||
*f = (struct filter) { .sym = $2, .root = $4 };
|
*f = (struct filter) { .sym = $2, .root = $4 };
|
||||||
$2->filter = f;
|
$2->filter = f;
|
||||||
@ -508,7 +513,10 @@ filter:
|
|||||||
cf_assert_symbol($1, SYM_FILTER);
|
cf_assert_symbol($1, SYM_FILTER);
|
||||||
$$ = $1->filter;
|
$$ = $1->filter;
|
||||||
}
|
}
|
||||||
| { cf_push_scope(new_config, NULL); } filter_body {
|
| {
|
||||||
|
cf_push_scope(new_config, NULL);
|
||||||
|
this_function = NULL;
|
||||||
|
} filter_body {
|
||||||
struct filter *f = cfg_alloc(sizeof(struct filter));
|
struct filter *f = cfg_alloc(sizeof(struct filter));
|
||||||
*f = (struct filter) { .root = $2 };
|
*f = (struct filter) { .root = $2 };
|
||||||
$$ = f;
|
$$ = f;
|
||||||
@ -532,29 +540,38 @@ function_body:
|
|||||||
;
|
;
|
||||||
|
|
||||||
conf: function_def ;
|
conf: function_def ;
|
||||||
|
maybe_type:
|
||||||
|
/* EMPTY */ { $$ = T_VOID; }
|
||||||
|
| type { $$ = $1; }
|
||||||
|
;
|
||||||
|
|
||||||
function_def:
|
function_def:
|
||||||
FUNCTION symbol {
|
FUNCTION maybe_type symbol {
|
||||||
DBG( "Beginning of function %s\n", $2->name );
|
DBG( "Beginning of function %s\n", $3->name );
|
||||||
$2 = cf_define_symbol(new_config, $2, SYM_FUNCTION, function, NULL);
|
this_function = cf_define_symbol(new_config, $3, SYM_FUNCTION, function, NULL);
|
||||||
cf_push_scope(new_config, $2);
|
/* if ($2 == T_VOID) log(L_WARN "Support for functions without explicit return type will be removed soon" ); */
|
||||||
|
cf_push_scope(new_config, this_function);
|
||||||
} function_args {
|
} function_args {
|
||||||
/* 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));
|
||||||
$2->function = dummy;
|
this_function->function = dummy;
|
||||||
|
|
||||||
|
dummy->return_type = $2;
|
||||||
|
|
||||||
/* Revert the args */
|
/* Revert the args */
|
||||||
while ($4) {
|
while ($5) {
|
||||||
struct f_arg *tmp = $4;
|
struct f_arg *tmp = $5;
|
||||||
$4 = $4->next;
|
$5 = $5->next;
|
||||||
|
|
||||||
tmp->next = dummy->arg_list;
|
tmp->next = dummy->arg_list;
|
||||||
dummy->arg_list = tmp;
|
dummy->arg_list = tmp;
|
||||||
dummy->args++;
|
dummy->args++;
|
||||||
}
|
}
|
||||||
} function_body {
|
} function_body {
|
||||||
$6->args = $2->function->args;
|
$7->args = this_function->function->args;
|
||||||
$6->arg_list = $2->function->arg_list;
|
$7->arg_list = this_function->function->arg_list;
|
||||||
$2->function = $6;
|
$7->return_type = this_function->function->return_type;
|
||||||
|
$3->function = $7;
|
||||||
cf_pop_scope(new_config);
|
cf_pop_scope(new_config);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -989,6 +1006,18 @@ cmd:
|
|||||||
}
|
}
|
||||||
| RETURN term ';' {
|
| RETURN term ';' {
|
||||||
DBG( "Ook, we'll return the value\n" );
|
DBG( "Ook, we'll return the value\n" );
|
||||||
|
if (!this_function)
|
||||||
|
cf_error("Can't return from a non-function, use accept or reject instead.");
|
||||||
|
if (this_function->function->return_type == T_VOID)
|
||||||
|
{
|
||||||
|
if ($2->type != T_VOID)
|
||||||
|
log(L_WARN "Inferring function %s return type from its return value: %s", this_function->name, f_type_name($2->type));
|
||||||
|
((struct f_line *) this_function->function)->return_type = $2->type;
|
||||||
|
}
|
||||||
|
else if (this_function->function->return_type != $2->type)
|
||||||
|
cf_error("Can't return type %s from function %s, expected %s",
|
||||||
|
f_type_name($2->type), this_function->name, f_type_name(this_function->function->return_type));
|
||||||
|
|
||||||
$$ = f_new_inst(FI_RETURN, $2);
|
$$ = f_new_inst(FI_RETURN, $2);
|
||||||
}
|
}
|
||||||
| dynamic_attr '=' term ';' {
|
| dynamic_attr '=' term ';' {
|
||||||
|
@ -1230,7 +1230,7 @@
|
|||||||
SYMBOL;
|
SYMBOL;
|
||||||
|
|
||||||
/* Fake result type declaration */
|
/* Fake result type declaration */
|
||||||
RESULT_TYPE(T_VOID);
|
RESULT_TYPE(sym->function->return_type);
|
||||||
|
|
||||||
FID_NEW_BODY()
|
FID_NEW_BODY()
|
||||||
ASSERT(sym->class == SYM_FUNCTION);
|
ASSERT(sym->class == SYM_FUNCTION);
|
||||||
|
@ -47,6 +47,7 @@ struct f_line {
|
|||||||
u8 args; /* Function: Args required */
|
u8 args; /* Function: Args required */
|
||||||
u8 vars;
|
u8 vars;
|
||||||
u8 results; /* Results left on stack: cmd -> 0, term -> 1 */
|
u8 results; /* Results left on stack: cmd -> 0, term -> 1 */
|
||||||
|
u8 return_type; /* Type which the function returns */
|
||||||
struct f_arg *arg_list;
|
struct f_arg *arg_list;
|
||||||
struct f_line_item items[0]; /* The items themselves */
|
struct f_line_item items[0]; /* The items themselves */
|
||||||
};
|
};
|
||||||
|
@ -21,17 +21,17 @@ attribute lclist mylclist;
|
|||||||
define one = 1;
|
define one = 1;
|
||||||
define ten = 10;
|
define ten = 10;
|
||||||
|
|
||||||
function onef(int a)
|
function int onef(int a)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function twof(int a)
|
function int twof(int a)
|
||||||
{
|
{
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
function oneg(int a)
|
function int oneg(int a)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -221,7 +221,7 @@ bt_test_suite(t_string, "Testing string matching");
|
|||||||
* -------------
|
* -------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function 'mkpair-a'(int a)
|
function pair 'mkpair-a'(int a)
|
||||||
{
|
{
|
||||||
return (1, a);
|
return (1, a);
|
||||||
}
|
}
|
||||||
@ -696,7 +696,7 @@ bt_test_suite(t_flowspec, "Testing flowspec routes");
|
|||||||
* -------------
|
* -------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function mkpath(int a; int b)
|
function bgpmask mkpath(int a; int b)
|
||||||
{
|
{
|
||||||
return [= a b 3 2 1 =];
|
return [= a b 3 2 1 =];
|
||||||
}
|
}
|
||||||
@ -1080,7 +1080,7 @@ bt_test_suite(t_ec_set, "Testing sets of extended communities");
|
|||||||
* -------------------------
|
* -------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function mktrip(int a)
|
function lc mktrip(int a)
|
||||||
{
|
{
|
||||||
return (a, 2*a, 3*a);
|
return (a, 2*a, 3*a);
|
||||||
}
|
}
|
||||||
@ -1310,7 +1310,7 @@ bt_test_suite(t_define, "Testing defined() function");
|
|||||||
* -------------------------
|
* -------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function callme(int arg1; int arg2)
|
function int callme(int arg1; int arg2)
|
||||||
int i;
|
int i;
|
||||||
{
|
{
|
||||||
case arg1 {
|
case arg1 {
|
||||||
@ -1321,12 +1321,12 @@ int i;
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function callmeagain(int a; int b; int c)
|
function int callmeagain(int a; int b; int c)
|
||||||
{
|
{
|
||||||
return a + b + c;
|
return a + b + c;
|
||||||
}
|
}
|
||||||
|
|
||||||
function fifteen()
|
function int fifteen()
|
||||||
{
|
{
|
||||||
return 15;
|
return 15;
|
||||||
}
|
}
|
||||||
@ -1359,28 +1359,28 @@ function local_vars(int j)
|
|||||||
bt_assert(j = 35 && k = 20 && m = 100);
|
bt_assert(j = 35 && k = 20 && m = 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
function factorial(int x)
|
function int factorial(int x)
|
||||||
{
|
{
|
||||||
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 fibonacci(int x)
|
function int fibonacci(int x)
|
||||||
{
|
{
|
||||||
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 hanoi_init(int a; int b)
|
function bgppath hanoi_init(int a; int b)
|
||||||
{
|
{
|
||||||
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 hanoi_solve(int n; bgppath h_src; bgppath h_dst; bgppath h_aux; bool x; bool y)
|
function bgppath hanoi_solve(int n; bgppath h_src; bgppath h_dst; bgppath h_aux; bool x; bool y)
|
||||||
{
|
{
|
||||||
# x -> return src or dst
|
# x -> return src or dst
|
||||||
# y -> print state
|
# y -> print state
|
||||||
|
Loading…
Reference in New Issue
Block a user