@@ -940,6 +940,57 @@ BOOST_AUTO_TEST_CASE(test_parser_bad_expressions) {
940940 }
941941}
942942
943+ BOOST_AUTO_TEST_CASE (test_parser_with_attached_tokens) {
944+ ECF_NAME_THIS_TEST ();
945+
946+ // ECFLOW-2112:
947+ // A word operator (and/or/eq/ne/ge/gt/le/lt) must be a token in its own right, i.e. surrounded by
948+ // whitespace/brackets. When it is "attached"/glued to an adjacent operand it forms a single identifier
949+ // (e.g. 'completeand', '515and', 'andb') which silently changes the meaning of the expression.
950+ // These expressions must be rejected by the parser.
951+
952+ using namespace std ::string_literals;
953+ std::vector expressions = {
954+ // ---- The exact malformed expressions observed in operational checkpoints (ECFLOW-2112) ----
955+ " :TIME ge 515and :ECF_DATE gt :YMD" s,
956+ " :TIME ge 1115and :ECF_DATE gt :YMD" s,
957+ " input eq completeand ( ( ../lag:YMD + 5) ge ../prep:YMD)" s,
958+
959+ // ---- 'and' attached to the preceding operand ----
960+ " a == completeand b == complete" s, // node state glued to 'and'
961+ " a eq completeand b eq complete" s, // word operators + state glued to 'and'
962+ " 1 == 1and 2 == 2" s, // integer glued to 'and'
963+ " a:value == 10and b:value == 20" s, // integer glued to 'and'
964+
965+ // ---- 'and' attached to the following operand ----
966+ " a == complete andb == complete" s, // 'and' glued to node
967+ " a == complete and2 == 2" s, // 'and' glued to integer
968+
969+ // ---- 'or' attached to an operand ----
970+ " a == completeor b == complete" s, // node state glued to 'or'
971+ " a == complete orb == complete" s, // 'or' glued to node
972+ " 1 == 1or 2 == 2" s, // integer glued to 'or'
973+
974+ // ---- word comparison operators glued to an operand ----
975+ " aeq complete" s, // 'eq' glued to left operand
976+ " a eqcomplete" s, // 'eq' glued to right operand
977+ " :TIME ge515 and :ECF_DATE gt :YMD" s, // 'ge' glued to integer
978+ " a ne completeand b == complete" s, // 'ne'/state glued to 'and'
979+
980+ // ---- symbolic '==' interacting with a glued word operator ----
981+ " a == completeand" s, // trailing glued 'and' after equality
982+ " a == b == completeor c == complete" s // 'or' glued to state after chained '=='
983+ };
984+
985+ for (const auto & expression : expressions) {
986+
987+ ExprParser theExprParser (expression);
988+ std::string error;
989+ auto actual = theExprParser.doParse (error);
990+ BOOST_CHECK_MESSAGE (!actual, expression << " expected to fail (ECFLOW-2112 attached tokens)" );
991+ }
992+ }
993+
943994BOOST_AUTO_TEST_SUITE_END ()
944995
945996BOOST_AUTO_TEST_SUITE_END ()
0 commit comments