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