summaryrefslogtreecommitdiff
path: root/lib/parse_plural.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/parse_plural.h')
-rw-r--r--lib/parse_plural.h524
1 files changed, 270 insertions, 254 deletions
diff --git a/lib/parse_plural.h b/lib/parse_plural.h
index 7af6809e..c3591881 100644
--- a/lib/parse_plural.h
+++ b/lib/parse_plural.h
@@ -7,11 +7,45 @@
#ifndef PARSE_PLURAL_H_INCLUDED
#define PARSE_PLURAL_H_INCLUDED
-#include <list>
#include <memory>
#include <functional>
#include <zen/string_base.h>
+namespace parse_plural
+{
+//expression interface
+struct Expression { virtual ~Expression() {} };
+
+template <class T>
+struct Expr : public Expression
+{
+ typedef T ValueType;
+ virtual ValueType eval() const = 0;
+};
+
+
+class ParsingError {};
+
+class PluralForm
+{
+public:
+ PluralForm(const std::string& stream); //throw ParsingError
+ int getForm(int n) const { n_ = n ; return expr->eval(); }
+
+private:
+ std::shared_ptr<Expr<int>> expr;
+ mutable int n_;
+};
+
+
+
+
+
+
+
+
+//--------------------------- implementation ---------------------------
+
//http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html
//http://translate.sourceforge.net/wiki/l10n/pluralforms
/*
@@ -52,55 +86,57 @@ pm-expression:
variable-number-n-expression
constant-number-expression
( expression )
-*/
-
-//expression interface
-struct Expression { virtual ~Expression() {} };
+.po format,e.g.: (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)
+*/
-template <class T>
-struct Expr : public Expression
+namespace implementation
{
- typedef T ValueType;
- virtual ValueType eval() const = 0;
-};
-
//specific binary expression based on STL function objects
template <class StlOp>
struct BinaryExp : public Expr<typename StlOp::result_type>
{
- typedef const Expr<typename StlOp::first_argument_type > ExpLhs;
- typedef const Expr<typename StlOp::second_argument_type> ExpRhs;
+ typedef std::shared_ptr<Expr<typename StlOp::first_argument_type >> ExpLhs;
+ typedef std::shared_ptr<Expr<typename StlOp::second_argument_type>> ExpRhs;
- BinaryExp(const ExpLhs& lhs, const ExpRhs& rhs, StlOp biop) : lhs_(lhs), rhs_(rhs), biop_(biop) {}
- virtual typename StlOp::result_type eval() const { return biop_(lhs_.eval(), rhs_.eval()); }
- const ExpLhs& lhs_;
- const ExpRhs& rhs_;
+ BinaryExp(const ExpLhs& lhs, const ExpRhs& rhs, StlOp biop) : lhs_(lhs), rhs_(rhs), biop_(biop) { assert(lhs && rhs); }
+ virtual typename StlOp::result_type eval() const { return biop_(lhs_->eval(), rhs_->eval()); }
+private:
+ ExpLhs lhs_;
+ ExpRhs rhs_;
StlOp biop_;
};
template <class StlOp> inline
-BinaryExp<StlOp> makeBiExp(const Expression& lhs, const Expression& rhs, StlOp biop) //throw std::bad_cast
+std::shared_ptr<BinaryExp<StlOp>> makeBiExp(const std::shared_ptr<Expression>& lhs, const std::shared_ptr<Expression>& rhs, StlOp biop) //throw ParsingError
{
- return BinaryExp<StlOp>(dynamic_cast<const Expr<typename StlOp::first_argument_type >&>(lhs), //throw std::bad_cast
- dynamic_cast<const Expr<typename StlOp::second_argument_type>&>(rhs), biop); //
+ auto exLeft = std::dynamic_pointer_cast<Expr<typename StlOp::first_argument_type >>(lhs);
+ auto exRight = std::dynamic_pointer_cast<Expr<typename StlOp::second_argument_type>>(rhs);
+ if (!exLeft || !exRight)
+ throw ParsingError();
+ return std::make_shared<BinaryExp<StlOp>>(exLeft, exRight, biop);
}
template <class T>
-struct TernaryExp : public Expr<T>
+struct ConditionalExp : public Expr<T>
{
- TernaryExp(const Expr<bool>& ifExp, const Expr<T>& thenExp, const Expr<T>& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) {}
- virtual typename Expr<T>::ValueType eval() const { return ifExp_.eval() ? thenExp_.eval() : elseExp_.eval(); }
- const Expr<bool>& ifExp_;
- const Expr<T>& thenExp_;
- const Expr<T>& elseExp_;
+ ConditionalExp(const std::shared_ptr<Expr<bool>>& ifExp,
+ const std::shared_ptr<Expr<T>>& thenExp,
+ const std::shared_ptr<Expr<T>>& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) { assert(ifExp && thenExp && elseExp); }
+
+ virtual typename Expr<T>::ValueType eval() const { return ifExp_->eval() ? thenExp_->eval() : elseExp_->eval(); }
+private:
+ std::shared_ptr<Expr<bool>> ifExp_;
+ std::shared_ptr<Expr<T>> thenExp_;
+ std::shared_ptr<Expr<T>> elseExp_;
};
struct ConstNumberExp : public Expr<int>
{
ConstNumberExp(int n) : n_(n) {}
virtual int eval() const { return n_; }
+private:
int n_;
};
@@ -108,296 +144,276 @@ struct VariableNumberNExp : public Expr<int>
{
VariableNumberNExp(int& n) : n_(n) {}
virtual int eval() const { return n_; }
+private:
int& n_;
};
+//-------------------------------------------------------------------------------
-class PluralForm
+struct Token
{
-public:
- struct ParsingError {};
-
- //.po format,e.g.: (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)
- PluralForm(const std::string& phrase) : n_(0)
+ enum Type
{
- Parser(phrase, //in
- expr, n_, dump); //out
- }
+ TK_TERNARY_QUEST,
+ TK_TERNARY_COLON,
+ TK_OR,
+ TK_AND,
+ TK_EQUAL,
+ TK_NOT_EQUAL,
+ TK_LESS,
+ TK_LESS_EQUAL,
+ TK_GREATER,
+ TK_GREATER_EQUAL,
+ TK_MODULUS,
+ TK_VARIABLE_N,
+ TK_CONST_NUMBER,
+ TK_BRACKET_LEFT,
+ TK_BRACKET_RIGHT,
+ TK_END
+ };
- int getForm(int n) const { n_ = n ; return expr->eval(); }
+ Token(Type t) : type(t), number(0) {}
+ Token(int num) : type(TK_CONST_NUMBER), number(num) {}
-private:
- typedef std::list<std::shared_ptr<Expression> > DumpList;
+ Type type;
+ int number; //if type == TK_CONST_NUMBER
+};
- struct Token
+class Scanner
+{
+public:
+ Scanner(const std::string& stream) : stream_(stream), pos(stream_.begin())
{
- enum Type
- {
- TK_TERNARY_QUEST,
- TK_TERNARY_COLON,
- TK_OR,
- TK_AND,
- TK_EQUAL,
- TK_NOT_EQUAL,
- TK_LESS,
- TK_LESS_EQUAL,
- TK_GREATER,
- TK_GREATER_EQUAL,
- TK_MODULUS,
- TK_VARIABLE_N,
- TK_CONST_NUMBER,
- TK_BRACKET_LEFT,
- TK_BRACKET_RIGHT,
- TK_END
- };
-
- Token(Type t) : type(t), number(0) {}
- Token(int num) : type(TK_CONST_NUMBER), number(num) {}
-
- Type type;
- int number; //if type == TK_CONST_NUMBER
- };
+ tokens.push_back(std::make_pair("?" , Token::TK_TERNARY_QUEST));
+ tokens.push_back(std::make_pair(":" , Token::TK_TERNARY_COLON));
+ tokens.push_back(std::make_pair("||", Token::TK_OR ));
+ tokens.push_back(std::make_pair("&&", Token::TK_AND ));
+ tokens.push_back(std::make_pair("==", Token::TK_EQUAL ));
+ tokens.push_back(std::make_pair("!=", Token::TK_NOT_EQUAL ));
+ tokens.push_back(std::make_pair("<=", Token::TK_LESS_EQUAL ));
+ tokens.push_back(std::make_pair("<" , Token::TK_LESS ));
+ tokens.push_back(std::make_pair(">=", Token::TK_GREATER_EQUAL));
+ tokens.push_back(std::make_pair(">" , Token::TK_GREATER ));
+ tokens.push_back(std::make_pair("%" , Token::TK_MODULUS ));
+ tokens.push_back(std::make_pair("n" , Token::TK_VARIABLE_N ));
+ tokens.push_back(std::make_pair("N" , Token::TK_VARIABLE_N ));
+ tokens.push_back(std::make_pair("(" , Token::TK_BRACKET_LEFT ));
+ tokens.push_back(std::make_pair(")" , Token::TK_BRACKET_RIGHT));
+ }
- class Scanner
+ Token nextToken()
{
- public:
- Scanner(const std::string& phrase) : stream(phrase), pos(stream.begin())
- {
- tokens.push_back(std::make_pair("?" , Token::TK_TERNARY_QUEST));
- tokens.push_back(std::make_pair(":" , Token::TK_TERNARY_COLON));
- tokens.push_back(std::make_pair("||", Token::TK_OR ));
- tokens.push_back(std::make_pair("&&", Token::TK_AND ));
- tokens.push_back(std::make_pair("==", Token::TK_EQUAL ));
- tokens.push_back(std::make_pair("!=", Token::TK_NOT_EQUAL ));
- tokens.push_back(std::make_pair("<=", Token::TK_LESS_EQUAL ));
- tokens.push_back(std::make_pair("<" , Token::TK_LESS ));
- tokens.push_back(std::make_pair(">=", Token::TK_GREATER_EQUAL));
- tokens.push_back(std::make_pair(">" , Token::TK_GREATER ));
- tokens.push_back(std::make_pair("%" , Token::TK_MODULUS ));
- tokens.push_back(std::make_pair("n" , Token::TK_VARIABLE_N ));
- tokens.push_back(std::make_pair("N" , Token::TK_VARIABLE_N ));
- tokens.push_back(std::make_pair("(" , Token::TK_BRACKET_LEFT ));
- tokens.push_back(std::make_pair(")" , Token::TK_BRACKET_RIGHT));
- }
-
- Token nextToken()
- {
- //skip whitespace
- pos = std::find_if(pos, stream.end(), [](char c) { return !zen::isWhiteSpace(c); });
+ //skip whitespace
+ pos = std::find_if(pos, stream_.end(), [](char c) { return !zen::isWhiteSpace(c); });
- if (pos == stream.end())
- return Token::TK_END;
+ if (pos == stream_.end())
+ return Token::TK_END;
- for (auto iter = tokens.begin(); iter != tokens.end(); ++iter)
- if (startsWith(iter->first))
- {
- pos += iter->first.size();
- return Token(iter->second);
- }
-
- auto digitEnd = std::find_if(pos, stream.end(), [](char c) { return !zen::isDigit(c); });
-
- if (digitEnd != pos)
+ for (auto iter = tokens.begin(); iter != tokens.end(); ++iter)
+ if (startsWith(iter->first))
{
- int number = zen::stringTo<int>(std::string(&*pos, digitEnd - pos));
- pos = digitEnd;
- return number;
+ pos += iter->first.size();
+ return Token(iter->second);
}
- throw ParsingError(); //unknown token
- }
+ auto digitEnd = std::find_if(pos, stream_.end(), [](char c) { return !zen::isDigit(c); });
- private:
- bool startsWith(const std::string& prefix) const
+ if (digitEnd != pos)
{
- if (stream.end() - pos < static_cast<ptrdiff_t>(prefix.size()))
- return false;
- return std::equal(prefix.begin(), prefix.end(), pos);
+ int number = zen::stringTo<int>(std::string(pos, digitEnd));
+ pos = digitEnd;
+ return number;
}
- typedef std::vector<std::pair<std::string, Token::Type> > TokenList;
- TokenList tokens;
+ throw ParsingError(); //unknown token
+ }
- const std::string stream;
- std::string::const_iterator pos;
- };
+private:
+ bool startsWith(const std::string& prefix) const
+ {
+ if (stream_.end() - pos < static_cast<ptrdiff_t>(prefix.size()))
+ return false;
+ return std::equal(prefix.begin(), prefix.end(), pos);
+ }
+ typedef std::vector<std::pair<std::string, Token::Type> > TokenList;
+ TokenList tokens;
+
+ const std::string stream_;
+ std::string::const_iterator pos;
+};
+
+//-------------------------------------------------------------------------------
+
+class Parser
+{
+public:
+ Parser(const std::string& stream, int& n) :
+ scn(stream),
+ tk(scn.nextToken()),
+ n_(n) {}
- class Parser
+ std::shared_ptr<Expr<int>> parse() //throw ParsingError; return value always bound!
{
- public:
- Parser(const std::string& phrase, //in
- const Expr<int>*& expr, int& n, PluralForm::DumpList& dump) : //out
- scn(phrase),
- tk(scn.nextToken()),
- n_(n),
- dump_(dump)
- {
- try
- {
- const Expression& e = parse(); //throw std::bad_cast, ParsingError
- expr = &dynamic_cast<const Expr<int>&>(e); //
- }
- catch (std::bad_cast&) { throw ParsingError(); }
+ auto e = std::dynamic_pointer_cast<Expr<int>>(parseExpression()); //throw ParsingError
+ if (!e)
+ throw ParsingError();
+ expectToken(Token::TK_END);
+ return e;
+ }
- consumeToken(Token::TK_END);
- }
+private:
+ std::shared_ptr<Expression> parseExpression() { return parseConditional(); }//throw ParsingError
- private:
- void nextToken() { tk = scn.nextToken(); }
- const Token& token() const { return tk; }
+ std::shared_ptr<Expression> parseConditional() //throw ParsingError
+ {
+ std::shared_ptr<Expression> e = parseLogicalOr();
- void consumeToken(Token::Type t) //throw ParsingError
+ if (token().type == Token::TK_TERNARY_QUEST)
{
- if (token().type != t)
- throw ParsingError();
nextToken();
- }
- const Expression& parse() { return parseConditional(); }; //throw std::bad_cast, ParsingError
+ auto ifExp = std::dynamic_pointer_cast<Expr<bool>>(e);
+ auto thenExp = std::dynamic_pointer_cast<Expr<int>>(parseExpression()); //associativity: <-
- const Expression& parseConditional()
- {
- const Expression& e = parseLogicalOr();
+ expectToken(Token::TK_TERNARY_COLON);
+ nextToken();
- if (token().type == Token::TK_TERNARY_QUEST)
- {
- nextToken();
- const Expression& thenEx = parse(); //associativity: <-
- consumeToken(Token::TK_TERNARY_COLON);
- const Expression& elseEx = parse(); //
-
- return manageObj(TernaryExp<int>(dynamic_cast<const Expr<bool>&>(e), //
- dynamic_cast<const Expr<int>&>(thenEx), //throw std::bad_cast
- dynamic_cast<const Expr<int>&>(elseEx))); //
- }
- return e;
+ auto elseExp = std::dynamic_pointer_cast<Expr<int>>(parseExpression()); //
+ if (!ifExp || !thenExp || !elseExp)
+ throw ParsingError();
+ return std::make_shared<ConditionalExp<int>>(ifExp, thenExp, elseExp);
}
+ return e;
+ }
- const Expression& parseLogicalOr()
+ std::shared_ptr<Expression> parseLogicalOr()
+ {
+ std::shared_ptr<Expression> e = parseLogicalAnd();
+ while (token().type == Token::TK_OR) //associativity: ->
{
- const Expression* e = &parseLogicalAnd();
- while (token().type == Token::TK_OR) //associativity: ->
- {
- nextToken();
- const Expression& rhs = parseLogicalAnd();
- e = &manageObj(makeBiExp(*e, rhs, std::logical_or<bool>())); //throw std::bad_cast
- }
- return *e;
+ nextToken();
+
+ std::shared_ptr<Expression> rhs = parseLogicalAnd();
+ e = makeBiExp(e, rhs, std::logical_or<bool>()); //throw ParsingError
}
+ return e;
+ }
- const Expression& parseLogicalAnd()
+ std::shared_ptr<Expression> parseLogicalAnd()
+ {
+ std::shared_ptr<Expression> e = parseEquality();
+ while (token().type == Token::TK_AND) //associativity: ->
{
- const Expression* e = &parseEquality();
- while (token().type == Token::TK_AND) //associativity: ->
- {
- nextToken();
- const Expression& rhs = parseEquality();
+ nextToken();
+ std::shared_ptr<Expression> rhs = parseEquality();
- e = &manageObj(makeBiExp(*e, rhs, std::logical_and<bool>())); //throw std::bad_cast
- }
- return *e;
+ e = makeBiExp(e, rhs, std::logical_and<bool>()); //throw ParsingError
}
+ return e;
+ }
- const Expression& parseEquality()
- {
- const Expression& e = parseRelational();
+ std::shared_ptr<Expression> parseEquality()
+ {
+ std::shared_ptr<Expression> e = parseRelational();
- Token::Type t = token().type;
- if (t == Token::TK_EQUAL || t == Token::TK_NOT_EQUAL) //associativity: n/a
- {
- nextToken();
- const Expression& rhs = parseRelational();
+ Token::Type t = token().type;
+ if (t == Token::TK_EQUAL || //associativity: n/a
+ t == Token::TK_NOT_EQUAL)
+ {
+ nextToken();
+ std::shared_ptr<Expression> rhs = parseRelational();
- if (t == Token::TK_EQUAL) return manageObj(makeBiExp(e, rhs, std::equal_to <int>())); //throw std::bad_cast
- if (t == Token::TK_NOT_EQUAL) return manageObj(makeBiExp(e, rhs, std::not_equal_to<int>())); //
- }
- return e;
+ if (t == Token::TK_EQUAL) return makeBiExp(e, rhs, std::equal_to <int>()); //throw ParsingError
+ if (t == Token::TK_NOT_EQUAL) return makeBiExp(e, rhs, std::not_equal_to<int>()); //
}
+ return e;
+ }
- const Expression& parseRelational()
- {
- const Expression& e = parseMultiplicative();
+ std::shared_ptr<Expression> parseRelational()
+ {
+ std::shared_ptr<Expression> e = parseMultiplicative();
- Token::Type t = token().type;
- if (t == Token::TK_LESS || //associativity: n/a
- t == Token::TK_LESS_EQUAL ||
- t == Token::TK_GREATER ||
- t == Token::TK_GREATER_EQUAL)
- {
- nextToken();
- const Expression& rhs = parseMultiplicative();
+ Token::Type t = token().type;
+ if (t == Token::TK_LESS || //associativity: n/a
+ t == Token::TK_LESS_EQUAL ||
+ t == Token::TK_GREATER ||
+ t == Token::TK_GREATER_EQUAL)
+ {
+ nextToken();
+ std::shared_ptr<Expression> rhs = parseMultiplicative();
- if (t == Token::TK_LESS) return manageObj(makeBiExp(e, rhs, std::less <int>())); //
- if (t == Token::TK_LESS_EQUAL) return manageObj(makeBiExp(e, rhs, std::less_equal <int>())); //throw std::bad_cast
- if (t == Token::TK_GREATER) return manageObj(makeBiExp(e, rhs, std::greater <int>())); //
- if (t == Token::TK_GREATER_EQUAL) return manageObj(makeBiExp(e, rhs, std::greater_equal<int>())); //
- }
- return e;
+ if (t == Token::TK_LESS) return makeBiExp(e, rhs, std::less <int>()); //
+ if (t == Token::TK_LESS_EQUAL) return makeBiExp(e, rhs, std::less_equal <int>()); //throw ParsingError
+ if (t == Token::TK_GREATER) return makeBiExp(e, rhs, std::greater <int>()); //
+ if (t == Token::TK_GREATER_EQUAL) return makeBiExp(e, rhs, std::greater_equal<int>()); //
}
+ return e;
+ }
- const Expression& parseMultiplicative()
- {
- const Expression* e = &parsePrimary();
+ std::shared_ptr<Expression> parseMultiplicative()
+ {
+ std::shared_ptr<Expression> e = parsePrimary();
- while (token().type == Token::TK_MODULUS) //associativity: ->
- {
- nextToken();
- const Expression& rhs = parsePrimary();
+ while (token().type == Token::TK_MODULUS) //associativity: ->
+ {
+ nextToken();
+ std::shared_ptr<Expression> rhs = parsePrimary();
- //"compile-time" check: n % 0
- if (auto literal = dynamic_cast<const ConstNumberExp*>(&rhs))
- if (literal->eval() == 0)
- throw ParsingError();
+ //"compile-time" check: n % 0
+ if (auto literal = std::dynamic_pointer_cast<ConstNumberExp>(rhs))
+ if (literal->eval() == 0)
+ throw ParsingError();
- e = &manageObj(makeBiExp(*e, rhs, std::modulus<int>())); //throw std::bad_cast
- }
- return *e;
+ e = makeBiExp(e, rhs, std::modulus<int>()); //throw ParsingError
}
+ return e;
+ }
- const Expression& parsePrimary()
+ std::shared_ptr<Expression> parsePrimary()
+ {
+ if (token().type == Token::TK_VARIABLE_N)
{
- if (token().type == Token::TK_VARIABLE_N)
- {
- nextToken();
- return manageObj(VariableNumberNExp(n_));
- }
- else if (token().type == Token::TK_CONST_NUMBER)
- {
- const int number = token().number;
- nextToken();
- return manageObj(ConstNumberExp(number));
- }
- else if (token().type == Token::TK_BRACKET_LEFT)
- {
- nextToken();
- const Expression& e = parse();
-
- consumeToken(Token::TK_BRACKET_RIGHT);
- return e;
- }
- else
- throw ParsingError();
+ nextToken();
+ return std::make_shared<VariableNumberNExp>(n_);
}
-
- template <class T>
- const T& manageObj(const T& obj)
+ else if (token().type == Token::TK_CONST_NUMBER)
{
- dump_.push_back(std::make_shared<T>(obj));
- return static_cast<T&>(*dump_.back());
+ const int number = token().number;
+ nextToken();
+ return std::make_shared<ConstNumberExp>(number);
}
+ else if (token().type == Token::TK_BRACKET_LEFT)
+ {
+ nextToken();
+ std::shared_ptr<Expression> e = parseExpression();
- Scanner scn;
- Token tk;
+ expectToken(Token::TK_BRACKET_RIGHT);
+ nextToken();
+ return e;
+ }
+ else
+ throw ParsingError();
+ }
- int& n_;
- DumpList& dump_; //manage polymorphc object lifetimes
- };
+ void nextToken() { tk = scn.nextToken(); }
+ const Token& token() const { return tk; }
- const Expr<int>* expr;
- mutable int n_;
+ void expectToken(Token::Type t) //throw ParsingError
+ {
+ if (token().type != t)
+ throw ParsingError();
+ }
- PluralForm::DumpList dump; //manage polymorphc object lifetimes
+ Scanner scn;
+ Token tk;
+ int& n_;
};
+}
+
+
+inline
+PluralForm::PluralForm(const std::string& stream) : expr(implementation::Parser(stream, n_).parse()) {} //throw ParsingError
+}
-#endif // PARSE_PLURAL_H_INCLUDED
+#endif // PARSE_PLURAL_H_INCLUDED \ No newline at end of file
bgstack15