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