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
| 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
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;
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);
[ 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"),
};
[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),
*/
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 },
{ 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 },
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);
{ 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 },
[T_OP_CMP_EQ] = "==",
+ [T_OP_CMP_EQ_TYPE] = "===",
+ [T_OP_CMP_NE_TYPE] = "!==",
+
[T_OP_PREPEND] = "^=",
[T_HASH] = "#",
T(CMP_TRUE),
T(CMP_FALSE),
T(CMP_EQ),
+ T(CMP_EQ_TYPE),
+ T(CMP_NE_TYPE),
};
#undef T
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 ???
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