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