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