Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F118898
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
26 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/libs/tinygettext/expression_parser.cpp b/src/libs/tinygettext/expression_parser.cpp
new file mode 100644
index 0000000..c48f8e7
--- /dev/null
+++ b/src/libs/tinygettext/expression_parser.cpp
@@ -0,0 +1,520 @@
+// tinygettext - A gettext replacement that works directly on .po files
+//
+// The expression parser module for plural forms
+// Copyright (c) 2018 acme_pjz <acme_pjz@hotmail.com>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgement in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+
+#include "tinygettext/expression_parser.hpp"
+#include "tinygettext/plural_forms.hpp"
+#include "tinygettext/log_stream.hpp"
+
+#include <stdlib.h>
+#include <map>
+
+using tinygettext::Log;
+
+// This is a simple expression parser which parses a subset of
+// standard C++ expressions. See
+// https://en.cppreference.com/w/cpp/language/operator_precedence
+// for a reference of C++ operator precedence.
+//
+// The lexer and parser are partially copied from LLVM Kaleidoscope tutorial
+// http://www.llvm.org/docs/tutorial/index.html
+//
+// According to official GNU Gettext implementation
+// https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html
+// http://git.savannah.gnu.org/cgit/gettext.git/tree/gettext-runtime/intl/plural-exp.h#n34
+// http://git.savannah.gnu.org/cgit/gettext.git/tree/gettext-runtime/intl/plural.y#n132
+// there are no bitwise operators and all the numbers should be regarded as UNSIGNED integers.
+// However, the existing tinygettext implementation treats numbers as SIGNED integers.
+// So our parser also uses SIGNED integers and preserves the unary minus operator.
+//
+// Supported operators and precedences are:
+//
+// Precedence | Operator | Description | Associativity
+// -----------|-----------|-----------------------------------------|---------------
+// N/A | ( ) | Parenthesis | -
+// N/A | + - ! | Unary plus and minus, Logical NOT | Right-to-left
+// 110 | * / % | Multiplication, division, and remainder | Left-to-right
+// 100 | + - | Addition and subtraction | Left-to-right
+// 80 | < <= > >= | Comparisons | Left-to-right
+// 70 | == != | Comparisons | Left-to-right
+// 30 | && | Logical AND (short-circuited) | Left-to-right
+// 20 | || | Logical OR (short-circuited) | Left-to-right
+// 10 | ? : | Ternary conditional | Right-to-left
+//
+// EBNF:
+//
+// expression ::= ternary_expression
+// ternary_expression ::= binary_expression | binary_expression '?' ternary_expression ':' ternary_expression
+// binary_expression ::= primary_expression ( BINARY_OP primary_expression ) *
+// NOTE: the parsing of binary_expression uses operator-precedence parser, see
+// https://en.wikipedia.org/wiki/Operator-precedence_parser
+// primary_expression ::= '(' expression ')' | NUMBER | VARIABLE | UNARY_OP primary_expression
+//
+// Lexer:
+//
+// NUMBER ::= [0-9]+
+// NOTE: we only support integers, and we don't support hex or oct
+// VARIABLE ::= 'n'
+// NOTE: we only support the variable n
+
+// The list of tokens.
+enum Token {
+ tok_invalid,
+
+ tok_eof,
+
+ tok_number,
+ tok_variable,
+
+ tok_logical_not,
+
+ // The following tokens are also used as types of binary operations,
+ // where the first 2 tokens together with tok_logical_not are also used as types
+ // of unary operations.
+ // NOTE: in the function getOperatorPrecedence() it's assumed that tok_plus
+ // is the first binary operator in this enum.
+
+ // Precendence 100
+ tok_plus, tok_minus,
+
+ // Precendence 110
+ tok_times, tok_divides, tok_modulo,
+
+ // Precendence 80
+ tok_lt, tok_le, tok_gt, tok_ge,
+
+ // Precendence 70
+ tok_eq, tok_ne,
+
+ // Precendence 30
+ tok_logical_and,
+
+ // Precendence 20
+ tok_logical_or,
+
+ // End of binary operations.
+
+ tok_question_mark,
+ tok_colon,
+ tok_left_parenthesis,
+ tok_right_parenthesis,
+};
+
+// Get the precendence of the binary operator (should be >0).
+// -1 means not a binary operator.
+int getOperatorPrecedence(Token token) {
+ const int precedence[] = {
+ 100, 100,
+ 110, 110, 110,
+ 80, 80, 80, 80,
+ 70, 70,
+ 30,
+ 20,
+ };
+
+ int i = (int)token - (int)tok_plus;
+ if (i >= 0 && i < sizeof(precedence) / sizeof(precedence[0])) {
+ return precedence[i];
+ }
+
+ //Not a binary operator.
+ return -1;
+}
+
+struct AST;
+struct Tokenizer;
+
+AST* parseTernaryExpression(Tokenizer& tokenizer);
+AST* parseBinaryExpression(Tokenizer& tokenizer);
+AST* parsePrimaryExpression(Tokenizer& tokenizer);
+
+struct Tokenizer {
+ // The expression string.
+ const char* expression;
+
+ // The next token.
+ Token token;
+
+ // The position.
+ int pos;
+
+ // The start position of current token.
+ int startPos;
+
+ // Filled in if tok_number
+ int numVal;
+
+ Tokenizer(const char* expression) : expression(expression), token(tok_invalid), pos(0), numVal(0) {}
+
+ // Return the next token.
+ Token getNextToken() {
+ token = getNextTokenInternal();
+ return token;
+ }
+
+ Token getNextTokenInternal();
+
+ // Print error message.
+ void printError(const char* message, int position = -1, int length = -1, int highlight = -1);
+};
+
+void Tokenizer::printError(const char* message, int position, int length, int highlight) {
+ if (position < 0) {
+ position = startPos;
+ if (length <= 0) length = pos - startPos;
+ }
+ if (length <= 0) length = 1;
+
+ std::string indicator = std::string(position, ' ') + std::string(length, '~');
+
+ if (highlight >= 0 && highlight < (int)indicator.size()) indicator[highlight] = '^';
+
+ log_error << "Error: " << message << "\n" << expression << "\n" << indicator << std::endl;
+}
+
+Token Tokenizer::getNextTokenInternal() {
+ char c;
+
+ // Skip any whitespace.
+ while (isspace(c = expression[pos++])) {
+ }
+
+ // Record the start position.
+ startPos = pos - 1;
+
+ // Check number.
+ if (isdigit(c)) {
+ numVal = c - '0';
+ while (isdigit(c = expression[pos++])) {
+ numVal = numVal * 10 + (c - '0');
+ }
+ pos--;
+ return tok_number;
+ }
+
+ // Check variable.
+ if (c == 'n') {
+ return tok_variable;
+ }
+
+ // Check operators.
+ switch (c) {
+ case '(': return tok_left_parenthesis;
+ case ')': return tok_right_parenthesis;
+ case '+': return tok_plus;
+ case '-': return tok_minus;
+ case '*': return tok_times;
+ case '/': return tok_divides;
+ case '%': return tok_modulo;
+ case '?': return tok_question_mark;
+ case ':': return tok_colon;
+ case '<':
+ switch (expression[pos++]) {
+ case '=': return tok_le;
+ default: pos--; return tok_lt;
+ }
+ case '>':
+ switch (expression[pos++]) {
+ case '=': return tok_ge;
+ default: pos--; return tok_gt;
+ }
+ case '!':
+ switch (expression[pos++]) {
+ case '=': return tok_ne;
+ default: pos--; return tok_logical_not;
+ }
+ case '=':
+ switch (expression[pos++]) {
+ case '=': return tok_eq;
+ default: printError("'=' expected", -1, -1, pos - 1); pos--; return tok_invalid;
+ }
+ case '&':
+ switch (expression[pos++]) {
+ case '&': return tok_logical_and;
+ default: printError("'&' expected", -1, -1, pos - 1); pos--; return tok_invalid;
+ }
+ case '|':
+ switch (expression[pos++]) {
+ case '|': return tok_logical_or;
+ default: printError("'|' expected", -1, -1, pos - 1); pos--; return tok_invalid;
+ }
+ case ';': case '\0':
+ pos--; return tok_eof;
+ case '^': case '~':
+ printError("This operator is unsupported in plural forms expression"); pos--; return tok_invalid;
+ default:
+ printError("Invalid character"); pos--; return tok_invalid;
+ }
+}
+
+struct AST {
+ virtual int evaluate(int n) = 0;
+ virtual ~AST() {}
+};
+
+struct ConstExprAST : public AST {
+ int value;
+
+ ConstExprAST(int value) : value(value) {}
+ int evaluate(int n) {
+ return value;
+ }
+};
+
+struct VariableExprAST : public AST {
+ VariableExprAST() {}
+ int evaluate(int n) {
+ return n;
+ }
+};
+
+struct UnaryExprAST : public AST {
+ Token type;
+ AST *child;
+
+ UnaryExprAST(Token type, AST *child) : type(type), child(child) {}
+ ~UnaryExprAST() {
+ delete child;
+ }
+ int evaluate(int n) {
+ int ret = child->evaluate(n);
+ switch (type) {
+ case tok_plus: return ret;
+ case tok_minus: return -ret;
+ case tok_logical_not: return ret ? 0 : 1;
+ default:
+ log_error << "Unknown unary operator: " << (int)type << std::endl;
+ return 0;
+ }
+ }
+};
+
+struct BinaryExprAST : public AST {
+ Token type;
+ AST *child1, *child2;
+
+ BinaryExprAST(Token type, AST *child1, AST *child2) : type(type), child1(child1), child2(child2) {}
+ ~BinaryExprAST() {
+ delete child1;
+ delete child2;
+ }
+ int evaluate(int n) {
+ switch (type) {
+ case tok_plus: return child1->evaluate(n) + child2->evaluate(n);
+ case tok_minus: return child1->evaluate(n) - child2->evaluate(n);
+ case tok_times: return child1->evaluate(n) * child2->evaluate(n);
+ case tok_divides: {
+ int ret1 = child1->evaluate(n);
+ int ret2 = child2->evaluate(n);
+ if (ret2 == 0) {
+ log_error << "Division by zero" << std::endl;
+ return 0;
+ } else if (ret2 == 1){
+ return ret1;
+ } else if (ret2 == -1) {
+ return -ret1;
+ } else {
+ return ret1 / ret2;
+ }
+ }
+ case tok_modulo: {
+ int ret1 = child1->evaluate(n);
+ int ret2 = child2->evaluate(n);
+ if (ret2 == 0) {
+ log_error << "Division by zero" << std::endl;
+ return 0;
+ } else if (ret2 == 1 || ret2 == -1){
+ return 0;
+ } else {
+ return ret1 % ret2;
+ }
+ }
+ case tok_logical_and: return (child1->evaluate(n) && child2->evaluate(n)) ? 1 : 0;
+ case tok_logical_or: return (child1->evaluate(n) || child2->evaluate(n)) ? 1 : 0;
+ case tok_lt: return (child1->evaluate(n) < child2->evaluate(n)) ? 1 : 0;
+ case tok_gt: return (child1->evaluate(n) > child2->evaluate(n)) ? 1 : 0;
+ case tok_le: return (child1->evaluate(n) <= child2->evaluate(n)) ? 1 : 0;
+ case tok_ge: return (child1->evaluate(n) >= child2->evaluate(n)) ? 1 : 0;
+ case tok_eq: return (child1->evaluate(n) == child2->evaluate(n)) ? 1 : 0;
+ case tok_ne: return (child1->evaluate(n) != child2->evaluate(n)) ? 1 : 0;
+ default:
+ log_error << "Unknown binary operator: " << (int)type << std::endl;
+ return 0;
+ }
+ }
+};
+
+struct TernaryExprAST : public AST {
+ AST *child1, *child2, *child3;
+
+ TernaryExprAST(AST *child1, AST *child2, AST *child3) : child1(child1), child2(child2), child3(child3) {}
+ ~TernaryExprAST() {
+ delete child1;
+ delete child2;
+ delete child3;
+ }
+ int evaluate(int n) {
+ return child1->evaluate(n) ? child2->evaluate(n) : child3->evaluate(n);
+ }
+};
+
+inline AST* parseExpression(Tokenizer& tokenizer) {
+ return parseTernaryExpression(tokenizer);
+}
+
+AST* parseTernaryExpression(Tokenizer& tokenizer) {
+ AST *child1 = parseBinaryExpression(tokenizer);
+ if (child1 == NULL) return NULL;
+
+ if (tokenizer.token != tok_question_mark) return child1;
+
+ tokenizer.getNextToken();
+
+ AST* child2 = parseTernaryExpression(tokenizer);
+ if (child2 == NULL) {
+ delete child1;
+ return NULL;
+ }
+
+ if (tokenizer.token != tok_colon) {
+ tokenizer.printError("':' expected");
+ delete child1;
+ delete child2;
+ return NULL;
+ }
+
+ tokenizer.getNextToken();
+
+ AST* child3 = parseTernaryExpression(tokenizer);
+ if (child3 == NULL) {
+ delete child1;
+ delete child2;
+ return NULL;
+ }
+
+ return new TernaryExprAST(child1, child2, child3);
+}
+
+AST* parseBinaryExpressionInternal(Tokenizer& tokenizer, AST* lhs, int min_precedence) {
+ // These codes are adapted from the pseudo code in
+ // https://en.wikipedia.org/wiki/Operator-precedence_parser
+
+ for (;;) {
+ const int current_precendence = getOperatorPrecedence(tokenizer.token);
+ if (current_precendence < min_precedence) return lhs;
+
+ Token type = tokenizer.token;
+
+ tokenizer.getNextToken();
+
+ AST *rhs = parsePrimaryExpression(tokenizer);
+ if (rhs == NULL) {
+ delete lhs;
+ return NULL;
+ }
+
+ for (;;) {
+ int precendence = getOperatorPrecedence(tokenizer.token);
+ if (precendence <= current_precendence) break;
+
+ rhs = parseBinaryExpressionInternal(tokenizer, rhs, precendence);
+ if (rhs == NULL) {
+ delete lhs;
+ return NULL;
+ }
+ }
+
+ lhs = new BinaryExprAST(type, lhs, rhs);
+ }
+}
+
+AST* parseBinaryExpression(Tokenizer& tokenizer) {
+ AST *lhs = parsePrimaryExpression(tokenizer);
+ if (lhs == NULL) return NULL;
+
+ return parseBinaryExpressionInternal(tokenizer, lhs, 0);
+}
+
+AST* parsePrimaryExpression(Tokenizer& tokenizer) {
+ switch (tokenizer.token) {
+ case tok_left_parenthesis: {
+ tokenizer.getNextToken();
+
+ AST *ret = parseExpression(tokenizer);
+ if (ret == NULL) return NULL;
+
+ if (tokenizer.token != tok_right_parenthesis) {
+ tokenizer.printError("')' expected");
+ delete ret;
+ return NULL;
+ }
+
+ tokenizer.getNextToken();
+
+ return ret;
+ }
+ case tok_number: {
+ AST *ret = new ConstExprAST(tokenizer.numVal);
+ tokenizer.getNextToken();
+ return ret;
+ }
+ case tok_variable:
+ tokenizer.getNextToken();
+ return new VariableExprAST();
+ case tok_plus: case tok_minus: case tok_logical_not: {
+ Token type = tokenizer.token;
+ tokenizer.getNextToken();
+
+ AST *ret = parsePrimaryExpression(tokenizer);
+ if (ret == NULL) return NULL;
+
+ if (type == tok_plus) return ret;
+ return new UnaryExprAST(type, ret);
+ }
+ case tok_invalid:
+ return NULL;
+ default:
+ tokenizer.printError("'(' or a number or 'n' or an unary operator expected");
+ return NULL;
+ }
+}
+
+class PluralExpressionFunctor : public tinygettext::PluralFunctor {
+private:
+ AST *root;
+public:
+ PluralExpressionFunctor(AST *root) : root(root) {}
+ ~PluralExpressionFunctor() {
+ delete root;
+ }
+ unsigned int operator()(int n) const {
+ if (root) return root->evaluate(n); else return 0;
+ }
+};
+
+tinygettext::PluralFunctor* tinygettext::plural_functor_from_expression(const char* expression) {
+ Tokenizer tokenizer(expression);
+ tokenizer.getNextToken();
+
+ AST *root = parseExpression(tokenizer);
+ if (root == NULL) return NULL;
+
+ return new PluralExpressionFunctor(root);
+}
diff --git a/src/libs/tinygettext/plural_forms.hpp b/src/libs/tinygettext/expression_parser.hpp
similarity index 51%
copy from src/libs/tinygettext/plural_forms.hpp
copy to src/libs/tinygettext/expression_parser.hpp
index 5a8ea9a..f99c6e8 100644
--- a/src/libs/tinygettext/plural_forms.hpp
+++ b/src/libs/tinygettext/expression_parser.hpp
@@ -1,63 +1,35 @@
// tinygettext - A gettext replacement that works directly on .po files
-// Copyright (c) 2006 Ingo Ruhnke <grumbel@gmail.com>
+//
+// The expression parser module for plural forms
+// Copyright (c) 2018 acme_pjz <acme_pjz@hotmail.com>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
-#ifndef HEADER_TINYGETTEXT_PLURAL_FORMS_HPP
-#define HEADER_TINYGETTEXT_PLURAL_FORMS_HPP
+#ifndef HEADER_TINYGETTEXT_EXPRESSION_PARSER_HPP
+#define HEADER_TINYGETTEXT_EXPRESSION_PARSER_HPP
#include <string>
namespace tinygettext {
-typedef unsigned int (*PluralFunc)(int n);
-
-class PluralForms
-{
-private:
- unsigned int nplural;
- PluralFunc plural;
-
-public:
- static PluralForms from_string(const std::string& str);
-
- PluralForms()
- : nplural(0),
- plural(0)
- {}
+class PluralFunctor;
- PluralForms(unsigned int nplural_, PluralFunc plural_)
- : nplural(nplural_),
- plural(plural_)
- {}
+PluralFunctor* plural_functor_from_expression(const char* expression);
- unsigned int get_nplural() const { return nplural; }
- unsigned int get_plural(int n) const { if (plural) return plural(n); else return 0; }
-
- bool operator==(const PluralForms& other) { return nplural == other.nplural && plural == other.plural; }
- bool operator!=(const PluralForms& other) { return !(*this == other); }
-
- explicit operator bool() const {
- return plural != NULL;
- }
-};
-
-} // namespace tinygettext
+}
#endif
-
-/* EOF */
diff --git a/src/libs/tinygettext/plural_forms.cpp b/src/libs/tinygettext/plural_forms.cpp
index 058a54b..faa84fb 100644
--- a/src/libs/tinygettext/plural_forms.cpp
+++ b/src/libs/tinygettext/plural_forms.cpp
@@ -1,95 +1,128 @@
// tinygettext - A gettext replacement that works directly on .po files
// Copyright (c) 2006 Ingo Ruhnke <grumbel@gmail.com>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "tinygettext/plural_forms.hpp"
+#include "tinygettext/log_stream.hpp"
+#include "tinygettext/expression_parser.hpp"
#include <unordered_map>
+#include <stdlib.h>
namespace tinygettext {
/**
* Plural functions are used to select a string that matches a given
* count. \a n is the count and the return value is the string index
* used in the .po file, for example:
*
* msgstr[0] = "You got %d error";
* msgstr[1] = "You got %d errors";
* ^-- return value of plural function
*/
unsigned int plural1(int ) { return 0; }
unsigned int plural2_1(int n) { return (n != 1); }
unsigned int plural2_2(int n) { return (n > 1); }
unsigned int plural2_mk(int n) { return n==1 || n%10==1 ? 0 : 1; }
unsigned int plural3_lv(int n) { return static_cast<unsigned int>(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2); }
unsigned int plural3_ga(int n) { return static_cast<unsigned int>(n==1 ? 0 : n==2 ? 1 : 2); }
unsigned int plural3_lt(int n) { return static_cast<unsigned int>(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2); }
unsigned int plural3_1(int n) { return static_cast<unsigned int>(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
unsigned int plural3_sk(int n) { return static_cast<unsigned int>( (n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2 ); }
unsigned int plural3_pl(int n) { return static_cast<unsigned int>(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
unsigned int plural3_sl(int n) { return static_cast<unsigned int>(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3); }
unsigned int plural4_gd(int n) { return static_cast<unsigned int>( n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3; }
unsigned int plural6_ar(int n) { return static_cast<unsigned int>( n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5); }
+PluralFunctor::~PluralFunctor() {
+}
+
+unsigned int PluralFuncFunctor::operator()(int n) const {
+ if (plural) return plural(n); else return 0;
+}
+
+PluralFuncFunctor::~PluralFuncFunctor() {
+}
PluralForms
PluralForms::from_string(const std::string& str)
{
typedef std::unordered_map<std::string, PluralForms> PluralFormsMap;
static PluralFormsMap plural_forms;
if (plural_forms.empty())
{
// Note that the plural forms here shouldn't contain any spaces
plural_forms["Plural-Forms:nplurals=1;plural=0;"] = PluralForms(1, plural1);
plural_forms["Plural-Forms:nplurals=2;plural=(n!=1);"] = PluralForms(2, plural2_1);
plural_forms["Plural-Forms:nplurals=2;plural=n!=1;"] = PluralForms(2, plural2_1);
plural_forms["Plural-Forms:nplurals=2;plural=(n>1);"] = PluralForms(2, plural2_2);
plural_forms["Plural-Forms:nplurals=2;plural=n==1||n%10==1?0:1;"] = PluralForms(2, plural2_mk);
plural_forms["Plural-Forms:nplurals=3;plural=n%10==1&&n%100!=11?0:n!=0?1:2);"] = PluralForms(2, plural3_lv);
plural_forms["Plural-Forms:nplurals=3;plural=n==1?0:n==2?1:2;"] = PluralForms(3, plural3_ga);
plural_forms["Plural-Forms:nplurals=3;plural=(n%10==1&&n%100!=11?0:n%10>=2&&(n%100<10||n%100>=20)?1:2);"] = PluralForms(3, plural3_lt);
plural_forms["Plural-Forms:nplurals=3;plural=(n%10==1&&n%100!=11?0:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?1:2);"] = PluralForms(3, plural3_1);
plural_forms["Plural-Forms:nplurals=3;plural=(n==1)?0:(n>=2&&n<=4)?1:2;"] = PluralForms(3, plural3_sk);
plural_forms["Plural-Forms:nplurals=3;plural=(n==1?0:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?1:2);"] = PluralForms(3, plural3_pl);
plural_forms["Plural-Forms:nplurals=3;plural=(n%100==1?0:n%100==2?1:n%100==3||n%100==4?2:3);"] = PluralForms(3, plural3_sl);
plural_forms["Plural-Forms:nplurals=4;plural=(n==1||n==11)?0:(n==2||n==12)?1:(n>2&&n<20)?2:3;"]=PluralForms(4, plural4_gd);
plural_forms["Plural-Forms:nplurals=6;plural=n==0?0:n==1?1:n==2?2:n%100>=3&&n%100<=10?3:n%100>=11?4:5"]=PluralForms(6, plural6_ar);
}
// Remove spaces from string before lookup
std::string space_less_str;
for(std::string::size_type i = 0; i < str.size(); ++i)
if (!isspace(str[i]))
space_less_str += str[i];
PluralFormsMap::const_iterator it= plural_forms.find(space_less_str);
if (it != plural_forms.end())
{
return it->second;
}
else
{
- return PluralForms();
+ log_warning << "The plural forms '" << space_less_str << "' is not in the predefined list! Try to parse it as an expression!" << std::endl;
+
+ size_t i = space_less_str.find("nplurals=");
+ if (i == std::string::npos) {
+ log_warning << "Can't find 'nplurals=' in the plural forms definition!" << std::endl;
+ return PluralForms();
+ }
+ unsigned int nplural = strtoul(space_less_str.c_str() + (i + 9), NULL, 10);
+
+ i = space_less_str.find("plural=");
+ if (i == std::string::npos) {
+ log_warning << "Can't find 'plural=' in the plural forms definition!" << std::endl;
+ return PluralForms();
+ }
+
+ PluralFunctor *plural = plural_functor_from_expression(space_less_str.c_str() + (i + 7));
+ if (plural == NULL) {
+ log_warning << "Failed to parse plural expression in the plural forms definition!" << std::endl;
+ return PluralForms();
+ }
+
+ return PluralForms(nplural, plural);
}
}
} // namespace tinygettext
/* EOF */
diff --git a/src/libs/tinygettext/plural_forms.hpp b/src/libs/tinygettext/plural_forms.hpp
index 5a8ea9a..8d2474a 100644
--- a/src/libs/tinygettext/plural_forms.hpp
+++ b/src/libs/tinygettext/plural_forms.hpp
@@ -1,63 +1,86 @@
// tinygettext - A gettext replacement that works directly on .po files
// Copyright (c) 2006 Ingo Ruhnke <grumbel@gmail.com>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifndef HEADER_TINYGETTEXT_PLURAL_FORMS_HPP
#define HEADER_TINYGETTEXT_PLURAL_FORMS_HPP
#include <string>
+#include <memory>
namespace tinygettext {
+class PluralFunctor
+{
+public:
+ virtual unsigned int operator()(int n) const = 0;
+ virtual ~PluralFunctor();
+};
+
typedef unsigned int (*PluralFunc)(int n);
+class PluralFuncFunctor : public PluralFunctor
+{
+private:
+ PluralFunc plural;
+public:
+ PluralFuncFunctor(PluralFunc plural_) : plural(plural_) {}
+ unsigned int operator()(int n) const override;
+ ~PluralFuncFunctor();
+};
+
class PluralForms
{
private:
unsigned int nplural;
- PluralFunc plural;
+ std::shared_ptr<PluralFunctor> plural;
public:
static PluralForms from_string(const std::string& str);
PluralForms()
: nplural(0),
plural(0)
{}
PluralForms(unsigned int nplural_, PluralFunc plural_)
+ : nplural(nplural_),
+ plural(new PluralFuncFunctor(plural_))
+ {}
+
+ PluralForms(unsigned int nplural_, PluralFunctor* plural_)
: nplural(nplural_),
plural(plural_)
{}
unsigned int get_nplural() const { return nplural; }
- unsigned int get_plural(int n) const { if (plural) return plural(n); else return 0; }
+ unsigned int get_plural(int n) const { if (plural) return (*plural)(n); else return 0; }
bool operator==(const PluralForms& other) { return nplural == other.nplural && plural == other.plural; }
bool operator!=(const PluralForms& other) { return !(*this == other); }
explicit operator bool() const {
return plural != NULL;
}
};
} // namespace tinygettext
#endif
/* EOF */
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, May 16, 8:21 PM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
63541
Default Alt Text
(26 KB)
Attached To
Mode
R79 meandmyshadow
Attached
Detach File
Event Timeline