Use lexme in redfish filter parser

Previously, the parser added space ignore instructions between every
node.  This is because there was one place where we actually cared about
spaces, when doing operator comparisons (x eq y).  If spaces are
ignored, it's impossible to determine the end of x and the beginning of
eq.

Spirit x3 has a lexeme, which allows us to ignore the parser skips
temporarily, which allows us to parse the operations in a much simpler
way.  This also requires that we change to phrase_parse instead of
parse.

Tested: Unit tests pass.  Good coverage.

Change-Id: Ifc6f1681e8524ba5032ee118cc3b3a18b30c639e
Signed-off-by: Ed Tanous <ed@tanous.net>
diff --git a/redfish-core/include/filter_expr_parser_grammar.hpp b/redfish-core/include/filter_expr_parser_grammar.hpp
index 7d71a0f..1b422ff 100644
--- a/redfish-core/include/filter_expr_parser_grammar.hpp
+++ b/redfish-core/include/filter_expr_parser_grammar.hpp
@@ -15,6 +15,7 @@
 {
 using boost::spirit::x3::char_;
 using boost::spirit::x3::int64;
+using boost::spirit::x3::lexeme;
 using boost::spirit::x3::lit;
 using boost::spirit::x3::real_parser;
 using boost::spirit::x3::rule;
@@ -49,15 +50,10 @@
 ///// BEGIN GRAMMAR
 
 // Two types of strings.
-const auto quotedString_def = '\'' >> *('\\' >> char_ | ~char_('\'')) >> '\'';
+const auto quotedString_def = '\'' >> lexeme[*('\\' >> char_ | ~char_('\''))] >>
+                              '\'';
 const auto unquotedString_def = char_("a-zA-Z") >> *(char_("a-zA-Z0-9[]/"));
 
-// Spaces
-// Filter examples have unclear guidelines about between which arguments spaces
-// are allowed or disallowed.  Specification is not clear, so in almost all
-// cases we allow zero or more
-const auto sp = *lit(' ');
-
 // Make sure we only parse true floating points as doubles
 // This requires we have a "." which causes 1 to parse as int64, and 1.0 to
 // parse as double
@@ -66,10 +62,6 @@
 // Argument
 const auto arg = strictDouble | int64 | unquotedString | quotedString;
 
-// Note, unlike most other comparisons, spaces are required here (one or more)
-// to differentiate keys from values (ex Fooeq eq foo)
-const auto rsp = +lit(' ');
-
 // Greater Than/Less Than/Equals
 const symbols<ComparisonOpEnum> compare{
     {"gt", ComparisonOpEnum::GreaterThan},
@@ -79,22 +71,25 @@
     {"ne", ComparisonOpEnum::NotEquals},
     {"eq", ComparisonOpEnum::Equals}};
 
-const auto comparison_def = arg >> rsp >> compare >> rsp >> arg;
+// Note, unlike most other comparisons, spaces are required here (one or more)
+// to differentiate keys from values (ex Fooeq eq foo)
+const auto comparison_def =
+    lexeme[arg >> +lit(' ') >> compare >> +lit(' ') >> arg];
 
 // Parenthesis
-const auto parens = lit('(') >> sp >> logicalAnd >> sp >> lit(')');
+const auto parens = lit('(') >> logicalAnd >> lit(')');
 
 // Logical values
 const auto booleanOp_def = comparison | parens;
 
 // Not
-const auto logicalNot_def = -(char_('n') >> lit("ot") >> sp) >> booleanOp;
+const auto logicalNot_def = -(char_('n') >> lit("ot")) >> booleanOp;
 
 // Or
-const auto logicalOr_def = logicalNot >> *(sp >> lit("or") >> sp >> logicalNot);
+const auto logicalOr_def = logicalNot >> *(lit("or") >> logicalNot);
 
 // And
-const auto logicalAnd_def = logicalOr >> *(sp >> lit("and") >> sp >> logicalOr);
+const auto logicalAnd_def = logicalOr >> *(lit("and") >> logicalOr);
 
 BOOST_SPIRIT_DEFINE(booleanOp, logicalAnd, logicalNot, logicalOr, quotedString,
                     comparison, unquotedString);
diff --git a/redfish-core/src/filter_expr_printer.cpp b/redfish-core/src/filter_expr_printer.cpp
index abf1daa..946f17c 100644
--- a/redfish-core/src/filter_expr_printer.cpp
+++ b/redfish-core/src/filter_expr_printer.cpp
@@ -133,11 +133,14 @@
     std::string_view::iterator iter = expr.begin();
     const std::string_view::iterator end = expr.end();
     BMCWEB_LOG_DEBUG("Parsing input string \"{}\"", expr);
-    bool r = boost::spirit::x3::parse(iter, end, grammar, program);
 
-    if (!r)
+    // Filter examples have unclear guidelines about between which arguments
+    // spaces are allowed or disallowed.  Specification is not clear, so in
+    // almost all cases we allow zero or more
+    using boost::spirit::x3::space;
+    if (!boost::spirit::x3::phrase_parse(iter, end, grammar, space, program))
     {
-        std::string rest(iter, end);
+        std::string_view rest(iter, end);
 
         BMCWEB_LOG_ERROR("Parsing failed stopped at \"{}\"", rest);
         return std::nullopt;
diff --git a/test/redfish-core/include/filter_expr_parser_test.cpp b/test/redfish-core/include/filter_expr_parser_test.cpp
index 2e5df83..1776028 100644
--- a/test/redfish-core/include/filter_expr_parser_test.cpp
+++ b/test/redfish-core/include/filter_expr_parser_test.cpp
@@ -107,6 +107,24 @@
     EXPECT_TRUE(parseFilter("'Physical' gt ProcessorSummary/Count"));
     EXPECT_TRUE(parseFilter("'Physical' ge ProcessorSummary/Count"));
 }
+
+TEST(FilterParser, Spaces)
+{
+    // Strings with spaces
+    parse("foo eq ' '", R"(unquoted_string("foo") Equals quoted_string(" "))");
+
+    // Lots of spaces between args
+    parse("foo       eq       ''",
+          R"(unquoted_string("foo") Equals quoted_string(""))");
+
+    // Lots of spaces between parens
+    parse("(      foo eq ''      )",
+          R"(unquoted_string("foo") Equals quoted_string(""))");
+
+    parse("not           foo eq ''",
+          R"(not(unquoted_string("foo") Equals quoted_string("")))");
+}
+
 TEST(FilterParser, Failures)
 {
     // Invalid expressions