]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add === and !==
authorAlan T. DeKok <aland@freeradius.org>
Thu, 1 Dec 2022 19:31:55 +0000 (14:31 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Thu, 1 Dec 2022 19:33:08 +0000 (14:33 -0500)
After other languages which have the same thing.

This is less useful for FreeRADIUS, as all "variables" are strongly
typed.  But it could be used for non-quoted xlats, and it could
be used in the future for "void" types

doc/antora/modules/reference/pages/unlang/condition/cmp.adoc
src/lib/unlang/xlat_expr.c
src/lib/util/calc.c
src/lib/util/token.c
src/lib/util/token.h
src/tests/unit/xlat/cond_base.txt

index bbefd0d4cd63b17cdd81172ac1b4d5dfc0c9521a..9762aeef4060c3715fc86d2d61712ada2ae66eff 100644 (file)
@@ -21,8 +21,10 @@ The comparison operators are given below.
 | Operator | Description
 | < | less than
 | \<= | less than or equals
-| == | equals
-| != | not equals
+| == | equals, with automatic type casting
+| != | not equals, with automatic type casting
+| === | equals, but both operands have to be of the same type
+| !== | not equals, or they are not of the same type
 | >= | greater than or equals
 | >  | greater than
 | xref:condition/regex.adoc[=~] | regular expression matches
index 94010f0e0fefb3797ead2139825bcfd47d6614cd..f5cb16093380db17ac74d1df4eeb657b72cf5e1a 100644 (file)
@@ -495,6 +495,8 @@ XLAT_CMP_FUNC(cmp_lt,  T_OP_LT)
 XLAT_CMP_FUNC(cmp_le,  T_OP_LE)
 XLAT_CMP_FUNC(cmp_gt,  T_OP_GT)
 XLAT_CMP_FUNC(cmp_ge,  T_OP_GE)
+XLAT_CMP_FUNC(cmp_eq_type,  T_OP_CMP_EQ_TYPE)
+XLAT_CMP_FUNC(cmp_ne_type,  T_OP_CMP_NE_TYPE)
 
 typedef struct {
        fr_token_t      op;
@@ -1696,6 +1698,8 @@ int xlat_register_expressions(void)
        XLAT_REGISTER_BINARY_CMP(T_OP_LE, le);
        XLAT_REGISTER_BINARY_CMP(T_OP_GT, gt);
        XLAT_REGISTER_BINARY_CMP(T_OP_GE, ge);
+       XLAT_REGISTER_BINARY_CMP(T_OP_CMP_EQ_TYPE, eq_type);
+       XLAT_REGISTER_BINARY_CMP(T_OP_CMP_NE_TYPE, ne_type);
 
        XLAT_REGISTER_REGEX_OP(T_OP_REG_EQ, reg_eq);
        XLAT_REGISTER_REGEX_OP(T_OP_REG_NE, reg_ne);
@@ -1758,6 +1762,9 @@ static const fr_sbuff_term_elem_t binary_ops[T_TOKEN_LAST] = {
        [ T_OP_GT ]             = L("cmp_gt"),
        [ T_OP_GE ]             = L("cmp_ge"),
 
+       [ T_OP_CMP_EQ_TYPE ]    = L("cmp_eq_type"),
+       [ T_OP_CMP_NE_TYPE ]    = L("cmp_ne_type"),
+
        [ T_OP_REG_EQ ]         = L("reg_eq"),
        [ T_OP_REG_NE ]         = L("reg_ne"),
 };
@@ -1811,6 +1818,9 @@ static const int precedence[T_TOKEN_LAST] = {
        [T_OP_CMP_EQ]   = P(4,1),
        [T_OP_NE]       = P(4,1),
 
+       [T_OP_CMP_EQ_TYPE] = P(4,1),
+       [T_OP_CMP_NE_TYPE] = P(4,1),
+
        [T_OP_LT]       = P(5,0),
        [T_OP_LE]       = P(5,0),
        [T_OP_GT]       = P(5,0),
@@ -2399,6 +2409,7 @@ done:
  */
 static fr_table_num_ordered_t const expr_assignment_op_table[] = {
        { L("!="),      T_OP_NE                 },
+       { L("!=="),     T_OP_CMP_NE_TYPE        },
 
        { L("&"),       T_AND                   },
        { L("&&"),      T_LAND                  },
@@ -2418,6 +2429,7 @@ static fr_table_num_ordered_t const expr_assignment_op_table[] = {
 
        { L("="),       T_OP_EQ                 },
        { L("=="),      T_OP_CMP_EQ             },
+       { L("==="),     T_OP_CMP_EQ_TYPE        },
 
        { L("=~"),      T_OP_REG_EQ             },
        { L("!~"),      T_OP_REG_NE             },
index f9ea4322086f943606f67fc9d7ccd6cb183e8e1c..63badf72533f73bc1a53f4c691042e52e90ea2e4 100644 (file)
@@ -1777,6 +1777,32 @@ int fr_value_calc_binary_op(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t hint
        if (!fr_type_is_leaf(a->type)) return invalid_type(a->type);
        if (!fr_type_is_leaf(b->type)) return invalid_type(b->type);
 
+       /*
+        *      === and !== also check types.  If the types are
+        *      different, it's a failure.  Otherwise they revert to == and !=.
+        */
+       switch (op) {
+       case T_OP_CMP_EQ_TYPE:
+               if (a->type != b->type) {
+               mismatch_type:
+                       fr_value_box_init(dst, FR_TYPE_BOOL, NULL, false); /* @todo - enum */
+                       dst->vb_bool = false;
+                       return 0;
+               }
+               op = T_OP_CMP_EQ;
+               break;
+
+       case T_OP_CMP_NE_TYPE:
+               if (a->type != b->type) goto mismatch_type;
+
+               op = T_OP_NE;
+               break;
+
+       default:
+               break;
+       }
+
+
        fr_value_box_init_null(&one);
        fr_value_box_init_null(&two);
 
index d194becf5e3bafef08633f3ed6c46963baa2e2b2..c447c303a4052365a9a9ce0b24d9b32e3c5cc997 100644 (file)
@@ -45,11 +45,13 @@ fr_table_num_ordered_t const fr_tokens_table[] = {
        { L("=*"),      T_OP_CMP_TRUE   },
        { L("!*"),      T_OP_CMP_FALSE  },
        { L("=="),      T_OP_CMP_EQ     },
+       { L("==="),     T_OP_CMP_EQ_TYPE },
        { L("^="),      T_OP_PREPEND    },
        { L("|="),      T_OP_OR_EQ      },
        { L("&="),      T_OP_AND_EQ     },
        { L("="),       T_OP_EQ         },
        { L("!="),      T_OP_NE         },
+       { L("!=="),     T_OP_CMP_NE_TYPE },
        { L(">="),      T_OP_GE         },
        { L(">"),       T_OP_GT         },
        { L("<="),      T_OP_LE         },
@@ -124,6 +126,9 @@ char const *fr_tokens[T_TOKEN_LAST] = {
 
        [T_OP_CMP_EQ] = "==",
 
+       [T_OP_CMP_EQ_TYPE] = "===",
+       [T_OP_CMP_NE_TYPE] = "!==",
+
        [T_OP_PREPEND] = "^=",
 
        [T_HASH]                  = "#",
@@ -199,6 +204,8 @@ const bool fr_equality_op[T_TOKEN_LAST] = {
        T(CMP_TRUE),
        T(CMP_FALSE),
        T(CMP_EQ),
+       T(CMP_EQ_TYPE),
+       T(CMP_NE_TYPE),
 };
 
 #undef T
index 79c01df894fe3cdd2d2c60471b4600718a4b5792..3507613f1ba15423f23bf48eff10d0f8429f04e8 100644 (file)
@@ -104,6 +104,8 @@ typedef enum fr_token {
        T_OP_CMP_TRUE,                  /* =* */
        T_OP_CMP_FALSE,                 /* !* */
        T_OP_CMP_EQ,                    /* == */
+       T_OP_CMP_EQ_TYPE,               /* === */
+       T_OP_CMP_NE_TYPE,               /* !== */
 
        /*
         *      Only used by LDAP ???
index 83c3a9b8b5d804a69ef06470967d7cd2343e487a..78d4397885a60a58a6eea825711aae5917cb43e4 100644 (file)
@@ -762,6 +762,38 @@ match ERROR offset 1: Unexpected text - attribute names must prefixed with '&'
 xlat_purify handled
 match %{rcode:'handled'}
 
+#
+#  Automatic casting
+#
+xlat_purify (192.168.0.1 == "192.168.0.1")
+match true
+
+xlat_purify (192.168.0.1 != "192.168.0.1")
+match false
+
+xlat_purify (192.168.0.1 != "192.168.0.2")
+match true
+
+#
+#  Types are different, so they don't match.
+#
+xlat_purify (192.168.0.1 === "192.168.0.1")
+match false
+
+xlat_purify (192.168.0.1 !== "192.168.0.1")
+match false
+
+xlat_purify (192.168.0.1 !== "192.168.0.2")
+match false
+
+xlat_purify (192.168.0.1 === 192.168.0.1)
+match true
+
+xlat_purify (192.168.0.1 !== 192.168.0.1)
+match false
+
+xlat_purify (192.168.0.1 !== 192.168.0.2)
+match true
 
 count
-match 316
+match 334