diff options
Diffstat (limited to 'lib/parse_plural.h')
-rw-r--r-- | lib/parse_plural.h | 524 |
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 |