summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--conf/cf-lex.l23
-rw-r--r--conf/conf.h13
-rw-r--r--conf/confbase.Y2
-rw-r--r--conf/gen_keywords.m47
-rw-r--r--doc/bird.sgml38
-rw-r--r--filter/config.Y121
-rw-r--r--filter/data.h8
-rw-r--r--filter/decl.m496
-rw-r--r--filter/f-inst.c411
-rw-r--r--filter/test.conf400
10 files changed, 824 insertions, 295 deletions
diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index 95c44181..0357f9ac 100644
--- a/conf/cf-lex.l
+++ b/conf/cf-lex.l
@@ -55,7 +55,6 @@
struct keyword {
byte *name;
int value;
- enum keyword_scope scope;
};
#include "conf/keywords.h"
@@ -81,7 +80,8 @@ static uint cf_hash(const byte *c);
HASH_DEFINE_REHASH_FN(SYM, struct symbol)
struct sym_scope *global_root_scope;
-static pool *global_root_scope_pool;
+pool *global_root_scope_pool;
+linpool *global_root_scope_linpool;
linpool *cfg_mem;
@@ -614,7 +614,7 @@ check_eof(void)
static inline void cf_swap_soft_scope(struct config *conf);
-static struct symbol *
+struct symbol *
cf_new_symbol(struct sym_scope *scope, pool *p, struct linpool *lp, const byte *c)
{
if (scope->readonly)
@@ -743,6 +743,8 @@ cf_lex_symbol(const char *data)
cf_lval.i = -val;
return ENUM;
}
+ case SYM_METHOD:
+ return sym->method->arg_num ? CF_SYM_METHOD_ARGS : CF_SYM_METHOD_BARE;
case SYM_VOID:
return CF_SYM_UNDEFINED;
default:
@@ -750,6 +752,8 @@ cf_lex_symbol(const char *data)
}
}
+void f_type_methods_register(void);
+
/**
* cf_lex_init - initialize the lexer
* @is_cli: true if we're going to parse CLI command, false for configuration
@@ -764,18 +768,19 @@ cf_lex_init(int is_cli, struct config *c)
if (!global_root_scope_pool)
{
global_root_scope_pool = rp_new(&root_pool, "Keywords pool");
- linpool *kwlp = lp_new(global_root_scope_pool);
- global_root_scope = lp_allocz(kwlp, sizeof(*global_root_scope) * CFK__MAX);
+ global_root_scope_linpool = lp_new(global_root_scope_pool);
+ global_root_scope = lp_allocz(global_root_scope_linpool, sizeof(*global_root_scope));
for (const struct keyword *k = keyword_list; k->name; k++)
{
- struct symbol *sym = cf_new_symbol(&global_root_scope[k->scope], global_root_scope_pool, kwlp, k->name);
+ struct symbol *sym = cf_new_symbol(global_root_scope, global_root_scope_pool, global_root_scope_linpool, k->name);
sym->class = SYM_KEYWORD;
sym->keyword = k;
}
- for (int s = 0; s < CFK__MAX; s++)
- global_root_scope[s].readonly = 1;
+ global_root_scope->readonly = 1;
+
+ f_type_methods_register();
}
ifs_head = ifs = push_ifs(NULL);
@@ -799,7 +804,7 @@ cf_lex_init(int is_cli, struct config *c)
if (is_cli)
c->current_scope->next = config->root_scope;
else
- c->current_scope->next = &global_root_scope[CFK_KEYWORDS];
+ c->current_scope->next = global_root_scope;
}
/**
diff --git a/conf/conf.h b/conf/conf.h
index b7c97ce5..1413cb58 100644
--- a/conf/conf.h
+++ b/conf/conf.h
@@ -126,6 +126,7 @@ struct symbol {
struct f_val *val; /* For SYM_CONSTANT */
uint offset; /* For SYM_VARIABLE */
const struct keyword *keyword; /* For SYM_KEYWORD */
+ const struct f_method *method; /* For SYM_METHOD */
};
char name[0];
@@ -144,13 +145,9 @@ struct sym_scope {
byte readonly:1; /* Do not add new symbols */
};
-enum keyword_scope {
- CFK_KEYWORDS,
- CFK_METHODS,
- CFK__MAX
-};
-
extern struct sym_scope *global_root_scope;
+extern pool *global_root_scope_pool;
+extern linpool *global_root_scope_linpool;
struct bytestring {
size_t length;
@@ -168,6 +165,7 @@ struct bytestring {
#define SYM_TABLE 5
#define SYM_ATTRIBUTE 6
#define SYM_KEYWORD 7
+#define SYM_METHOD 8
#define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */
#define SYM_VARIABLE_RANGE SYM_VARIABLE ... (SYM_VARIABLE | 0xff)
@@ -215,6 +213,9 @@ struct symbol *cf_localize_symbol(struct config *conf, struct symbol *sym);
static inline int cf_symbol_is_local(struct config *conf, struct symbol *sym)
{ return (sym->scope == conf->current_scope) && !conf->current_scope->soft_scopes; }
+/* internal */
+struct symbol *cf_new_symbol(struct sym_scope *scope, pool *p, struct linpool *lp, const byte *c);
+
/**
* cf_define_symbol - define meaning of a symbol
* @sym: symbol to be defined
diff --git a/conf/confbase.Y b/conf/confbase.Y
index e10666f8..c62731b5 100644
--- a/conf/confbase.Y
+++ b/conf/confbase.Y
@@ -104,7 +104,7 @@ CF_DECLS
%token <ip4> IP4
%token <ip6> IP6
%token <i64> VPN_RD
-%token <s> CF_SYM_KNOWN CF_SYM_UNDEFINED
+%token <s> CF_SYM_KNOWN CF_SYM_UNDEFINED CF_SYM_METHOD_BARE CF_SYM_METHOD_ARGS
%token <t> TEXT
%token <bs> BYTETEXT
%type <iface> ipa_scope
diff --git a/conf/gen_keywords.m4 b/conf/gen_keywords.m4
index 06a38ffd..4e8651f6 100644
--- a/conf/gen_keywords.m4
+++ b/conf/gen_keywords.m4
@@ -23,11 +23,10 @@ m4_define(CF_DECLS, `m4_divert(-1)')
m4_define(CF_DEFINES, `m4_divert(-1)')
# Keywords are translated to C initializers
-m4_define(CF_handle_kw, `m4_divert(1){ "m4_translit($1,[[A-Z]],[[a-z]])", $1, CF_keywd_target },
+m4_define(CF_handle_kw, `m4_divert(1){ "m4_translit($1,[[A-Z]],[[a-z]])", $1 },
m4_divert(-1)')
-m4_define(CF_keywd, `m4_ifdef([[CF_tok_]]CF_keywd_target[[_$1]],,[[m4_define([[CF_tok_]]CF_keywd_target[[_$1]],1)CF_handle_kw($1)]])')
-m4_define(CF_KEYWORDS, `m4_define([[CF_keywd_target]],CFK_KEYWORDS)CF_iterate([[CF_keywd]], [[$@]])DNL')
-m4_define(CF_METHODS, `m4_define([[CF_keywd_target]],CFK_METHODS)CF_iterate([[CF_keywd]], [[$@]])DNL')
+m4_define(CF_keywd, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)CF_handle_kw($1)]])')
+m4_define(CF_KEYWORDS, `CF_iterate([[CF_keywd]], [[$@]])DNL')
m4_define(CF_KEYWORDS_EXCLUSIVE, `CF_KEYWORDS($@)')
# CLI commands generate keywords as well
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 46e34c04..3fcf0025 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -1592,24 +1592,24 @@ in the foot).
<cf><m/P/.len</cf> returns the length of path <m/P/.
- <cf><m/P/.empty</cf> makes the path <m/P/ empty.
+ <cf><m/P/.empty</cf> makes the path <m/P/ empty. Can't be used as a value, always modifies the object.
- <cf>prepend(<m/P/,<m/A/)</cf> prepends ASN <m/A/ to path <m/P/ and
+ <cf><m/P/.prepend(<m/A/)</cf> prepends ASN <m/A/ to path <m/P/ and
returns the result.
- <cf>delete(<m/P/,<m/A/)</cf> deletes all instances of ASN <m/A/ from
+ <cf><m/P/.delete(<m/A/)</cf> deletes all instances of ASN <m/A/ from
from path <m/P/ and returns the result. <m/A/ may also be an integer
set, in that case the operator deletes all ASNs from path <m/P/ that are
also members of set <m/A/.
- <cf>filter(<m/P/,<m/A/)</cf> deletes all ASNs from path <m/P/ that are
- not members of integer set <m/A/. I.e., <cf/filter/ do the same as
- <cf/delete/ with inverted set <m/A/.
+ <cf><m/P/.filter(<m/A/)</cf> deletes all ASNs from path <m/P/ that are
+ not members of integer set <m/A/, and returns the result.
+ I.e., <cf/filter/ do the same as <cf/delete/ with inverted set <m/A/.
- Statement <cf><m/P/ = prepend(<m/P/, <m/A/);</cf> can be shortened to
- <cf><m/P/.prepend(<m/A/);</cf> if <m/P/ is appropriate route attribute
- (for example <cf/bgp_path/) or a local variable.
- Similarly for <cf/delete/ and <cf/filter/.
+ Methods <cf>prepend</cf>, <cf>delete</cf> and <cf>filter</cf> keep the
+ original object intact as long as you use the result in any way. You can
+ also write e.g. <cf><m/P/.prepend(<m/A/);</cf> as a standalone statement.
+ This variant does modify the original object with the result of the operation.
<tag><label id="type-bgpmask">bgpmask</tag>
BGP masks are patterns used for BGP path matching (using <cf>path
@@ -1637,29 +1637,29 @@ in the foot).
<cf><m/C/.len</cf> returns the length of clist <m/C/.
- <cf><m/C/.empty</cf> makes the list <m/C/ empty.
+ <cf><m/C/.empty</cf> makes the list <m/C/ empty. Can't be used as a value, always modifies the object.
- <cf>add(<m/C/,<m/P/)</cf> adds pair (or quad) <m/P/ to clist <m/C/ and
+ <cf><m/C/.add(<m/P/)</cf> adds pair (or quad) <m/P/ to clist <m/C/ and
returns the result. If item <m/P/ is already in clist <m/C/, it does
nothing. <m/P/ may also be a clist, in that case all its members are
added; i.e., it works as clist union.
- <cf>delete(<m/C/,<m/P/)</cf> deletes pair (or quad) <m/P/ from clist
+ <cf><m/C/.delete(<m/P/)</cf> deletes pair (or quad) <m/P/ from clist
<m/C/ and returns the result. If clist <m/C/ does not contain item
<m/P/, it does nothing. <m/P/ may also be a pair (or quad) set, in that
case the operator deletes all items from clist <m/C/ that are also
members of set <m/P/. Moreover, <m/P/ may also be a clist, which works
analogously; i.e., it works as clist difference.
- <cf>filter(<m/C/,<m/P/)</cf> deletes all items from clist <m/C/ that are
- not members of pair (or quad) set <m/P/. I.e., <cf/filter/ do the same
+ <cf><m/C/.filter(<m/P/)</cf> deletes all items from clist <m/C/ that are
+ not members of pair (or quad) set <m/P/, and returns the result. I.e., <cf/filter/ do the same
as <cf/delete/ with inverted set <m/P/. <m/P/ may also be a clist, which
works analogously; i.e., it works as clist intersection.
- Statement <cf><m/C/ = add(<m/C/, <m/P/);</cf> can be shortened to
- <cf><m/C/.add(<m/P/);</cf> if <m/C/ is appropriate route attribute (for
- example <cf/bgp_community/) or a local variable.
- Similarly for <cf/delete/ and <cf/filter/.
+ Methods <cf>add</cf>, <cf>delete</cf> and <cf>filter</cf> keep the
+ original object intact as long as you use the result in any way. You can
+ also write e.g. <cf><m/P/.add(<m/A/);</cf> as a standalone statement.
+ This variant does modify the original object with the result of the operation.
<cf><m/C/.min</cf> returns the minimum element of clist <m/C/.
diff --git a/filter/config.Y b/filter/config.Y
index 5554fd27..09f5dcfa 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -23,21 +23,30 @@ static struct symbol *this_function;
static struct f_method_scope {
struct f_inst *object;
+ struct sym_scope *main;
struct sym_scope scope;
} f_method_scope_stack[32];
static int f_method_scope_pos = -1;
#define FM (f_method_scope_stack[f_method_scope_pos])
-static inline void f_push_method_scope(struct f_inst *object)
+static inline void f_method_call_start(struct f_inst *object)
{
+ if (object->type == T_VOID)
+ cf_error("Can't infer type to properly call a method, please assign the value to a variable");
if (++f_method_scope_pos >= (int) ARRAY_SIZE(f_method_scope_stack))
cf_error("Too many nested method calls");
+
+ struct sym_scope *scope = f_type_method_scope(object->type);
+ if (!scope)
+ cf_error("No methods defined for type %s", f_type_name(object->type));
+
FM = (struct f_method_scope) {
.object = object,
+ .main = new_config->current_scope,
.scope = {
- .next = new_config->current_scope,
- .hash = global_root_scope[CFK_METHODS].hash,
+ .next = NULL,
+ .hash = scope->hash,
.active = 1,
.block = 1,
.readonly = 1,
@@ -46,12 +55,23 @@ static inline void f_push_method_scope(struct f_inst *object)
new_config->current_scope = &FM.scope;
}
-static inline void f_pop_method_scope(void)
+static inline void f_method_call_args(void)
+{
+ ASSERT_DIE(FM.scope.active);
+ FM.scope.active = 0;
+
+ new_config->current_scope = FM.main;
+}
+
+static inline void f_method_call_end(void)
{
ASSERT_DIE(f_method_scope_pos >= 0);
+ if (FM.scope.active) {
+ ASSERT_DIE(&FM.scope == new_config->current_scope);
+ new_config->current_scope = FM.main;
- ASSERT_DIE(&FM.scope == new_config->current_scope);
- new_config->current_scope = FM.scope.next;
+ FM.scope.active = 0;
+ }
f_method_scope_pos--;
}
@@ -496,7 +516,7 @@ CF_METHODS(IS_V4, TYPE, IP, RD, LEN, MAXLEN, ASN, SRC, DST, MASK,
%nonassoc ELSE
%type <xp> cmds_int cmd_prep
-%type <x> term term_bs cmd cmd_var cmds cmds_scoped constant constructor print_list var var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail method_cmd method_term
+%type <x> term term_bs cmd cmd_var cmds cmds_scoped constant constructor print_list var var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail term_dot_method method_name_cont
%type <fda> dynamic_attr
%type <fsa> static_attr
%type <f> filter where_filter
@@ -681,7 +701,7 @@ 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) log(L_WARN "Support for functions without explicit return type will be removed soon" ); */
+/* if ($2 == T_VOID) cf_warn("Support for functions without explicit return type will be removed soon" ); */
cf_push_scope(new_config, this_function);
} function_args {
/* Make dummy f_line for storing function prototype */
@@ -998,33 +1018,18 @@ static_attr:
| ONLINK { $$ = f_new_static_attr(T_BOOL, SA_ONLINK, 0); }
;
-method_term:
- IS_V4 { $$ = f_new_inst(FI_IS_V4, FM.object); }
- | TYPE { $$ = f_new_inst(FI_TYPE, FM.object); }
- | IP { $$ = f_new_inst(FI_IP, FM.object); }
- | RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER, FM.object); }
- | LEN { $$ = f_new_inst(FI_LENGTH, FM.object); }
- | MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN, FM.object); }
- | ASN { $$ = f_new_inst(FI_ASN, FM.object); }
- | SRC { $$ = f_new_inst(FI_NET_SRC, FM.object); }
- | DST { $$ = f_new_inst(FI_NET_DST, FM.object); }
- | MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK, FM.object, $3); }
- | FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST, FM.object); }
- | LAST { $$ = f_new_inst(FI_AS_PATH_LAST, FM.object); }
- | LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG, FM.object); }
- | DATA { $$ = f_new_inst(FI_PAIR_DATA, FM.object); }
- | DATA1 { $$ = f_new_inst(FI_LC_DATA1, FM.object); }
- | DATA2 { $$ = f_new_inst(FI_LC_DATA2, FM.object); }
- | MIN { $$ = f_new_inst(FI_MIN, FM.object); }
- | MAX { $$ = f_new_inst(FI_MAX, FM.object); }
- ;
-
-method_cmd:
- EMPTY { $$ = f_const_empty(FM.object->i_FI_EA_GET.da.f_type); }
- | PREPEND '(' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, FM.object, $3 ); }
- | ADD '(' term ')' { $$ = f_new_inst(FI_CLIST_ADD, FM.object, $3 ); }
- | DELETE '(' term ')' { $$ = f_new_inst(FI_CLIST_DEL, FM.object, $3 ); }
- | FILTER '(' term ')' { $$ = f_new_inst(FI_CLIST_FILTER, FM.object, $3 ); }
+term_dot_method: term '.' { f_method_call_start($1); } method_name_cont { $$ = $4; };
+method_name_cont:
+ CF_SYM_METHOD_BARE {
+ $$ = $1->method->new_inst(FM.object, NULL);
+ f_method_call_end();
+ }
+ | CF_SYM_METHOD_ARGS {
+ f_method_call_args();
+ } '(' var_list ')' {
+ $$ = $1->method->new_inst(FM.object, $4);
+ f_method_call_end();
+ }
;
term:
@@ -1054,12 +1059,7 @@ term:
| dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); }
- | term '.' {
- f_push_method_scope($1);
- } method_term {
- f_pop_method_scope();
- $$ = $4;
- }
+ | term_dot_method
| EMPTY { $$ = f_const_empty(T_EMPTY_LIST); }
| '+' EMPTY '+' { $$ = f_const_empty(T_PATH); }
@@ -1068,9 +1068,35 @@ term:
| '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_const_empty(T_LCLIST); }
| '-' '-' '-' '-' EMPTY '-' '-' '-' '-' { $$ = f_const_empty(T_TLVLIST); }
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5); }
- | ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD, $3, $5); }
- | DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_DEL, $3, $5); }
- | FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_FILTER, $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));
+ }
+ 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));
+ }
+ 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));
+ }
+ cf_warn("filter(x,y) function is deprecated, please use x.filter(y)");
+ }
| ROA_CHECK '(' rtable ')' { $$ = f_new_inst(FI_ROA_CHECK_IMPLICIT, $3); }
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK_EXPLICIT, $5, $7, $3); }
@@ -1158,7 +1184,7 @@ cmd:
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));
+ cf_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)
@@ -1202,9 +1228,8 @@ cmd:
}
| lvalue '.' {
- f_push_method_scope(f_lval_getter(&$1));
- } method_cmd ';' {
- f_pop_method_scope();
+ f_method_call_start(f_lval_getter(&$1));
+ } method_name_cont ';' {
$$ = f_lval_setter(&$1, $4);
}
| BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
diff --git a/filter/data.h b/filter/data.h
index 1032eacf..158e2c04 100644
--- a/filter/data.h
+++ b/filter/data.h
@@ -67,6 +67,12 @@ enum f_type {
T_PREFIX_SET = 0x81,
} PACKED;
+struct f_method {
+ struct symbol *sym;
+ struct f_inst *(*new_inst)(struct f_inst *obj, struct f_inst *args);
+ uint arg_num;
+};
+
/* Filter value; size of this affects filter memory consumption */
struct f_val {
enum f_type type; /* T_* */
@@ -283,8 +289,8 @@ trie_match_next_longest_ip6(net_addr_ip6 *n, ip6_addr *found)
#define F_CMP_ERROR 999
const char *f_type_name(enum f_type t);
-
enum f_type f_type_element_type(enum f_type t);
+struct sym_scope *f_type_method_scope(enum f_type t);
int val_same(const struct f_val *v1, const struct f_val *v2);
int val_compare(const struct f_val *v1, const struct f_val *v2);
diff --git a/filter/decl.m4 b/filter/decl.m4
index 98b72be6..9ee3ae1a 100644
--- a/filter/decl.m4
+++ b/filter/decl.m4
@@ -34,6 +34,9 @@ m4_divert(-1)m4_dnl
# 102 constructor arguments
# 110 constructor attributes
# 103 constructor body
+# 111 method constructor body
+# 112 instruction constructor call from method constructor
+# 113 method constructor symbol registrator
# 104 dump line item content
# (there may be nothing in dump-line content and
# it must be handled specially in phase 2)
@@ -48,6 +51,8 @@ m4_define(FID_STRUCT_IN, `m4_divert(101)')
m4_define(FID_NEW_ARGS, `m4_divert(102)')
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_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)')
@@ -120,7 +125,13 @@ FID_IFCONST([[
constargs = 0;
]])
} while (child$1 = child$1->next);
-FID_LINEARIZE_BODY
+m4_define([[INST_METHOD_NUM_ARGS]],m4_eval($1-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 */
+ args = args->next;
+ FID_METHOD_CALL() , arg$1]])
+FID_LINEARIZE_BODY()m4_dnl
pos = linearize(dest, whati->f$1, pos);
FID_INTERPRET_BODY()')
@@ -170,28 +181,29 @@ FID_HIC(,[[
m4_define(ARG, `ARG_ANY($1) ARG_TYPE($1,$2)')
m4_define(ARG_TYPE, `ARG_TYPE_STATIC($1,$2) ARG_TYPE_DYNAMIC($1,$2)')
-m4_define(ARG_TYPE_STATIC, `
+m4_define(ARG_TYPE_STATIC, `m4_dnl
+m4_ifelse($1,1,[[m4_define([[INST_METHOD_OBJECT_TYPE]],$2)]],)m4_dnl
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",
f_instruction_name(what->fi_code), f_type_name($2), f_type_name(f$1->type));
FID_INTERPRET_BODY()')
-m4_define(ARG_TYPE_DYNAMIC, `
+m4_define(ARG_TYPE_DYNAMIC, `m4_dnl
FID_INTERPRET_EXEC()m4_dnl
if (v$1.type != ($2))
runtime("Argument $1 of %s must be of type %s, got type %s",
f_instruction_name(what->fi_code), f_type_name($2), f_type_name(v$1.type));
FID_INTERPRET_BODY()')
-m4_define(ARG_SAME_TYPE, `
+m4_define(ARG_SAME_TYPE, `m4_dnl
FID_NEW_BODY()m4_dnl
if (f$1->type && f$2->type && (f$1->type != f$2->type) &&
!f_const_promotion(f$2, f$1->type) && !f_const_promotion(f$1, f$2->type))
cf_error("Arguments $1 and $2 of %s must be of the same type", f_instruction_name(what->fi_code));
FID_INTERPRET_BODY()')
-m4_define(ARG_PREFER_SAME_TYPE, `
+m4_define(ARG_PREFER_SAME_TYPE, `m4_dnl
FID_NEW_BODY()m4_dnl
if (f$1->type && f$2->type && (f$1->type != f$2->type))
(void) (f_const_promotion(f$2, f$1->type) || f_const_promotion(f$1, f$2->type));
@@ -266,6 +278,14 @@ m4_define(STATIC_ATTR, `FID_MEMBER(struct f_static_attr, sa, f1->sa.sa_code != f
m4_define(DYNAMIC_ATTR, `FID_MEMBER(struct f_dynamic_attr, da, f1->da.ea_code != f2->da.ea_code,,)')
m4_define(ACCESS_RTE, `FID_HIC(,[[do { if (!fs->rte) runtime("No route to access"); } while (0)]],NEVER_CONSTANT())')
+# Method constructor block
+m4_define(METHOD_CONSTRUCTOR, `m4_dnl
+FID_NEW_METHOD()m4_dnl
+ if (args) cf_error("Too many arguments");
+m4_define([[INST_IS_METHOD]])
+m4_define([[INST_METHOD_NAME]],$1)
+FID_INTERPRET_BODY()')
+
# 2) Code wrapping
# The code produced in 1xx temporary diversions is a raw code without
# any auxiliary commands and syntactical structures around. When the
@@ -285,6 +305,7 @@ m4_define(ACCESS_RTE, `FID_HIC(,[[do { if (!fs->rte) runtime("No route to access
# 10 iterate
# 1 union in struct f_inst
# 3 constructors + interpreter
+# 11 method constructors
#
# These global diversions contain blocks of code that can be directly
# put into the final file, yet it still can't be written out now as
@@ -304,6 +325,9 @@ m4_define(FID_DUMP_CALLER, `FID_ZONE(7, Dump line caller)')
m4_define(FID_LINEARIZE, `FID_ZONE(8, Linearize)')
m4_define(FID_SAME, `FID_ZONE(9, Comparison)')
m4_define(FID_ITERATE, `FID_ZONE(10, Iteration)')
+m4_define(FID_METHOD, `FID_ZONE(11, Method constructor)')
+m4_define(FID_METHOD_SCOPE_INIT, `FID_ZONE(12, Method scope initializator)')
+m4_define(FID_METHOD_REGISTER, `FID_ZONE(13, Method registrator)')
# This macro does all the code wrapping. See inline comments.
m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[
@@ -361,6 +385,35 @@ m4_undivert(102)m4_dnl
}
]])
+m4_ifdef([[INST_IS_METHOD]],m4_dnl
+FID_METHOD()m4_dnl
+[[struct f_inst * NONNULL(1)
+f_new_method_]]INST_NAME()[[(struct f_inst *obj, struct f_inst *args)
+ {
+ /* Unwind the arguments (INST_METHOD_NUM_ARGS) */
+ m4_undivert(111)m4_dnl
+ return f_new_inst(INST_NAME, obj
+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,
+ };
+
+]])m4_dnl
+
FID_DUMP_CALLER()m4_dnl Case in another big switch used in instruction dumping (debug)
case INST_NAME(): f_dump_line_item_]]INST_NAME()[[(item, indent + 1); break;
@@ -414,6 +467,8 @@ m4_define([[INST_INVAL]], [[$2]])m4_dnl instruction input value count,
m4_define([[INST_OUTVAL]], [[$3]])m4_dnl instruction output value count,
m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl reset NEVER_CONSTANT trigger,
m4_undefine([[INST_RESULT_TYPE]])m4_dnl and reset RESULT_TYPE value.
+m4_undefine([[INST_IS_METHOD]])m4_dnl and reset method constructor request.
+m4_undefine([[INST_METHOD_OBJECT_TYPE]],)m4_dnl reset method object type,
FID_INTERPRET_BODY()m4_dnl By default, every code is interpreter code.
')
@@ -561,6 +616,37 @@ FID_WR_PUT(3)
#undef v3
#undef vv
+/* Method constructor wrappers */
+FID_WR_PUT(11)
+
+#if defined(__GNUC__) && __GNUC__ >= 6
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Woverride-init"
+#endif
+
+static struct sym_scope f_type_method_scopes[] = {
+FID_WR_PUT(12)
+};
+
+#if defined(__GNUC__) && __GNUC__ >= 6
+#pragma GCC diagnostic pop
+#endif
+
+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;
+}
+
+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++)
+ f_type_method_scopes[i].readonly = 1;
+}
+
/* Line dumpers */
#define INDENT (((const char *) f_dump_line_indent_str) + sizeof(f_dump_line_indent_str) - (indent) - 1)
static const char f_dump_line_indent_str[] = " ";
diff --git a/filter/f-inst.c b/filter/f-inst.c
index 2c79ccbd..1b89fdcb 100644
--- a/filter/f-inst.c
+++ b/filter/f-inst.c
@@ -72,6 +72,8 @@
* m4_dnl ACCESS_RTE; this instruction needs route
* m4_dnl ACCESS_EATTRS; this instruction needs extended attributes
*
+ * m4_dnl METHOD_CONSTRUCTOR(name); this instruction is in fact a method of the first argument's type; register it with the given name for that type
+ *
* m4_dnl FID_MEMBER( custom instruction member
* m4_dnl C type, for storage in structs
* m4_dnl name, how the member is named
@@ -489,20 +491,15 @@
RESULT(T_BOOL, i, (v1.type != T_VOID) && !undef_value(v1));
}
- INST(FI_TYPE, 1, 1) {
- ARG_ANY(1); /* There may be more types supporting this operation */
- switch (v1.type)
- {
- case T_NET:
- RESULT(T_ENUM_NETTYPE, i, v1.val.net->type);
- break;
- default:
- runtime( "Can't determine type of this item" );
- }
+ INST(FI_NET_TYPE, 1, 1) {
+ ARG(1, T_NET);
+ METHOD_CONSTRUCTOR("type");
+ RESULT(T_ENUM_NETTYPE, i, v1.val.net->type);
}
INST(FI_IS_V4, 1, 1) {
ARG(1, T_IP);
+ METHOD_CONSTRUCTOR("is_v4");
RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip));
}
@@ -560,6 +557,30 @@
RESULT_VAL(val);
}
+ INST(FI_PATH_EMPTY, 1, 1) {
+ ARG(1, T_PATH);
+ METHOD_CONSTRUCTOR("empty");
+ RESULT(T_PATH, ad, &null_adata);
+ }
+
+ INST(FI_CLIST_EMPTY, 1, 1) {
+ ARG(1, T_CLIST);
+ METHOD_CONSTRUCTOR("empty");
+ RESULT(T_CLIST, ad, &null_adata);
+ }
+
+ INST(FI_ECLIST_EMPTY, 1, 1) {
+ ARG(1, T_ECLIST);
+ METHOD_CONSTRUCTOR("empty");
+ RESULT(T_ECLIST, ad, &null_adata);
+ }
+
+ INST(FI_LCLIST_EMPTY, 1, 1) {
+ ARG(1, T_LCLIST);
+ METHOD_CONSTRUCTOR("empty");
+ RESULT(T_LCLIST, ad, &null_adata);
+ }
+
INST(FI_FOR_INIT, 1, 0) {
NEVER_CONSTANT;
ARG_ANY(1);
@@ -1002,20 +1023,39 @@
ea_unset_attr(fs->eattrs, fs->pool, 1, da.ea_code);
}
- INST(FI_LENGTH, 1, 1) { /* Get length of */
- ARG_ANY(1);
- switch(v1.type) {
- case T_NET: RESULT(T_INT, i, net_pxlen(v1.val.net)); break;
- case T_PATH: RESULT(T_INT, i, as_path_getlen(v1.val.ad)); break;
- case T_CLIST: RESULT(T_INT, i, int_set_get_size(v1.val.ad)); break;
- case T_ECLIST: RESULT(T_INT, i, ec_set_get_size(v1.val.ad)); break;
- case T_LCLIST: RESULT(T_INT, i, lc_set_get_size(v1.val.ad)); break;
- default: runtime( "Prefix, path, clist or eclist expected" );
- }
+ INST(FI_NET_LENGTH, 1, 1) { /* Get length of */
+ ARG(1, T_NET);
+ METHOD_CONSTRUCTOR("len");
+ RESULT(T_INT, i, net_pxlen(v1.val.net));
+ }
+
+ INST(FI_PATH_LENGTH, 1, 1) { /* Get length of */
+ ARG(1, T_PATH);
+ METHOD_CONSTRUCTOR("len");
+ RESULT(T_INT, i, as_path_getlen(v1.val.ad));
+ }
+
+ INST(FI_CLIST_LENGTH, 1, 1) { /* Get length of */
+ ARG(1, T_CLIST);
+ METHOD_CONSTRUCTOR("len");
+ RESULT(T_INT, i, int_set_get_size(v1.val.ad));
+ }
+
+ INST(FI_ECLIST_LENGTH, 1, 1) { /* Get length of */
+ ARG(1, T_ECLIST);
+ METHOD_CONSTRUCTOR("len");
+ RESULT(T_INT, i, ec_set_get_size(v1.val.ad));
+ }
+
+ INST(FI_LCLIST_LENGTH, 1, 1) { /* Get length of */
+ ARG(1, T_LCLIST);
+ METHOD_CONSTRUCTOR("len");
+ RESULT(T_INT, i, lc_set_get_size(v1.val.ad));
}
INST(FI_NET_SRC, 1, 1) { /* Get src prefix */
ARG(1, T_NET);
+ METHOD_CONSTRUCTOR("src");
net_addr_union *net = (void *) v1.val.net;
net_addr *src = falloc(sizeof(net_addr_ip6));
@@ -1051,6 +1091,7 @@
INST(FI_NET_DST, 1, 1) { /* Get dst prefix */
ARG(1, T_NET);
+ METHOD_CONSTRUCTOR("dst");
net_addr_union *net = (void *) v1.val.net;
net_addr *dst = falloc(sizeof(net_addr_ip6));
@@ -1086,6 +1127,7 @@
INST(FI_ROA_MAXLEN, 1, 1) { /* Get ROA max prefix length */
ARG(1, T_NET);
+ METHOD_CONSTRUCTOR("maxlen")
if (!net_is_roa(v1.val.net))
runtime( "ROA expected" );
@@ -1094,40 +1136,38 @@
((net_addr_roa6 *) v1.val.net)->max_pxlen);
}
- INST(FI_ASN, 1, 1) { /* Get ROA ASN or community ASN part */
- ARG_ANY(1);
- RESULT_TYPE(T_INT);
- switch(v1.type)
- {
- case T_NET:
+ INST(FI_NET_ASN, 1, 1) { /* Get ROA ASN or community ASN part */
+ ARG(1, T_NET);
+ METHOD_CONSTRUCTOR("asn");
if (!net_is_roa(v1.val.net))
runtime( "ROA expected" );
- RESULT_(T_INT, i, (v1.val.net->type == NET_ROA4) ?
+ RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ?
((net_addr_roa4 *) v1.val.net)->asn :
((net_addr_roa6 *) v1.val.net)->asn);
- break;
-
- case T_PAIR:
- RESULT_(T_INT, i, v1.val.i >> 16);
- break;
+ }
- case T_LC:
- RESULT_(T_INT, i, v1.val.lc.asn);
- break;
+ INST(FI_PAIR_ASN, 1, 1) { /* Get ROA ASN or community ASN part */
+ ARG(1, T_PAIR);
+ METHOD_CONSTRUCTOR("asn");
+ RESULT(T_INT, i, v1.val.i >> 16);
+ }
- default:
- runtime( "Net, pair or lc expected" );
- }
+ INST(FI_LC_ASN, 1, 1) { /* Get ROA ASN or community ASN part */
+ ARG(1, T_LC);
+ METHOD_CONSTRUCTOR("asn");
+ RESULT(T_INT, i, v1.val.lc.asn);
}
- INST(FI_IP, 1, 1) { /* Convert prefix to ... */
+ INST(FI_NET_IP, 1, 1) { /* Convert prefix to ... */
ARG(1, T_NET);
+ METHOD_CONSTRUCTOR("ip");
RESULT(T_IP, ip, net_prefix(v1.val.net));
}
INST(FI_ROUTE_DISTINGUISHER, 1, 1) {
ARG(1, T_NET);
+ METHOD_CONSTRUCTOR("rd");
if (!net_is_vpn(v1.val.net))
runtime( "VPN address expected" );
RESULT(T_RD, ec, net_rd(v1.val.net));
@@ -1135,6 +1175,7 @@
INST(FI_AS_PATH_FIRST, 1, 1) { /* Get first ASN from AS PATH */
ARG(1, T_PATH);
+ METHOD_CONSTRUCTOR("first");
u32 as = 0;
as_path_get_first(v1.val.ad, &as);
RESULT(T_INT, i, as);
@@ -1142,6 +1183,7 @@
INST(FI_AS_PATH_LAST, 1, 1) { /* Get last ASN from AS PATH */
ARG(1, T_PATH);
+ METHOD_CONSTRUCTOR("last");
u32 as = 0;
as_path_get_last(v1.val.ad, &as);
RESULT(T_INT, i, as);
@@ -1149,90 +1191,74 @@
INST(FI_AS_PATH_LAST_NAG, 1, 1) { /* Get last ASN from non-aggregated part of AS PATH */
ARG(1, T_PATH);
+ METHOD_CONSTRUCTOR("last_nonaggregated");
RESULT(T_INT, i, as_path_get_last_nonaggregated(v1.val.ad));
}
INST(FI_PAIR_DATA, 1, 1) { /* Get data part from the standard community */
ARG(1, T_PAIR);
+ METHOD_CONSTRUCTOR("data");
RESULT(T_INT, i, v1.val.i & 0xFFFF);
}
INST(FI_LC_DATA1, 1, 1) { /* Get data1 part from the large community */
ARG(1, T_LC);
+ METHOD_CONSTRUCTOR("data1");
RESULT(T_INT, i, v1.val.lc.ldp1);
}
INST(FI_LC_DATA2, 1, 1) { /* Get data2 part from the large community */
ARG(1, T_LC);
+ METHOD_CONSTRUCTOR("data2");
RESULT(T_INT, i, v1.val.lc.ldp2);
}
- INST(FI_MIN, 1, 1) { /* Get minimum element from list */
- ARG_ANY(1);
- RESULT_TYPE(f_type_element_type(v1.type));
- switch(v1.type)
- {
- case T_CLIST:
- {
- u32 val = 0;
- int_set_min(v1.val.ad, &val);
- RESULT_(T_PAIR, i, val);
- }
- break;
-
- case T_ECLIST:
- {
- u64 val = 0;
- ec_set_min(v1.val.ad, &val);
- RESULT_(T_EC, ec, val);
- }
- break;
-
- case T_LCLIST:
- {
- lcomm val = { 0, 0, 0 };
- lc_set_min(v1.val.ad, &val);
- RESULT_(T_LC, lc, val);
- }
- break;
+ INST(FI_CLIST_MIN, 1, 1) { /* Get minimum element from list */
+ ARG(1, T_CLIST);
+ METHOD_CONSTRUCTOR("min");
+ u32 val = 0;
+ int_set_min(v1.val.ad, &val);
+ RESULT(T_PAIR, i, val);
+ }
- default:
- runtime( "Clist or lclist expected" );
- }
+ INST(FI_CLIST_MAX, 1, 1) { /* Get minimum element from list */
+ ARG(1, T_CLIST);
+ METHOD_CONSTRUCTOR("max");
+ u32 val = 0;
+ int_set_max(v1.val.ad, &val);
+ RESULT(T_PAIR, i, val);
}
- INST(FI_MAX, 1, 1) { /* Get maximum element from list */
- ARG_ANY(1);
- RESULT_TYPE(f_type_element_type(v1.type));
- switch(v1.type)
- {
- case T_CLIST:
- {
- u32 val = 0;
- int_set_max(v1.val.ad, &val);
- RESULT_(T_PAIR, i, val);
- }
- break;
+ INST(FI_ECLIST_MIN, 1, 1) { /* Get minimum element from list */
+ ARG(1, T_ECLIST);
+ METHOD_CONSTRUCTOR("min");
+ u64 val = 0;
+ ec_set_min(v1.val.ad, &val);
+ RESULT(T_EC, ec, val);
+ }
- case T_ECLIST:
- {
- u64 val = 0;
- ec_set_max(v1.val.ad, &val);
- RESULT_(T_EC, ec, val);
- }
- break;
+ INST(FI_ECLIST_MAX, 1, 1) { /* Get minimum element from list */
+ ARG(1, T_ECLIST);
+ METHOD_CONSTRUCTOR("max");
+ u64 val = 0;
+ ec_set_max(v1.val.ad, &val);
+ RESULT(T_EC, ec, val);
+ }
- case T_LCLIST:
- {
- lcomm val = { 0, 0, 0 };
- lc_set_max(v1.val.ad, &val);
- RESULT_(T_LC, lc, val);
- }
- break;
+ INST(FI_LCLIST_MIN, 1, 1) { /* Get minimum element from list */
+ ARG(1, T_LCLIST);
+ METHOD_CONSTRUCTOR("min");
+ lcomm val = {};
+ lc_set_min(v1.val.ad, &val);
+ RESULT(T_LC, lc, val);
+ }
- default:
- runtime( "Clist or lclist expected" );
- }
+ INST(FI_LCLIST_MAX, 1, 1) { /* Get minimum element from list */
+ ARG(1, T_LCLIST);
+ METHOD_CONSTRUCTOR("max");
+ lcomm val = {};
+ lc_set_max(v1.val.ad, &val);
+ RESULT(T_LC, lc, val);
}
INST(FI_RETURN, 1, 0) {
@@ -1371,6 +1397,7 @@
INST(FI_IP_MASK, 2, 1) { /* IP.MASK(val) */
ARG(1, T_IP);
ARG(2, T_INT);
+ METHOD_CONSTRUCTOR("mask");
RESULT(T_IP, ip, [[ ipa_is_ip4(v1.val.ip) ?
ipa_from_ip4(ip4_and(ipa_to_ip4(v1.val.ip), ip4_mkmask(v2.val.i))) :
ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i))) ]]);
@@ -1379,199 +1406,195 @@
INST(FI_PATH_PREPEND, 2, 1) { /* Path prepend */
ARG(1, T_PATH);
ARG(2, T_INT);
+ METHOD_CONSTRUCTOR("prepend");
RESULT(T_PATH, ad, [[ as_path_prepend(fpool, v1.val.ad, v2.val.i) ]]);
}
INST(FI_CLIST_ADD, 2, 1) { /* (Extended) Community list add */
- ARG_ANY(1);
+ ARG(1, T_CLIST);
ARG_ANY(2);
- RESULT_TYPE(f1->type);
-
- if (v1.type == T_PATH)
- runtime("Can't add to path");
-
- else if (v1.type == T_CLIST)
- {
- /* Community (or cluster) list */
+ 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) ]]);
+ RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]);
/* IP->Quad implicit conversion */
else if (val_is_ip4(&v2))
- RESULT_(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
+ 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)
- RESULT_(T_CLIST, ad, [[ int_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
+ RESULT(T_CLIST, ad, [[ int_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
else
runtime("Can't add non-pair");
- }
+ }
- else if (v1.type == T_ECLIST)
- {
+ INST(FI_ECLIST_ADD, 2, 1) {
+ ARG(1, T_ECLIST);
+ ARG_ANY(2);
+ 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) ]]);
+ 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) ]]);
+ }
- else if (v1.type == T_LCLIST)
- {
+ INST(FI_LCLIST_ADD, 2, 1) {
+ ARG(1, T_LCLIST);
+ ARG_ANY(2);
+ 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) ]]);
+ 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) ]]);
+ }
- else if (v1.type == T_TLVLIST)
- {
+ INST(FI_TLVLIST_ADD, 2, 1) {
+ ARG(1, T_TLVLIST);
+ ARG_ANY(2);
+ METHOD_CONSTRUCTOR("add");
/* v2.val is either TLV or TLV-set */
if ((v2.type == T_SET) && subtlv_set_type(v2.val.t))
- RESULT_(T_TLVLIST, tl, [[ tlv_set_add(fpool, v1.val.tl, tlv_alloc(fpool, v2.val.t)) ]]);
- //runtime("Can't add set");
+ RESULT(T_TLVLIST, tl, [[ tlv_set_add(fpool, v1.val.tl, tlv_alloc(fpool, v2.val.t)) ]]);
else if (v2.type == T_TLVLIST)
- RESULT_(T_TLVLIST, tl, [[ tlv_set_union(fpool, v1.val.tl, v2.val.tl) ]]);
+ RESULT(T_TLVLIST, tl, [[ tlv_set_union(fpool, v1.val.tl, v2.val.tl) ]]);
else
runtime("Can't add non-tlv");
- }
-
- else
- runtime("Can't add to non-[e|l]clist");
}
- INST(FI_CLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
- ARG_ANY(1);
+ INST(FI_PATH_DEL, 2, 1) { /* Path delete */
+ ARG(1, T_PATH);
ARG_ANY(2);
- RESULT_TYPE(f1->type);
-
- if (v1.type == T_PATH)
- {
+ 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) ]]);
+ RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]);
else
runtime("Can't delete non-integer (set)");
- }
+ }
- else if (v1.type == T_CLIST)
- {
+ INST(FI_CLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
+ ARG(1, T_CLIST);
+ ARG_ANY(2);
+ 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) ]]);
+ RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]);
/* IP->Quad implicit conversion */
else if (val_is_ip4(&v2))
- RESULT_(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
+ 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))
- RESULT_(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 0) ]]);
+ RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 0) ]]);
else
runtime("Can't delete non-pair");
- }
+ }
- else if (v1.type == T_ECLIST)
- {
+ INST(FI_ECLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
+ ARG(1, T_ECLIST);
+ ARG_ANY(2);
+ 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) ]]);
+ 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) ]]);
+ }
- else if (v1.type == T_LCLIST)
- {
+ INST(FI_LCLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
+ ARG(1, T_LCLIST);
+ ARG_ANY(2);
+ 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) ]]);
+ 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_TLVLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
+ ARG(1, T_TLVLIST);
+ ARG_ANY(2);
+ METHOD_CONSTRUCTOR("delete");
#if 0
- else if (v1.type == T_TLVLIST)
- {
- /* v2.val is sub-TLV-set */
- if ((v2.type == T_SET) && subtlv_set_type(v2.val.t))
- RESULT_(T_TLVLIST, tl, [[ tlvlist_filter(fpool, v1.val.tl, &v2, 0) ]]);
- else
- runtime("Can't delete non-sub-TLV-set");
- }
-#endif
-
+ /* v2.val is sub-TLV-set */
+ if ((v2.type == T_SET) && subtlv_set_type(v2.val.t))
+ RESULT(T_TLVLIST, tl, [[ tlvlist_filter(fpool, v1.val.tl, &v2, 0) ]]);
else
- runtime("Can't delete in non-[e|l]clist");
+#endif
+ runtime("Can't delete non-sub-TLV-set");
}
- INST(FI_CLIST_FILTER, 2, 1) { /* (Extended) Community list add or delete */
- ARG_ANY(1);
+ INST(FI_PATH_FILTER, 2, 1) { /* (Extended) Community list add or delete */
+ ARG(1, T_PATH);
ARG_ANY(2);
- RESULT_TYPE(f1->type);
+ METHOD_CONSTRUCTOR("filter");
- if (v1.type == T_PATH)
- {
if ((v2.type == T_SET) && path_set_type(v2.val.t))
- RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 1) ]]);
+ RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter integer");
}
- else if (v1.type == T_CLIST)
- {
+ INST(FI_CLIST_FILTER, 2, 1) {
+ ARG(1, T_CLIST);
+ ARG_ANY(2);
+ 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) ]]);
+ RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter pair");
- }
+ }
- else if (v1.type == T_ECLIST)
- {
+ INST(FI_ECLIST_FILTER, 2, 1) {
+ ARG(1, T_ECLIST);
+ ARG_ANY(2);
+ 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) ]]);
+ RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter ec");
- }
+ }
- else if (v1.type == T_LCLIST)
- {
+ INST(FI_LCLIST_FILTER, 2, 1) {
+ ARG(1, T_LCLIST);
+ ARG_ANY(2);
+ 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) ]]);
+ RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter lc");
- }
+ }
+ INST(FI_TLVLIST_FILTER, 2, 1) {
+ ARG(1, T_TLVLIST);
+ ARG_ANY(2);
+ METHOD_CONSTRUCTOR("filter");
#if 0
- else if (v1.type == T_TLVLIST)
- {
/* v2.val is either TLV or sub-TLV-set */
if ((v2.type == T_SET) && subtlv_set_type(v2.val.t) || (v2.type == T_TLVLIST))
- RESULT_(T_TLVLIST, tl, [[ tlvlist_filter(fpool, v1.val.tl, &v2, 1) ]]);
+ RESULT(T_TLVLIST, tl, [[ tlvlist_filter(fpool, v1.val.tl, &v2, 1) ]]);
else
- runtime("Can't filter tlv");
- }
#endif
-
- else
- runtime("Can't filter non-[e|l]clist");
+ runtime("Can't filter tlv");
}
INST(FI_ROA_CHECK_IMPLICIT, 0, 1) { /* ROA Check */
diff --git a/filter/test.conf b/filter/test.conf
index 33389a17..90f5e694 100644
--- a/filter/test.conf
+++ b/filter/test.conf
@@ -756,7 +756,7 @@ function bgpmask mkpath(int a; int b)
define set35 = [3 .. 5];
-function t_path()
+function t_path_old()
bgpmask pm1;
bgppath p2;
int set set12;
@@ -835,7 +835,91 @@ int set set12;
bt_assert(x = 18 && y = 50);
}
-bt_test_suite(t_path, "Testing paths");
+bt_test_suite(t_path_old, "Testing paths (old syntax)");
+
+
+function t_path_new()
+{
+ bgpmask pm1 = [= 4 3 2 1 =];
+ int set set12 = [1, 2];
+
+ bt_assert(format(pm1) = "[= 4 3 2 1 =]");
+
+ bt_assert(+empty+ = +empty+);
+ bt_assert(10 !~ +empty+);
+
+ bgppath p2;
+ bt_assert(p2 = +empty+);
+ p2.prepend(1);
+ p2.prepend(2);
+ p2.prepend(3);
+ p2.prepend(4);
+
+ bt_assert(p2.empty = +empty+);
+
+ bt_assert(format(p2) = "(path 4 3 2 1)");
+ bt_assert(p2.len = 4);
+ bt_assert(p2 ~ pm1);
+ bt_assert(3 ~ p2);
+ bt_assert(p2 ~ [2, 10..20]);
+ bt_assert(p2 ~ [4, 10..20]);
+ bt_assert(p2 !~ []);
+
+ p2.prepend(5);
+ bt_assert(p2 !~ pm1);
+ bt_assert(10 !~ p2);
+ bt_assert(p2 !~ [8, ten..(2*ten)]);
+ bt_assert(p2 ~ [= * 4 3 * 1 =]);
+ bt_assert(p2 ~ [= (3+2) (2*2) 3 2 1 =]);
+ bt_assert(p2 ~ [= 5 [2, 4, 6] 3 [1..2] 1 =]);
+ bt_assert(p2 ~ [= 5 set35 3 set12 set12 =]);
+ bt_assert(p2 ~ mkpath(5, 4));
+ bt_assert(p2 ~ [= * [3] * =]);
+ bt_assert(p2 !~ [= * [] * =]);
+
+ bt_assert(p2.len = 5);
+ bt_assert(p2.first = 5);
+ bt_assert(p2.last = 1);
+
+ bt_assert(p2.len = 5);
+ bt_assert(p2.delete(3) = +empty+.prepend(1).prepend(2).prepend(4).prepend(5));
+ bt_assert(p2.filter([1..3]) = +empty+.prepend(1).prepend(2).prepend(3));
+ bt_assert(p2.delete([]) = p2);
+ bt_assert(p2.filter([]) = +empty+);
+ bt_assert(+empty+.prepend(0).prepend(1).delete([]) = +empty+.prepend(0).prepend(1));
+ bt_assert(+empty+.prepend(0).prepend(1).filter([]) = +empty+);
+
+ p2 = +empty+;
+ p2.prepend(5);
+ p2.prepend(4);
+ p2.prepend(3);
+ p2.prepend(3);
+ p2.prepend(2);
+ p2.prepend(1);
+
+ bt_assert(p2 !~ [= 1 2 3 4 5 =]);
+ bt_assert(p2 ~ [= 1 2 * 4 5 =]);
+ bt_assert(p2 ~ [= 1 2 * 3 4 5 =]);
+ bt_assert(p2 ~ [= 1 2 3+ 4 5 =]);
+ bt_assert(p2 ~ [= 1 2 3+ 4+ 5 =]);
+ bt_assert(p2 !~ [= 1 2 3+ 5+ 4 5 =]);
+ bt_assert(p2 !~ [= 1 2 3 3 5+ 4 5 =]);
+ bt_assert(p2.delete(3) = +empty+.prepend(5).prepend(4).prepend(2).prepend(1));
+ bt_assert(p2.delete([4..5]) = +empty+.prepend(3).prepend(3).prepend(2).prepend(1));
+
+ bt_assert(format([= 1 2+ 3 =]) = "[= 1 2 + 3 =]");
+
+ # iteration over path
+ int x = 0;
+ int y = 0;
+ for int i in p2 do {
+ x = x + i;
+ y = y + x;
+ }
+ bt_assert(x = 18 && y = 50);
+}
+
+bt_test_suite(t_path_new, "Testing paths (new syntax)");
@@ -847,7 +931,7 @@ bt_test_suite(t_path, "Testing paths");
define p23 = (2, 3);
-function t_clist()
+function t_clist_old()
clist l;
clist l2;
clist r;
@@ -967,7 +1051,129 @@ clist r;
bt_assert(x = 36);
}
-bt_test_suite(t_clist, "Testing lists of communities");
+bt_test_suite(t_clist_old, "Testing lists of communities (old syntax)");
+
+function t_clist_new()
+{
+ bt_assert((10, 20).asn = 10);
+ bt_assert((10, 20).data = 20);
+ bt_assert(p23.asn = 2);
+ bt_assert(p23.data = 3);
+
+ clist l;
+ bt_assert(l = -empty-);
+ bt_assert(l !~ [(*,*)]);
+ bt_assert((l ~ [(*,*)]) != (l !~ [(*,*)]));
+
+ bt_assert(-empty- = -empty-);
+
+ l.add( (one,2) );
+ bt_assert(l ~ [(*,*)]);
+ l.add( (2,one+2) );
+ bt_assert(format(l) = "(clist (1,2) (2,3))");
+
+ bt_assert(l.empty = -empty-);
+
+ bt_assert((2,3) ~ l);
+ bt_assert(l ~ [(1,*)]);
+ bt_assert(l ~ [p23]);
+ bt_assert(l ~ [(2,2..3)]);
+ bt_assert(l ~ [(1,1..2)]);
+ bt_assert(l ~ [(1,1)..(1,2)]);
+ bt_assert(l !~ []);
+
+ l.add((2,5));
+ l.add((5,one));
+ l.add((6,one));
+ l.add((one,one));
+ l.delete([(5,1),(6,one),(one,1)]);
+ l.delete([(5,one),(6,one)]);
+ l.filter([(1,*)]);
+ bt_assert(l = -empty-.add((1,2)));
+
+ bt_assert((2,3) !~ l);
+ bt_assert(l !~ [(2,*)]);
+ bt_assert(l !~ [(one,3..6)]);
+ bt_assert(l ~ [(*,*)]);
+
+ l.add((3,one));
+ l.add((one+one+one,one+one));
+ l.add((3,3));
+ l.add((3,4));
+ l.add((3,5));
+ clist l2 = l.filter([(3,*)]);
+ l.delete([(3,2..4)]);
+ bt_assert(l = -empty-.add((1,2)).add((3,1)).add((3,5)));
+ bt_assert(l.len = 3);
+
+ l.add((3,2));
+ l.add((4,5));
+ bt_assert(l = -empty-.add((1,2)).add((3,1)).add((3,5)).add((3,2)).add((4,5)));
+
+ bt_assert(l.len = 5);
+ bt_assert(l ~ [(*,2)]);
+ bt_assert(l ~ [(*,5)]);
+ bt_assert(l ~ [(*, one)]);
+ bt_assert(l !~ [(*,3)]);
+ bt_assert(l !~ [(*,(one+6))]);
+ bt_assert(l !~ [(*, (one+one+one))]);
+
+ bt_assert(l.delete([]) = l);
+ bt_assert(l.filter([]) = -empty-);
+
+ l.delete([(*,(one+onef(3)))]);
+ l.delete([(*,(4+one))]);
+ bt_assert(l = -empty-.add((3,1)));
+
+ l.delete([(*,(onef(5)))]);
+ bt_assert(l = -empty-);
+
+ l2.add((3,6));
+ l = l2.filter([(3,1..4)]);
+ l2.filter([(3,3..6)]);
+
+ # 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))");
+
+ # clist B (30,40,50)
+ bt_assert(l2 = -empty-.add((3,3)).add((3,4)).add((3,5)).add((3,6)));
+ bt_assert(format(l2) = "(clist (3,3) (3,4) (3,5) (3,6))");
+
+ # clist A union B
+ clist r = l.add(l2);
+ bt_assert(r = -empty-.add((3,1)).add((3,2)).add((3,3)).add((3,4)).add((3,5)).add((3,6)));
+ bt_assert(format(r) = "(clist (3,1) (3,2) (3,3) (3,4) (3,5) (3,6))");
+
+ # clist A isect B
+ r = l.filter(l2);
+ bt_assert(r = -empty-.add((3,3)).add((3,4)));
+ bt_assert(format(r) = "(clist (3,3) (3,4))");
+
+ # clist A \ B
+ r = l.delete(l2);
+ bt_assert(r = -empty-.add((3,1)).add((3,2)));
+ bt_assert(format(r) = "(clist (3,1) (3,2))");
+
+ # clist in c set
+ r = l.filter([(3,1), (*,2)]);
+ bt_assert(r = -empty-.add((3,1)).add((3,2)));
+ bt_assert(format(r) = "(clist (3,1) (3,2))");
+
+ # minimim & maximum element
+ r = -empty-.add((2,1)).add((1,3)).add((2,2)).add((3,1)).add((2,3));
+ bt_assert(format(r) = "(clist (2,1) (1,3) (2,2) (3,1) (2,3))");
+ bt_assert(r.min = (1,3));
+ bt_assert(r.max = (3,1));
+
+ # iteration over clist
+ int x = 0;
+ for pair c in r do
+ x = x + c.asn * c.asn * c.data;
+ bt_assert(x = 36);
+}
+
+bt_test_suite(t_clist_new, "Testing lists of communities (new syntax)");
@@ -1002,11 +1208,12 @@ bt_test_suite(t_ec, "Testing extended communities");
* -------------------------------
*/
-function t_eclist()
+function t_eclist_old()
eclist el;
eclist el2;
eclist r;
{
+ # Deprecated syntax
el = -- empty --;
el = add(el, (rt, 10, 20));
el = add(el, (ro, 10.20.30.40, 100));
@@ -1089,7 +1296,99 @@ eclist r;
bt_assert(x = 3);
}
-bt_test_suite(t_eclist, "Testing lists of extended communities");
+bt_test_suite(t_eclist_old, "Testing lists of extended communities");
+
+
+function t_eclist_new()
+{
+ # New syntax
+ eclist el;
+ bt_assert(el = --empty--);
+ el.add((rt, 10, 20));
+ el.add((ro, 10.20.30.40, 100));
+ el.add((ro, 11.21.31.41.mask(16), 200));
+
+ bt_assert(--empty-- = --empty--);
+ bt_assert(((rt, 10, 20)) !~ --empty--);
+
+ bt_assert(format(el) = "(eclist (rt, 10, 20) (ro, 10.20.30.40, 100) (ro, 11.21.0.0, 200))");
+ bt_assert(el.len = 3);
+ el.delete((rt, 10, 20));
+ el.delete((rt, 10, 30));
+ bt_assert(el = (--empty--).add((ro, 10.20.30.40, 100)).add((ro, 11.21.0.0, 200)));
+
+ bt_assert(el.empty = --empty--);
+
+ el.add((unknown 2, ten, 1));
+ el.add((unknown 5, ten, 1));
+ el.add((rt, ten, one+one));
+ el.add((rt, 10, 3));
+ el.add((rt, 10, 4));
+ el.add((rt, 10, 5));
+ el.add((generic, 0x2000a, 3*ten));
+ el.delete([(rt, 10, 2..ten)]);
+ bt_assert(el = (--empty--).add((ro, 10.20.30.40, 100)).add((ro, 11.21.0.0, 200)).add((rt, 10, 1)).add((unknown 5, 10, 1)).add((rt, 10, 30)));
+
+ el.filter([(rt, 10, *)]);
+ bt_assert(el = (--empty--).add((rt, 10, 1)).add((rt, 10, 30)));
+ bt_assert((rt, 10, 1) ~ el);
+ bt_assert(el ~ [(rt, 10, ten..40)]);
+ bt_assert((rt, 10, 20) !~ el);
+ bt_assert((ro, 10.20.30.40, 100) !~ el);
+ bt_assert(el !~ [(rt, 10, 35..40)]);
+ bt_assert(el !~ [(ro, 10, *)]);
+ bt_assert(el !~ []);
+
+ el.add((rt, 10, 40));
+ eclist el2 = el.filter([(rt, 10, 20..40)] );
+ el2.add((rt, 10, 50));
+
+ bt_assert(el.delete([]) = el);
+ bt_assert(el.filter([]) = --empty--);
+
+ # eclist A (1,30,40)
+ bt_assert(el = --empty--.add((rt, 10, 1)).add((rt, 10, 30)).add((rt, 10, 40)));
+ bt_assert(format(el) = "(eclist (rt, 10, 1) (rt, 10, 30) (rt, 10, 40))");
+
+ # eclist B (30,40,50)
+ bt_assert(el2 = --empty--.add((rt, 10, 30)).add((rt, 10, 40)).add((rt, 10, 50)));
+ bt_assert(format(el2) = "(eclist (rt, 10, 30) (rt, 10, 40) (rt, 10, 50))");
+
+ # eclist A union B
+ eclist r = el2.add(el);
+ bt_assert(r = --empty--.add((rt, 10, 30)).add((rt, 10, 40)).add((rt, 10, 50)).add((rt, 10, 1)));
+ bt_assert(format(r) = "(eclist (rt, 10, 30) (rt, 10, 40) (rt, 10, 50) (rt, 10, 1))");
+
+ # eclist A isect B
+ r = el.filter(el2);
+ bt_assert(r = --empty--.add((rt, 10, 30)).add((rt, 10, 40)));
+ bt_assert(format(r) = "(eclist (rt, 10, 30) (rt, 10, 40))");
+
+ # eclist A \ B
+ r = el.delete(el2);
+ bt_assert(r = --empty--.add((rt, 10, 1)));
+ bt_assert(format(r) = "(eclist (rt, 10, 1))");
+
+ # eclist in ec set
+ r = el.filter([(rt, 10, 1), (rt, 10, 25..30), (ro, 10, 40)]);
+ bt_assert(r = --empty--.add((rt, 10, 1)).add((rt, 10, 30)));
+ bt_assert(format(r) = "(eclist (rt, 10, 1) (rt, 10, 30))");
+
+ # minimim & maximum element
+ r = --empty--.add((rt, 2, 1)).add((rt, 1, 3)).add((rt, 2, 2)).add((rt, 3, 1)).add((rt, 2, 3));
+ bt_assert(format(r) = "(eclist (rt, 2, 1) (rt, 1, 3) (rt, 2, 2) (rt, 3, 1) (rt, 2, 3))");
+ bt_assert(r.min = (rt, 1, 3));
+ bt_assert(r.max = (rt, 3, 1));
+
+ # iteration over eclist
+ int x = 0;
+ for ec c in r do
+ if c > (rt, 2, 0) && c < (rt, 3, 0) then
+ x = x + 1;
+ bt_assert(x = 3);
+}
+
+bt_test_suite(t_eclist_new, "Testing lists of extended communities");
@@ -1138,7 +1437,7 @@ function lc mktrip(int a)
return (a, 2*a, 3*a);
}
-function t_lclist()
+function t_lclist_old()
lclist ll;
lclist ll2;
lclist r;
@@ -1220,7 +1519,92 @@ lclist r;
bt_assert(mx = r.max);
}
-bt_test_suite(t_lclist, "Testing lists of large communities");
+bt_test_suite(t_lclist_old, "Testing lists of large communities");
+
+
+function t_lclist_new()
+{
+ bt_assert(---empty--- = ---empty---);
+ bt_assert((10, 20, 30) !~ ---empty---);
+
+ bt_assert((10, 20, 30).asn = 10);
+ bt_assert((10, 20, 30).data1 = 20);
+ bt_assert((10, 20, 30).data2 = 30);
+
+ lclist ll;
+ bt_assert(ll = ---empty---);
+ ll.add((ten, 20, 30));
+ ll.add((1000, 2000, 3000));
+ ll.add(mktrip(100000));
+
+ bt_assert(ll.empty = ---empty---);
+ bt_assert(format(ll) = "(lclist (10, 20, 30) (1000, 2000, 3000) (100000, 200000, 300000))");
+ bt_assert(ll.len = 3);
+ bt_assert(ll = ---empty---.add((10, 20, 30)).add((1000, 2000, 3000)).add((100000, 200000, 300000)));
+
+ bt_assert(mktrip(1000) ~ ll);
+ bt_assert(mktrip(100) !~ ll);
+
+ ll.empty;
+ ll.add((10, 10, 10));
+ ll.add((20, 20, 20));
+ ll.add((30, 30, 30));
+
+ lclist ll2;
+ ll2.add((20, 20, 20));
+ ll2.add((30, 30, 30));
+ ll2.add((40, 40, 40));
+
+ bt_assert(ll.delete([]) = ll);
+ bt_assert(ll.filter([]) = ---empty---);
+
+ # lclist A (10, 20, 30)
+ bt_assert(format(ll) = "(lclist (10, 10, 10) (20, 20, 20) (30, 30, 30))");
+
+ # lclist B (20, 30, 40)
+ bt_assert(format(ll2) = "(lclist (20, 20, 20) (30, 30, 30) (40, 40, 40))");
+
+ # lclist A union B
+ lclist r = ll.add(ll2);
+ bt_assert(r = ---empty---.add((10,10,10)).add((20,20,20)).add((30,30,30)).add((40,40,40)));
+ bt_assert(format(r) = "(lclist (10, 10, 10) (20, 20, 20) (30, 30, 30) (40, 40, 40))");
+
+ # lclist A isect B
+ r = ll.filter(ll2);
+ bt_assert(r = ---empty---.add((20, 20, 20)).add((30, 30, 30)));
+ bt_assert(format(r) = "(lclist (20, 20, 20) (30, 30, 30))");
+
+ # lclist A \ B
+ r = ll.delete(ll2);
+ bt_assert(r = ---empty---.add((10, 10, 10)));
+ bt_assert(format(r) = "(lclist (10, 10, 10))");
+
+ # lclist in lc set
+ r = ll.filter([(5..15, *, *), (20, 15..25, *)]);
+ bt_assert(r = ---empty---.add((10, 10, 10)).add((20, 20, 20)));
+ bt_assert(format(r) = "(lclist (10, 10, 10) (20, 20, 20))");
+
+ # minimim & maximum element
+ r = ---empty---.add((2, 3, 3)).add((1, 2, 3)).add((2, 3, 1)).add((3, 1, 2)).add((2, 1, 3));
+ bt_assert(format(r) = "(lclist (2, 3, 3) (1, 2, 3) (2, 3, 1) (3, 1, 2) (2, 1, 3))");
+ bt_assert(r.min = (1, 2, 3));
+ bt_assert(r.max = (3, 1, 2));
+
+ # iteration over lclist
+ int x = 0;
+ int y = 0;
+ lc mx = (0, 0, 0);
+ for lc c in r do {
+ int asn2 = c.asn * c.asn;
+ x = x + asn2 * c.data1;
+ y = y + asn2 * c.data2;
+ if c > mx then mx = c;
+ }
+ bt_assert(x = 39 && y = 49);
+ bt_assert(mx = r.max);
+}
+
+bt_test_suite(t_lclist_new, "Testing lists of large communities");