mapnik/include/mapnik/expression_grammar_x3_def.hpp
Nikita Kniazev c191e196c0 Replace MAPNIK_INIT_PRIORITY workaround
Spirit rules are just placeholders. They are cheap to construct and by doing
this static initialization order fiasco problem is effectively avoided.

Also had to fix name clash between geojson and generic json parsers.
2019-07-02 01:04:31 +03:00

453 lines
15 KiB
C++

/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2017 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#ifndef MAPNIK_EXPRESSIONS_GRAMMAR_X3_DEF_HPP
#define MAPNIK_EXPRESSIONS_GRAMMAR_X3_DEF_HPP
#include <mapnik/expression_grammar_x3.hpp>
#include <mapnik/json/unicode_string_grammar_x3_def.hpp>
#include <mapnik/expression_node.hpp>
#include <mapnik/function_call.hpp>
#include <mapnik/unicode.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/std_pair.hpp>
#pragma GCC diagnostic pop
BOOST_FUSION_ADAPT_STRUCT(mapnik::unary_function_call,
(mapnik::unary_function_impl, fun)
(mapnik::unary_function_call::argument_type, arg))
BOOST_FUSION_ADAPT_STRUCT(mapnik::binary_function_call,
(mapnik::binary_function_impl, fun)
(mapnik::binary_function_call::argument_type, arg1)
(mapnik::binary_function_call::argument_type, arg2))
namespace mapnik { namespace grammar {
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
using ascii::char_;
using ascii::string;
using x3::lit;
using x3::double_;
using x3::int_;
using x3::bool_;
using x3::_attr;
using x3::_val;
using x3::no_skip;
using x3::lexeme;
using x3::no_case;
using x3::alpha;
using x3::alnum;
x3::uint_parser<char, 16, 2, 2> const hex2 {};
namespace {
auto const& escaped_unicode = json::grammar::escaped_unicode;
}
template <typename Context>
inline mapnik::transcoder const& extract_transcoder(Context const& ctx)
{
return x3::get<transcoder_tag>(ctx);
}
auto append = [](auto const& ctx)
{
_val(ctx) += _attr(ctx);
};
auto do_assign = [] (auto const& ctx)
{
_val(ctx) = std::move(_attr(ctx));
};
auto do_negate = [] (auto const& ctx)
{
_val(ctx) = std::move(unary_node<mapnik::tags::negate>(_attr(ctx)));
};
auto do_attribute = [] (auto const& ctx)
{
auto const& attr = _attr(ctx);
if (attr == "mapnik::geometry_type")
{
_val(ctx) = std::move(geometry_type_attribute());
}
else
{
_val(ctx) = std::move(attribute(attr));
}
};
auto do_global_attribute = [] (auto const& ctx)
{
_val(ctx) = std::move(global_attribute(_attr(ctx)));
};
auto do_add = [] (auto const& ctx)
{
_val(ctx) = std::move(mapnik::binary_node<mapnik::tags::plus>(std::move(_val(ctx)), std::move(_attr(ctx))));
};
auto do_subt = [] (auto const& ctx)
{
_val(ctx) = std::move(mapnik::binary_node<mapnik::tags::minus>(std::move(_val(ctx)), std::move(_attr(ctx))));
};
auto do_mult = [] (auto const& ctx)
{
_val(ctx) = std::move(mapnik::binary_node<mapnik::tags::mult>(std::move(_val(ctx)), std::move(_attr(ctx))));
};
auto do_div = [] (auto const& ctx)
{
_val(ctx) = std::move(mapnik::binary_node<mapnik::tags::div>(std::move(_val(ctx)), std::move(_attr(ctx))));
};
auto do_mod = [] (auto const& ctx)
{
_val(ctx) = std::move(mapnik::binary_node<mapnik::tags::mod>(std::move(_val(ctx)), std::move(_attr(ctx))));
};
auto do_unicode = [] (auto const& ctx)
{
auto const& tr = extract_transcoder(ctx);
_val(ctx) = std::move(tr.transcode(_attr(ctx).c_str()));
};
auto do_null = [] (auto const& ctx)
{
_val(ctx) = std::move(mapnik::value_null());
};
auto do_not = [] (auto const& ctx)
{
mapnik::unary_node<mapnik::tags::logical_not> node(_attr(ctx));
_val(ctx) = std::move(node);
};
auto do_and = [] (auto const& ctx)
{
_val(ctx) = std::move(mapnik::binary_node<mapnik::tags::logical_and>(std::move(_val(ctx)), std::move(_attr(ctx))));
};
auto do_or = [] (auto const& ctx)
{
_val(ctx) = std::move(mapnik::binary_node<mapnik::tags::logical_or>(std::move(_val(ctx)), std::move(_attr(ctx))));
};
auto do_equal = [] (auto const& ctx)
{
_val(ctx) = std::move(mapnik::binary_node<mapnik::tags::equal_to>(std::move(_val(ctx)), std::move(_attr(ctx))));
};
auto do_not_equal = [] (auto const& ctx)
{
_val(ctx) = std::move(mapnik::binary_node<mapnik::tags::not_equal_to>(std::move(_val(ctx)), std::move(_attr(ctx))));
};
auto do_less = [] (auto const& ctx)
{
_val(ctx) = std::move(mapnik::binary_node<mapnik::tags::less>(std::move(_val(ctx)), std::move(_attr(ctx))));
};
auto do_less_equal = [] (auto const& ctx)
{
_val(ctx) = std::move(mapnik::binary_node<mapnik::tags::less_equal>(std::move(_val(ctx)), std::move(_attr(ctx))));
};
auto do_greater = [] (auto const& ctx)
{
_val(ctx) = std::move(mapnik::binary_node<mapnik::tags::greater>(std::move(_val(ctx)), std::move(_attr(ctx))));
};
auto do_greater_equal = [] (auto const& ctx)
{
_val(ctx) = std::move(mapnik::binary_node<mapnik::tags::greater_equal>(std::move(_val(ctx)), std::move(_attr(ctx))));
};
// regex
auto do_regex_match = [] (auto const& ctx)
{
auto const& tr = extract_transcoder(ctx);
_val(ctx) = std::move(mapnik::regex_match_node(tr, std::move(_val(ctx)) , std::move(_attr(ctx))));
};
auto do_regex_replace = [] (auto const& ctx)
{
auto const& tr = extract_transcoder(ctx);
auto const& pair = _attr(ctx);
auto const& pattern = std::get<0>(pair);
auto const& format = std::get<1>(pair);
_val(ctx) = mapnik::regex_replace_node(tr, _val(ctx) , pattern, format);
};
// mapnik::value_integer
auto const mapnik_int = x3::int_parser<value_integer,10,1,-1>();
// mapnik::value_double
auto const mapnik_double = x3::real_parser<value_double, x3::strict_real_policies<value_double>>();
// mapnik::value_bool
struct boolean_ : x3::symbols<mapnik::value_bool>
{
boolean_()
{
add
("true", true)
("false", false)
;
}
} boolean;
struct floating_point_constants : x3::symbols<mapnik::value_double>
{
floating_point_constants()
{
add
("pi", 3.1415926535897932384626433832795)
("deg_to_rad",0.017453292519943295769236907684886)
("rad_to_deg",57.295779513082320876798154814105)
;
}
} float_const;
// unary functions
struct unary_function_types_ : x3::symbols<unary_function_impl>
{
unary_function_types_()
{
add
("sin", sin_impl())
("cos", cos_impl())
("tan", tan_impl())
("atan", atan_impl())
("exp", exp_impl())
("log", log_impl())
("abs", abs_impl())
("length",length_impl())
;
}
} unary_func_types ;
// binary functions
struct binary_function_types_ : x3::symbols<binary_function_impl>
{
binary_function_types_()
{
add
("min", binary_function_impl(min_impl))
("max", binary_function_impl(max_impl))
("pow", binary_function_impl(pow_impl))
;
}
} binary_func_types;
// geometry types
struct geometry_types_ : x3::symbols<mapnik::value_integer>
{
geometry_types_()
{
add
("point", 1)
("linestring", 2)
("polygon",3)
("collection",4)
;
}
} geometry_type;
struct unesc_chars_ : x3::symbols<char>
{
unesc_chars_()
{
add
("\\a", '\a')
("\\b", '\b')
("\\f", '\f')
("\\n", '\n')
("\\r", '\r')
("\\t", '\t')
("\\v", '\v')
("\\\\", '\\')
("\\\'", '\'')
("\\\"", '\"')
;
}
} unesc_char;
// rules
x3::rule<class logical_expression, mapnik::expr_node> const logical_expression("logical expression");
x3::rule<class not_expression, mapnik::expr_node> const not_expression("not expression");
x3::rule<class conditional_expression, mapnik::expr_node> const conditional_expression("conditional expression");
x3::rule<class equality_expression, mapnik::expr_node> const equality_expression("equality expression");
x3::rule<class relational_expression, mapnik::expr_node> const relational_expression("relational expression");
x3::rule<class additive_expression, mapnik::expr_node> const additive_expression("additive expression");
x3::rule<class multiplicative_expression, mapnik::expr_node> const multiplicative_expression("multiplicative expression");
x3::rule<class unary_func_expression, mapnik::unary_function_call> const unary_func_expression("unary function expression");
x3::rule<class binary_func_expression, mapnik::binary_function_call> const binary_func_expression("binary function expression");
x3::rule<class unary_expression, mapnik::expr_node> const unary_expression("unary expression");
x3::rule<class primary_expression, mapnik::expr_node> const primary_expression("primary expression");
x3::rule<class regex_match_expression, std::string> const regex_match_expression("regex match expression");
x3::rule<class regex_replace_expression, std::pair<std::string,std::string> > const regex_replace_expression("regex replace expression");
// strings
auto const single_quoted_string = x3::rule<class single_quoted_string, std::string> {} = lit('\'')
>> no_skip[*(unesc_char[append]
|
(lit('\\') >> escaped_unicode[append])
|
(~char_('\''))[append])] > lit('\'');
auto const double_quoted_string = x3::rule<class double_quoted_string, std::string> {} = lit('"')
>> no_skip[*(unesc_char[append]
|
(lit('\\') >> escaped_unicode[append])
|
(~char_('"'))[append])] > lit('"');
auto const quoted_string = x3::rule<class quoted_string, std::string> {} = single_quoted_string | double_quoted_string;
auto const unquoted_ustring = x3::rule<class ustring, std::string> {} = no_skip[alpha > *alnum] - lit("not");
// start
auto const expression_def = logical_expression [do_assign]
;
auto const logical_expression_def = not_expression[do_assign] >
*(((lit("and") | lit("&&")) > not_expression[do_and])
|
((lit("or") | lit("||")) > not_expression[do_or]));
auto const not_expression_def = conditional_expression[do_assign]
|
((lit("not") | lit('!')) > conditional_expression[do_not])
;
auto const conditional_expression_def = equality_expression[do_assign]
|
additive_expression[do_assign]
;
auto const equality_expression_def = relational_expression[do_assign] >
*( ( ( lit("=") | lit("eq") | lit("is")) > relational_expression [do_equal])
| (( lit( "!=") | lit("<>") | lit("neq") ) > relational_expression [do_not_equal])
);
auto const relational_expression_def = additive_expression[do_assign] >
*( ( (lit("<=") | lit("le")) > additive_expression [do_less_equal])
|
( (lit("<") | lit("lt")) >> additive_expression[do_less]) // allow backtracking to be able to handle '<' and '<>' correctly
|
( (lit(">=") | lit("ge")) > additive_expression [do_greater_equal])
|
( (lit(">") | lit("gt")) > additive_expression [do_greater]));
auto const additive_expression_def = multiplicative_expression[do_assign]
> *( ('+' > multiplicative_expression[do_add])
|
('-' > multiplicative_expression[do_subt]));
auto const feature_attr = lexeme['[' > +~char_(']') > ']'];
auto const global_attr = x3::rule<class global_attr, std::string> {} = lexeme[lit('@') > char_("a-zA-Z_") > *char_("a-zA-Z0-9_")];
auto const regex_match_expression_def = lit(".match") > '(' > quoted_string > ')';
auto const regex_replace_expression_def = lit(".replace") > '(' > quoted_string > ',' > quoted_string > ')';
auto const multiplicative_expression_def = unary_expression [do_assign]
> *( ('*' > unary_expression [do_mult])
|
('/' > unary_expression [do_div])
|
('%' > unary_expression [do_mod])
|
regex_match_expression[do_regex_match]
|
regex_replace_expression[do_regex_replace]
);
auto const unary_func_expression_def = unary_func_types > '(' > expression > ')';
auto const binary_func_expression_def = binary_func_types > '(' > expression > ',' > expression > ')';
auto const unary_expression_def =
primary_expression[do_assign]
|
('+' > primary_expression[do_assign])
|
('-' > primary_expression[do_negate])
;
auto const primary_expression_def =
mapnik_double[do_assign]
|
mapnik_int[do_assign]
|
no_case[boolean][do_assign]
|
no_case["null"][do_null]
|
no_case[geometry_type][do_assign]
|
float_const[do_assign]
|
quoted_string[do_unicode]
|
feature_attr[do_attribute]
|
global_attr[do_global_attribute]
|
unary_func_expression[do_assign]
|
binary_func_expression[do_assign]
|
('(' > logical_expression[do_assign] > ')')
|
unquoted_ustring[do_unicode]
// ^ https://github.com/mapnik/mapnik/pull/3389
;
BOOST_SPIRIT_DEFINE (
expression,
logical_expression,
not_expression,
conditional_expression,
equality_expression,
relational_expression,
additive_expression,
regex_match_expression,
regex_replace_expression,
multiplicative_expression,
unary_func_expression,
binary_func_expression,
unary_expression,
primary_expression
);
}}
#endif // MAPNIK_EXPRESSIONS_GRAMMAR_X3_DEF_HPP