return tokens.pop()
+def value_node(value):
+ return 'value', (value, )
+
+
+def ident_node(name):
+ return name, ()
+
+
+def range_list_node(range_list):
+ return 'range_list', range_list
+
+
+def negate(rv):
+ return 'not', (rv,)
+
+
class _Parser(object):
"""Internal parser. This class can translate a single rule into an abstract
tree of tuples. It implements the following grammar::
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
+ return negate(rv) if negated else rv
def newfangled_relation(self, left):
if skip_token(self.tokens, 'symbol', '='):
range_list = [self.range_or_value()]
while skip_token(self.tokens, 'symbol', ','):
range_list.append(self.range_or_value())
- return 'range_list', range_list
+ return range_list_node(range_list)
def expr(self):
word = skip_token(self.tokens, 'word')
return 'mod', ((name, ()), self.value())
elif skip_token(self.tokens, 'symbol', '%'):
return 'mod', ((name, ()), self.value())
- return name, ()
+ return ident_node(name)
def value(self):
- return 'value', (int(self.expect('value')[1]),)
+ return value_node(int(self.expect('value')[1]))
def _binary_compiler(tmpl):
assert plural.test_next_token([('word', 'and')], 'word', 'and')
def test_type_not_ok_and_value_ok(self):
- assert not plural.test_next_token([('abc', 'and')], 'word', 'and')
\ No newline at end of file
+ assert not plural.test_next_token([('abc', 'and')], 'word', 'and')
+
+
+def make_range_list(*values):
+ ranges = []
+ for v in values:
+ if isinstance(v, int):
+ val_node = plural.value_node(v)
+ ranges.append((val_node, val_node))
+ else:
+ assert isinstance(v, tuple)
+ ranges.append((plural.value_node(v[0]),
+ plural.value_node(v[1])))
+ return plural.range_list_node(ranges)
+
+
+class PluralRuleParserTestCase(unittest.TestCase):
+ def setUp(self):
+ self.n = plural.ident_node('n')
+ self.n_eq_1 = ('relation', ('in', self.n, make_range_list(1)))
+
+ def test_error_when_unexpected_end(self):
+ with pytest.raises(plural.RuleError):
+ plural._Parser('n =')
+
+ def test_eq_relation(self):
+ assert plural._Parser('n = 1').ast == self.n_eq_1
+
+ def test_in_range_relation(self):
+ assert plural._Parser('n = 2..4').ast == \
+ ('relation', ('in', self.n, make_range_list((2, 4))))
+
+ def test_negate(self):
+ assert plural._Parser('n != 1').ast == plural.negate(self.n_eq_1)
\ No newline at end of file