1 /*
2 * Copyright Andrey Semashev 2007 - 2015.
3 * Distributed under the Boost Software License, Version 1.0.
4 * (See accompanying file LICENSE_1_0.txt or copy at
5 * http://www.boost.org/LICENSE_1_0.txt)
6 */
7 /*!
8 * \file filter_parser.cpp
9 * \author Andrey Semashev
10 * \date 31.03.2008
11 *
12 * \brief This header is the Boost.Log library implementation, see the library documentation
13 * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html.
14 */
15
16 #ifndef BOOST_LOG_WITHOUT_SETTINGS_PARSERS
17
18 #include <boost/log/detail/setup_config.hpp>
19 #include <cstddef>
20 #include <map>
21 #include <stack>
22 #include <string>
23 #include <sstream>
24 #include <stdexcept>
25 #include <utility>
26 #include <boost/assert.hpp>
27 #include <boost/none.hpp>
28 #include <boost/move/core.hpp>
29 #include <boost/move/utility_core.hpp>
30 #include <boost/optional/optional.hpp>
31 #include <boost/utility/in_place_factory.hpp>
32 #include <boost/phoenix/core.hpp>
33 #include <boost/phoenix/bind/bind_function_object.hpp>
34 #include <boost/phoenix/operator/logical.hpp>
35 #include <boost/log/detail/singleton.hpp>
36 #include <boost/log/exceptions.hpp>
37 #include <boost/log/utility/setup/filter_parser.hpp>
38 #if !defined(BOOST_LOG_NO_THREADS)
39 #include <boost/log/detail/locks.hpp>
40 #include <boost/log/detail/light_rw_mutex.hpp>
41 #endif // !defined(BOOST_LOG_NO_THREADS)
42 #include "parser_utils.hpp"
43 #if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES)
44 #include "default_filter_factory.hpp"
45 #endif
46 #include "spirit_encoding.hpp"
47 #include <boost/log/detail/header.hpp>
48
49 namespace boost {
50
51 BOOST_LOG_OPEN_NAMESPACE
52
53 BOOST_LOG_ANONYMOUS_NAMESPACE {
54
55 //! Filter factories repository
56 template< typename CharT >
57 struct filters_repository :
58 public log::aux::lazy_singleton< filters_repository< CharT > >
59 {
60 typedef CharT char_type;
61 typedef log::aux::lazy_singleton< filters_repository< char_type > > base_type;
62 typedef std::basic_string< char_type > string_type;
63 typedef filter_factory< char_type > filter_factory_type;
64
65 //! Attribute name ordering predicate
66 struct attribute_name_order
67 {
68 typedef bool result_type;
69 result_type operator() (attribute_name const& left, attribute_name const& right) const
70 {
71 return left.id() < right.id();
72 }
73 };
74
75 typedef std::map< attribute_name, shared_ptr< filter_factory_type >, attribute_name_order > factories_map;
76
77 #if !defined(BOOST_LOG_BROKEN_FRIEND_TEMPLATE_SPECIALIZATIONS)
78 friend class log::aux::lazy_singleton< filters_repository< char_type > >;
79 #else
80 friend class base_type;
81 #endif
82
83 #if !defined(BOOST_LOG_NO_THREADS)
84 //! Synchronization mutex
85 mutable log::aux::light_rw_mutex m_Mutex;
86 #endif
87 //! The map of filter factories
88 factories_map m_Map;
89 #if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES)
90 //! Default factory
91 mutable aux::default_filter_factory< char_type > m_DefaultFactory;
92 #endif
93
94 //! The method returns the filter factory for the specified attribute name
95 filter_factory_type& get_factory(attribute_name const& name) const
96 {
97 typename factories_map::const_iterator it = m_Map.find(name);
98 if (it != m_Map.end())
99 {
100 return *it->second;
101 }
102 else
103 {
104 #if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES)
105 return m_DefaultFactory;
106 #else
107 BOOST_LOG_THROW_DESCR(setup_error, "No filter factory registered for attribute " + name.string());
108 #endif
109 }
110 }
111
112 private:
113 filters_repository()
114 {
115 }
116 };
117
118 //! Filter parser
119 template< typename CharT >
120 class filter_parser
121 {
122 private:
123 typedef CharT char_type;
124 typedef const char_type* iterator_type;
125 typedef typename log::aux::encoding< char_type >::type encoding;
126 typedef log::aux::encoding_specific< encoding > encoding_specific;
127 typedef std::basic_string< char_type > string_type;
128 typedef log::aux::char_constants< char_type > constants;
129 typedef filter_factory< char_type > filter_factory_type;
130
131 typedef filter (filter_factory_type::*comparison_relation_handler_t)(attribute_name const&, string_type const&);
132
133 private:
134 //! Parsed attribute name
135 mutable attribute_name m_AttributeName;
136 //! The second operand of a relation
137 mutable optional< string_type > m_Operand;
138 //! Comparison relation handler
139 comparison_relation_handler_t m_ComparisonRelation;
140 //! The custom relation string
141 mutable string_type m_CustomRelation;
142
143 //! Filter subexpressions as they are parsed
144 mutable std::stack< filter > m_Subexpressions;
145
146 public:
147 //! Constructor
148 filter_parser() :
149 m_ComparisonRelation(NULL)
150 {
151 }
152
153 //! The method returns the constructed filter
154 filter get_filter()
155 {
156 if (m_Subexpressions.empty())
157 return filter();
158 return boost::move(m_Subexpressions.top());
159 }
160
161 //! The pethod parses filter from the string
162 void parse(iterator_type& begin, iterator_type end, unsigned int depth = 0)
163 {
164 typedef void (filter_parser::*logical_op_t)();
165 logical_op_t logical_op = NULL;
166 iterator_type p = constants::trim_spaces_left(begin, end);
167 while (p != end)
168 {
169 // Parse subexpression
170 parse_subexpression(p, end, depth);
171 if (logical_op)
172 {
173 // This was the right-hand subexpression. Compose the two top subexpressions into a single filter.
174 (this->*logical_op)();
175 logical_op = NULL;
176 }
177
178 p = constants::trim_spaces_left(p, end);
179 if (p != end)
180 {
181 char_type c = *p;
182 iterator_type next = p + 1;
183 if (c == constants::char_paren_bracket_right)
184 {
185 // The subexpression has ended
186 if (depth == 0)
187 BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unmatched closing parenthesis");
188
189 p = next;
190 --depth;
191 break;
192 }
193 else if (c == constants::char_and || scan_keyword(p, end, next, constants::and_keyword()))
194 {
195 logical_op = &filter_parser::on_and;
196 }
197 else if (c == constants::char_or || scan_keyword(p, end, next, constants::or_keyword()))
198 {
199 logical_op = &filter_parser::on_or;
200 }
201 else
202 {
203 BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unexpected character encountered");
204 }
205
206 p = constants::trim_spaces_left(next, end);
207 }
208 else
209 break;
210 }
211
212 if (logical_op)
213 {
214 BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: logical operation without the right-hand subexpression");
215 }
216
217 if (p == end && depth > 0)
218 {
219 BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unterminated parenthesis");
220 }
221
222 begin = p;
223 }
224
225 private:
226 //! The method parses a single subexpression
227 void parse_subexpression(iterator_type& begin, iterator_type end, unsigned int depth)
228 {
229 bool negated = false, negation_present = false, done = false;
230 iterator_type p = begin;
231
232 while (p != end)
233 {
234 char_type c = *p;
235 iterator_type next = p + 1;
236 if (c == constants::char_percent)
237 {
238 // We found an attribute placeholder
239 iterator_type start = constants::trim_spaces_left(next, end);
240 p = constants::scan_attr_placeholder(start, end);
241 if (p == end)
242 BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the filter string");
243
244 on_attribute_name(start, p);
245
246 p = constants::trim_spaces_left(p, end);
247 if (p == end || *p != constants::char_percent)
248 BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the filter string");
249
250 // Skip the closing char_percent
251 p = constants::trim_spaces_left(++p, end);
252
253 // If the filter has negation operator, do not expect a relation (i.e. "!%attr% > 1" is not valid because "!%attr%" is interpreted as an attribute presence test)
254 if (!negation_present)
255 p = parse_relation(p, end);
256 else
257 on_relation_complete();
258 }
259 else if (c == constants::char_exclamation || scan_keyword(p, end, next, constants::not_keyword()))
260 {
261 // We found negation operation. Parse the subexpression to be negated.
262 negated ^= true;
263 negation_present = true;
264 p = constants::trim_spaces_left(next, end);
265 continue;
266 }
267 else if (c == constants::char_paren_bracket_left)
268 {
269 // We found a nested subexpression
270 parse(next, end, depth + 1);
271 p = next;
272 }
273 else
274 {
275 BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unexpected character");
276 }
277
278 if (negated)
279 on_negation();
280
281 done = true;
282
283 break;
284 }
285
286 if (negation_present && !done)
287 {
288 // This would happen if a filter consists of a single '!'
289 BOOST_LOG_THROW_DESCR(parse_error, "Filter parsing error: negation operator applied to nothingness");
290 }
291
292 begin = p;
293 }
294
295 //! Parses filtering relation
296 iterator_type parse_relation(iterator_type begin, iterator_type end)
297 {
298 iterator_type p = begin;
299 if (p != end)
300 {
301 iterator_type next = p;
302 if (scan_keyword(p, end, next, constants::equal_keyword()))
303 m_ComparisonRelation = &filter_factory_type::on_equality_relation;
304 else if (scan_keyword(p, end, next, constants::not_equal_keyword()))
305 m_ComparisonRelation = &filter_factory_type::on_inequality_relation;
306 else if (scan_keyword(p, end, next, constants::greater_keyword()))
307 m_ComparisonRelation = &filter_factory_type::on_greater_relation;
308 else if (scan_keyword(p, end, next, constants::less_keyword()))
309 m_ComparisonRelation = &filter_factory_type::on_less_relation;
310 else if (scan_keyword(p, end, next, constants::greater_or_equal_keyword()))
311 m_ComparisonRelation = &filter_factory_type::on_greater_or_equal_relation;
312 else if (scan_keyword(p, end, next, constants::less_or_equal_keyword()))
313 m_ComparisonRelation = &filter_factory_type::on_less_or_equal_relation;
314 else
315 {
316 // Check for custom relation
317 while (next != end && (encoding::isalnum(*next) || *next == constants::char_underline))
318 ++next;
319 if (p == next)
320 goto DoneL;
321 m_CustomRelation.assign(p, next);
322 }
323
324 // We have parsed a relation operator, there must be an operand
325 next = constants::trim_spaces_left(next, end);
326 string_type operand;
327 p = constants::parse_operand(next, end, operand);
328 if (next == p)
329 BOOST_LOG_THROW_DESCR(parse_error, "Missing operand for a relation in the filter string");
330
331 m_Operand = boost::in_place(operand);
332 }
333
334 DoneL:
335 // The relation can be as simple as a sole attribute placeholder (which means that only attribute presence has to be checked).
336 // So regardless how we get here, the relation is parsed completely.
337 on_relation_complete();
338
339 return p;
340 }
341
342 //! Checks if the string contains a keyword
343 static bool scan_keyword(iterator_type begin, iterator_type end, iterator_type& next, iterator_type keyword)
344 {
345 for (iterator_type p = begin; p != end; ++p, ++keyword)
346 {
347 char_type c1 = *p, c2 = *keyword;
348 if (c2 == 0)
349 {
350 if (encoding::isspace(c1))
351 {
352 next = p;
353 return true;
354 }
355 break;
356 }
357 if (c1 != c2)
358 break;
359 }
360
361 return false;
362 }
363
364 //! The attribute name handler
365 void on_attribute_name(iterator_type begin, iterator_type end)
366 {
367 if (begin == end)
368 BOOST_LOG_THROW_DESCR(parse_error, "Empty attribute name encountered");
369 m_AttributeName = attribute_name(log::aux::to_narrow(string_type(begin, end)));
370 }
371
372 //! The comparison relation handler
373 void on_relation_complete()
374 {
375 if (!!m_AttributeName)
376 {
377 filters_repository< char_type > const& repo = filters_repository< char_type >::get();
378 filter_factory_type& factory = repo.get_factory(m_AttributeName);
379
380 if (!!m_Operand)
381 {
382 if (!!m_ComparisonRelation)
383 {
384 m_Subexpressions.push((factory.*m_ComparisonRelation)(m_AttributeName, m_Operand.get()));
385 m_ComparisonRelation = NULL;
386 }
387 else if (!m_CustomRelation.empty())
388 {
389 m_Subexpressions.push(factory.on_custom_relation(m_AttributeName, m_CustomRelation, m_Operand.get()));
390 m_CustomRelation.clear();
391 }
392 else
393 {
394 // This should never happen
395 BOOST_ASSERT_MSG(false, "Filter parser internal error: the attribute name or subexpression operation is not set while trying to construct a relation");
396 BOOST_LOG_THROW_DESCR(parse_error, "Filter parser internal error: the attribute name or subexpression operation is not set while trying to construct a subexpression");
397 }
398
399 m_Operand = none;
400 }
401 else
402 {
403 // This branch is taken if the relation is a single attribute name, which is recognized as the attribute presence check
404 BOOST_ASSERT_MSG(!m_ComparisonRelation && m_CustomRelation.empty(), "Filter parser internal error: the relation operation is set while operand is not");
405 m_Subexpressions.push(factory.on_exists_test(m_AttributeName));
406 }
407
408 m_AttributeName = attribute_name();
409 }
410 else
411 {
412 // This should never happen
413 BOOST_ASSERT_MSG(false, "Filter parser internal error: the attribute name is not set while trying to construct a relation");
414 BOOST_LOG_THROW_DESCR(parse_error, "Filter parser internal error: the attribute name is not set while trying to construct a relation");
415 }
416 }
417
418 //! The negation operation handler
419 void on_negation()
420 {
421 if (!m_Subexpressions.empty())
422 {
423 m_Subexpressions.top() = !phoenix::bind(m_Subexpressions.top(), phoenix::placeholders::_1);
424 }
425 else
426 {
427 // This would happen if a filter consists of "!()"
428 BOOST_LOG_THROW_DESCR(parse_error, "Filter parsing error: negation operator applied to an empty subexpression");
429 }
430 }
431
432 //! The logical AND operation handler
433 void on_and()
434 {
435 if (!m_Subexpressions.empty())
436 {
437 filter right = boost::move(m_Subexpressions.top());
438 m_Subexpressions.pop();
439 if (!m_Subexpressions.empty())
440 {
441 filter const& left = m_Subexpressions.top();
442 m_Subexpressions.top() = phoenix::bind(left, phoenix::placeholders::_1) && phoenix::bind(right, phoenix::placeholders::_1);
443 return;
444 }
445 }
446
447 // This should never happen
448 BOOST_LOG_THROW_DESCR(parse_error, "Filter parser internal error: the subexpression is not set while trying to construct a filter");
449 }
450
451 //! The logical OR operation handler
452 void on_or()
453 {
454 if (!m_Subexpressions.empty())
455 {
456 filter right = boost::move(m_Subexpressions.top());
457 m_Subexpressions.pop();
458 if (!m_Subexpressions.empty())
459 {
460 filter const& left = m_Subexpressions.top();
461 m_Subexpressions.top() = phoenix::bind(left, phoenix::placeholders::_1) || phoenix::bind(right, phoenix::placeholders::_1);
462 return;
463 }
464 }
465
466 // This should never happen
467 BOOST_LOG_THROW_DESCR(parse_error, "Filter parser internal error: the subexpression is not set while trying to construct a filter");
468 }
469
470 // Assignment and copying are prohibited
471 BOOST_DELETED_FUNCTION(filter_parser(filter_parser const&))
472 BOOST_DELETED_FUNCTION(filter_parser& operator= (filter_parser const&))
473 };
474
475 } // namespace
476
477 //! The function registers a filter factory object for the specified attribute name
478 template< typename CharT >
479 BOOST_LOG_SETUP_API void register_filter_factory(attribute_name const& name, shared_ptr< filter_factory< CharT > > const& factory)
480 {
481 BOOST_ASSERT(!!name);
482 BOOST_ASSERT(!!factory);
483
484 filters_repository< CharT >& repo = filters_repository< CharT >::get();
485
486 BOOST_LOG_EXPR_IF_MT(log::aux::exclusive_lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);)
487 repo.m_Map[name] = factory;
488 }
489
490 //! The function parses a filter from the string
491 template< typename CharT >
parse_filter(const CharT * begin,const CharT * end)492 BOOST_LOG_SETUP_API filter parse_filter(const CharT* begin, const CharT* end)
493 {
494 typedef CharT char_type;
495
496 filter_parser< char_type > parser;
497 const char_type* p = begin;
498
499 BOOST_LOG_EXPR_IF_MT(filters_repository< CharT >& repo = filters_repository< CharT >::get();)
500 BOOST_LOG_EXPR_IF_MT(log::aux::shared_lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);)
501
502 parser.parse(p, end);
503
504 return parser.get_filter();
505 }
506
507 #ifdef BOOST_LOG_USE_CHAR
508
509 template BOOST_LOG_SETUP_API
510 void register_filter_factory< char >(attribute_name const& name, shared_ptr< filter_factory< char > > const& factory);
511 template BOOST_LOG_SETUP_API
512 filter parse_filter< char >(const char* begin, const char* end);
513
514 #endif // BOOST_LOG_USE_CHAR
515
516 #ifdef BOOST_LOG_USE_WCHAR_T
517
518 template BOOST_LOG_SETUP_API
519 void register_filter_factory< wchar_t >(attribute_name const& name, shared_ptr< filter_factory< wchar_t > > const& factory);
520 template BOOST_LOG_SETUP_API
521 filter parse_filter< wchar_t >(const wchar_t* begin, const wchar_t* end);
522
523 #endif // BOOST_LOG_USE_WCHAR_T
524
525 BOOST_LOG_CLOSE_NAMESPACE // namespace log
526
527 } // namespace boost
528
529 #include <boost/log/detail/footer.hpp>
530
531 #endif // BOOST_LOG_WITHOUT_SETTINGS_PARSERS
532