mirror of
https://github.com/boostorg/log.git
synced 2026-02-09 11:12:38 +00:00
Merged latest changes from trunk.
[SVN r85461]
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
* \date 31.03.2008
|
||||
*
|
||||
* \brief This header is the Boost.Log library implementation, see the library documentation
|
||||
* at http://www.boost.org/libs/log/doc/log.html.
|
||||
* at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_LOG_WITHOUT_SETTINGS_PARSERS
|
||||
@@ -23,23 +23,14 @@
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/none.hpp>
|
||||
#include <boost/move/core.hpp>
|
||||
#include <boost/move/utility.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <boost/utility/in_place_factory.hpp>
|
||||
#include <boost/spirit/include/qi_core.hpp>
|
||||
#include <boost/spirit/include/qi_char.hpp>
|
||||
#include <boost/spirit/include/qi_lit.hpp>
|
||||
#include <boost/spirit/include/qi_raw.hpp>
|
||||
#include <boost/spirit/include/qi_lexeme.hpp>
|
||||
#include <boost/spirit/include/qi_as.hpp>
|
||||
#include <boost/spirit/include/qi_symbols.hpp>
|
||||
#include <boost/phoenix/core.hpp>
|
||||
#include <boost/phoenix/bind/bind_function_object.hpp>
|
||||
#include <boost/phoenix/operator/logical.hpp>
|
||||
#include <boost/range/iterator_range_core.hpp>
|
||||
#include <boost/log/detail/singleton.hpp>
|
||||
#include <boost/log/exceptions.hpp>
|
||||
#include <boost/log/utility/setup/filter_parser.hpp>
|
||||
@@ -54,8 +45,6 @@
|
||||
#include "spirit_encoding.hpp"
|
||||
#include <boost/log/detail/header.hpp>
|
||||
|
||||
namespace qi = boost::spirit::qi;
|
||||
|
||||
namespace boost {
|
||||
|
||||
BOOST_LOG_OPEN_NAMESPACE
|
||||
@@ -125,23 +114,17 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
//! Filter parsing grammar
|
||||
//! Filter parser
|
||||
template< typename CharT >
|
||||
class filter_grammar :
|
||||
public qi::grammar<
|
||||
const CharT*,
|
||||
typename log::aux::encoding_specific< typename log::aux::encoding< CharT >::type >::space_type
|
||||
>
|
||||
class filter_parser
|
||||
{
|
||||
private:
|
||||
typedef CharT char_type;
|
||||
typedef const char_type* iterator_type;
|
||||
typedef log::aux::encoding_specific< typename log::aux::encoding< char_type >::type > encoding_specific;
|
||||
typedef typename log::aux::encoding< char_type >::type encoding;
|
||||
typedef log::aux::encoding_specific< encoding > encoding_specific;
|
||||
typedef std::basic_string< char_type > string_type;
|
||||
typedef log::aux::char_constants< char_type > constants;
|
||||
typedef filter_grammar< char_type > this_type;
|
||||
typedef qi::grammar< iterator_type, typename encoding_specific::space_type > base_type;
|
||||
typedef typename base_type::start_type rule_type;
|
||||
typedef filter_factory< char_type > filter_factory_type;
|
||||
|
||||
typedef filter (filter_factory_type::*comparison_relation_handler_t)(attribute_name const&, string_type const&);
|
||||
@@ -159,147 +142,230 @@ private:
|
||||
//! Filter subexpressions as they are parsed
|
||||
mutable std::stack< filter > m_Subexpressions;
|
||||
|
||||
//! A parser for an attribute name in a single relation
|
||||
rule_type attr_name;
|
||||
//! A parser for a quoted string
|
||||
rule_type quoted_string_operand;
|
||||
//! A parser for an operand in a single relation
|
||||
rule_type operand;
|
||||
//! A parser for a single relation that consists of two operands and an operation between them
|
||||
rule_type relation;
|
||||
//! A set of comparison relation symbols with corresponding pointers to callbacks
|
||||
qi::symbols< char_type, comparison_relation_handler_t > comparison_relation;
|
||||
//! A parser for a custom relation word
|
||||
rule_type custom_relation;
|
||||
//! A parser for a term, which can be a relation, an expression in parenthesis or a negation thereof
|
||||
rule_type term;
|
||||
//! A parser for the complete filter expression that consists of one or several terms with boolean operations between them
|
||||
rule_type expression;
|
||||
|
||||
public:
|
||||
//! Constructor
|
||||
filter_grammar() :
|
||||
base_type(expression),
|
||||
filter_parser() :
|
||||
m_ComparisonRelation(NULL)
|
||||
{
|
||||
attr_name = qi::lexeme
|
||||
[
|
||||
// An attribute name in form %name%
|
||||
qi::as< string_type >()[ qi::lit(constants::char_percent) >> +(encoding_specific::print - constants::char_percent) >> qi::lit(constants::char_percent) ]
|
||||
[boost::bind(&filter_grammar::on_attribute_name, this, _1)]
|
||||
];
|
||||
|
||||
quoted_string_operand = qi::raw
|
||||
[
|
||||
qi::lexeme
|
||||
[
|
||||
// A quoted string with C-style escape sequences support
|
||||
qi::lit(constants::char_quote) >>
|
||||
*(
|
||||
(qi::lit(constants::char_backslash) >> qi::char_) |
|
||||
(qi::char_ - qi::lit(constants::char_quote))
|
||||
) >>
|
||||
qi::lit(constants::char_quote)
|
||||
]
|
||||
]
|
||||
[boost::bind(&filter_grammar::on_quoted_string_operand, this, _1)];
|
||||
|
||||
operand =
|
||||
(
|
||||
quoted_string_operand |
|
||||
// A single word, enclosed with white spaces. It cannot contain parenthesis, since it is used by the filter parser.
|
||||
qi::raw[ qi::lexeme[ +(encoding_specific::graph - qi::lit(constants::char_paren_bracket_left) - qi::lit(constants::char_paren_bracket_right)) ] ]
|
||||
[boost::bind(&filter_grammar::on_operand, this, _1)]
|
||||
);
|
||||
|
||||
// Custom relation is a keyword that may contain either alphanumeric characters or an underscore
|
||||
custom_relation = qi::as< string_type >()[ qi::lexeme[ +(encoding_specific::alnum | qi::char_(constants::char_underline)) ] ]
|
||||
[boost::bind(&filter_grammar::set_custom_relation, this, _1)];
|
||||
|
||||
comparison_relation.add
|
||||
(constants::equal_keyword(), &filter_factory_type::on_equality_relation)
|
||||
(constants::not_equal_keyword(), &filter_factory_type::on_inequality_relation)
|
||||
(constants::greater_keyword(), &filter_factory_type::on_greater_relation)
|
||||
(constants::less_keyword(), &filter_factory_type::on_less_relation)
|
||||
(constants::greater_or_equal_keyword(), &filter_factory_type::on_greater_or_equal_relation)
|
||||
(constants::less_or_equal_keyword(), &filter_factory_type::on_less_or_equal_relation);
|
||||
|
||||
relation =
|
||||
(
|
||||
attr_name >> // The relation may be as simple as a sole attribute name, in which case the filter checks for the attribute value presence
|
||||
-(
|
||||
(comparison_relation[boost::bind(&filter_grammar::set_comparison_relation, this, _1)] >> operand) |
|
||||
(custom_relation >> operand)
|
||||
)
|
||||
)
|
||||
[boost::bind(&filter_grammar::on_relation_complete, this)];
|
||||
|
||||
term =
|
||||
(
|
||||
(qi::lit(constants::char_paren_bracket_left) >> expression >> constants::char_paren_bracket_right) |
|
||||
((qi::lit(constants::not_keyword()) | constants::char_exclamation) >> term)[boost::bind(&filter_grammar::on_negation, this)] |
|
||||
relation
|
||||
);
|
||||
|
||||
expression =
|
||||
(
|
||||
term >>
|
||||
*(
|
||||
((qi::lit(constants::and_keyword()) | constants::char_and) >> term)[boost::bind(&filter_grammar::on_and, this)] |
|
||||
((qi::lit(constants::or_keyword()) | constants::char_or) >> term)[boost::bind(&filter_grammar::on_or, this)]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
//! The method returns the constructed filter
|
||||
filter get_filter()
|
||||
{
|
||||
BOOST_ASSERT(!m_Subexpressions.empty());
|
||||
if (m_Subexpressions.empty())
|
||||
return filter();
|
||||
return boost::move(m_Subexpressions.top());
|
||||
}
|
||||
|
||||
//! The pethod parses filter from the string
|
||||
void parse(iterator_type& begin, iterator_type end, unsigned int depth = 0)
|
||||
{
|
||||
typedef void (filter_parser::*logical_op_t)();
|
||||
logical_op_t logical_op = NULL;
|
||||
iterator_type p = constants::trim_spaces_left(begin, end);
|
||||
while (p != end)
|
||||
{
|
||||
// Parse subexpression
|
||||
parse_subexpression(p, end, depth);
|
||||
if (logical_op)
|
||||
{
|
||||
// This was the right-hand subexpression. Compose the two top subexpressions into a single filter.
|
||||
(this->*logical_op)();
|
||||
logical_op = NULL;
|
||||
}
|
||||
|
||||
p = constants::trim_spaces_left(p, end);
|
||||
if (p != end)
|
||||
{
|
||||
char_type c = *p;
|
||||
iterator_type next = p + 1;
|
||||
if (c == constants::char_paren_bracket_right)
|
||||
{
|
||||
// The subexpression has ended
|
||||
if (depth == 0)
|
||||
BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unmatched closing parenthesis");
|
||||
|
||||
p = next;
|
||||
--depth;
|
||||
break;
|
||||
}
|
||||
else if (c == constants::char_and || scan_keyword(p, end, next, constants::and_keyword()))
|
||||
{
|
||||
logical_op = &filter_parser::on_and;
|
||||
}
|
||||
else if (c == constants::char_or || scan_keyword(p, end, next, constants::or_keyword()))
|
||||
{
|
||||
logical_op = &filter_parser::on_or;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unexpected character encountered");
|
||||
}
|
||||
|
||||
p = constants::trim_spaces_left(next, end);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (logical_op)
|
||||
{
|
||||
BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: logical operation without the right-hand subexpression");
|
||||
}
|
||||
|
||||
if (p == end && depth > 0)
|
||||
{
|
||||
BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unterminated parenthesis");
|
||||
}
|
||||
|
||||
begin = p;
|
||||
}
|
||||
|
||||
private:
|
||||
//! The method parses a single subexpression
|
||||
void parse_subexpression(iterator_type& begin, iterator_type end, unsigned int depth)
|
||||
{
|
||||
bool negated = false, negation_present = false, done = false;
|
||||
iterator_type p = begin;
|
||||
|
||||
while (p != end)
|
||||
{
|
||||
char_type c = *p;
|
||||
iterator_type next = p + 1;
|
||||
if (c == constants::char_percent)
|
||||
{
|
||||
// We found an attribute placeholder
|
||||
iterator_type start = constants::trim_spaces_left(next, end);
|
||||
p = constants::scan_attr_placeholder(start, end);
|
||||
if (p == end)
|
||||
BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the filter string");
|
||||
|
||||
on_attribute_name(start, p);
|
||||
|
||||
p = constants::trim_spaces_left(p, end);
|
||||
if (p == end || *p != constants::char_percent)
|
||||
BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the filter string");
|
||||
|
||||
// Skip the closing char_percent
|
||||
p = constants::trim_spaces_left(++p, end);
|
||||
|
||||
// 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)
|
||||
if (!negation_present)
|
||||
p = parse_relation(p, end);
|
||||
else
|
||||
on_relation_complete();
|
||||
}
|
||||
else if (c == constants::char_exclamation || scan_keyword(p, end, next, constants::not_keyword()))
|
||||
{
|
||||
// We found negation operation. Parse the subexpression to be negated.
|
||||
negated ^= true;
|
||||
negation_present = true;
|
||||
p = constants::trim_spaces_left(next, end);
|
||||
continue;
|
||||
}
|
||||
else if (c == constants::char_paren_bracket_left)
|
||||
{
|
||||
// We found a nested subexpression
|
||||
parse(next, end, depth + 1);
|
||||
p = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unexpected character");
|
||||
}
|
||||
|
||||
if (negated)
|
||||
on_negation();
|
||||
|
||||
done = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (negation_present && !done)
|
||||
{
|
||||
// This would happen if a filter consists of a single '!'
|
||||
BOOST_LOG_THROW_DESCR(parse_error, "Filter parsing error: negation operator applied to nothingness");
|
||||
}
|
||||
|
||||
begin = p;
|
||||
}
|
||||
|
||||
//! Parses filtering relation
|
||||
iterator_type parse_relation(iterator_type begin, iterator_type end)
|
||||
{
|
||||
iterator_type p = begin;
|
||||
if (p != end)
|
||||
{
|
||||
iterator_type next = p;
|
||||
if (scan_keyword(p, end, next, constants::equal_keyword()))
|
||||
m_ComparisonRelation = &filter_factory_type::on_equality_relation;
|
||||
else if (scan_keyword(p, end, next, constants::not_equal_keyword()))
|
||||
m_ComparisonRelation = &filter_factory_type::on_inequality_relation;
|
||||
else if (scan_keyword(p, end, next, constants::greater_keyword()))
|
||||
m_ComparisonRelation = &filter_factory_type::on_greater_relation;
|
||||
else if (scan_keyword(p, end, next, constants::less_keyword()))
|
||||
m_ComparisonRelation = &filter_factory_type::on_less_relation;
|
||||
else if (scan_keyword(p, end, next, constants::greater_or_equal_keyword()))
|
||||
m_ComparisonRelation = &filter_factory_type::on_greater_or_equal_relation;
|
||||
else if (scan_keyword(p, end, next, constants::less_or_equal_keyword()))
|
||||
m_ComparisonRelation = &filter_factory_type::on_less_or_equal_relation;
|
||||
else
|
||||
{
|
||||
// Check for custom relation
|
||||
while (next != end && (encoding::isalnum(*next) || *next == constants::char_underline))
|
||||
++next;
|
||||
if (p == next)
|
||||
goto DoneL;
|
||||
m_CustomRelation.assign(p, next);
|
||||
}
|
||||
|
||||
// We have parsed a relation operator, there must be an operand
|
||||
next = constants::trim_spaces_left(next, end);
|
||||
string_type operand;
|
||||
p = constants::parse_operand(next, end, operand);
|
||||
if (next == p)
|
||||
BOOST_LOG_THROW_DESCR(parse_error, "Missing operand for a relation in the filter string");
|
||||
|
||||
m_Operand = boost::in_place(operand);
|
||||
}
|
||||
|
||||
DoneL:
|
||||
// The relation can be as simple as a sole attribute placeholder (which means that only attribute presence has to be checked).
|
||||
// So regardless how we get here, the relation is parsed completely.
|
||||
on_relation_complete();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
//! Checks if the string contains a keyword
|
||||
static bool scan_keyword(iterator_type begin, iterator_type end, iterator_type& next, iterator_type keyword)
|
||||
{
|
||||
for (iterator_type p = begin; p != end; ++p, ++keyword)
|
||||
{
|
||||
char_type c1 = *p, c2 = *keyword;
|
||||
if (c2 == 0)
|
||||
{
|
||||
if (encoding::isspace(c1))
|
||||
{
|
||||
next = p;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (c1 != c2)
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//! The attribute name handler
|
||||
void on_attribute_name(string_type const& name)
|
||||
void on_attribute_name(iterator_type begin, iterator_type end)
|
||||
{
|
||||
m_AttributeName = attribute_name(log::aux::to_narrow(name));
|
||||
}
|
||||
|
||||
//! The operand string handler
|
||||
void on_operand(iterator_range< iterator_type > const& arg)
|
||||
{
|
||||
// An attribute name should have been parsed at this point
|
||||
if (!m_AttributeName)
|
||||
BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: operand is not expected");
|
||||
|
||||
m_Operand = boost::in_place(arg.begin(), arg.end());
|
||||
}
|
||||
|
||||
//! The quoted string handler
|
||||
void on_quoted_string_operand(iterator_range< iterator_type > const& arg)
|
||||
{
|
||||
// An attribute name should have been parsed at this point
|
||||
if (!m_AttributeName)
|
||||
BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: quoted string operand is not expected");
|
||||
|
||||
// Cut off the quotes
|
||||
string_type str(arg.begin() + 1, arg.end() - 1);
|
||||
|
||||
// Translate escape sequences
|
||||
constants::translate_escape_sequences(str);
|
||||
m_Operand = str;
|
||||
}
|
||||
|
||||
//! The method saves the relation word into an internal string
|
||||
void set_comparison_relation(comparison_relation_handler_t rel)
|
||||
{
|
||||
m_ComparisonRelation = rel;
|
||||
}
|
||||
|
||||
//! The method saves the relation word into an internal string
|
||||
void set_custom_relation(string_type const& rel)
|
||||
{
|
||||
m_CustomRelation = rel;
|
||||
if (begin == end)
|
||||
BOOST_LOG_THROW_DESCR(parse_error, "Empty attribute name encountered");
|
||||
m_AttributeName = attribute_name(log::aux::to_narrow(string_type(begin, end)));
|
||||
}
|
||||
|
||||
//! The comparison relation handler
|
||||
@@ -357,8 +423,8 @@ private:
|
||||
}
|
||||
else
|
||||
{
|
||||
// This would happen if a filter consists of a single '!'
|
||||
BOOST_LOG_THROW_DESCR(parse_error, "Filter parsing error: a negation operator applied to nothingness");
|
||||
// This would happen if a filter consists of "!()"
|
||||
BOOST_LOG_THROW_DESCR(parse_error, "Filter parsing error: negation operator applied to an empty subexpression");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,8 +467,8 @@ private:
|
||||
}
|
||||
|
||||
// Assignment and copying are prohibited
|
||||
BOOST_LOG_DELETED_FUNCTION(filter_grammar(filter_grammar const&))
|
||||
BOOST_LOG_DELETED_FUNCTION(filter_grammar& operator= (filter_grammar const&))
|
||||
BOOST_DELETED_FUNCTION(filter_parser(filter_parser const&))
|
||||
BOOST_DELETED_FUNCTION(filter_parser& operator= (filter_parser const&))
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -427,21 +493,15 @@ filter parse_filter(const CharT* begin, const CharT* end)
|
||||
typedef CharT char_type;
|
||||
typedef log::aux::encoding_specific< typename log::aux::encoding< char_type >::type > encoding_specific;
|
||||
|
||||
filter_grammar< char_type > gram;
|
||||
filter_parser< char_type > parser;
|
||||
const char_type* p = begin;
|
||||
|
||||
BOOST_LOG_EXPR_IF_MT(filters_repository< CharT >& repo = filters_repository< CharT >::get();)
|
||||
BOOST_LOG_EXPR_IF_MT(log::aux::shared_lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);)
|
||||
|
||||
bool result = qi::phrase_parse(p, end, gram, encoding_specific::space);
|
||||
if (!result || p != end)
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm << "Could not parse the filter, parsing stopped at position " << p - begin;
|
||||
BOOST_LOG_THROW_DESCR(parse_error, strm.str());
|
||||
}
|
||||
parser.parse(p, end);
|
||||
|
||||
return gram.get_filter();
|
||||
return parser.get_filter();
|
||||
}
|
||||
|
||||
#ifdef BOOST_LOG_USE_CHAR
|
||||
|
||||
Reference in New Issue
Block a user