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