The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
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
70#include <boost/optional/optional.hpp>
71#include <boost/spirit/include/qi.hpp>
72#include <string>
73
74namespace spirit_po {
75
76namespace qi = boost::spirit::qi;
77typedef unsigned int uint;
78
79namespace default_plural_forms {
80
82 mutable stack_machine machine_;
83 boost::optional<std::string> parse_error_;
84
85public:
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
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
98struct 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)
unsigned int uint
Definition catalog.hpp:39
STL namespace.
result_type operator()(const std::string &str) const
static map_location::direction n
static map_location::direction s
#define e