0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-09-19 20:05:21 +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 AND;
\|\| return OR;
\-\> return IMP;
\[\= return PO;
\=\] return PC;

View File

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

View File

@ -551,7 +551,7 @@ include "tablename.conf";;
Define a filter. You can learn more about filters in the following
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.
<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
block of code conditional.
<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
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:
<p>BIRD supports functions, so that you don not have to repeat the same blocks
of code over and over. Functions can have zero or more parameters and they can
have local variables. If the function returns value, then you should always
specify its return type. Direct recursion is possible. Function definitions look
like this:
<code>
function int name ()
function name() -> int
{
int local_variable;
int another_variable = 5;
return 42;
}
function pair with_parameters (int parameter)
function with_parameters(int parameter) -> pair
{
print parameter;
return (1, 2);

View File

@ -374,12 +374,12 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%nonassoc ELSE
%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 <f> filter where_filter
%type <fl> filter_body function_body
%type <flv> lvalue
%type <i> type maybe_type function_vars
%type <i> type function_vars function_type
%type <fa> function_argsn function_args
%type <ecs> ec_kind
%type <fret> break_command
@ -527,6 +527,11 @@ function_vars:
}
;
function_type:
/* EMPTY */ { $$ = T_VOID; }
| IMP type { $$ = $2; }
;
filter_body: function_body ;
filter:
@ -566,29 +571,24 @@ function_body:
;
conf: function_def ;
maybe_type:
/* EMPTY */ { $$ = T_VOID; }
| type { $$ = $1; }
;
function_def:
FUNCTION maybe_type symbol {
DBG( "Beginning of function %s\n", $3->name );
this_function = cf_define_symbol(new_config, $3, SYM_FUNCTION, function, NULL);
/* if ($2 == T_VOID) cf_warn("Support for functions without explicit return type will be removed soon" ); */
FUNCTION symbol {
DBG( "Beginning of function %s\n", $2->name );
this_function = cf_define_symbol(new_config, $2, SYM_FUNCTION, function, NULL);
cf_enter_filters();
cf_push_scope(new_config, this_function);
} function_args {
} function_args function_type {
/* Make dummy f_line for storing function prototype */
struct f_line *dummy = cfg_allocz(sizeof(struct f_line));
this_function->function = dummy;
dummy->return_type = $2;
dummy->return_type = $5;
/* Revert the args */
while ($5) {
struct f_arg *tmp = $5;
$5 = $5->next;
while ($4) {
struct f_arg *tmp = $4;
$4 = $4->next;
tmp->next = dummy->arg_list;
dummy->arg_list = tmp;
@ -598,7 +598,7 @@ function_def:
$7->args = this_function->function->args;
$7->arg_list = this_function->function->arg_list;
$7->return_type = this_function->function->return_type;
$3->function = $7;
$2->function = $7;
cf_pop_scope(new_config);
cf_exit_filters();
}
@ -828,10 +828,27 @@ constructor:
;
/* This generates the function_call variable list backwards. */
var_list: /* EMPTY */ { $$ = NULL; }
/* This generates the function_call variable list backwards */
var_list_r:
/* EMPTY */ { $$ = NULL; }
| 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:
CF_SYM_KNOWN '(' var_list ')'
@ -839,17 +856,7 @@ function_call:
if ($1->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really.");
/* Revert the var_list */
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);
$$ = f_new_inst(FI_CALL, $3, $1);
}
;
@ -885,12 +892,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 {
$$ = f_dispatch_method($1, FM.object, NULL);
$$ = f_dispatch_method($1, FM.object, NULL, 1);
}
| CF_SYM_METHOD_ARGS {
f_method_call_args();
} '(' 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_ECLIST)); }
| '-' '-' '-' 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 ')' {
$$ = f_dispatch_method_x("add", $3->type, $3, $5);
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);
$$ = f_new_inst(FI_EA_UNSET, $3->attribute);
}
| break_command var_list ';' {
| break_command var_list_r ';' {
$$ = f_print($2, !!$2, $1);
}
| PRINT var_list ';' {
| PRINT var_list_r ';' {
$$ = f_print($2, 1, F_NOP);
}
| PRINTN var_list ';' {
| PRINTN var_list_r ';' {
$$ = f_print($2, 0, F_NOP);
}
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }

View File

@ -27,6 +27,8 @@
static const char * const f_type_str[] = {
[T_VOID] = "void",
[T_NONE] = "none",
[T_OPAQUE] = "opaque byte string",
[T_IFACE] = "interface",
@ -62,10 +64,12 @@ static const char * const f_type_str[] = {
[T_PREFIX_SET] = "prefix set",
};
STATIC_ASSERT((1 << (8 * sizeof(btype))) == ARRAY_SIZE(f_type_str));
const char *
f_type_name(btype t)
{
return (t < ARRAY_SIZE(f_type_str)) ? (f_type_str[t] ?: "?") : "?";
return f_type_str[t] ?: "?";
}
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 */
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) ||
(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
case 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
#undef whati
break;
@ -573,8 +574,8 @@ fi_constant(struct f_inst *what, struct f_val val)
return what;
}
static int
f_const_promotion(struct f_inst *arg, btype want)
int
f_const_promotion_(struct f_inst *arg, btype want, int update)
{
if (arg->fi_code != FI_CONSTANT)
return 0;
@ -582,6 +583,7 @@ f_const_promotion(struct f_inst *arg, btype want)
struct f_val *c = &arg->i_FI_CONSTANT.val;
if ((c->type == T_IP) && ipa_is_ip4(c->val.ip) && (want == T_QUAD)) {
if (update)
*c = (struct f_val) {
.type = T_QUAD,
.val.i = ipa_to_u32(c->val.ip),
@ -590,6 +592,7 @@ f_const_promotion(struct f_inst *arg, btype want)
}
else if ((c->type == T_SET) && (!c->val.t) && (want == T_PREFIX_SET)) {
if (update)
*c = f_const_empty_prefix_set;
return 1;
}

View File

@ -500,7 +500,7 @@
INST(FI_DEFINED, 1, 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);
@ -1101,8 +1101,6 @@
NEVER_CONSTANT;
VARARG;
SYMBOL;
/* Fake result type declaration */
RESULT_TYPE(sym->function->return_type);
FID_NEW_BODY()
@ -1222,22 +1220,39 @@
}
/* Community list add */
INST(FI_CLIST_ADD, 2, 1) {
INST(FI_CLIST_ADD_PAIR, 2, 1) {
ARG(1, T_CLIST);
ARG_ANY(2);
ARG(2, T_PAIR);
METHOD_CONSTRUCTOR("add");
struct f_val dummy;
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 */
else if (val_is_ip4(&v2))
}
INST(FI_CLIST_ADD_IP, 2, 1) {
ARG(1, T_CLIST);
ARG(2, T_IP);
METHOD_CONSTRUCTOR("add");
FID_NEW_BODY();
/* IP->Quad implicit conversion, must be before FI_CLIST_ADD_QUAD */
cf_warn("Method add(clist, ip) is deprecated, please use add(clist, quad)");
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)) ]]);
else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy))
runtime("Can't add set");
else if (v2.type == T_CLIST)
}
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) ]]);
else
runtime("Can't add non-pair");
}
INST(FI_ECLIST_ADD_EC, 2, 1) {
@ -1287,22 +1302,50 @@
}
/* Community list delete */
INST(FI_CLIST_DELETE, 2, 1) {
INST(FI_CLIST_DELETE_PAIR, 2, 1) {
ARG(1, T_CLIST);
ARG_ANY(2);
ARG(2, T_PAIR);
METHOD_CONSTRUCTOR("delete");
/* Community (or cluster) list */
struct f_val dummy;
if ((v2.type == T_PAIR) || (v2.type == T_QUAD))
RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]);
/* IP->Quad implicit conversion */
else if (val_is_ip4(&v2))
}
INST(FI_CLIST_DELETE_IP, 2, 1) {
ARG(1, T_CLIST);
ARG(2, T_IP);
METHOD_CONSTRUCTOR("delete");
FID_NEW_BODY();
/* IP->Quad implicit conversion, must be before FI_CLIST_DELETE_QUAD */
cf_warn("Method delete(clist, ip) is deprecated, please use delete(clist, quad)");
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)) ]]);
else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST))
}
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) ]]);
else
runtime("Can't delete non-pair");
}
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)
{ 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 symbol *arg;
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 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_for_cycle(struct symbol *var, struct f_inst *term, struct f_inst *block);
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
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++)
if (dsc->args_type[i] && (args->type != dsc->args_type[i]))
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]))
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 *
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)
if (f_match_signature(dsc, 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 *
@ -75,7 +150,7 @@ f_dispatch_method_x(const char *name, enum btype t, struct f_inst *obj, struct f
if (!sym)
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 */
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);
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,
f_new_inst(FI_RTA_GET, fsa),
f_dispatch_method(ms, path_getter, NULL),
ms->method->new_inst(path_getter, NULL),
tab);
}

View File

@ -124,17 +124,17 @@ attribute lclist test_ca_lclist_max21;
define one = 1;
define ten = 10;
function int onef(int a)
function onef(int a) -> int
{
return 1;
}
function int twof(int a)
function twof(int a) -> int
{
return 2;
}
function int oneg(int a)
function oneg(int a) -> int
{
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);
}
@ -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 =];
}
@ -1121,6 +1121,12 @@ clist r;
l = filter(l2, [(3,1..4)]);
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)
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))");
@ -1243,6 +1249,12 @@ function t_clist_new()
l = l2.filter([(3,1..4)]);
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)
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))");
@ -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);
}
@ -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;
{
case arg1 {
@ -1869,12 +1881,12 @@ int i;
return 0;
}
function int callmeagain(int a; int b; int c)
function callmeagain(int a; int b; int c) -> int
{
return a + b + c;
}
function int fifteen()
function fifteen() -> int
{
return 15;
}
@ -1907,28 +1919,28 @@ function local_vars(int j)
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 = 1 then return 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 = 1 then return 1;
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
then return +empty+;
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
# y -> print state

View File

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