From: benselme Date: Thu, 8 Jan 2015 22:29:24 +0000 (-0500) Subject: Include Armin Ronacher's work on PluralRule mitsuhiko/babel@774047a X-Git-Tag: dev-2a51c9b95d06~51^2~17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ad2ff1b53a2c5c803b6cbdec08fe9ea1a3865333;p=thirdparty%2Fbabel.git Include Armin Ronacher's work on PluralRule mitsuhiko/babel@774047a --- diff --git a/babel/plural.py b/babel/plural.py index e2eb88b0..03f92fc3 100644 --- a/babel/plural.py +++ b/babel/plural.py @@ -255,9 +255,12 @@ def cldr_modulo(a, b): class RuleError(Exception): """Raised if a rule is malformed.""" +_VARS = 'nivwft' + _RULES = [ (None, re.compile(r'\s+(?u)')), - ('word', re.compile(r'\b(and|or|is|(?:with)?in|not|mod|[nivwft])\b')), + ('word', re.compile(r'\b(and|or|is|(?:with)?in|not|mod|[{0}])\b' + .format(_VARS))), ('value', re.compile(r'\d+')), ('symbol', re.compile(r'%|,|!=|=')), ('ellipsis', re.compile(r'\.\.')) @@ -335,20 +338,20 @@ class _Parser(object): raise RuleError('Expected end of rule, got %r' % self.tokens[-1][1]) - def test(self, type, value=None): - return self.tokens and self.tokens[-1][0] == type and \ - (value is None or self.tokens[-1][1] == value) + def test(self, type_, value=None): + return self.tokens and self.tokens[-1][0] == type_ and \ + (value is None or self.tokens[-1][1] == value) - def skip(self, type, value=None): - if self.test(type, value): + def skip(self, type_, value=None): + if self.test(type_, value): return self.tokens.pop() - def expect(self, type, value=None, term=None): - token = self.skip(type, value) + def expect(self, type_, value=None, term=None): + token = self.skip(type_, value) if token is not None: return token if term is None: - term = repr(value is None and type or value) + term = repr(value is None and type_ or value) if not self.tokens: raise RuleError('expected %s but end of rule reached' % term) raise RuleError('expected %s but got %r' % (term, self.tokens[-1][1])) @@ -369,36 +372,56 @@ class _Parser(object): left = self.expr() if self.skip('word', 'is'): return self.skip('word', 'not') and 'isnot' or 'is', \ - (left, self.value()) + (left, self.value()) negated = self.skip('word', 'not') method = 'in' if self.skip('word', 'within'): method = 'within' else: - self.expect('word', 'in', term="'within' or 'in'") + if not self.skip('word', 'in'): + if negated: + raise RuleError('Cannot negate operator based rules.') + return self.newfangled_relation(left) rv = 'relation', (method, left, self.range_list()) if negated: rv = 'not', (rv,) return rv + def newfangled_relation(self, left): + if self.skip('symbol', '='): + negated = False + elif self.skip('symbol', '!='): + negated = True + else: + raise RuleError('Expected "=" or "!=" or legacy relation') + rv = 'relation', ('in', left, self.range_list()) + if negated: + rv = 'not', (rv,) + return rv + def range_or_value(self): left = self.value() if self.skip('ellipsis'): - return((left, self.value())) + return left, self.value() else: - return((left, left)) + return left, left def range_list(self): range_list = [self.range_or_value()] - while self.skip('comma'): + while self.skip('symbol', ','): range_list.append(self.range_or_value()) return 'range_list', range_list def expr(self): - self.expect('word', 'n') + word = self.skip('word') + if word is None or word[1] not in _VARS: + raise RuleError('Expected identifier variable') + name = word[1] if self.skip('word', 'mod'): - return 'mod', (('n', ()), self.value()) - return 'n', () + return 'mod', ((name, ()), self.value()) + elif self.skip('symbol', '%'): + return 'mod', ((name, ()), self.value()) + return name, () def value(self): return 'value', (int(self.expect('value')[1]),) diff --git a/tests/test_plural.py b/tests/test_plural.py index ad6da703..95e23302 100644 --- a/tests/test_plural.py +++ b/tests/test_plural.py @@ -18,7 +18,7 @@ import pytest from babel import plural -class test_plural_rule(): +def test_plural_rule(): rule = plural.PluralRule({'one': 'n is 1'}) assert rule(1) == 'one' assert rule(2) == 'other'