--- a/nodes.py Tue Aug 02 17:05:40 2011 +0200
+++ b/nodes.py Tue Aug 02 17:05:41 2011 +0200
@@ -76,6 +76,31 @@
yield vref
+class OperatorExpressionMixin(object):
+
+ def initargs(self, stmt):
+ """return list of arguments to give to __init__ to clone this node"""
+ return (self.operator,)
+
+ def is_equivalent(self, other):
+ if not Node.is_equivalent(self, other):
+ return False
+ return self.operator == other.operator
+
+ def get_description(self, mainindex, tr):
+ """if there is a variable in the math expr used as rhs of a relation,
+ return the name of this relation, else return the type of the math
+ expression
+ """
+ try:
+ return tr(self.get_type())
+ except CoercionError:
+ for vref in self.iget_nodes(VariableRef):
+ vtype = vref.get_description(mainindex, tr)
+ if vtype != 'Any':
+ return tr(vtype)
+
+
class HSMixin(object):
"""mixin class for classes which may be the lhs or rhs of an expression"""
__slots__ = ()
@@ -541,23 +566,14 @@
return '%s %s' % (self.operator, ', '.join(repr(c) for c in self.children))
-class MathExpression(HSMixin, BinaryNode):
- """Operators plus, minus, multiply, divide."""
+class MathExpression(OperatorExpressionMixin, HSMixin, BinaryNode):
+ """Mathematical Operators"""
__slots__ = ('operator',)
def __init__(self, operator, lhs=None, rhs=None):
BinaryNode.__init__(self, lhs, rhs)
self.operator = operator.encode()
- def initargs(self, stmt):
- """return list of arguments to give to __init__ to clone this node"""
- return (self.operator,)
-
- def is_equivalent(self, other):
- if not Node.is_equivalent(self, other):
- return False
- return self.operator == other.operator
-
def as_string(self, encoding=None, kwargs=None):
"""return the tree as an encoded rql string"""
return '(%s %s %s)' % (self.children[0].as_string(encoding, kwargs),
@@ -592,18 +608,31 @@
return 'Float'
raise CoercionError(key)
- def get_description(self, mainindex, tr):
- """if there is a variable in the math expr used as rhs of a relation,
- return the name of this relation, else return the type of the math
- expression
+
+class UnaryExpression(OperatorExpressionMixin, Node):
+ """Unary Operators"""
+ __slots__ = ('operator',)
+
+ def __init__(self, operator, child=None):
+ Node.__init__(self)
+ self.operator = operator.encode()
+ if child is not None:
+ self.append(child)
+
+ def as_string(self, encoding=None, kwargs=None):
+ """return the tree as an encoded rql string"""
+ return '%s%s' % (self.operator.encode(),
+ self.children[0].as_string(encoding, kwargs))
+
+ def __repr__(self):
+ return '%s%r' % (self.operator, self.children[0])
+
+ def get_type(self, solution=None, kwargs=None):
+ """return the type of object returned by this expression if known
+
+ solution is an optional variable/etype mapping
"""
- try:
- return tr(self.get_type())
- except CoercionError:
- for vref in self.iget_nodes(VariableRef):
- vtype = vref.get_description(mainindex, tr)
- if vtype != 'Any':
- return tr(vtype)
+ return self.children[0].get_type(solution, kwargs)
class Function(HSMixin, Node):
@@ -1065,5 +1094,5 @@
build_visitor_stub((SubQuery, And, Or, Not, Exists, Relation,
- Comparison, MathExpression, Function, Constant,
- VariableRef, SortTerm, ColumnAlias, Variable))
+ Comparison, MathExpression, UnaryExpression, Function,
+ Constant, VariableRef, SortTerm, ColumnAlias, Variable))
--- a/parser.py Tue Aug 02 17:05:40 2011 +0200
+++ b/parser.py Tue Aug 02 17:05:41 2011 +0200
@@ -99,6 +99,7 @@
('ADD_OP', re.compile('\\+|-|\\||#')),
('MUL_OP', re.compile('\\*|/|%|&')),
('POW_OP', re.compile('\\^|>>|<<')),
+ ('UNARY_OP', re.compile('-|~')),
('FUNCTION', re.compile('[A-Za-z_]+\\s*(?=\\()')),
('R_TYPE', re.compile('[a-z_][a-z0-9_]*')),
('E_TYPE', re.compile('[A-Z][A-Za-z0-9]*[a-z]+[0-9]*')),
@@ -500,7 +501,7 @@
def exprs_not(self, S, _parent=None):
_context = self.Context(_parent, self._scanner, 'exprs_not', [S])
- _token = self._peek('NOT', 'r"\\("', 'NULL', 'DATE', 'DATETIME', 'TRUE', 'FALSE', 'FLOAT', 'INT', 'STRING', 'SUBSTITUTE', 'VARIABLE', 'E_TYPE', 'FUNCTION', context=_context)
+ _token = self._peek('NOT', 'r"\\("', 'UNARY_OP', 'NULL', 'DATE', 'DATETIME', 'TRUE', 'FALSE', 'FLOAT', 'INT', 'STRING', 'SUBSTITUTE', 'VARIABLE', 'E_TYPE', 'FUNCTION', context=_context)
if _token == 'NOT':
NOT = self._scan('NOT', context=_context)
balanced_expr = self.balanced_expr(S, _context)
@@ -511,7 +512,7 @@
def balanced_expr(self, S, _parent=None):
_context = self.Context(_parent, self._scanner, 'balanced_expr', [S])
- _token = self._peek('r"\\("', 'NULL', 'DATE', 'DATETIME', 'TRUE', 'FALSE', 'FLOAT', 'INT', 'STRING', 'SUBSTITUTE', 'VARIABLE', 'E_TYPE', 'FUNCTION', context=_context)
+ _token = self._peek('r"\\("', 'UNARY_OP', 'NULL', 'DATE', 'DATETIME', 'TRUE', 'FALSE', 'FLOAT', 'INT', 'STRING', 'SUBSTITUTE', 'VARIABLE', 'E_TYPE', 'FUNCTION', context=_context)
if _token == 'r"\\("':
self._scan('r"\\("', context=_context)
logical_expr = self.logical_expr(S, _context)
@@ -578,7 +579,7 @@
def expr(self, S, _parent=None):
_context = self.Context(_parent, self._scanner, 'expr', [S])
- _token = self._peek('CMP_OP', 'r"\\("', 'NULL', 'DATE', 'DATETIME', 'TRUE', 'FALSE', 'FLOAT', 'INT', 'STRING', 'SUBSTITUTE', 'VARIABLE', 'E_TYPE', 'FUNCTION', context=_context)
+ _token = self._peek('CMP_OP', 'UNARY_OP', 'r"\\("', 'NULL', 'DATE', 'DATETIME', 'TRUE', 'FALSE', 'FLOAT', 'INT', 'STRING', 'SUBSTITUTE', 'VARIABLE', 'E_TYPE', 'FUNCTION', context=_context)
if _token == 'CMP_OP':
CMP_OP = self._scan('CMP_OP', context=_context)
expr_add = self.expr_add(S, _context)
@@ -589,13 +590,24 @@
def expr_add(self, S, _parent=None):
_context = self.Context(_parent, self._scanner, 'expr_add', [S])
- expr_mul = self.expr_mul(S, _context)
- node = expr_mul
- while self._peek('ADD_OP', 'QMARK', 'r"\\)"', "','", 'SORT_DESC', 'SORT_ASC', 'CMP_OP', 'R_TYPE', "'IN'", 'GROUPBY', 'ORDERBY', 'WHERE', 'LIMIT', 'OFFSET', 'HAVING', "';'", 'WITH', 'AND', 'OR', context=_context) == 'ADD_OP':
- ADD_OP = self._scan('ADD_OP', context=_context)
+ _token = self._peek('UNARY_OP', 'r"\\("', 'NULL', 'DATE', 'DATETIME', 'TRUE', 'FALSE', 'FLOAT', 'INT', 'STRING', 'SUBSTITUTE', 'VARIABLE', 'E_TYPE', 'FUNCTION', context=_context)
+ if _token != 'UNARY_OP':
expr_mul = self.expr_mul(S, _context)
- node = MathExpression( ADD_OP, node, expr_mul )
- return node
+ node = expr_mul
+ while self._peek('ADD_OP', 'QMARK', 'r"\\)"', "','", 'SORT_DESC', 'SORT_ASC', 'CMP_OP', 'R_TYPE', "'IN'", 'GROUPBY', 'ORDERBY', 'WHERE', 'LIMIT', 'OFFSET', 'HAVING', "';'", 'WITH', 'AND', 'OR', context=_context) == 'ADD_OP':
+ ADD_OP = self._scan('ADD_OP', context=_context)
+ expr_mul = self.expr_mul(S, _context)
+ node = MathExpression( ADD_OP, node, expr_mul )
+ return node
+ else: # == 'UNARY_OP'
+ UNARY_OP = self._scan('UNARY_OP', context=_context)
+ expr_mul = self.expr_mul(S, _context)
+ node = UnaryExpression( UNARY_OP, expr_mul )
+ while self._peek('ADD_OP', 'QMARK', 'r"\\)"', "','", 'SORT_DESC', 'SORT_ASC', 'CMP_OP', 'R_TYPE', "'IN'", 'GROUPBY', 'ORDERBY', 'WHERE', 'LIMIT', 'OFFSET', 'HAVING', "';'", 'WITH', 'AND', 'OR', context=_context) == 'ADD_OP':
+ ADD_OP = self._scan('ADD_OP', context=_context)
+ expr_mul = self.expr_mul(S, _context)
+ node = MathExpression( ADD_OP, node, expr_mul )
+ return node
def expr_mul(self, S, _parent=None):
_context = self.Context(_parent, self._scanner, 'expr_mul', [S])
@@ -643,7 +655,7 @@
FUNCTION = self._scan('FUNCTION', context=_context)
self._scan('r"\\("', context=_context)
F = Function(FUNCTION)
- if self._peek('r"\\)"', 'r"\\("', 'NULL', 'DATE', 'DATETIME', 'TRUE', 'FALSE', 'FLOAT', 'INT', 'STRING', 'SUBSTITUTE', 'VARIABLE', 'E_TYPE', 'FUNCTION', context=_context) != 'r"\\)"':
+ if self._peek('UNARY_OP', 'r"\\)"', 'r"\\("', 'NULL', 'DATE', 'DATETIME', 'TRUE', 'FALSE', 'FLOAT', 'INT', 'STRING', 'SUBSTITUTE', 'VARIABLE', 'E_TYPE', 'FUNCTION', context=_context) != 'r"\\)"':
expr_add = self.expr_add(S, _context)
while self._peek("','", 'QMARK', 'r"\\)"', 'SORT_DESC', 'SORT_ASC', 'CMP_OP', 'R_TYPE', "'IN'", 'GROUPBY', 'ORDERBY', 'WHERE', 'LIMIT', 'OFFSET', 'HAVING', "';'", 'WITH', 'AND', 'OR', context=_context) == "','":
F.append(expr_add)
@@ -658,7 +670,7 @@
self._scan("'IN'", context=_context)
self._scan('r"\\("', context=_context)
F = Function('IN')
- if self._peek('r"\\)"', 'r"\\("', 'NULL', 'DATE', 'DATETIME', 'TRUE', 'FALSE', 'FLOAT', 'INT', 'STRING', 'SUBSTITUTE', 'VARIABLE', 'E_TYPE', 'FUNCTION', context=_context) != 'r"\\)"':
+ if self._peek('UNARY_OP', 'r"\\)"', 'r"\\("', 'NULL', 'DATE', 'DATETIME', 'TRUE', 'FALSE', 'FLOAT', 'INT', 'STRING', 'SUBSTITUTE', 'VARIABLE', 'E_TYPE', 'FUNCTION', context=_context) != 'r"\\)"':
expr_add = self.expr_add(S, _context)
while self._peek("','", 'QMARK', 'r"\\)"', 'SORT_DESC', 'SORT_ASC', 'CMP_OP', 'R_TYPE', "'IN'", 'GROUPBY', 'ORDERBY', 'WHERE', 'LIMIT', 'OFFSET', 'HAVING', "';'", 'WITH', 'AND', 'OR', context=_context) == "','":
F.append(expr_add)