commit 1c1c612ae671281e9d7db4376d9664aac9b8492a
Author: Alexander Zubkov <green@qrator.net>
Date:   Thu Dec 16 11:00:27 2021 +0100

    Filter: Add operation to pick community components
    
    Add new operation '@', that can be used to pick community components
    from pair (community) or from lc (large community) types. For example:
    
    (10, 20) @ 1 --> 10
    (10, 20) @ 2 --> 20
    
    (10, 20, 30) @ 1 --> 10
    (10, 20, 30) @ 2 --> 20
    (10, 20, 30) @ 3 --> 30
    
    Signed-off-by: Alexander Zubkov <green@qrator.net>

diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index 704a1750..769bb7b6 100644
--- a/conf/cf-lex.l
+++ b/conf/cf-lex.l
@@ -347,7 +347,7 @@ else: {
   return DDOT;
 }
 
-[={}:;,.()+*/%<>~\[\]?!\|-] {
+[={}:;,.()+*/%<>~@\[\]?!\|-] {
   return yytext[0];
 }
 
diff --git a/conf/confbase.Y b/conf/confbase.Y
index 6985783b..2b9c345a 100644
--- a/conf/confbase.Y
+++ b/conf/confbase.Y
@@ -119,7 +119,7 @@ CF_DECLS
 
 %nonassoc PREFIX_DUMMY
 %left AND OR
-%nonassoc '=' '<' '>' '~' GEQ LEQ NEQ NMA PO PC
+%nonassoc '=' '<' '>' '~' '@' GEQ LEQ NEQ NMA PO PC
 %left '+' '-'
 %left '*' '/' '%'
 %left '!'
diff --git a/filter/config.Y b/filter/config.Y
index 7820e719..570f1e32 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -770,6 +770,7 @@ term:
  | term GEQ term	{ $$ = f_new_inst(FI_LTE, $3, $1); }
  | term '~' term	{ $$ = f_new_inst(FI_MATCH, $1, $3); }
  | term NMA term	{ $$ = f_new_inst(FI_NOT_MATCH, $1, $3); }
+ | term '@' term	{ $$ = f_new_inst(FI_PICK, $1, $3); }
  | '!' term		{ $$ = f_new_inst(FI_NOT, $2); }
  | DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED, $3); }
 
diff --git a/filter/f-inst.c b/filter/f-inst.c
index b876a937..ab0d0a2b 100644
--- a/filter/f-inst.c
+++ b/filter/f-inst.c
@@ -366,6 +366,32 @@
     RESULT(T_PATH_MASK, path_mask, pm);
   }
 
+  INST(FI_PICK, 2, 1) {
+    ARG_ANY(1);
+    ARG(2, T_INT);
+    int index = v2.val.i;
+    switch(v1.type)
+    {
+      case T_PAIR:
+        switch(index) {
+          case 1: RESULT(T_INT, i, v1.val.i >> 16); break;
+          case 2: RESULT(T_INT, i, v1.val.i & 0xFFFF); break;
+          default: runtime( "Invalid index for pair" );
+        }
+        break;
+      case T_LC:
+        switch(index) {
+          case 1: RESULT(T_INT, i, v1.val.lc.asn); break;
+          case 2: RESULT(T_INT, i, v1.val.lc.ldp1); break;
+          case 3: RESULT(T_INT, i, v1.val.lc.ldp2); break;
+          default: runtime( "Invalid index for lc" );
+        }
+        break;
+      default:
+        runtime( "Pair or lc expected" );
+    }
+  }
+
 /* Relational operators */
 
   INST(FI_NEQ, 2, 1) {
diff --git a/filter/test.conf b/filter/test.conf
index 3a8804a1..3fa8560c 100644
--- a/filter/test.conf
+++ b/filter/test.conf
@@ -684,6 +684,9 @@ clist l;
 clist l2;
 clist r;
 {
+	bt_assert(((10, 20) @ 1) = 10);
+	bt_assert(((10, 20) @ 2) = 20);
+
 	l = - empty -;
 	bt_assert(l !~ [(*,*)]);
 	bt_assert((l ~ [(*,*)]) != (l !~ [(*,*)]));
@@ -940,6 +943,10 @@ lclist r;
 	bt_assert(---empty--- = ---empty---);
 	bt_assert((10, 20, 30) !~ ---empty---);
 
+	bt_assert(((10, 20, 30) @ 1) = 10);
+	bt_assert(((10, 20, 30) @ 2) = 20);
+	bt_assert(((10, 20, 30) @ 3) = 30);
+
 	ll = --- empty ---;
 	ll = add(ll, (ten, 20, 30));
 	ll = add(ll, (1000, 2000, 3000));
