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