The Battle for Wesnoth  1.19.0-dev
default_plural_forms_compiler.hpp
Go to the documentation of this file.
1 // (C) Copyright 2015 - 2017 Christopher Beck
2 
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef SPIRIT_PO_DEFAULT_PLURAL_FORMS_COMPILER_HPP_INCLUDED
7 #define SPIRIT_PO_DEFAULT_PLURAL_FORMS_COMPILER_HPP_INCLUDED
8 
9 /***
10  * In GNU gettext, a language is permitted to define any number of 'plural forms'.
11  * For instance, in English and most romance languages there are only two forms,
12  * singular and plural. However in many other languages, there may be only one
13  * form, or there may be many plural forms reserved for various numbers of items.
14  *
15  * In the header of a po file, as part of the metadata, translators are expected
16  * to specify exactly how many plural forms there are, (how many different
17  * variations of a pluralized string they will provide), and also a function that
18  * computes which form (the appropriate index) should be used when the number of
19  * items is a number "n".
20  *
21  * Traditionally, this function is specified as a single line of pseudo C code.
22  *
23  * Examples:
24  *
25  * Russian:
26  * Po header:
27  * num_plurals = 3
28  * plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
29  * Messages:
30  *
31  *
32  * There are many many more examples shown here:
33  * http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html
34  *
35  * The code in *this* file is concerned with converting these strings into
36  * function objects implementing a function uint -> uint.
37  *
38  * These function objects are then associated to each catalog and used when
39  * looking up plurals.
40  *
41  * In spirit-po, we provide support for the standard gettext pseudo-C language
42  * using the 'default_plural_forms_compiler', which compiles these run-time
43  * pseudo-C expressions into expression trees which can be evaluated.
44  *
45  * By using non-default template parameters and providing an appropriate
46  * function object, you can make spirit-po use your favorite programming
47  * language for these instead. (Or, your translators' favorite?)
48  *
49  * The 'plural_forms_compiler' concept must be a class/struct and provide:
50  * - The plural_forms_compiler must be default constructible.
51  * - It must have a typedef 'result_type' which is the type of the function
52  * object it produces.
53  * - An operator() overload which takes const std::string &, and return an
54  * instance of 'result_type'.
55  * - result_type must be default constructible and move constructible.
56  * - result_type must have an operator() overload which takes and yields
57  * unsigned int.
58  * - result_type must have an explicit operator bool() const overload which
59  * returns whether the function object is valid (compilation succeeded)
60  * - result_type must have a function `error()` which returns a std::string
61  * representing a compilation error message in the case of failure.
62  */
63 
64 #ifndef BOOST_SPIRIT_USE_PHOENIX_V3
65 #define BOOST_SPIRIT_USE_PHOENIX_V3
66 #endif
67 
69 #include <spirit_po/exceptions.hpp>
70 #include <boost/optional/optional.hpp>
71 #include <boost/spirit/include/qi.hpp>
72 #include <string>
73 
74 namespace spirit_po {
75 
76 namespace qi = boost::spirit::qi;
77 typedef unsigned int uint;
78 
79 namespace default_plural_forms {
80 
82  mutable stack_machine machine_;
83  boost::optional<std::string> parse_error_;
84 
85 public:
86  explicit function_object(const expr & _e) : machine_(_e), parse_error_() {}
87  explicit function_object(const std::string & s) : machine_(constant{0}), parse_error_(s) {}
88  function_object() : function_object(std::string{"uninitialized"}) {}
89 
90  uint operator()(uint n) const {
91  return machine_.compute(n);
92  }
93 
94  explicit operator bool() const { return !parse_error_; }
95  std::string error() const { return *parse_error_; }
96 };
97 
98 struct compiler {
100  result_type operator()(const std::string & str) const {
101  expr e;
102 
103  typedef std::string::const_iterator str_it;
104  str_it it = str.begin();
105  str_it end = str.end();
106  op_grammar<str_it> grammar;
107 
108  if (qi::phrase_parse(it, end, grammar, qi::space, e) && it == end) {
109  return function_object(std::move(e));
110  } else {
111  return function_object("Plural-Forms expression reader: Could not parse expression, stopped parsing at:\n" + string_iterator_context(str, it));
112  }
113  }
114 };
115 
116 } // end namespace default_plural_forms
117 
118 } // end namespace spirit_po
119 
120 #endif // SPIRIT_PO_DEFAULT_PLURAL_FORMS_COMPILER_HPP_INCLUDED
boost::variant< constant, n_var, boost::recursive_wrapper< not_op >, boost::recursive_wrapper< ternary_op > > expr
std::string string_iterator_context(const std::string &str, std::string::const_iterator it)
Definition: exceptions.hpp:36
unsigned int uint
Definition: catalog.hpp:39
result_type operator()(const std::string &str) const
static map_location::DIRECTION n
static map_location::DIRECTION s
#define e