1 #include "filter_expr_printer.hpp"
2
3 #include "filter_expr_parser_ast.hpp"
4 #include "filter_expr_parser_grammar.hpp"
5 #include "logging.hpp"
6
7 #include <boost/spirit/home/x3/char/char_class.hpp>
8 #include <boost/spirit/home/x3/core/parse.hpp>
9
10 #include <cstdint>
11 #include <format>
12 #include <list>
13 #include <optional>
14 #include <string>
15 #include <string_view>
16 #include <utility>
17
18 namespace redfish
19 {
20
21 ///////////////////////////////////////////////////////////////////////////
22 // The AST Printer
23 // Prints a $filter AST as a string to be compared, including explicit braces
24 // around all operations to help debugging AST issues.
25 ///////////////////////////////////////////////////////////////////////////
26
27 using result_type = std::string;
operator ()(double x) const28 std::string FilterExpressionPrinter::operator()(double x) const
29 {
30 return std::format("double({})", x);
31 }
operator ()(int64_t x) const32 std::string FilterExpressionPrinter::operator()(int64_t x) const
33 {
34 return std::format("int({})", x);
35 }
36 std::string
operator ()(const filter_ast::QuotedString & x) const37 FilterExpressionPrinter::operator()(const filter_ast::QuotedString& x) const
38 {
39 return std::format("quoted_string(\"{}\")", static_cast<std::string>(x));
40 }
operator ()(const filter_ast::UnquotedString & x) const41 std::string FilterExpressionPrinter::operator()(
42 const filter_ast::UnquotedString& x) const
43 {
44 return std::format("unquoted_string(\"{}\")", static_cast<std::string>(x));
45 }
46
47 std::string
operator ()(const filter_ast::LogicalNot & x) const48 FilterExpressionPrinter::operator()(const filter_ast::LogicalNot& x) const
49 {
50 std::string prefix;
51 std::string postfix;
52 if (x.isLogicalNot)
53 {
54 prefix = "not(";
55 postfix = ")";
56 }
57 return std::format("{}{}{}", prefix, (*this)(x.operand), postfix);
58 }
59 std::string
operator ()(const filter_ast::LogicalOr & x) const60 FilterExpressionPrinter::operator()(const filter_ast::LogicalOr& x) const
61 {
62 std::string prefix;
63 std::string postfix;
64 if (!x.rest.empty())
65 {
66 prefix = "(";
67 postfix = ")";
68 }
69 std::string out = std::format("{}{}{}", prefix, (*this)(x.first), postfix);
70
71 for (const filter_ast::LogicalNot& oper : x.rest)
72 {
73 out += std::format(" or ({})", (*this)(oper));
74 }
75 return out;
76 }
77
78 std::string
operator ()(const filter_ast::LogicalAnd & x) const79 FilterExpressionPrinter::operator()(const filter_ast::LogicalAnd& x) const
80 {
81 std::string prefix;
82 std::string postfix;
83 if (!x.rest.empty())
84 {
85 prefix = "(";
86 postfix = ")";
87 }
88 std::string out = std::format("{}{}{}", prefix, (*this)(x.first), postfix);
89
90 for (const filter_ast::LogicalOr& oper : x.rest)
91 {
92 out += std::format(" and ({})", (*this)(oper));
93 }
94 return out;
95 }
96
toString(filter_ast::ComparisonOpEnum rel)97 static std::string toString(filter_ast::ComparisonOpEnum rel)
98 {
99 switch (rel)
100 {
101 case filter_ast::ComparisonOpEnum::GreaterThan:
102 return "Greater Than";
103 case filter_ast::ComparisonOpEnum::GreaterThanOrEqual:
104 return "Greater Than Or Equal";
105 case filter_ast::ComparisonOpEnum::LessThan:
106 return "Less Than";
107 case filter_ast::ComparisonOpEnum::LessThanOrEqual:
108 return "Less Than Or Equal";
109 case filter_ast::ComparisonOpEnum::Equals:
110 return "Equals";
111 case filter_ast::ComparisonOpEnum::NotEquals:
112 return "Not Equal";
113 default:
114 return "Invalid";
115 }
116 }
117
118 std::string
operator ()(const filter_ast::Comparison & x) const119 FilterExpressionPrinter::operator()(const filter_ast::Comparison& x) const
120 {
121 std::string left = boost::apply_visitor(*this, x.left);
122 std::string right = boost::apply_visitor(*this, x.right);
123
124 return std::format("{} {} {}", left, toString(x.token), right);
125 }
126
operator ()(const filter_ast::BooleanOp & operation) const127 std::string FilterExpressionPrinter::operator()(
128 const filter_ast::BooleanOp& operation) const
129 {
130 return boost::apply_visitor(*this, operation);
131 }
132
parseFilter(std::string_view expr)133 std::optional<filter_grammar::program> parseFilter(std::string_view expr)
134 {
135 const auto& grammar = filter_grammar::grammar;
136 filter_grammar::program program;
137
138 std::string_view::iterator iter = expr.begin();
139 const std::string_view::iterator end = expr.end();
140 BMCWEB_LOG_DEBUG("Parsing input string \"{}\"", expr);
141
142 // Filter examples have unclear guidelines about between which arguments
143 // spaces are allowed or disallowed. Specification is not clear, so in
144 // almost all cases we allow zero or more
145 using boost::spirit::x3::space;
146 if (!boost::spirit::x3::phrase_parse(iter, end, grammar, space, program))
147 {
148 std::string_view rest(iter, end);
149
150 BMCWEB_LOG_ERROR("Parsing failed stopped at \"{}\"", rest);
151 return std::nullopt;
152 }
153 BMCWEB_LOG_DEBUG("Parsed AST: \"{}\"", FilterExpressionPrinter()(program));
154 return {std::move(program)};
155 }
156 } // namespace redfish
157