6 #ifndef SPIRIT_PO_DEFAULT_PLURAL_FORMS_EXPRESSIONS_HPP_INCLUDED
7 #define SPIRIT_PO_DEFAULT_PLURAL_FORMS_EXPRESSIONS_HPP_INCLUDED
19 #ifndef BOOST_SPIRIT_USE_PHOENIX_V3
20 #define BOOST_SPIRIT_USE_PHOENIX_V3
25 #include <boost/spirit/include/qi.hpp>
26 #include <boost/phoenix/core.hpp>
27 #include <boost/phoenix/operator.hpp>
28 #include <boost/fusion/include/adapt_struct.hpp>
29 #include <boost/variant/variant.hpp>
30 #include <boost/variant/recursive_wrapper.hpp>
32 #ifdef SPIRIT_PO_DEBUG
39 namespace qi = boost::spirit::qi;
40 typedef unsigned int uint;
42 namespace default_plural_forms {
46 #define FOREACH_SPIRIT_PO_BINARY_OP(X_) \
47 X_(eq_op, ==) X_(neq_op, !=) X_(ge_op, >=) X_(le_op, <=) X_(gt_op, >) X_(lt_op, <) X_(mod_op, %)
51 #define FOREACH_SPIRIT_PO_CONJUNCTION(X_) \
52 X_(and_op, &&) X_(or_op, ||)
63 #define FWD_DECL_(name, op) \
75 #define WRAP_(name, op) boost::recursive_wrapper< name >, \
77 typedef boost::variant<constant, n_var, boost::recursive_wrapper<not_op>,
91 #define DECL_(name, op) \
92 struct name { expr e1, e2; }; \
110 #define EVAL_OP_(name, OPERATOR) \
111 uint operator()(const name & op) const { return (boost::apply_visitor(*this, op.e1)) OPERATOR (boost::apply_visitor(*this, op.e2)); } \
129 (
unsigned int, value))
137 #define ADAPT_STRUCT_(name, op) \
138 BOOST_FUSION_ADAPT_STRUCT(spirit_po::default_plural_forms:: name, \
139 (spirit_po::default_plural_forms::expr, e1) \
140 (spirit_po::default_plural_forms::expr, e2)) \
149 namespace default_plural_forms {
178 template <
typename Iterator>
179 struct op_grammar : qi::grammar<Iterator, expr(), qi::space_type> {
180 qi::rule<Iterator, constant(), qi::space_type> constant_;
181 qi::rule<Iterator, n_var(), qi::space_type> n_;
182 qi::rule<Iterator, not_op(), qi::space_type> not_;
183 qi::rule<Iterator, and_op(
expr), qi::space_type> and_;
184 qi::rule<Iterator, or_op(
expr), qi::space_type> or_;
185 qi::rule<Iterator, eq_op(
expr), qi::space_type> eq_;
186 qi::rule<Iterator, neq_op(
expr), qi::space_type> neq_;
187 qi::rule<Iterator, ge_op(
expr), qi::space_type> ge_;
188 qi::rule<Iterator, le_op(
expr), qi::space_type> le_;
189 qi::rule<Iterator, gt_op(
expr), qi::space_type> gt_;
190 qi::rule<Iterator, lt_op(
expr), qi::space_type> lt_;
191 qi::rule<Iterator, mod_op(
expr), qi::space_type> mod_;
192 qi::rule<Iterator, ternary_op(
expr), qi::space_type> ternary_;
193 qi::rule<Iterator,
expr(), qi::space_type> paren_expr_;
196 qi::rule<Iterator,
expr(), qi::space_type, qi::locals<expr>> ternary_level_;
197 qi::rule<Iterator,
expr(), qi::space_type, qi::locals<expr>> or_level_;
198 qi::rule<Iterator,
expr(), qi::space_type, qi::locals<expr>> and_level_;
199 qi::rule<Iterator,
expr(), qi::space_type, qi::locals<expr>> eq_level_;
200 qi::rule<Iterator,
expr(), qi::space_type, qi::locals<expr>> rel_level_;
201 qi::rule<Iterator,
expr(), qi::space_type, qi::locals<expr>> mod_level_;
202 qi::rule<Iterator,
expr(), qi::space_type> atom_level_;
203 qi::rule<Iterator,
expr(), qi::space_type>
expr_;
206 qi::rule<Iterator,
expr(), qi::space_type> main_;
208 op_grammar() : op_grammar::base_type(main_) {
212 constant_ = qi::uint_;
214 paren_expr_ = lit(
'(') >>
expr_ >> lit(
')');
215 not_ = lit(
'!') >> atom_level_;
216 atom_level_ = paren_expr_ | not_ | n_ | constant_;
218 mod_ = lit(
'%') >> attr(qi::_r1) >> atom_level_;
219 mod_level_ = qi::omit[atom_level_[qi::_a = qi::_1]] >> (mod_(qi::_a) | attr(qi::_a));
221 ge_ = lit(
">=") >> attr(qi::_r1) >> mod_level_;
222 le_ = lit(
"<=") >> attr(qi::_r1) >> mod_level_;
223 gt_ = lit(
'>') >> attr(qi::_r1) >> mod_level_;
224 lt_ = lit(
'<') >> attr(qi::_r1) >> mod_level_;
225 rel_level_ = qi::omit[mod_level_[qi::_a = qi::_1]] >> (ge_(qi::_a) | le_(qi::_a) | gt_(qi::_a) | lt_(qi::_a) | attr(qi::_a));
227 eq_ = lit(
"==") >> attr(qi::_r1) >> rel_level_;
228 neq_ = lit(
"!=") >> attr(qi::_r1) >> rel_level_;
229 eq_level_ = qi::omit[rel_level_[qi::_a = qi::_1]] >> (eq_(qi::_a) | neq_(qi::_a) | attr(qi::_a));
231 and_ = lit(
"&&") >> attr(qi::_r1) >> and_level_;
232 and_level_ = qi::omit[eq_level_[qi::_a = qi::_1]] >> (and_(qi::_a) | attr(qi::_a));
234 or_ = lit(
"||") >> attr(qi::_r1) >> or_level_;
235 or_level_ = qi::omit[and_level_[qi::_a = qi::_1]] >> (or_(qi::_a) | attr(qi::_a));
237 ternary_ = lit(
'?') >> attr(qi::_r1) >> ternary_level_ >> lit(
':') >> ternary_level_;
238 ternary_level_ = qi::omit[or_level_[qi::_a = qi::_1]] >> (ternary_(qi::_a) | attr(qi::_a));
240 expr_ = ternary_level_;
242 main_ =
expr_ >> -lit(
';');
252 #define ENUMERATE(X, Y) X,
275 typedef boost::variant<constant, skip, skip_if, skip_if_not, op_code> instruction;
280 #ifdef SPIRIT_PO_DEBUG
281 inline std::string op_code_string(op_code oc) {
282 std::string result =
"[ ";
284 case op_code::n_var: {
288 case op_code::not_op: {
292 #define OP_CODE_STR_CASE_(X, Y) \
300 #undef OP_CODE_STR_CASE_
303 if (result.size() < 5) { result +=
' '; } \
308 struct instruction_debug_string_maker : boost::static_visitor<std::string> {
309 std::string operator()(
const constant &
c)
const {
310 return "[ push : " + std::to_string(
c.value) +
" ]";
312 std::string operator()(
const skip &
s)
const {
313 return "[ skip : " + std::to_string(
s.distance) +
" ]";
315 std::string operator()(
const skip_if &
s)
const {
316 return "[ sif : " + std::to_string(
s.distance) +
" ]";
318 std::string operator()(
const skip_if_not &
s)
const {
319 return "[ sifn : " + std::to_string(
s.distance) +
" ]";
321 std::string operator()(
const op_code & oc)
const {
322 return op_code_string(oc);
326 inline std::string debug_string(
const instruction &
i) {
335 struct is_boolean :
public boost::static_visitor<bool> {
336 bool operator()(
const and_op &)
const {
return true; }
337 bool operator()(
const or_op &)
const {
return true; }
338 bool operator()(
const not_op &)
const {
return true; }
339 bool operator()(
const eq_op &)
const {
return true; }
340 bool operator()(
const neq_op &)
const {
return true; }
341 bool operator()(
const ge_op &)
const {
return true; }
342 bool operator()(
const le_op &)
const {
return true; }
343 bool operator()(
const gt_op &)
const {
return true; }
344 bool operator()(
const lt_op &)
const {
return true; }
345 bool operator()(
const n_var &)
const {
return false; }
346 bool operator()(
const constant &
c)
const {
return (
c.value == 0 ||
c.value == 1); }
355 struct emitter :
public boost::static_visitor<std::vector<instruction>> {
356 std::vector<instruction> operator()(
const constant &
c)
const {
357 return std::vector<instruction>{instruction{
c}};
359 std::vector<instruction> operator()(
const n_var &)
const {
360 return std::vector<instruction>{instruction{op_code::n_var}};
362 std::vector<instruction> operator()(
const not_op & o)
const {
364 result.emplace_back(op_code::not_op);
367 #define EMIT_OP_(name, op) \
368 std::vector<instruction> operator()(const name & o) const { \
369 auto result = boost::apply_visitor(*this, o.e1); \
370 auto temp = boost::apply_visitor(*this, o.e2); \
371 std::move(temp.begin(), temp.end(), std::back_inserter(result)); \
372 result.emplace_back(op_code::name); \
383 std::vector<instruction> operator()(
const and_op & o)
const {
388 uint sec_size =
static_cast<uint>(second.size());
389 if (!second_is_boolean) { sec_size += 2; }
391 result.emplace_back(skip_if{2});
392 result.emplace_back(constant{0});
393 result.emplace_back(skip{sec_size});
395 std::move(second.begin(), second.end(), std::back_inserter(result));
396 if (!second_is_boolean) {
397 result.emplace_back(op_code::not_op);
398 result.emplace_back(op_code::not_op);
404 std::vector<instruction> operator()(
const or_op & o)
const {
409 uint sec_size =
static_cast<uint>(second.size());
410 if (!second_is_boolean) { sec_size += 2; }
412 result.emplace_back(skip_if_not{2});
413 result.emplace_back(constant{1});
414 result.emplace_back(skip{sec_size});
416 std::move(second.begin(), second.end(), std::back_inserter(result));
417 if (!second_is_boolean) {
418 result.emplace_back(op_code::not_op);
419 result.emplace_back(op_code::not_op);
425 std::vector<instruction> operator()(
const ternary_op & o)
const {
430 uint tsize =
static_cast<uint>(tbranch.size());
431 uint fsize =
static_cast<uint>(fbranch.size());
434 if (tbranch.size() > fbranch.size()) {
436 result.emplace_back(skip_if{fsize + 1});
437 std::move(fbranch.begin(), fbranch.end(), std::back_inserter(result));
438 result.emplace_back(skip{tsize});
439 std::move(tbranch.begin(), tbranch.end(), std::back_inserter(result));
441 result.emplace_back(skip_if_not{tsize + 1});
442 std::move(tbranch.begin(), tbranch.end(), std::back_inserter(result));
443 result.emplace_back(skip{fsize});
444 std::move(fbranch.begin(), fbranch.end(), std::back_inserter(result));
454 class stack_machine :
public boost::static_visitor<uint> {
455 std::vector<instruction> instruction_seq_;
456 std::vector<uint> stack_;
459 #ifdef SPIRIT_PO_DEBUG
461 void debug_print_instructions()
const {
462 std::cerr <<
"Instruction sequence:\n";
463 for (
const auto &
i : instruction_seq_) {
464 std::cerr << debug_string(
i) << std::endl;
469 #define MACHINE_ASSERT(X) \
472 std::cerr << "Stack machine failure:\n"; \
473 debug_print_instructions(); \
474 assert(false && #X); \
480 #define MACHINE_ASSERT(...) do {} while(0)
487 uint result = stack_.back();
488 stack_.resize(stack_.size() - 1);
493 explicit stack_machine(
const expr &
e)
505 uint operator()(
const constant &
c) {
506 stack_.emplace_back(
c.value);
510 uint operator()(
const skip &
s) {
511 return 1 +
s.distance;
514 uint operator()(
const skip_if &
s) {
515 return 1 + (pop_one() ?
s.distance : 0);
518 uint operator()(
const skip_if_not &
s) {
519 return 1 + (pop_one() ? 0 :
s.distance);
522 uint operator()(op_code oc) {
524 case op_code::n_var: {
525 stack_.emplace_back(n_value_);
528 case op_code::not_op: {
530 stack_.back() = !stack_.back();
533 #define STACK_MACHINE_CASE_(name, op) \
534 case op_code::name: { \
535 MACHINE_ASSERT(stack_.size() >= 2); \
536 uint parm2 = pop_one(); \
538 if (op_code::name == op_code::mod_op) { \
539 MACHINE_ASSERT(parm2 && "Division by zero when evaluating gettext plural form expression"); \
542 stack_.back() = (stack_.back() op parm2); \
548 #undef STACK_MACHINE_CASE_
558 while (pc < instruction_seq_.size()) {
567 #undef MACHINE_ASSERT
570 #undef FOREACH_SPIRIT_PO_BINARY_OP
571 #undef FOREACH_SPIRIT_PO_CONJUNCTION
V::result_t apply_visitor(typename V::param_t state, T &&... args)
Helper function to apply the result of a specified visitor to a variable_info object.
static map_location::DIRECTION n
static map_location::DIRECTION s
static std::size_t compute(std::string expr, std::size_t ref1, std::size_t ref2=0)