1 #pragma once 2 3 #include "filter_expr_parser_ast.hpp" 4 5 #include <boost/spirit/home/x3.hpp> 6 7 namespace redfish::filter_grammar 8 { 9 10 // The below rules very intentionally use the same naming as section 7.3.4 of 11 // the redfish specification and are declared in the order of the precedence 12 // that the standard requires. 13 14 namespace details 15 { 16 using boost::spirit::x3::char_; 17 using boost::spirit::x3::int64; 18 using boost::spirit::x3::lit; 19 using boost::spirit::x3::real_parser; 20 using boost::spirit::x3::rule; 21 using boost::spirit::x3::strict_real_policies; 22 using boost::spirit::x3::symbols; 23 24 using filter_ast::BooleanOp; 25 using filter_ast::Comparison; 26 using filter_ast::ComparisonOpEnum; 27 using filter_ast::LogicalAnd; 28 using filter_ast::LogicalNot; 29 using filter_ast::LogicalOr; 30 using filter_ast::QuotedString; 31 using filter_ast::UnquotedString; 32 33 // Clang format makes a mess of these rules and makes them hard to read 34 // clang-format on 35 36 // Basic argument types 37 const rule<class QuotedStringId, QuotedString> quotedString("QuotedString"); 38 const rule<class UnquotedStrId, UnquotedString> unquotedString("UnquotedStr"); 39 40 // Value comparisons -> boolean (value eq value) (value lt number) 41 const rule<class BooleanOpId, BooleanOp> booleanOp("BooleanOp"); 42 const rule<class ComparisonId, Comparison> comparison("Comparison"); 43 44 // Logical Comparisons (bool eq bool) 45 const rule<class LogicalAndId, LogicalAnd> logicalAnd("LogicalAnd"); 46 const rule<class LogicalOrId, LogicalOr> logicalOr("LogicalOr"); 47 const rule<class LogicalNotId, LogicalNot> logicalNot("LogicalNot"); 48 49 ///// BEGIN GRAMMAR 50 51 // Two types of strings. 52 const auto quotedString_def = '\'' >> *('\\' >> char_ | ~char_('\'')) >> '\''; 53 const auto unquotedString_def = char_("a-zA-Z") >> *(char_("a-zA-Z0-9[]/")); 54 55 // Spaces 56 // Filter examples have unclear guidelines about between which arguments spaces 57 // are allowed or disallowed. Specification is not clear, so in almost all 58 // cases we allow zero or more 59 const auto sp = *lit(' '); 60 61 // Make sure we only parse true floating points as doubles 62 // This requires we have a "." which causes 1 to parse as int64, and 1.0 to 63 // parse as double 64 constexpr const real_parser<double, strict_real_policies<double>> strictDouble; 65 66 // Argument 67 const auto arg = strictDouble | int64 | unquotedString | quotedString; 68 69 // Note, unlike most other comparisons, spaces are required here (one or more) 70 // to differentiate keys from values (ex Fooeq eq foo) 71 const auto rsp = +lit(' '); 72 73 // Greater Than/Less Than/Equals 74 const symbols<ComparisonOpEnum> compare{ 75 {"gt", ComparisonOpEnum::GreaterThan}, 76 {"ge", ComparisonOpEnum::GreaterThanOrEqual}, 77 {"lt", ComparisonOpEnum::LessThan}, 78 {"le", ComparisonOpEnum::LessThanOrEqual}, 79 {"ne", ComparisonOpEnum::NotEquals}, 80 {"eq", ComparisonOpEnum::Equals}}; 81 82 const auto comparison_def = arg >> rsp >> compare >> rsp >> arg; 83 84 // Parenthesis 85 const auto parens = lit('(') >> sp >> logicalAnd >> sp >> lit(')'); 86 87 // Logical values 88 const auto booleanOp_def = comparison | parens; 89 90 // Not 91 const auto logicalNot_def = -(char_('n') >> lit("ot") >> sp) >> booleanOp; 92 93 // Or 94 const auto logicalOr_def = logicalNot >> *(sp >> lit("or") >> sp >> logicalNot); 95 96 // And 97 const auto logicalAnd_def = logicalOr >> *(sp >> lit("and") >> sp >> logicalOr); 98 99 BOOST_SPIRIT_DEFINE(booleanOp, logicalAnd, logicalNot, logicalOr, quotedString, 100 comparison, unquotedString); 101 ///// END GRAMMAR 102 103 // Make the grammar and AST available outside of the system 104 static constexpr auto& grammar = logicalAnd; 105 using program = filter_ast::LogicalAnd; 106 107 } // namespace details 108 109 using details::grammar; 110 using details::program; 111 112 } // namespace redfish::filter_grammar 113