# HG changeset patch
# User Sylvain <syt@logilab.fr>
# Date 1208793570 -7200
#      Mon Apr 21 17:59:30 2008 +0200
# Node ID b66c3bc2e92a42b5aa05dbf4fbe4fcfc92b55d6c
# Parent  5c8280470677ef4e3d03d0ce8fb91757b722b9b1
grammar finalization, api refactoring/cleanup, unittests ok

diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -4,6 +4,7 @@
 :copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
+__docformat__ = "restructuredtext en"
 
 import sys
 import threading
@@ -50,7 +51,7 @@
                 raise UsesReservedWord(etype)
         for rtype in schema.relations():
             rtype = str(rtype)
-            if is_keyword(rtype):# or rtype.lower() == 'is':
+            if is_keyword(rtype):
                 raise UsesReservedWord(rtype)
         self._checker.schema = schema
         self._annotator.schema = schema
@@ -93,24 +94,12 @@
             from rql import nodes
             for select in rqlst.children:
                 self._simplify(select)
-                # deal with rewritten variable which are used in orderby
-                for vname in select.stinfo['rewritten']:
-                    try:
-                        var = rqlst.defined_vars.pop(vname)
-                    except KeyError:
-                        continue
-                    else:
-                        for vref in var.references():
-                            term = vref.parent
-                            while not isinstance(term, nodes.SortTerm):
-                                term = term.parent
-                            rqlst.remove_sort_term(term)
         return rqlst
         
     def _simplify(self, select):
         # recurse on subqueries first
-        for subquery in select.from_:
-            for select in subquery.children:
+        for subquery in select.with_:
+            for select in subquery.query.children:
                 self._simplify(select)
         for var in select.defined_vars.values():
             stinfo = var.stinfo
@@ -122,10 +111,25 @@
                 rhs = uidrel.children[1].children[0]
                 #from rql.nodes import Constant
                 #assert isinstance(rhs, nodes.Constant), rhs
-                for varref in var.references():
-                    rel = varref.relation()
-                    #assert varref.parent
-                    if rel and (rel is uidrel or rel.is_types_restriction()):
+                for vref in var.references():
+                    rel = vref.relation()
+                    #assert vref.parent
+                    if rel is None:
+                        term = vref
+                        while not term.parent is select:
+                            term = term.parent
+                        if term in select.selection:
+                            rhs = rhs.copy(select)
+                            rhs.uid = True
+                            vconsts.append(rhs)
+                            if vref is term:
+                                select.selection[select.selection.index(vref)] = rhs
+                                rhs.parent = select
+                            else:
+                                vref.parent.replace(vref, rhs)
+                        else:
+                            select.remove(term)
+                    elif rel is uidrel or rel.is_types_restriction():
                         # drop this relation
                         rel.parent.remove(rel)
                     else:
@@ -137,9 +141,9 @@
 #                         # substitute rhs
 #                         if rel and uidrel._not:
 #                             rel._not = rel._not or uidrel._not
-                        varref.parent.replace(varref, rhs)
+                        vref.parent.replace(vref, rhs)
                 del select.defined_vars[var.name]
-        if select.stinfo['rewritten']:
+        if select.stinfo['rewritten'] and select.solutions:
             select.clean_solutions()
         
     def compare(self, rqlstring1, rqlstring2):
diff --git a/analyze.py b/analyze.py
--- a/analyze.py
+++ b/analyze.py
@@ -128,10 +128,9 @@
                 (var,), '%s == %r' % (var, etype)))
         for relation in node.main_relations:
             self._visit(relation, constraints)
-        restriction = node.get_restriction()
-        if restriction is not None:
-            # get constraints from the restriction subtree
-            self._visit(restriction, constraints)
+        # get constraints from the restriction subtree
+        if node.where is not None:
+            self._visit(node.where, constraints)
         self.solve(node, domains, constraints)
         
     visit_delete = visit_insert
@@ -144,21 +143,20 @@
         constraints = []
         for relation in node.main_relations:
             self._visit(relation, constraints)
-        restriction = node.get_restriction()
-        if restriction is not None:
-            # get constraints from the restriction subtree
-            self._visit(restriction, constraints)
+        # get constraints from the restriction subtree
+        if node.where is not None:
+            self._visit(node.where, constraints)
         self.solve(node, domains, constraints)
         
     def visit_select(self, node):
         if not (node.defined_vars or node.aliases):
             node.set_possible_types([{}])
             return
-        for subquery in node.from_: # resolve subqueries first
-            self.visit_union(subquery)
+        for subquery in node.with_: # resolve subqueries first
+            self.visit_union(subquery.query)
         domains = self._init_stmt(node)
         for ca in node.aliases.itervalues():
-            etypes = set(stmt.selected[ca.colnum].get_type(sol, self.kwargs)
+            etypes = set(stmt.selection[ca.colnum].get_type(sol, self.kwargs)
                          for stmt in ca.query.children for sol in stmt.solutions)
             domains[ca.name] = fd.FiniteDomain(etypes)
         constraints = []
@@ -170,11 +168,10 @@
                 uidtype = self.uid_func(consts[0].eval(self.kwargs))
                 for const in consts:
                     const.uidtype = uidtype
-        restriction = node.get_restriction()
-        if not restriction is None:
-            # get constraints from the restriction subtree
-            self._visit(restriction, constraints)
-        elif not node.from_:
+        # get constraints from the restriction subtree
+        if node.where is not None:
+            self._visit(node.where, constraints)
+        elif not node.with_:
             varnames = [v.name for v in node.get_selected_variables()]
             if varnames:
                 # add constraint on real relation types if no restriction
@@ -474,7 +471,7 @@
 
     def visit_select(self, node):
         sols = self.visit_children(node)
-        for n in node.selected:
+        for n in node.selection:
             sols2 = n.accept(self)
             for s in sols2:
                 del s['type']
diff --git a/base.py b/base.py
--- a/base.py
+++ b/base.py
@@ -14,6 +14,10 @@
     def __str__(self):
         return self.as_string(encoding='utf-8')
 
+    def as_string(self, encoding=None, kwargs=None):
+        """return the tree as an encoded rql string"""
+        raise NotImplementedError()
+
     def initargs(self, stmt):
         """return list of arguments to give to __init__ to clone this node
 
@@ -88,6 +92,16 @@
         if not path:
             return self
         return self.children[path[0]].go_to_index_path(path[1:])
+    
+    def copy(self, stmt):
+        """create and return a copy of this node and its descendant
+
+        stmt is the root node, which should be use to get new variables
+        """
+        new = self.__class__(*self.initargs(stmt))
+        for child in self.children:
+            new.append(child.copy(stmt))
+        return new
 
         
 class Node(BaseNode):
@@ -119,16 +133,7 @@
         self.children.pop(i)
         self.children.insert(i, new_child)
         new_child.parent = self
-    
-    def copy(self, stmt):
-        """create and return a copy of this node and its descendant
 
-        stmt is the root node, which should be use to get new variables
-        """
-        new = self.__class__(*self.initargs(stmt))
-        for child in self.children:
-            new.append(child.copy(stmt))
-        return new
 
 class BinaryNode(Node):
     __slots__ = ()
@@ -168,4 +173,3 @@
         """
         return self.__class__(*self.initargs(stmt))
     
-    
diff --git a/nodes.py b/nodes.py
--- a/nodes.py
+++ b/nodes.py
@@ -24,8 +24,8 @@
                'TODAY': today}
 
 from rql import CoercionError
-from rql.base import Node, BinaryNode, LeafNode
-from rql.utils import function_description, quote, uquote
+from rql.base import BaseNode, Node, BinaryNode, LeafNode
+from rql.utils import function_description, quote, uquote, build_visitor_stub
 
 CONSTANT_TYPES = frozenset((None, 'Date', 'Datetime', 'Boolean', 'Float', 'Int',
                             'String', 'Substitute', 'etype'))
@@ -50,6 +50,11 @@
     assert isinstance(var, VariableRef)
     return var    
 
+def variable_refs(node):
+    for vref in node.iget_nodes(VariableRef):
+        if isinstance(vref.variable, Variable):
+            yield vref
+
 
 class HSMixin(object):
     """mixin class for classes which may be the lhs or rhs of an expression"""
@@ -94,7 +99,7 @@
     def should_register_op(self):
         root = self.root
         # root is None during parsing
-        return root is not None and root.memorizing and not root.undoing
+        return root is not None and root.should_register_op
 
     def remove_node(self, node):
         """remove the given node from the tree
@@ -114,19 +119,18 @@
     
     def add_restriction(self, relation):
         """add a restriction relation"""
-        r = self.get_restriction()
+        r = self.where
         if r is not None:
-            newnode = AND(r, relation)
-            self.replace(r, newnode)
+            newnode = And(r, relation)
+            self.set_where(newnode)
             if self.should_register_op:
                 from rql.undo import ReplaceNodeOperation
                 self.undo_manager.add_operation(ReplaceNodeOperation(r, newnode))
         else:
-            self.insert(0, relation)
+            self.set_where(relation)
             if self.should_register_op:
                 from rql.undo import AddNodeOperation
                 self.undo_manager.add_operation(AddNodeOperation(relation))
-        #assert check_relations(self)
         return relation
     
     def add_constant_restriction(self, var, rtype, value, ctype,
@@ -161,19 +165,116 @@
             etype = iter(etype).next() # may be a set
         return self.add_constant_restriction(var, 'is', etype, 'etype')
     
+# keywordsection nodes ########################################################
+
+# class KWNode(Node):
+#     __slots__ = ()
+    
+#     def as_string(self, encoding=None, kwargs=None):
+#         return '%s %s' % (self.__class__.__name__.upper(),
+#                           ', '.join(child.as_string(encoding, kwargs)
+#                                     for child in self.children))
+#     def __repr__(self):
+#         return '%s %s' % (self.__class__.__name__.upper(),
+#                           ', '.join(repr(child) for child in self.children))
+        
+# class Where(BaseNode): 
+#     """WHERE clause"""
+#     __slots__ = ('node',)
+#     def __init__(self, node):
+#         self.node = node
+#         node.parent = self
+#     @property
+#     def children(self):
+#         return (self.node,)
+    
+    
+#     def copy(self, stmt):
+#         """create and return a copy of this node and its descendant
+
+#         stmt is the root node, which should be use to get new variables
+#         """
+#         return Where(self.node.copy(stmt))
+    
+#     def accept(self, visitor, *args, **kwargs):
+#         return visitor.visit_where(self, *args, **kwargs)
+#     def leave(self, visitor, *args, **kwargs):
+#         return visitor.leave_where(self, *args, **kwargs)
+#     def as_string(self, encoding=None, kwargs=None):
+#         return 'WHERE %s' % ', '.join(child.as_string(encoding, kwargs)
+#                                       for child in self.children)
+#         return 'WHERE %s' % ', '.join(repr(child) for child in self.children)
+
+        
+# class GroupBy(KWNode): 
+#     """GROUPBY clause"""
+#     __slots__ = ()
+#     def accept(self, visitor, *args, **kwargs):
+#         return visitor.visit_groupby(self, *args, **kwargs)
+#     def leave(self, visitor, *args, **kwargs):
+#         return visitor.leave_groupby(self, *args, **kwargs)
+
+    
+# class Having(KWNode): 
+#     """HAVING clause"""
+#     __slots__ = ()
+#     def accept(self, visitor, *args, **kwargs):
+#         return visitor.visit_having(self, *args, **kwargs)
+#     def leave(self, visitor, *args, **kwargs):
+#         return visitor.leave_having(self, *args, **kwargs)
+
+    
+# class OrderBy(KWNode):
+#     """ORDERBY clause"""
+#     __slots__ = ()
+#     def accept(self, visitor, *args, **kwargs):
+#         return visitor.visit_orderby(self, *args, **kwargs)
+#     def leave(self, visitor, *args, **kwargs):
+#         return visitor.leave_orderby(self, *args, **kwargs)
+
+    
+# class With(KWNode):
+#     """WITH clause"""
+#     __slots__ = ()
+#     def accept(self, visitor, *args, **kwargs):
+#         return visitor.visit_sort(self, *args, **kwargs)
+#     def leave(self, visitor, *args, **kwargs):
+#         return visitor.leave_sort(self, *args, **kwargs)
+    
 
 # base RQL nodes ##############################################################
 
-class AND(BinaryNode):
+class SubQuery(BaseNode):
+    """WITH clause"""
+    __slots__ = ('aliases', 'query')
+    def set_aliases(self, aliases):
+        self.aliases = aliases
+        for node in aliases:
+            node.parent = self
+            
+    def set_query(self, node):
+        self.query = node
+        node.parent = self
+
+    def copy(self, stmt):
+        self.set_aliases([v.copy(stmt) for v in self.aliases])
+        self.set_query(self.query.copy())
+        
+    @property
+    def children(self):
+        return self.aliases + [self.query]
+    
+    def as_string(self, encoding=None, kwargs=None):
+        return '%s BEING (%s)' % (','.join(v.name for v in self.aliases),
+                                  self.query.as_string())
+    def __repr__(self):
+        return '%s BEING (%s)' % (','.join(repr(v) for v in self.aliases),
+                                  repr(self.query))
+    
+class And(BinaryNode):
     """a logical AND node (binary)"""
     __slots__ = ()
     
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_and(self, *args, **kwargs)
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_and(self, *args, **kwargs)
-    
     def as_string(self, encoding=None, kwargs=None):
         """return the tree as an encoded rql string"""
         return '%s, %s' % (self.children[0].as_string(encoding, kwargs),
@@ -187,16 +288,10 @@
         return self.parent.neged_rel(_fromnode or self)
 
     
-class OR(BinaryNode):
+class Or(BinaryNode):
     """a logical OR node (binary)"""
     __slots__ = ()
     
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_or(self, *args, **kwargs)
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_or(self, *args, **kwargs)
-    
     def as_string(self, encoding=None, kwargs=None):
         return '(%s) OR (%s)' % (self.children[0].as_string(encoding, kwargs),
                                  self.children[1].as_string(encoding, kwargs))
@@ -213,11 +308,6 @@
 class Not(Node):
     """a logical NOT node (unary)"""
     __slots__ = ()
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_not(self, *args, **kwargs)
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_not(self, *args, **kwargs)
     
     def as_string(self, encoding=None, kwargs=None):
         if isinstance(self.children[0], (Exists, Relation)):
@@ -233,34 +323,35 @@
         return self
 
 
-class Exists(EditableMixIn, Node):
+class Exists(EditableMixIn, BaseNode):
     """EXISTS sub query"""
-    __slots__ = ()
+    __slots__ = ('query',)
 
     def __init__(self, restriction=None):
-        Node.__init__(self)
         if restriction is not None:
-            self.append(restriction)
+            self.set_where(restriction)
 
+    @property
+    def children(self):
+        return (self.query,)
+    
     def is_equivalent(self, other):
         raise NotImplementedError
-    
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_exists(self, *args, **kwargs)
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_exists(self, *args, **kwargs)
-                
+                    
     def as_string(self, encoding=None, kwargs=None):
-        content = self.children and self.children[0].as_string(encoding, kwargs)
+        content = self.query and self.query.as_string(encoding, kwargs)
         return 'EXISTS(%s)' % content
 
     def __repr__(self):
-        content = self.children and repr(self.children[0])
-        return 'EXISTS(%s)' % content
+        return 'EXISTS(%s)' % repr(self.query)
 
-    def get_restriction(self):
-        return self.children[0]
+    def set_where(self, node):
+        self.query = node
+        node.parent = self
+    
+    @property
+    def where(self):
+        return self.query
 
     @property
     def scope(self):
@@ -298,12 +389,6 @@
             return False
         return True
     
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_relation( self, *args, **kwargs )
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_relation( self, *args, **kwargs )
-    
     def as_string(self, encoding=None, kwargs=None):
         """return the tree as an encoded rql string"""
         try:
@@ -342,7 +427,9 @@
     
     def ored_rel(self, _fromnode=None):
         return self.parent.ored_rel(_fromnode or self)
-    def neged_rel(self, _fromnode=None):
+    def neged_rel(self, _fromnode=None, strict=False):
+        if strict:
+            return isinstance(self.parent, Not)
         return self.parent.neged_rel(_fromnode or self)
 
     def is_types_restriction(self):
@@ -417,12 +504,6 @@
             return False
         return self.operator == other.operator
     
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_comparison( self, *args, **kwargs )
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_comparison( self, *args, **kwargs )
-    
     def as_string(self, encoding=None, kwargs=None):
         """return the tree as an encoded rql string"""
         if len(self.children) == 0:
@@ -457,12 +538,6 @@
             return False
         return self.operator == other.operator
 
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_mathexpression(self, *args, **kwargs)
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_mathexpression(self, *args, **kwargs)
-        
     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),
@@ -529,12 +604,6 @@
             return False
         return self.name == other.name
 
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_function(self, *args, **kwargs)
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_function(self, *args, **kwargs)
-        
     def as_string(self, encoding=None, kwargs=None):
         """return the tree as an encoded rql string"""
         return '%s(%s)' % (self.name, ', '.join(c.as_string(encoding, kwargs)
@@ -587,12 +656,6 @@
             return False
         return self.type == other.type and self.value == other.value
     
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_constant(self, *args, **kwargs)
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_constant(self, *args, **kwargs)
-    
     def as_string(self, encoding=None, kwargs=None):
         """return the tree as an encoded rql string (an unicode string is
         returned if encoding is None)
@@ -667,12 +730,6 @@
             return False
         return self.name == other.name
     
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_variableref(self, *args, **kwargs)
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_variableref(self, *args, **kwargs)
-
     def as_string(self, encoding=None, kwargs=None):
         """return the tree as an encoded rql string"""
         return self.name
@@ -694,50 +751,6 @@
 
     def get_description(self):
         return self.variable.get_description()
-
-class KWNode(Node):
-    __slots__ = ()    
-    def as_string(self, encoding=None, kwargs=None):
-        return '%s %s' % (self.keyword,
-                          ', '.join(child.as_string(encoding, kwargs)
-                                    for child in self.children))
-
-    def __repr__(self):
-        return '%s %s' % (self.keyword,
-                          ', '.join(repr(child) for child in self.children))
-        
-class Group(KWNode): 
-    """a group (GROUPBY) node"""
-    __slots__ = ()
-    keyword = 'GROUPBY'
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_group(self, *args, **kwargs)
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_group(self, *args, **kwargs)
-
-    
-class Having(KWNode): 
-    """a having (HAVING) node"""
-    __slots__ = ()
-    keyword = 'HAVING'
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_having(self, *args, **kwargs)
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_having(self, *args, **kwargs)
-
-    
-class Sort(KWNode):
-    """a sort (ORDERBY) node"""
-    __slots__ = ()
-    keyword = 'ORDERBY'
-    
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_sort(self, *args, **kwargs)
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_sort(self, *args, **kwargs)
     
 
 class SortTerm(Node):
@@ -772,12 +785,6 @@
             return '%r ASC' % self.term
         return '%r DESC' % self.term
     
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_sortterm(self, *args, **kwargs)
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_sortterm(self, *args, **kwargs)
-
     @property
     def term(self): 
         return self.children[0]
@@ -805,12 +812,6 @@
             return solution[self.name]
         return 'Any'    
     
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_columnalias(self, *args, **kwargs)
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_columnalias(self, *args, **kwargs)
-    
 #     def as_string(self, encoding=None, kwargs=None):
 #         return self.alias
     
@@ -869,18 +870,6 @@
     def __repr__(self):
         return '%s(%#X)' % (self.name, id(self))
     
-    def accept(self, visitor, *args, **kwargs):
-        """though variable are not actually tree nodes, they may be visited in
-        some cases
-        """
-        return visitor.visit_variable(self, *args, **kwargs)
-    
-    def leave(self, visitor, *args, **kwargs):
-        """though variable are not actually tree nodes, they may be visited in
-        some cases
-        """
-        return visitor.leave_variable(self, *args, **kwargs)
-    
     def set_scope(self, scopenode):
         if scopenode is self.stmt or self.stinfo['scope'] is None:
             self.stinfo['scope'] = scopenode
@@ -920,10 +909,9 @@
         """return the index of this variable in the selection if it's selected,
         else None
         """
-        for i, term in enumerate(self.stmt.selected_terms()):
-            for node in term.iget_nodes(VariableRef):
-                if node.variable is self:
-                    return i
+        if not self.stinfo['selected']:
+            return None
+        return iter(self.stinfo['selected']).next()
 
     @property
     def schema(self):
@@ -989,3 +977,6 @@
                 return rel
         return None
 
+build_visitor_stub((SubQuery, And, Or, Not, Exists, Relation,
+                    Comparison, MathExpression, Function, Constant,
+                    VariableRef, SortTerm, ColumnAlias, Variable))
diff --git a/parser.g b/parser.g
--- a/parser.g
+++ b/parser.g
@@ -3,14 +3,50 @@
 :organization: Logilab
 :copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
+
+
+Select statement grammar
+------------------------
+
+query = <squery> | <union>
+
+union = (<squery>) UNION (<squery>) [UNION (<squery>)]*
+
+squery = Any <selection>
+        [GROUPBY <variables>]
+        [ORDERBY <sortterms>]
+        [WHERE <restriction>]
+        [HAVING <aggregat restriction>]
+        [WITH <subquery> [,<subquery]*]
+
+subquery = <variables> BEING (<query>)
+
+variables = <variable> [, <variable>]*
 
 
-from rql.stmts import Union, Select, Delete, Insert, Update
-from rql.nodes import *
+Abbreviations in this code
+--------------------------
 
-_OR = OR
-_AND = AND
+rules:
+* rel -> relation
+* decl -> declaration
+* expr -> expression
+* restr -> restriction
+* var -> variable
+* func -> function
+* const -> constant
+* cmp -> comparison
+
+variables:
+* R -> syntax tree root
+* S -> select node
+* P -> parent node
+
+"""
+
+from warnings import warn
+from rql.stmts import Union, Select, Delete, Insert, Set
+from rql.nodes import *
 
 
 def unquote(string):
@@ -32,9 +68,9 @@
     token INSERT:      r'(?i)INSERT'
     token UNION:       r'(?i)UNION'
     token DISTINCT:    r'(?i)DISTINCT'
-    token FROM:        r'(?i)FROM'
+    token WITH:        r'(?i)WITH'
     token WHERE:       r'(?i)WHERE'
-    token AS:          r'(?i)AS'
+    token BEING:          r'(?i)BEING'
     token OR:          r'(?i)OR'
     token AND:         r'(?i)AND'
     token NOT:         r'(?i)NOT'
@@ -68,116 +104,102 @@
 
 
 # Grammar entry ###############################################################
-#
-# abbreviations :
-#
-#  rel -> relation
-#  decl -> declaration
-#  expr -> expression
-#  restr -> restriction
-#  var -> variable
-#  func -> function
-#  const -> constant
-#  cmp -> comparison
+
+        
+rule goal: DELETE _delete<<Delete()>> ';'             {{ return _delete }}
 
-rule goal: DELETE _delete<<Delete()>> ';'      {{ return _delete }}
-
-         | INSERT _insert<<Insert()>> ';'      {{ return _insert }}
+         | INSERT _insert<<Insert()>> ';'             {{ return _insert }}
  
-         | SET update<<Update()>> ';'          {{ return update }}
+         | SET update<<Set()>> ';'                    {{ return update }}
 
-         | union
-           sort<<union>> limit_offset<<union>> ';'  {{ return union }}
+         | union<<Union()>> limit_offset<<union>> ';' {{ return union }}
 
 # Deletion  ###################################################################
 
-rule _delete<<V>>: rels_decl<<V>> restr<<V>> {{ return V }}
+rule _delete<<R>>: decl_rels<<R>> where<<R>> {{ return R }}
 
-                 | vars_decl<<V>> restr<<V>>    {{ return V }}
+                 | decl_vars<<R>> where<<R>> {{ return R }}
 
 
 # Insertion  ##################################################################
 
-rule _insert<<V>>: vars_decl<<V>> insert_rels<<V>> {{ return V }}
+rule _insert<<R>>: decl_vars<<R>> insert_rels<<R>> {{ return R }}
                     
 
-rule insert_rels<<V>>: ":" rels_decl<<V>> restr<<V>> {{ return V }}
+rule insert_rels<<R>>: ":" decl_rels<<R>> where<<R>> {{ return R }}
 
                      |
 
 
 # Update  #####################################################################
 
-rule update<<V>>: rels_decl<<V>> restr<<V>> {{ return V }}
+rule update<<R>>: decl_rels<<R>> where<<R>> {{ return R }}
 
 
 # Selection  ##################################################################
 
-rule union: select<<Select()>>         {{ root = Union(); root.append(select) }}
-            ( UNION select<<Select()>> {{ root.append(select) }} 
-            )*                         {{ return root }}
+rule union<<R>>: select<<Select()>>               {{ R.append(select); return R }}
 
-rule select<<V>>: _select<<V>>              {{ return _select }}
-                 | r"\(" _select<<V>> r"\)" {{ return _select }}
-        
-rule _select<<V>>: DISTINCT select_base<<V>>  {{ V.distinct = True ; return V }}
-                 | select_base<<V>>          {{ return V }}
+               | r"\(" select<<Select()>> r"\)"   {{ R.append(select) }}
+                 ( UNION
+                   r"\(" select<<Select()>> r"\)" {{ R.append(select) }} 
+                 )*                               {{ return R }}
 
-
-rule select_base<<V>>: E_TYPE selected_terms<<V>> select_from<<V>>  restr<<V>> 
-                       group<<V>> having<<V>> {{ V.set_statement_type(E_TYPE) ; return V }}
-
-rule select_from<<V>>: FROM  subqueries<<V>>
-                     |
+rule select<<S>>: DISTINCT select_<<S>>  {{ S.distinct = True ; return S }}
+                 | select_<<S>>          {{ return S }}
 
-rule subqueries<<V>>: subquery<<V>> (
-                         ',' subquery<<V>>
-                      )* 
-            
-        
-rule subquery<<V>>: r"\(" union r"\)" AS aliases {{ V.add_subquery(union, aliases) }}
+rule select_<<S>>: E_TYPE selection<<S>> 
+                   groupby<<S>>
+                   orderby<<S>>
+                   where<<S>>
+                   having<<S>>
+                   with_<<S>>
+                   dgroupby<<S>>
+                   dorderby<<S>>          {{ S.set_statement_type(E_TYPE); return S }}
             
-rule aliases: VARIABLE               {{ return [VARIABLE] }}
-             | r"\("  VARIABLE  (     {{ variables = [VARIABLE] }}
-                      ',' VARIABLE    {{ variables.append(VARIABLE) }}
-                      )*  
-               r"\)"                  {{ return variables }}
-        
-rule selected_terms<<V>>: added_expr<<V>> (   {{ V.append_selected(added_expr) }}
-                            ',' added_expr<<V>>
-                            )*                    {{ V.append_selected(added_expr) }}
+rule selection<<S>>: expr_add<<S>>        {{ S.append_selected(expr_add) }}
+                     (  ',' expr_add<<S>> {{ S.append_selected(expr_add) }}
+                     )*                  
 
 
 
-# Groups and sorts ############################################################
+# other clauses (groupby, orderby, with, having) ##############################
+
+# to remove in rql 1.0
+rule dorderby<<S>>: orderby<<S>> {{ if orderby: warn('ORDERBY is now before WHERE clause') }}
+rule dgroupby<<S>>: groupby<<S>> {{ if groupby: warn('GROUPBY is now before WHERE clause') }}
 
-rule group<<V>>: GROUPBY        {{ G = Group() }}
-                   var<<V>> (   {{ G.append(var) }}
-                   ',' var<<V>>
-                   )*           {{ G.append(var) ; V.append(G) }}
-
+rule groupby<<S>>: GROUPBY variables<<S>> {{ S.set_groupby(variables); return True }}
+                 |
+            
+rule having<<S>>: HAVING               {{ nodes = [] }}
+                   expr_cmp<<S>>       {{ nodes.append(expr_cmp) }}
+                   ( ',' expr_cmp<<S>> {{ nodes.append(expr_cmp) }}
+                   )*                  {{ S.set_having(nodes) }}
+                |
+        
+rule orderby<<S>>: ORDERBY              {{ nodes = [] }}
+                   sort_term<<S>>       {{ nodes.append(sort_term) }}
+                   ( ',' sort_term<<S>> {{ nodes.append(sort_term) }}
+                   )*                   {{ S.set_orderby(nodes); return True }}
                  |
 
-rule having<<V>>: HAVING               {{ G = Having() }}
-                   cmp_expr<<V>> (     {{ G.append(cmp_expr) }}
-                   ',' cmp_expr<<V>>
-                   )*                  {{ G.append(cmp_expr) ; V.append(G) }}
-
-                 |
+rule with_<<S>>: WITH                {{ nodes = [] }}
+                 subquery<<S>>       {{ nodes.append(subquery) }}
+                 ( ',' subquery<<S>> {{ nodes.append(subquery) }}
+                 )*                  {{ S.set_with(nodes) }}
+               |
         
-rule cmp_expr<<V>>: added_expr<<V>>  {{ c1 = added_expr }}
-                    CMP_OP           {{ cmp = Comparison(CMP_OP.upper(), c1); }}
-                    added_expr<<V>>  {{ cmp.append(added_expr); return cmp }}
+rule subquery<<S>>: variables<<S>>                     {{ node = SubQuery() ; node.set_aliases(variables) }}
+                    BEING r"\(" union<<Union()>> r"\)" {{ node.set_query(union); return node }}
+
         
-rule sort<<V>>: ORDERBY              {{ S = Sort(); V.set_sortterms(S) }}
-                  sort_term<<V>> (   {{ S.append(sort_term) }}
-                  ',' sort_term<<V>>
-                  )*                 {{ S.append(sort_term) }}
+rule expr_cmp<<S>>: expr_add<<S>>  {{ c1 = expr_add }}
+                    CMP_OP         {{ cmp = Comparison(CMP_OP.upper(), c1) }}
+                    expr_add<<S>>  {{ cmp.append(expr_add); return cmp }}
+        
 
-                |
-
-
-rule sort_term<<V>>: added_expr<<V>> sort_meth {{ return SortTerm(added_expr, sort_meth) }}
+rule sort_term<<S>>: expr_add<<S>> sort_meth {{ return SortTerm(expr_add, sort_meth) }}
 
 
 rule sort_meth: SORT_DESC {{ return 0 }}
@@ -189,110 +211,101 @@
 
 # Limit and offset ############################################################
 
-rule limit_offset<<V>> :  limit<<V>> offset<<V>>
+rule limit_offset<<R>> :  limit<<R>> offset<<R>>
 		  
-rule limit<<V>> : LIMIT INT {{ V.set_limit(int(INT)) }} 
+rule limit<<R>> : LIMIT INT {{ R.set_limit(int(INT)) }} 
                 |
 
-
-rule offset<<V>> : OFFSET INT {{ V.set_offset(int(INT)) }}
+rule offset<<R>> : OFFSET INT {{ R.set_offset(int(INT)) }}
   		         | 
 
 
 # Restriction statements ######################################################
 
-rule restr<<V>>: WHERE rels<<V>> {{ V.append(rels) }}
-
+rule where<<S>>: WHERE restriction<<S>> {{ S.set_where(restriction) }}
                | 
 
-rule rels<<V>>: ored_rels<<V>>       {{ lhs = ored_rels }}
-                ( ',' ored_rels<<V>> {{ lhs = AND(lhs, ored_rels) }}
-                )*                  {{ return lhs }}
+rule restriction<<S>>: rels_or<<S>>       {{ node = rels_or }}
+                       ( ',' rels_or<<S>> {{ node = And(node, rels_or) }}
+                       )*                 {{ return node }}
         
-        
+rule rels_or<<S>>: rels_and<<S>>      {{ node = rels_and }}
+                   ( OR rels_and<<S>> {{ node = Or(node, rels_and) }}
+                   )*                 {{ return node }}
         
-rule ored_rels<<V>>: anded_rels<<V>>  {{ lhs = anded_rels }}
-                     ( OR anded_rels<<V>> {{ lhs = _OR(lhs,anded_rels) }}
-                     )*                 {{ return lhs }}
-        
-rule anded_rels<<V>>: not_rels<<V>>        {{ lhs = not_rels }}
-                      (  AND not_rels<<V>> {{ lhs = _AND(lhs, not_rels) }}
-                      )*                  {{ return lhs }}
+rule rels_and<<S>>: rels_not<<S>>        {{ node = rels_not }}
+                    (  AND rels_not<<S>> {{ node = And(node, rels_not) }}
+                    )*                   {{ return node }}
 
-rule not_rels<<V>>: NOT rel<<V>> {{ not_ = Not(); not_.append(rel); return not_ }}
-        
-                  | rel<<V>>     {{ return rel }}
+rule rels_not<<S>>: NOT rel<<S>> {{ node = Not(); node.append(rel); return node }}
+                  | rel<<S>>     {{ return rel }}
+
+rule rel<<S>>: rel_base<<S>>                {{ return rel_base }}
+             | r"\(" restriction<<S>> r"\)" {{ return restriction }}
 
 
-rule rel<<V>>: base_rel<<V>>           {{ return base_rel }}
-
-               | r"\(" rels<<V>> r"\)" {{ return rels }}
-
+rule rel_base<<S>>: var<<S>> opt_left<<S>> rtype        {{ rtype.append(var) ; rtype.set_optional(opt_left) }} 
+                    expr<<S>> opt_right<<S>>            {{ rtype.append(expr) ; rtype.set_optional(opt_right) ; return rtype }}
+                  | EXISTS r"\(" restriction<<S>> r"\)" {{ return Exists(restriction) }}
+               
+rule rtype: R_TYPE {{ return Relation(R_TYPE) }}
 
-rule base_rel<<V>>: var<<V>> opt_left<<V>> rtype<<V>> {{ rtype.append(var) ; rtype.set_optional(opt_left) }} 
-                    expr<<V>> opt_right<<V>>          {{ rtype.append(expr) ; rtype.set_optional(opt_right) ; return rtype }}
-                   | EXISTS r"\(" rels<<V>> r"\)"     {{ return Exists(rels) }}
-               
-                  
-rule rtype<<V>>: R_TYPE {{ return Relation(R_TYPE) }}
-
-rule opt_left<<V>>: QMARK  {{ return 'left' }}
-                   | 
-rule opt_right<<V>>: QMARK  {{ return 'right' }}
+rule opt_left<<S>>: QMARK  {{ return 'left' }}
+                  | 
+rule opt_right<<S>>: QMARK  {{ return 'right' }}
                    | 
                     
 # common statements ###########################################################
 
-rule vars_decl<<V>>: E_TYPE var<<V>> (     {{ V.add_main_variable(E_TYPE, var) }}
-                     ',' E_TYPE var<<V>>)* {{ V.add_main_variable(E_TYPE, var) }}
-
-
-rule rels_decl<<V>>: simple_rel<<V>> (     {{ V.add_main_relation(simple_rel) }}
-                     ',' simple_rel<<V>>)* {{ V.add_main_relation(simple_rel) }}
+rule variables<<S>>:                   {{ vars = [] }}
+                       var<<S>>        {{ vars.append(var) }}
+                       (  ',' var<<S>> {{ vars.append(var) }}
+                       )*              {{ return vars }}
+        
+rule decl_vars<<R>>: E_TYPE var<<R>> (     {{ R.add_main_variable(E_TYPE, var) }}
+                     ',' E_TYPE var<<R>>)* {{ R.add_main_variable(E_TYPE, var) }}
 
 
-rule simple_rel<<V>>: var<<V>> R_TYPE    {{ e = Relation(R_TYPE) ; e.append(var) }} 
-                      added_expr<<V>>    {{ e.append(Comparison('=', added_expr)) ; return e }}
+rule decl_rels<<R>>: simple_rel<<R>> (     {{ R.add_main_relation(simple_rel) }}
+                     ',' simple_rel<<R>>)* {{ R.add_main_relation(simple_rel) }}
 
 
-rule expr<<V>>: CMP_OP added_expr<<V>> {{ return Comparison(CMP_OP.upper(), added_expr) }}
-
-                | added_expr<<V>>      {{ return Comparison('=', added_expr) }}
+rule simple_rel<<R>>: var<<R>> R_TYPE  {{ e = Relation(R_TYPE) ; e.append(var) }} 
+                      expr_add<<R>>    {{ e.append(Comparison('=', expr_add)) ; return e }}
 
 
-rule added_expr<<V>>: muled_expr<<V>>          {{ lhs = muled_expr }}
-                      ( ADD_OP muled_expr<<V>> {{ lhs = MathExpression( ADD_OP, lhs, muled_expr ) }}
-                      )*                       {{ return lhs }}
+rule expr<<S>>: CMP_OP expr_add<<S>> {{ return Comparison(CMP_OP.upper(), expr_add) }}
+
+                | expr_add<<S>>      {{ return Comparison('=', expr_add) }}
 
 
-rule muled_expr<<V>>: base_expr<<V>>          {{ lhs = base_expr }}
-                      ( MUL_OP base_expr<<V>> {{ lhs = MathExpression( MUL_OP, lhs, base_expr) }}
-                      )*                      {{ return lhs }}
+rule expr_add<<S>>: expr_mul<<S>>          {{ node = expr_mul }}
+                    ( ADD_OP expr_mul<<S>> {{ node = MathExpression( ADD_OP, node, expr_mul ) }}
+                    )*                     {{ return node }}
+
+
+rule expr_mul<<S>>: expr_base<<S>>          {{ node = expr_base }}
+                    ( MUL_OP expr_base<<S>> {{ node = MathExpression( MUL_OP, node, expr_base) }}
+                    )*                      {{ return node }}
 
 
-rule base_expr<<V>>: const                         {{ return const }}
-
-                     | var<<V>>                    {{ return var }} 
-
-                     | etype<<V>>                  {{ return etype }} 
-
-                     | func<<V>>                   {{ return func }}
-
-                     | r"\(" added_expr<<V>> r"\)" {{ return added_expr }}
+rule expr_base<<S>>: const                     {{ return const }}
+                   | var<<S>>                  {{ return var }} 
+                   | etype<<S>>                {{ return etype }} 
+                   | func<<S>>                 {{ return func }}
+                   | r"\(" expr_add<<S>> r"\)" {{ return expr_add }}
 
 
-rule func<<V>>: FUNCTION r"\("              {{ F = Function(FUNCTION) }}
-                  added_expr<<V>> (         {{ F.append(added_expr) }}
-                  ',' added_expr<<V>>             
-                  )*                          {{ F.append(added_expr) }}
-                  r"\)"                       {{ return F }} 
+rule func<<S>>: FUNCTION r"\("        {{ F = Function(FUNCTION) }}
+                  expr_add<<S>> (     {{ F.append(expr_add) }}
+                    ',' expr_add<<S>>       
+                  )*                  {{ F.append(expr_add) }}
+                r"\)"                 {{ return F }} 
 
 
-rule var<<V>>: VARIABLE {{ return VariableRef(V.get_variable(VARIABLE)) }}
+rule var<<S>>: VARIABLE {{ return VariableRef(S.get_variable(VARIABLE)) }}
         
-#             | COLALIAS {{ return ColumnAlias(COLALIAS) }} 
-        
-rule etype<<V>>: E_TYPE {{ return V.get_etype(E_TYPE) }} 
+rule etype<<S>>: E_TYPE {{ return S.get_etype(E_TYPE) }} 
 
 
 rule const: NULL       {{ return Constant(None, None) }}
diff --git a/parser.py b/parser.py
--- a/parser.py
+++ b/parser.py
@@ -3,14 +3,50 @@
 :organization: Logilab
 :copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
+
+
+Select statement grammar
+------------------------
+
+query = <squery> | <union>
+
+union = (<squery>) UNION (<squery>) [UNION (<squery>)]*
+
+squery = Any <selection>
+        [GROUPBY <variables>]
+        [ORDERBY <sortterms>]
+        [WHERE <restriction>]
+        [HAVING <aggregat restriction>]
+        [WITH <subquery> [,<subquery]*]
+
+subquery = <variables> BEING (<query>)
+
+variables = <variable> [, <variable>]*
 
 
-from rql.stmts import Union, Select, Delete, Insert, Update
-from rql.nodes import *
+Abbreviations in this code
+--------------------------
 
-_OR = OR
-_AND = AND
+rules:
+* rel -> relation
+* decl -> declaration
+* expr -> expression
+* restr -> restriction
+* var -> variable
+* func -> function
+* const -> constant
+* cmp -> comparison
+
+variables:
+* R -> syntax tree root
+* S -> select node
+* P -> parent node
+
+"""
+
+from warnings import warn
+from rql.stmts import Union, Select, Delete, Insert, Set
+from rql.nodes import *
 
 
 def unquote(string):
@@ -38,9 +74,9 @@
         ('INSERT', re.compile('(?i)INSERT')),
         ('UNION', re.compile('(?i)UNION')),
         ('DISTINCT', re.compile('(?i)DISTINCT')),
-        ('FROM', re.compile('(?i)FROM')),
+        ('WITH', re.compile('(?i)WITH')),
         ('WHERE', re.compile('(?i)WHERE')),
-        ('AS', re.compile('(?i)AS')),
+        ('BEING', re.compile('(?i)BEING')),
         ('OR', re.compile('(?i)OR')),
         ('AND', re.compile('(?i)AND')),
         ('NOT', re.compile('(?i)NOT')),
@@ -91,330 +127,330 @@
             return _insert
         elif _token == 'SET':
             SET = self._scan('SET', context=_context)
-            update = self.update(Update(), _context)
+            update = self.update(Set(), _context)
             self._scan("';'", context=_context)
             return update
         else: # in ['r"\\("', 'DISTINCT', 'E_TYPE']
-            union = self.union(_context)
-            sort = self.sort(union, _context)
+            union = self.union(Union(), _context)
             limit_offset = self.limit_offset(union, _context)
             self._scan("';'", context=_context)
             return union
 
-    def _delete(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, '_delete', [V])
+    def _delete(self, R, _parent=None):
+        _context = self.Context(_parent, self._scanner, '_delete', [R])
         _token = self._peek('E_TYPE', 'VARIABLE', context=_context)
         if _token == 'VARIABLE':
-            rels_decl = self.rels_decl(V, _context)
-            restr = self.restr(V, _context)
-            return V
+            decl_rels = self.decl_rels(R, _context)
+            where = self.where(R, _context)
+            return R
         else: # == 'E_TYPE'
-            vars_decl = self.vars_decl(V, _context)
-            restr = self.restr(V, _context)
-            return V
+            decl_vars = self.decl_vars(R, _context)
+            where = self.where(R, _context)
+            return R
 
-    def _insert(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, '_insert', [V])
-        vars_decl = self.vars_decl(V, _context)
-        insert_rels = self.insert_rels(V, _context)
-        return V
+    def _insert(self, R, _parent=None):
+        _context = self.Context(_parent, self._scanner, '_insert', [R])
+        decl_vars = self.decl_vars(R, _context)
+        insert_rels = self.insert_rels(R, _context)
+        return R
 
-    def insert_rels(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'insert_rels', [V])
+    def insert_rels(self, R, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'insert_rels', [R])
         _token = self._peek('":"', "';'", context=_context)
         if _token == '":"':
             self._scan('":"', context=_context)
-            rels_decl = self.rels_decl(V, _context)
-            restr = self.restr(V, _context)
-            return V
+            decl_rels = self.decl_rels(R, _context)
+            where = self.where(R, _context)
+            return R
         else: # == "';'"
             pass
 
-    def update(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'update', [V])
-        rels_decl = self.rels_decl(V, _context)
-        restr = self.restr(V, _context)
-        return V
+    def update(self, R, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'update', [R])
+        decl_rels = self.decl_rels(R, _context)
+        where = self.where(R, _context)
+        return R
 
-    def union(self, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'union', [])
-        select = self.select(Select(), _context)
-        root = Union(); root.append(select)
-        while self._peek('UNION', 'r"\\)"', 'ORDERBY', "';'", 'LIMIT', 'OFFSET', context=_context) == 'UNION':
-            UNION = self._scan('UNION', context=_context)
-            select = self.select(Select(), _context)
-            root.append(select)
-        return root
-
-    def select(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'select', [V])
+    def union(self, R, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'union', [R])
         _token = self._peek('r"\\("', 'DISTINCT', 'E_TYPE', context=_context)
         if _token != 'r"\\("':
-            _select = self._select(V, _context)
-            return _select
+            select = self.select(Select(), _context)
+            R.append(select); return R
         else: # == 'r"\\("'
             self._scan('r"\\("', context=_context)
-            _select = self._select(V, _context)
+            select = self.select(Select(), _context)
             self._scan('r"\\)"', context=_context)
-            return _select
+            R.append(select)
+            while self._peek('UNION', 'r"\\)"', "';'", 'LIMIT', 'OFFSET', context=_context) == 'UNION':
+                UNION = self._scan('UNION', context=_context)
+                self._scan('r"\\("', context=_context)
+                select = self.select(Select(), _context)
+                self._scan('r"\\)"', context=_context)
+                R.append(select)
+            return R
 
-    def _select(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, '_select', [V])
+    def select(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'select', [S])
         _token = self._peek('DISTINCT', 'E_TYPE', context=_context)
         if _token == 'DISTINCT':
             DISTINCT = self._scan('DISTINCT', context=_context)
-            select_base = self.select_base(V, _context)
-            V.distinct = True ; return V
+            select_ = self.select_(S, _context)
+            S.distinct = True ; return S
         else: # == 'E_TYPE'
-            select_base = self.select_base(V, _context)
-            return V
-
-    def select_base(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'select_base', [V])
-        E_TYPE = self._scan('E_TYPE', context=_context)
-        selected_terms = self.selected_terms(V, _context)
-        select_from = self.select_from(V, _context)
-        restr = self.restr(V, _context)
-        group = self.group(V, _context)
-        having = self.having(V, _context)
-        V.set_statement_type(E_TYPE) ; return V
+            select_ = self.select_(S, _context)
+            return S
 
-    def select_from(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'select_from', [V])
-        _token = self._peek('FROM', 'WHERE', "','", 'GROUPBY', 'HAVING', "';'", 'r"\\)"', 'UNION', 'ORDERBY', 'LIMIT', 'OFFSET', context=_context)
-        if _token == 'FROM':
-            FROM = self._scan('FROM', context=_context)
-            subqueries = self.subqueries(V, _context)
-        else:
-            pass
-
-    def subqueries(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'subqueries', [V])
-        subquery = self.subquery(V, _context)
-        while self._peek("','", 'WHERE', 'GROUPBY', 'HAVING', "';'", 'r"\\)"', 'UNION', 'ORDERBY', 'LIMIT', 'OFFSET', context=_context) == "','":
-            self._scan("','", context=_context)
-            subquery = self.subquery(V, _context)
+    def select_(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'select_', [S])
+        E_TYPE = self._scan('E_TYPE', context=_context)
+        selection = self.selection(S, _context)
+        groupby = self.groupby(S, _context)
+        orderby = self.orderby(S, _context)
+        where = self.where(S, _context)
+        having = self.having(S, _context)
+        with_ = self.with_(S, _context)
+        dgroupby = self.dgroupby(S, _context)
+        dorderby = self.dorderby(S, _context)
+        S.set_statement_type(E_TYPE); return S
 
-    def subquery(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'subquery', [V])
-        self._scan('r"\\("', context=_context)
-        union = self.union(_context)
-        self._scan('r"\\)"', context=_context)
-        AS = self._scan('AS', context=_context)
-        aliases = self.aliases(_context)
-        V.add_subquery(union, aliases)
+    def selection(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'selection', [S])
+        expr_add = self.expr_add(S, _context)
+        S.append_selected(expr_add)
+        while self._peek("','", 'GROUPBY', 'ORDERBY', 'WHERE', 'HAVING', 'WITH', "';'", 'r"\\)"', 'LIMIT', 'OFFSET', context=_context) == "','":
+            self._scan("','", context=_context)
+            expr_add = self.expr_add(S, _context)
+            S.append_selected(expr_add)
 
-    def aliases(self, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'aliases', [])
-        _token = self._peek('VARIABLE', 'r"\\("', context=_context)
-        if _token == 'VARIABLE':
-            VARIABLE = self._scan('VARIABLE', context=_context)
-            return [VARIABLE]
-        else: # == 'r"\\("'
-            self._scan('r"\\("', context=_context)
-            VARIABLE = self._scan('VARIABLE', context=_context)
-            while self._peek('r"\\)"', "','", context=_context) == "','":
-                variables = [VARIABLE]
-                self._scan("','", context=_context)
-                VARIABLE = self._scan('VARIABLE', context=_context)
-                variables.append(VARIABLE)
-            self._scan('r"\\)"', context=_context)
-            return variables
+    def dorderby(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'dorderby', [S])
+        orderby = self.orderby(S, _context)
+        if orderby: warn('ORDERBY is now before WHERE clause')
 
-    def selected_terms(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'selected_terms', [V])
-        added_expr = self.added_expr(V, _context)
-        while self._peek("','", 'r"\\)"', 'CMP_OP', 'SORT_DESC', 'SORT_ASC', 'FROM', 'WHERE', 'GROUPBY', 'QMARK', 'HAVING', "';'", 'LIMIT', 'OFFSET', 'UNION', 'AND', 'ORDERBY', 'OR', context=_context) == "','":
-            V.append_selected(added_expr)
-            self._scan("','", context=_context)
-            added_expr = self.added_expr(V, _context)
-        V.append_selected(added_expr)
+    def dgroupby(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'dgroupby', [S])
+        groupby = self.groupby(S, _context)
+        if groupby: warn('GROUPBY is now before WHERE clause')
 
-    def group(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'group', [V])
-        _token = self._peek('GROUPBY', 'HAVING', 'r"\\)"', 'UNION', 'ORDERBY', "';'", 'LIMIT', 'OFFSET', context=_context)
+    def groupby(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'groupby', [S])
+        _token = self._peek('GROUPBY', 'ORDERBY', 'WHERE', 'HAVING', 'WITH', "';'", 'r"\\)"', 'LIMIT', 'OFFSET', context=_context)
         if _token == 'GROUPBY':
             GROUPBY = self._scan('GROUPBY', context=_context)
-            G = Group()
-            var = self.var(V, _context)
-            while self._peek("','", 'R_TYPE', 'QMARK', 'HAVING', 'WHERE', '":"', 'MUL_OP', 'GROUPBY', "';'", 'r"\\)"', 'ADD_OP', 'UNION', 'CMP_OP', 'SORT_DESC', 'SORT_ASC', 'FROM', 'ORDERBY', 'LIMIT', 'OFFSET', 'AND', 'OR', context=_context) == "','":
-                G.append(var)
-                self._scan("','", context=_context)
-                var = self.var(V, _context)
-            G.append(var) ; V.append(G)
+            variables = self.variables(S, _context)
+            S.set_groupby(variables); return True
+        elif 1:
+            pass
         else:
-            pass
+            raise runtime.SyntaxError(_token[0], 'Could not match groupby')
 
-    def having(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'having', [V])
-        _token = self._peek('HAVING', 'r"\\)"', 'UNION', 'ORDERBY', "';'", 'LIMIT', 'OFFSET', context=_context)
+    def having(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'having', [S])
+        _token = self._peek('HAVING', 'WITH', 'GROUPBY', 'ORDERBY', 'WHERE', "';'", 'r"\\)"', 'LIMIT', 'OFFSET', context=_context)
         if _token == 'HAVING':
             HAVING = self._scan('HAVING', context=_context)
-            G = Having()
-            cmp_expr = self.cmp_expr(V, _context)
-            while self._peek("','", 'r"\\)"', 'UNION', 'ORDERBY', "';'", 'LIMIT', 'OFFSET', context=_context) == "','":
-                G.append(cmp_expr)
+            nodes = []
+            expr_cmp = self.expr_cmp(S, _context)
+            nodes.append(expr_cmp)
+            while self._peek("','", 'WITH', 'GROUPBY', 'ORDERBY', 'WHERE', 'HAVING', "';'", 'r"\\)"', 'LIMIT', 'OFFSET', context=_context) == "','":
                 self._scan("','", context=_context)
-                cmp_expr = self.cmp_expr(V, _context)
-            G.append(cmp_expr) ; V.append(G)
-        else: # in ['r"\\)"', 'UNION', 'ORDERBY', "';'", 'LIMIT', 'OFFSET']
+                expr_cmp = self.expr_cmp(S, _context)
+                nodes.append(expr_cmp)
+            S.set_having(nodes)
+        elif 1:
             pass
+        else:
+            raise runtime.SyntaxError(_token[0], 'Could not match having')
 
-    def cmp_expr(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'cmp_expr', [V])
-        added_expr = self.added_expr(V, _context)
-        c1 = added_expr
-        CMP_OP = self._scan('CMP_OP', context=_context)
-        cmp = Comparison(CMP_OP.upper(), c1);
-        added_expr = self.added_expr(V, _context)
-        cmp.append(added_expr); return cmp
-
-    def sort(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'sort', [V])
-        _token = self._peek('ORDERBY', "';'", 'LIMIT', 'OFFSET', context=_context)
+    def orderby(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'orderby', [S])
+        _token = self._peek('ORDERBY', 'WHERE', 'HAVING', 'WITH', "';'", 'GROUPBY', 'r"\\)"', 'LIMIT', 'OFFSET', context=_context)
         if _token == 'ORDERBY':
             ORDERBY = self._scan('ORDERBY', context=_context)
-            S = Sort(); V.set_sortterms(S)
-            sort_term = self.sort_term(V, _context)
-            while self._peek("','", "';'", 'LIMIT', 'OFFSET', context=_context) == "','":
-                S.append(sort_term)
+            nodes = []
+            sort_term = self.sort_term(S, _context)
+            nodes.append(sort_term)
+            while self._peek("','", 'WHERE', 'HAVING', 'WITH', "';'", 'GROUPBY', 'ORDERBY', 'r"\\)"', 'LIMIT', 'OFFSET', context=_context) == "','":
+                self._scan("','", context=_context)
+                sort_term = self.sort_term(S, _context)
+                nodes.append(sort_term)
+            S.set_orderby(nodes); return True
+        elif 1:
+            pass
+        else:
+            raise runtime.SyntaxError(_token[0], 'Could not match orderby')
+
+    def with_(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'with_', [S])
+        _token = self._peek('WITH', 'GROUPBY', 'ORDERBY', 'WHERE', 'HAVING', "';'", 'r"\\)"', 'LIMIT', 'OFFSET', context=_context)
+        if _token == 'WITH':
+            WITH = self._scan('WITH', context=_context)
+            nodes = []
+            subquery = self.subquery(S, _context)
+            nodes.append(subquery)
+            while self._peek("','", 'GROUPBY', 'ORDERBY', 'WHERE', 'HAVING', 'WITH', "';'", 'r"\\)"', 'LIMIT', 'OFFSET', context=_context) == "','":
                 self._scan("','", context=_context)
-                sort_term = self.sort_term(V, _context)
-            S.append(sort_term)
-        else: # in ["';'", 'LIMIT', 'OFFSET']
+                subquery = self.subquery(S, _context)
+                nodes.append(subquery)
+            S.set_with(nodes)
+        elif 1:
             pass
+        else:
+            raise runtime.SyntaxError(_token[0], 'Could not match with_')
 
-    def sort_term(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'sort_term', [V])
-        added_expr = self.added_expr(V, _context)
+    def subquery(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'subquery', [S])
+        variables = self.variables(S, _context)
+        node = SubQuery() ; node.set_aliases(variables)
+        BEING = self._scan('BEING', context=_context)
+        self._scan('r"\\("', context=_context)
+        union = self.union(Union(), _context)
+        self._scan('r"\\)"', context=_context)
+        node.set_query(union); return node
+
+    def expr_cmp(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'expr_cmp', [S])
+        expr_add = self.expr_add(S, _context)
+        c1 = expr_add
+        CMP_OP = self._scan('CMP_OP', context=_context)
+        cmp = Comparison(CMP_OP.upper(), c1)
+        expr_add = self.expr_add(S, _context)
+        cmp.append(expr_add); return cmp
+
+    def sort_term(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'sort_term', [S])
+        expr_add = self.expr_add(S, _context)
         sort_meth = self.sort_meth(_context)
-        return SortTerm(added_expr, sort_meth)
+        return SortTerm(expr_add, sort_meth)
 
     def sort_meth(self, _parent=None):
         _context = self.Context(_parent, self._scanner, 'sort_meth', [])
-        _token = self._peek('SORT_DESC', 'SORT_ASC', "','", "';'", 'LIMIT', 'OFFSET', context=_context)
+        _token = self._peek('SORT_DESC', 'SORT_ASC', "','", 'WHERE', 'HAVING', 'WITH', "';'", 'GROUPBY', 'ORDERBY', 'r"\\)"', 'LIMIT', 'OFFSET', context=_context)
         if _token == 'SORT_DESC':
             SORT_DESC = self._scan('SORT_DESC', context=_context)
             return 0
         elif _token == 'SORT_ASC':
             SORT_ASC = self._scan('SORT_ASC', context=_context)
             return 1
-        else: # in ["','", "';'", 'LIMIT', 'OFFSET']
+        else:
             return 1 # default to SORT_ASC
 
-    def limit_offset(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'limit_offset', [V])
-        limit = self.limit(V, _context)
-        offset = self.offset(V, _context)
+    def limit_offset(self, R, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'limit_offset', [R])
+        limit = self.limit(R, _context)
+        offset = self.offset(R, _context)
 
-    def limit(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'limit', [V])
+    def limit(self, R, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'limit', [R])
         _token = self._peek('LIMIT', 'OFFSET', "';'", context=_context)
         if _token == 'LIMIT':
             LIMIT = self._scan('LIMIT', context=_context)
             INT = self._scan('INT', context=_context)
-            V.set_limit(int(INT))
+            R.set_limit(int(INT))
         else: # in ['OFFSET', "';'"]
             pass
 
-    def offset(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'offset', [V])
+    def offset(self, R, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'offset', [R])
         _token = self._peek('OFFSET', "';'", context=_context)
         if _token == 'OFFSET':
             OFFSET = self._scan('OFFSET', context=_context)
             INT = self._scan('INT', context=_context)
-            V.set_offset(int(INT))
+            R.set_offset(int(INT))
         else: # == "';'"
             pass
 
-    def restr(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'restr', [V])
-        _token = self._peek('WHERE', 'GROUPBY', "';'", 'HAVING', 'r"\\)"', 'UNION', 'ORDERBY', 'LIMIT', 'OFFSET', context=_context)
+    def where(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'where', [S])
+        _token = self._peek('WHERE', 'HAVING', "';'", 'WITH', 'GROUPBY', 'ORDERBY', 'r"\\)"', 'LIMIT', 'OFFSET', context=_context)
         if _token == 'WHERE':
             WHERE = self._scan('WHERE', context=_context)
-            rels = self.rels(V, _context)
-            V.append(rels)
+            restriction = self.restriction(S, _context)
+            S.set_where(restriction)
+        elif 1:
+            pass
         else:
-            pass
+            raise runtime.SyntaxError(_token[0], 'Could not match where')
 
-    def rels(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'rels', [V])
-        ored_rels = self.ored_rels(V, _context)
-        lhs = ored_rels
-        while self._peek("','", 'r"\\)"', 'GROUPBY', "';'", 'HAVING', 'UNION', 'ORDERBY', 'LIMIT', 'OFFSET', context=_context) == "','":
+    def restriction(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'restriction', [S])
+        rels_or = self.rels_or(S, _context)
+        node = rels_or
+        while self._peek("','", 'r"\\)"', 'HAVING', "';'", 'WITH', 'GROUPBY', 'ORDERBY', 'WHERE', 'LIMIT', 'OFFSET', context=_context) == "','":
             self._scan("','", context=_context)
-            ored_rels = self.ored_rels(V, _context)
-            lhs = AND(lhs, ored_rels)
-        return lhs
+            rels_or = self.rels_or(S, _context)
+            node = And(node, rels_or)
+        return node
 
-    def ored_rels(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'ored_rels', [V])
-        anded_rels = self.anded_rels(V, _context)
-        lhs = anded_rels
-        while self._peek('OR', "','", 'r"\\)"', 'GROUPBY', "';'", 'HAVING', 'UNION', 'ORDERBY', 'LIMIT', 'OFFSET', context=_context) == 'OR':
+    def rels_or(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'rels_or', [S])
+        rels_and = self.rels_and(S, _context)
+        node = rels_and
+        while self._peek('OR', "','", 'r"\\)"', 'HAVING', "';'", 'WITH', 'GROUPBY', 'ORDERBY', 'WHERE', 'LIMIT', 'OFFSET', context=_context) == 'OR':
             OR = self._scan('OR', context=_context)
-            anded_rels = self.anded_rels(V, _context)
-            lhs = _OR(lhs,anded_rels)
-        return lhs
+            rels_and = self.rels_and(S, _context)
+            node = Or(node, rels_and)
+        return node
 
-    def anded_rels(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'anded_rels', [V])
-        not_rels = self.not_rels(V, _context)
-        lhs = not_rels
-        while self._peek('AND', 'OR', "','", 'r"\\)"', 'GROUPBY', "';'", 'HAVING', 'UNION', 'ORDERBY', 'LIMIT', 'OFFSET', context=_context) == 'AND':
+    def rels_and(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'rels_and', [S])
+        rels_not = self.rels_not(S, _context)
+        node = rels_not
+        while self._peek('AND', 'OR', "','", 'r"\\)"', 'HAVING', "';'", 'WITH', 'GROUPBY', 'ORDERBY', 'WHERE', 'LIMIT', 'OFFSET', context=_context) == 'AND':
             AND = self._scan('AND', context=_context)
-            not_rels = self.not_rels(V, _context)
-            lhs = _AND(lhs, not_rels)
-        return lhs
+            rels_not = self.rels_not(S, _context)
+            node = And(node, rels_not)
+        return node
 
-    def not_rels(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'not_rels', [V])
+    def rels_not(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'rels_not', [S])
         _token = self._peek('NOT', 'r"\\("', 'EXISTS', 'VARIABLE', context=_context)
         if _token == 'NOT':
             NOT = self._scan('NOT', context=_context)
-            rel = self.rel(V, _context)
-            not_ = Not(); not_.append(rel); return not_
+            rel = self.rel(S, _context)
+            node = Not(); node.append(rel); return node
         else: # in ['r"\\("', 'EXISTS', 'VARIABLE']
-            rel = self.rel(V, _context)
+            rel = self.rel(S, _context)
             return rel
 
-    def rel(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'rel', [V])
+    def rel(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'rel', [S])
         _token = self._peek('r"\\("', 'EXISTS', 'VARIABLE', context=_context)
         if _token != 'r"\\("':
-            base_rel = self.base_rel(V, _context)
-            return base_rel
+            rel_base = self.rel_base(S, _context)
+            return rel_base
         else: # == 'r"\\("'
             self._scan('r"\\("', context=_context)
-            rels = self.rels(V, _context)
+            restriction = self.restriction(S, _context)
             self._scan('r"\\)"', context=_context)
-            return rels
+            return restriction
 
-    def base_rel(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'base_rel', [V])
+    def rel_base(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'rel_base', [S])
         _token = self._peek('EXISTS', 'VARIABLE', context=_context)
         if _token == 'VARIABLE':
-            var = self.var(V, _context)
-            opt_left = self.opt_left(V, _context)
-            rtype = self.rtype(V, _context)
+            var = self.var(S, _context)
+            opt_left = self.opt_left(S, _context)
+            rtype = self.rtype(_context)
             rtype.append(var) ; rtype.set_optional(opt_left)
-            expr = self.expr(V, _context)
-            opt_right = self.opt_right(V, _context)
+            expr = self.expr(S, _context)
+            opt_right = self.opt_right(S, _context)
             rtype.append(expr) ; rtype.set_optional(opt_right) ; return rtype
         else: # == 'EXISTS'
             EXISTS = self._scan('EXISTS', context=_context)
             self._scan('r"\\("', context=_context)
-            rels = self.rels(V, _context)
+            restriction = self.restriction(S, _context)
             self._scan('r"\\)"', context=_context)
-            return Exists(rels)
+            return Exists(restriction)
 
-    def rtype(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'rtype', [V])
+    def rtype(self, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'rtype', [])
         R_TYPE = self._scan('R_TYPE', context=_context)
         return Relation(R_TYPE)
 
-    def opt_left(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'opt_left', [V])
+    def opt_left(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'opt_left', [S])
         _token = self._peek('QMARK', 'R_TYPE', context=_context)
         if _token == 'QMARK':
             QMARK = self._scan('QMARK', context=_context)
@@ -422,118 +458,129 @@
         else: # == 'R_TYPE'
             pass
 
-    def opt_right(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'opt_right', [V])
-        _token = self._peek('QMARK', 'AND', 'OR', "','", 'r"\\)"', 'GROUPBY', "';'", 'HAVING', 'UNION', 'ORDERBY', 'LIMIT', 'OFFSET', context=_context)
+    def opt_right(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'opt_right', [S])
+        _token = self._peek('QMARK', 'AND', 'OR', "','", 'r"\\)"', 'HAVING', "';'", 'WITH', 'GROUPBY', 'ORDERBY', 'WHERE', 'LIMIT', 'OFFSET', context=_context)
         if _token == 'QMARK':
             QMARK = self._scan('QMARK', context=_context)
             return 'right'
         else:
             pass
 
-    def vars_decl(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'vars_decl', [V])
+    def variables(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'variables', [S])
+        vars = []
+        var = self.var(S, _context)
+        vars.append(var)
+        while self._peek("','", 'BEING', 'ORDERBY', 'WHERE', 'HAVING', 'WITH', "';'", 'GROUPBY', 'r"\\)"', 'LIMIT', 'OFFSET', context=_context) == "','":
+            self._scan("','", context=_context)
+            var = self.var(S, _context)
+            vars.append(var)
+        return vars
+
+    def decl_vars(self, R, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'decl_vars', [R])
         E_TYPE = self._scan('E_TYPE', context=_context)
-        var = self.var(V, _context)
-        while self._peek("','", 'R_TYPE', 'QMARK', 'WHERE', '":"', 'GROUPBY', "';'", 'MUL_OP', 'HAVING', 'ADD_OP', 'r"\\)"', 'CMP_OP', 'SORT_DESC', 'SORT_ASC', 'UNION', 'FROM', 'ORDERBY', 'LIMIT', 'OFFSET', 'AND', 'OR', context=_context) == "','":
-            V.add_main_variable(E_TYPE, var)
+        var = self.var(R, _context)
+        while self._peek("','", 'R_TYPE', 'QMARK', 'WHERE', '":"', 'HAVING', "';'", 'MUL_OP', 'BEING', 'WITH', 'GROUPBY', 'ORDERBY', 'ADD_OP', 'r"\\)"', 'CMP_OP', 'SORT_DESC', 'SORT_ASC', 'LIMIT', 'OFFSET', 'AND', 'OR', context=_context) == "','":
+            R.add_main_variable(E_TYPE, var)
             self._scan("','", context=_context)
             E_TYPE = self._scan('E_TYPE', context=_context)
-            var = self.var(V, _context)
-        V.add_main_variable(E_TYPE, var)
+            var = self.var(R, _context)
+        R.add_main_variable(E_TYPE, var)
 
-    def rels_decl(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'rels_decl', [V])
-        simple_rel = self.simple_rel(V, _context)
-        while self._peek("','", 'WHERE', 'GROUPBY', "';'", 'HAVING', 'r"\\)"', 'UNION', 'ORDERBY', 'LIMIT', 'OFFSET', context=_context) == "','":
-            V.add_main_relation(simple_rel)
+    def decl_rels(self, R, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'decl_rels', [R])
+        simple_rel = self.simple_rel(R, _context)
+        while self._peek("','", 'WHERE', 'HAVING', "';'", 'WITH', 'GROUPBY', 'ORDERBY', 'r"\\)"', 'LIMIT', 'OFFSET', context=_context) == "','":
+            R.add_main_relation(simple_rel)
             self._scan("','", context=_context)
-            simple_rel = self.simple_rel(V, _context)
-        V.add_main_relation(simple_rel)
+            simple_rel = self.simple_rel(R, _context)
+        R.add_main_relation(simple_rel)
 
-    def simple_rel(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'simple_rel', [V])
-        var = self.var(V, _context)
+    def simple_rel(self, R, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'simple_rel', [R])
+        var = self.var(R, _context)
         R_TYPE = self._scan('R_TYPE', context=_context)
         e = Relation(R_TYPE) ; e.append(var)
-        added_expr = self.added_expr(V, _context)
-        e.append(Comparison('=', added_expr)) ; return e
+        expr_add = self.expr_add(R, _context)
+        e.append(Comparison('=', expr_add)) ; return e
 
-    def expr(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'expr', [V])
+    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)
         if _token == 'CMP_OP':
             CMP_OP = self._scan('CMP_OP', context=_context)
-            added_expr = self.added_expr(V, _context)
-            return Comparison(CMP_OP.upper(), added_expr)
+            expr_add = self.expr_add(S, _context)
+            return Comparison(CMP_OP.upper(), expr_add)
         else:
-            added_expr = self.added_expr(V, _context)
-            return Comparison('=', added_expr)
+            expr_add = self.expr_add(S, _context)
+            return Comparison('=', expr_add)
 
-    def added_expr(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'added_expr', [V])
-        muled_expr = self.muled_expr(V, _context)
-        lhs = muled_expr
-        while self._peek('ADD_OP', "','", 'r"\\)"', 'CMP_OP', 'SORT_DESC', 'SORT_ASC', 'FROM', 'QMARK', 'WHERE', 'GROUPBY', 'HAVING', "';'", 'LIMIT', 'OFFSET', 'AND', 'UNION', 'OR', 'ORDERBY', context=_context) == 'ADD_OP':
+    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', 'r"\\)"', "','", 'CMP_OP', 'SORT_DESC', 'SORT_ASC', 'GROUPBY', 'QMARK', 'ORDERBY', 'WHERE', 'HAVING', 'WITH', "';'", 'AND', 'LIMIT', 'OFFSET', 'OR', context=_context) == 'ADD_OP':
             ADD_OP = self._scan('ADD_OP', context=_context)
-            muled_expr = self.muled_expr(V, _context)
-            lhs = MathExpression( ADD_OP, lhs, muled_expr )
-        return lhs
+            expr_mul = self.expr_mul(S, _context)
+            node = MathExpression( ADD_OP, node, expr_mul )
+        return node
 
-    def muled_expr(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'muled_expr', [V])
-        base_expr = self.base_expr(V, _context)
-        lhs = base_expr
-        while self._peek('MUL_OP', 'ADD_OP', "','", 'r"\\)"', 'CMP_OP', 'SORT_DESC', 'SORT_ASC', 'FROM', 'QMARK', 'WHERE', 'GROUPBY', 'HAVING', "';'", 'LIMIT', 'OFFSET', 'AND', 'UNION', 'OR', 'ORDERBY', context=_context) == 'MUL_OP':
+    def expr_mul(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'expr_mul', [S])
+        expr_base = self.expr_base(S, _context)
+        node = expr_base
+        while self._peek('MUL_OP', 'ADD_OP', 'r"\\)"', "','", 'CMP_OP', 'SORT_DESC', 'SORT_ASC', 'GROUPBY', 'QMARK', 'ORDERBY', 'WHERE', 'HAVING', 'WITH', "';'", 'AND', 'LIMIT', 'OFFSET', 'OR', context=_context) == 'MUL_OP':
             MUL_OP = self._scan('MUL_OP', context=_context)
-            base_expr = self.base_expr(V, _context)
-            lhs = MathExpression( MUL_OP, lhs, base_expr)
-        return lhs
+            expr_base = self.expr_base(S, _context)
+            node = MathExpression( MUL_OP, node, expr_base)
+        return node
 
-    def base_expr(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'base_expr', [V])
+    def expr_base(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'expr_base', [S])
         _token = self._peek('r"\\("', 'NULL', 'DATE', 'DATETIME', 'TRUE', 'FALSE', 'FLOAT', 'INT', 'STRING', 'SUBSTITUTE', 'VARIABLE', 'E_TYPE', 'FUNCTION', context=_context)
         if _token not in ['r"\\("', 'VARIABLE', 'E_TYPE', 'FUNCTION']:
             const = self.const(_context)
             return const
         elif _token == 'VARIABLE':
-            var = self.var(V, _context)
+            var = self.var(S, _context)
             return var
         elif _token == 'E_TYPE':
-            etype = self.etype(V, _context)
+            etype = self.etype(S, _context)
             return etype
         elif _token == 'FUNCTION':
-            func = self.func(V, _context)
+            func = self.func(S, _context)
             return func
         else: # == 'r"\\("'
             self._scan('r"\\("', context=_context)
-            added_expr = self.added_expr(V, _context)
+            expr_add = self.expr_add(S, _context)
             self._scan('r"\\)"', context=_context)
-            return added_expr
+            return expr_add
 
-    def func(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'func', [V])
+    def func(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'func', [S])
         FUNCTION = self._scan('FUNCTION', context=_context)
         self._scan('r"\\("', context=_context)
         F = Function(FUNCTION)
-        added_expr = self.added_expr(V, _context)
-        while self._peek("','", 'r"\\)"', 'CMP_OP', 'SORT_DESC', 'SORT_ASC', 'FROM', 'QMARK', 'WHERE', 'GROUPBY', 'HAVING', "';'", 'LIMIT', 'OFFSET', 'AND', 'UNION', 'OR', 'ORDERBY', context=_context) == "','":
-            F.append(added_expr)
+        expr_add = self.expr_add(S, _context)
+        while self._peek("','", 'r"\\)"', 'CMP_OP', 'SORT_DESC', 'SORT_ASC', 'GROUPBY', 'QMARK', 'ORDERBY', 'WHERE', 'HAVING', 'WITH', "';'", 'AND', 'LIMIT', 'OFFSET', 'OR', context=_context) == "','":
+            F.append(expr_add)
             self._scan("','", context=_context)
-            added_expr = self.added_expr(V, _context)
-        F.append(added_expr)
+            expr_add = self.expr_add(S, _context)
+        F.append(expr_add)
         self._scan('r"\\)"', context=_context)
         return F
 
-    def var(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'var', [V])
+    def var(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'var', [S])
         VARIABLE = self._scan('VARIABLE', context=_context)
-        return VariableRef(V.get_variable(VARIABLE))
+        return VariableRef(S.get_variable(VARIABLE))
 
-    def etype(self, V, _parent=None):
-        _context = self.Context(_parent, self._scanner, 'etype', [V])
+    def etype(self, S, _parent=None):
+        _context = self.Context(_parent, self._scanner, 'etype', [S])
         E_TYPE = self._scan('E_TYPE', context=_context)
-        return V.get_etype(E_TYPE)
+        return S.get_etype(E_TYPE)
 
     def const(self, _parent=None):
         _context = self.Context(_parent, self._scanner, 'const', [])
diff --git a/stcheck.py b/stcheck.py
--- a/stcheck.py
+++ b/stcheck.py
@@ -8,11 +8,13 @@
 
 from logilab.common.compat import any
 
-from rql import nodes
 from rql._exceptions import BadRQLQuery
 from rql.utils import function_description
+from rql.nodes import (VariableRef, Constant, Not, Exists, Function,
+                       Variable, variable_refs)
 from rql.stmts import Union
 
+
 class GoTo(Exception):
     """exception used to control the visit of the tree"""
     def __init__(self, node):
@@ -45,19 +47,17 @@
         #        result.append(term.eval(kwargs))
             
     def _visit(self, node, errors):
-        skipfurther = None
         try:
             node.accept(self, errors)
         except GoTo, ex:
-            skipfurther = True
             self._visit(ex.node, errors)
-        if skipfurther is None:
+        else:
             for c in node.children:
                 self._visit(c, errors)
             node.leave(self, errors)
             
     def _visit_selectedterm(self, node, errors):
-        for i, term in enumerate(node.selected_terms()):
+        for i, term in enumerate(node.selection):
             # selected terms are not included by the default visit,
             # accept manually each of them
             self._visit(term, errors)
@@ -77,50 +77,64 @@
     # statement nodes #########################################################
 
     def visit_union(self, node, errors):
-        nbselected = len(node.children[0].selected)
+        nbselected = len(node.children[0].selection)
         for select in node.children[1:]:
-            if not len(select.selected) == nbselected:
+            if not len(select.selection) == nbselected:
                 errors.append('when using union, all subqueries should have '
-                              'the same number of selected terms')
-        if node.sortterms:
-            self._visit(node.sortterms, errors)
-            
+                              'the same number of selected terms')            
     def leave_union(self, node, errors):
         pass
     
     def visit_select(self, node, errors):
         self._visit_selectedterm(node, errors)
-        #XXX from should be added to children, no ?
-        for subquery in node.from_:
-            self.visit_union(subquery, errors)
-            
-    def leave_select(self, selection, errors):
-        assert len(selection.children) <= 4
-        selected = selection.selected
+#         #XXX from should be added to children, no ?
+#         for subquery in node.from_:
+#             self.visit_union(subquery, errors)
+#         if node.sortterms:
+#             self._visit(node.sortterms, errors)            
+    def leave_select(self, node, errors):
+        selected = node.selection
         # check selected variable are used in restriction
-        if selection.get_restriction() is not None or len(selected) > 1:
+        if node.where is not None or len(selected) > 1:
             for term in selected:
                 self._check_selected(term, 'selection', errors)
+        if node.groupby:
+            # check that selected variables are used in groups
+            for var in node.selection:
+                if isinstance(var, VariableRef) and not var in node.groupby:
+                    errors.append('variable %s should be grouped' % var)
+            for group in node.groupby:
+                self._check_selected(group, 'group', errors)
+#         # check that variables referenced in the given term are selected
+#         for term in node.orderby:
+#             for vref in term.iget_nodes(VariableRef):
+#                 # no stinfo yet, use references
+#                 try:
+#                     for ovref in node.defined_vars[vref.name].references():
+#                         rel = ovref.relation()
+#                         if rel is not None:
+#                             break
+#                     else:
+#                         msg = 'variable %s used in %s is not referenced by %s'
+#                         errors.append(msg % (vref.name, termtype, node.as_string()))
+#                 except KeyError:
+#                     msg = 'variable %s used in %s is not referenced by %s'
+#                     errors.append(msg % (vref.name, termtype, node.as_string()))
 
     def visit_insert(self, insert, errors):
         self._visit_selectedterm(insert, errors)
-        assert len(insert.children) <= 1
-
     def leave_insert(self, node, errors):
         pass
 
     def visit_delete(self, delete, errors):
         self._visit_selectedterm(delete, errors)
-        assert len(delete.children) <= 1
-
     def leave_delete(self, node, errors):
         pass
         
-    def visit_update(self, update, errors):
+    def visit_set(self, update, errors):
         self._visit_selectedterm(update, errors)
-        assert len(update.children) <= 1        
-        
-    def leave_update(self, node, errors):
+        assert len(update.children) <= 1                
+    def leave_set(self, node, errors):
         pass                
 
     # tree nodes ##############################################################
@@ -129,50 +143,25 @@
         pass
     def leave_exists(self, node, errors):
         pass
-        
-    def visit_group(self, group, errors):
-        """check that selected variables are used in groups """
-        # XXX that's not necessarily true, for instance:
-        #     Any X, P, MAX(R) WHERE X content_for F, F path P, X revision R GROUPBY P
-        for var in group.stmt.selected:
-            if isinstance(var, nodes.VariableRef) and not var in group.children:
-                errors.append('variable %s should be grouped' % var)
-        self._check_selected(group, 'group', errors)
-                
-    def _check_union_selected(self, term, termtype, errors):
-        """check that variables referenced in the given term are selected"""
-        union = term.root
-        for vref in term.iget_nodes(nodes.VariableRef):
-            # no stinfo yet, use references
-            for select in union.children:
-                try:
-                    for ovref in select.defined_vars[vref.name].references():
-                        rel = ovref.relation()
-                        if rel is not None:
-                            break
-                    else:
-                        msg = 'variable %s used in %s is not referenced by subquery %s'
-                        errors.append(msg % (vref.name, termtype, select.as_string()))
-                except KeyError:
-                    msg = 'variable %s used in %s is not referenced by subquery %s'
-                    errors.append(msg % (vref.name, termtype, select.as_string()))
-                    
-    def visit_having(self, node, errors):
+    
+    def visit_subquery(self, node, errors):
         pass
-                
-    def visit_sort(self, sort, errors):
-        """check that variables used in sort are selected on DISTINCT query"""
-        self._check_union_selected(sort, 'sort', errors)
+    def leave_subquery(self, node, errors):
+        pass 
     
     def visit_sortterm(self, sortterm, errors):
         term = sortterm.term
-        if isinstance(term, nodes.Constant):
+        if isinstance(term, Constant):
             for select in sortterm.root.children:
-                if len(select.selected) < term.value:
+                if len(select.selection) < term.value:
                     errors.append('order column out of bound %s' % term.value)
+    def leave_sortterm(self, node, errors):
+        pass
     
     def visit_and(self, et, errors):
         assert len(et.children) == 2, len(et.children)
+    def leave_and(self, node, errors):
+        pass
         
     def visit_or(self, ou, errors):
         assert len(ou.children) == 2, len(ou.children)
@@ -190,11 +179,13 @@
                 if (lhs1.variable is rhs2.variable and
                     rhs1.variable is lhs2.variable):
                     ou.parent.replace(ou, r1)
-                    for vref in r2.get_nodes(nodes.VariableRef):
+                    for vref in r2.get_nodes(VariableRef):
                         vref.unregister_reference()
                     raise GoTo(r1)
             except AttributeError:
                 pass
+    def leave_or(self, node, errors):
+        pass
 
     def visit_not(self, not_, errors):
         pass
@@ -208,26 +199,29 @@
         # special case "X identity Y"
         if relation.r_type == 'identity':
             lhs, rhs = relation.children
-            assert not isinstance(relation.parent, nodes.Not)
+            assert not isinstance(relation.parent, Not)
             assert rhs.operator == '='
         # special case "C is NULL"
         elif relation.r_type == 'is' and relation.children[1].operator == 'IS':
             lhs, rhs = relation.children
-            assert isinstance(lhs, nodes.VariableRef), lhs
-            assert isinstance(rhs.children[0], nodes.Constant)
+            assert isinstance(lhs, VariableRef), lhs
+            assert isinstance(rhs.children[0], Constant)
             assert rhs.operator == 'IS', rhs.operator
             assert rhs.children[0].type == None
-    
     def leave_relation(self, relation, errors):
         pass
-        #assert isinstance(lhs, nodes.VariableRef), '%s: %s' % (lhs.__class__,
+        #assert isinstance(lhs, VariableRef), '%s: %s' % (lhs.__class__,
         #                                                       relation)
         
     def visit_comparison(self, comparison, errors):
         assert len(comparison.children) in (1,2), len(comparison.children)
+    def leave_comparison(self, node, errors):
+        pass
     
     def visit_mathexpression(self, mathexpr, errors):
         assert len(mathexpr.children) == 2, len(mathexpr.children)
+    def leave_mathexpression(self, node, errors):
+        pass
         
     def visit_function(self, function, errors):
         try:
@@ -240,7 +234,7 @@
             except BadRQLQuery, ex:
                 errors.append(str(ex))
             if funcdescr.aggregat:
-                if isinstance(function.children[0], nodes.Function) and \
+                if isinstance(function.children[0], Function) and \
                        function.children[0].descr().aggregat:
                     errors.append('can\'t nest aggregat functions')
             if funcdescr.name == 'IN':
@@ -250,6 +244,8 @@
                     function.parent.remove(function)
                 else:
                     assert len(function.children) >= 1
+    def leave_function(self, node, errors):
+        pass
 
     def visit_variableref(self, variableref, errors):
         assert len(variableref.children)==0
@@ -270,30 +266,7 @@
     def leave_constant(self, node, errors):
         pass 
 
-    def leave_comparison(self, node, errors):
-        pass
-    def leave_mathexpression(self, node, errors):
-        pass
-    def leave_or(self, node, errors):
-        pass
-    def leave_and(self, node, errors):
-        pass
-    def leave_sortterm(self, node, errors):
-        pass
-    def leave_function(self, node, errors):
-        pass
-    def leave_group(self, node, errors):
-        pass
-    def leave_having(self, node, errors):
-        pass
-    def leave_sort(self, node, errors):
-        pass
 
-        
-def variable_refs(node):
-    for vref in node.iget_nodes(nodes.VariableRef):
-        if isinstance(vref.variable, nodes.Variable):
-            yield vref
             
 class RQLSTAnnotator(object):
     """ annotate RQL syntax tree to ease further code generation from it.
@@ -308,21 +281,21 @@
 
     def annotate(self, node):
         #assert not node.annotated
-        if isinstance(node, Union):
-            self._annotate_union(node)
-        else:
-            self._annotate_stmt(node)
-        node.annotated = True
+        node.accept(self)
+        #node.annotated = True
 
-    def _annotate_union(self, node):
+    visit_insert = visit_delete = visit_set = lambda s,n: None
+    
+    def visit_union(self, node):
         for select in node.children:
-            for subquery in select.from_:
-                self._annotate_union(subquery)
-            self._annotate_stmt(select)
+            self.visit_select(select)
             
-    def _annotate_stmt(self, node):
-        for i, term in enumerate(node.selected_terms()):
-            for func in term.iget_nodes(nodes.Function):
+    def visit_select(self, node):
+        if node.with_ is not None:
+            for subquery in node.with_:
+                self.visit_union(subquery.query)
+        for i, term in enumerate(node.selection):
+            for func in term.iget_nodes(Function):
                 if func.descr().aggregat:
                     node.has_aggregat = True
                     break
@@ -330,10 +303,9 @@
             for vref in variable_refs(term):
                 vref.variable.stinfo['selected'].add(i)
                 vref.variable.set_scope(node)
-        restr = node.get_restriction()
-        if restr is not None:
-            restr.accept(self, node)
-        
+        if node.where is not None:
+            node.where.accept(self, node)
+            
     def rewrite_shared_optional(self, exists, var):
         """if variable is shared across multiple scopes, need some tree
         rewriting
@@ -345,7 +317,7 @@
                 if vref.scope is exists:
                     rel = vref.relation()
                     vref.unregister_reference()
-                    newvref = nodes.VariableRef(newvar)
+                    newvref = VariableRef(newvar)
                     vref.parent.replace(vref, newvref)
                     # update stinfo structure which may have already been
                     # partially processed
@@ -396,7 +368,7 @@
         lhs, rhs = relation.get_parts()
         # may be a constant once rqlst has been simplified
         lhsvar = getattr(lhs, 'variable', None)
-        if not isinstance(lhsvar, nodes.Variable):
+        if not isinstance(lhsvar, Variable):
             lhsvar = None
         if relation.is_types_restriction():
             assert rhs.operator == '='
@@ -406,7 +378,7 @@
             return
         if relation.optional is not None:
             exists = relation.scope
-            if not isinstance(exists, nodes.Exists):
+            if not isinstance(exists, Exists):
                 exists = None
             if lhsvar is not None:
                 if exists is not None:
@@ -449,8 +421,8 @@
                 if key == 'uidrels':
                     constnode = relation.get_variable_parts()[1]
                     if not (relation.operator() != '=' or
-                            isinstance(relation.parent, nodes.Not)):
-                        if isinstance(constnode, nodes.Constant):
+                            isinstance(relation.parent, Not)):
+                        if isinstance(constnode, Constant):
                             lhsvar.stinfo['constnode'] = constnode
                         lhsvar.stinfo.setdefault(key, set()).add(relation)
                 else:
@@ -470,6 +442,6 @@
     var.stinfo['attrvars'].add( (lhsvar, relation.r_type) )
     # give priority to variable which is not in an EXISTS as
     # "main" attribute variable
-    if var.stinfo['attrvar'] is None or not isinstance(relation.scope, nodes.Exists):
+    if var.stinfo['attrvar'] is None or not isinstance(relation.scope, Exists):
         var.stinfo['attrvar'] = lhsvar or lhs
     
diff --git a/stmts.py b/stmts.py
--- a/stmts.py
+++ b/stmts.py
@@ -13,8 +13,8 @@
 from logilab.common.decorators import cached
 
 from rql import BadRQLQuery, CoercionError, nodes
-from rql.base import Node
-from rql.utils import rqlvar_maker
+from rql.base import BaseNode, Node
+from rql.utils import rqlvar_maker, build_visitor_stub
 
             
 def _check_references(defined, varrefs):
@@ -23,38 +23,30 @@
         for vref in var.references():
             # be careful, Variable and VariableRef define __cmp__
             if not [v for v in varrefs if v is vref]:
-                raise AssertionError('buggy reference %r in %r (actual var: %r)' %
-                                     (varref, self, var))
+                raise AssertionError('vref %r is not in the tree' % vref)
             refs[id(vref)] = 1
     for vref in varrefs:
         if not refs.has_key(id(vref)):
-            raise AssertionError('unreferenced varref %r' % vref)
+            raise AssertionError('vref %r is not referenced' % vref)
     return True
 
 
-class Statement(nodes.EditableMixIn, Node):
-    """base class for statement nodes"""
-
-    # default values for optional instance attributes, set on the instance when
-    # used
+class ScopeNode(BaseNode):
     solutions = None # list of possibles solutions for used variables
-    schema = None    # ISchema
     _varmaker = None # variable names generator, built when necessary
+    where = None     # where clause node
     
     def __init__(self):
-        Node.__init__(self)
         # dictionnary of defined variables in the original RQL syntax tree
         self.defined_vars = {}
-        # syntax tree meta-information
-        self.stinfo = {}
+        
+    def get_selected_variables(self):
+        return self.selected_terms()
         
-    def __str__(self):
-        return self.as_string(None, {})
-
-    def as_string(self, encoding=None, kwargs=None):
-        """return the tree as an encoded rql string"""
-        raise NotImplementedError()
-    
+    def set_where(self, node):
+        self.where = node
+        node.parent = self
+        
     def copy(self, copy_solutions=True, solutions=None):
         new = self.__class__()
         if self.schema is not None:
@@ -63,47 +55,8 @@
             new.solutions = solutions
         elif copy_solutions and self.solutions is not None:
             new.solutions = deepcopy(self.solutions)
-        for child in self.children:
-            new.append(child.copy(new))
         return new
-        
-    # navigation helper methods #############################################
-    
-    @property 
-    def root(self):
-        """return the root node of the tree"""
-        return self
-        
-    @property
-    def stmt(self):
-        return self
 
-    @property
-    def scope(self):
-        return self
-    
-    def ored_rel(self, _fromnode=None):
-        return None
-    def neged_rel(self, _fromnode=None):
-        return None
-        
-    def get_selected_variables(self):
-        return self.selected_terms()
-        
-    def get_restriction(self):
-        """return all the subtree with restriction clauses. That maybe a Or,
-        And, or Relation instance.
-        return None if there is no restriction clauses.
-        """
-        for c in self.children:
-            if not isinstance(c, nodes.Group) and not isinstance(c, nodes.Sort):
-                return c
-            break
-        return None
-    
-    def selected_terms(self):
-        raise NotImplementedError()
-    
     # construction helper methods #############################################
     
     def get_etype(self, name):
@@ -150,17 +103,50 @@
             for solution in solutions:
                 var.stinfo['possibletypes'].add(solution[var.name])
 
+        
+class Statement(object):
+    """base class for statement nodes"""
+
+    # default values for optional instance attributes, set on the instance when
+    # used
+    schema = None    # ISchema
+    
+#     def __init__(self):
+#         Node.__init__(self)
+#         # syntax tree meta-information
+#         self.stinfo = {}
+
+    # navigation helper methods #############################################
+    
+    @property 
+    def root(self):
+        """return the root node of the tree"""
+        return self
+        
+    @property
+    def stmt(self):
+        return self
+
+    @property
+    def scope(self):
+        return self
+    
+    def ored_rel(self, _fromnode=None):
+        return None
+    def neged_rel(self, _fromnode=None):
+        return None
+
     def check_references(self):
         """test function"""
         varrefs = self.get_nodes(nodes.VariableRef)
-        varrefs += self.get_selected_variables()
-        for n in getattr(self, 'main_relations', ()):
-            varrefs += n.iget_nodes(nodes.VariableRef)
-        return _check_references(self.defined_vars, varrefs)
+        try:
+            return _check_references(self.defined_vars, varrefs)
+        except:
+            print repr(self)
+            raise
 
 
-
-class Union(Statement):
+class Union(Statement, Node):
     """the select node is the root of the syntax tree for selection statement
     using UNION
     """
@@ -171,18 +157,26 @@
     memorizing = 0   # recoverable modification attributes
 
     def __init__(self):
-        Statement.__init__(self)
+        Node.__init__(self)
         # limit / offset
         self.limit = None
         self.offset = 0
-        # for sort variables
-        self.sortterms = None
+            
+    @property 
+    def root(self):
+        """return the root node of the tree"""
+        if self.parent is None:
+            return self
+        return self.parent.root
+    
+    def get_description(self):
+        return [c.get_description() for c in self.children]
 
+    # repr / as_string / copy #################################################
+    
     def __repr__(self):
         s = [repr(select) for select in self.children]
         s = ['\nUNION\n'.join(s)]
-        if self.sortterms is not None:
-            s.append(repr(self.sortterms))
         if self.limit is not None:
             s.append('LIMIT %s' % self.limit)
         if self.offset:
@@ -193,8 +187,6 @@
         """return the tree as an encoded rql string"""
         s = [select.as_string(encoding, kwargs) for select in self.children]
         s = [' UNION '.join(s)]
-        if self.sortterms is not None:
-            s.append(self.sortterms.as_string(encoding, kwargs))
         if self.limit is not None:
             s.append('LIMIT %s' % self.limit)
         if self.offset:
@@ -207,22 +199,12 @@
             for child in self.children:
                 new.append(child.copy())
                 assert new.children[-1].parent is new
-        if self.sortterms is not None:
-            new.set_sortterms(self.sortterms.copy(new))
         new.limit = self.limit
         new.offset = self.offset
         return new
-    
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_union(self, *args, **kwargs)
+
+    # union specific methods ##################################################
     
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_union(self, *args, **kwargs)
-    
-    def set_sortterms(self, node):
-        self.sortterms = node
-        node.parent = self
-
     def set_limit(self, limit):
         if limit is not None and (not isinstance(limit, (int, long)) or limit <= 0):
             raise BadRQLQuery('bad limit %s' % limit)
@@ -238,65 +220,6 @@
             from rql.undo import SetOffsetOperation
             self.undo_manager.add_operation(SetOffsetOperation(self.offset))
         self.offset = offset
-        
-    def set_possible_types(self, solutions):
-        raise RuntimeError('Union has no solutions')
-            
-    @property 
-    def root(self):
-        """return the root node of the tree"""
-        if self.parent is None:
-            return self
-        return self.parent.root
-    
-    # access to select statements property, which in certain condition
-    # should have homogeneous values (don't use this in other cases)
-    def get_restriction(self):
-        raise ValueError('Union has no restriction')
-    
-    def get_description(self):
-        return [c.get_description() for c in self.children]
-
-#     @property
-#     @cached
-#     def groups(self):
-#         """return a list of grouped variables (i.e a Group object) or None if
-#         there is no grouped variable.
-#         """
-#         groups = self.children[0].get_groups()
-#         if groups is None:
-#             for c in self.children[1:]:
-#                 if c.get_groups() is not None:
-#                     raise BadRQLQuery('inconsistent groups among subqueries')
-#         else:
-#             for c in self.children[1:]:
-#                 if not groups.is_equivalent(c.get_groups()):
-#                     raise BadRQLQuery('inconsistent groups among subqueries')
-#         return groups
-
-#     @cached
-#     def selected_terms(self):
-#         selected = self.children[0].selected_terms()
-#         for c in self.children[1:]:
-#             cselected = c.selected_terms()
-#             for i, term in enumerate(selected):
-#                 if not term.is_equivalent(cselected[i]):
-#                     raise BadRQLQuery('inconsistent selection among subqueries')
-#         return selected
-        
-#     @property
-#     def selected(self):
-#         # consistency check done by selected_terms
-#         return self.children[0].selected
-        
-#     @property
-#     @cached
-#     def distinct(self):
-#         distinct = self.children[0].distinct
-#         for c in self.children[1:]:
-#             if c.distinct != distinct:
-#                 raise BadRQLQuery('inconsistent distinct among subqueries')
-#         return distinct
 
     # recoverable modification methods ########################################
     
@@ -306,6 +229,10 @@
         from rql.undo import SelectionManager
         return SelectionManager(self)
 
+    @property
+    def should_register_op(self):
+        return self.memorizing and not self.undoing
+
     def save_state(self):
         """save the current tree"""
         self.undo_manager.push_state()
@@ -317,104 +244,97 @@
         assert self.memorizing >= 0
         self.undo_manager.recover()    
 
-    def add_sort_var(self, var, asc=True):
-        """add var in 'orderby' constraints
-        asc is a boolean indicating the sort order (ascendent or descendent)
-        """
-        var = nodes.variable_ref(var)
-        var.register_reference()
-        term = nodes.SortTerm(var, asc)
-        self.add_sort_term(term)
-        
-    def add_sort_term(self, term):
-        if self.sortterms is None:
-            self.set_sortterms(nodes.Sort())
-        self.sortterms.append(term)
-        if self.should_register_op:
-            from rql.undo import AddSortOperation
-            self.undo_manager.add_operation(AddSortOperation(term))
-
-    def remove_sort_terms(self):
-        if self.sortterms is not None:
-            for term in self.sortterms.children:
-                self.remove_sort_term(term)
-        
-    def remove_sort_term(self, term):
-        """remove a sort term and the sort node if necessary"""
-        if self.should_register_op:
-            from rql.undo import RemoveSortOperation
-            self.undo_manager.add_operation(RemoveSortOperation(term))        
-        self.remove_node(term)        
-        if not self.sortterms.children:
-            self.sortterms = None
-
     def check_references(self):
         """test function"""
         for select in self.children:
             select.check_references()
-        if self.sortterms:
-            varrefs = self.sortterms.get_nodes(nodes.VariableRef)
-            _check_references(self.defined_vars, varrefs)
         return True
 
 
-class Select(Statement):
+class Select(Statement, nodes.EditableMixIn, ScopeNode):
     """the select node is the base statement of the syntax tree for selection
     statement, always child of a UNION root.
     """
-
+    parent = None
+    distinct = False
+    # select clauses
+    groupby = ()
+    orderby = ()
+    having = ()
+    with_ = ()
+    # set by the annotator
+    has_aggregat = False
+    
     def __init__(self):
         Statement.__init__(self)
-        # distinct ?
-        self.distinct = False
-        # list of selected relations (maybe variables or functions)
-        self.selected = []
+        ScopeNode.__init__(self)
+        self.selection = []
         # subqueries alias
         self.aliases = {}
-        self.from_ = []
-        # set by the annotator
-        self.has_aggregat = False
         # syntax tree meta-information
-        self.stinfo['rewritten'] = {}
+        self.stinfo = {'rewritten': {}}
 
+    @property 
+    def root(self):
+        """return the root node of the tree"""
+        return self.parent
+                    
+    def get_description(self):
+        """return the list of types or relations (if not found) associated to
+        selected variables
+        """
+        descr = []
+        for term in self.selection:
+            try:
+                descr.append(term.get_description())
+            except CoercionError:
+                descr.append('Any')
+        return descr
+
+    @property
+    def children(self):
+        children = self.selection[:]
+        if self.groupby:
+            children += self.groupby
+        if self.orderby:
+            children += self.orderby
+        if self.where:
+            children.append(self.where)
+        if self.having:
+            children += self.having
+        if self.with_:
+            children += self.with_
+        return children
+
+    # repr / as_string / copy #################################################
+    
     def __repr__(self):
-        if self.distinct:
-            base = 'DISTINCT Any'
-        else:
-            base = 'Any'
-        s = ['%s %s WHERE' % (base,
-                              ','.join([repr(v) for v in self.selected]))]
-        for child in self.children:
-            s.append(repr(child))
-        return '\n'.join(s)
+        return self.as_string(userepr=True)
     
-    def as_string(self, encoding=None, kwargs=None):
+    def as_string(self, encoding=None, kwargs=None, userepr=False):
         """return the tree as an encoded rql string"""
-        if self.distinct:
-            base = 'DISTINCT Any'
+        if userepr:
+            as_string = repr
         else:
-            base = 'Any'
-        s = ['%s %s' % (base, ','.join(v.as_string(encoding, kwargs)
-                                       for v in self.selected))]
-        if self.from_:
-            s.append('FROM')
-            for subquery in self.from_:
-                s.append('(%s) AS' % subquery.as_string(encoding, kwargs))
-                aliases = self.subquery_aliases(subquery)
-                if len(aliases) == 1:
-                    s.append(aliases[0].name)
-                else:
-                    s.append('(%s)' % ','.join(ca.name for ca in aliases))
-        r = self.get_restriction()
-        if r is not None:
-            s.append('WHERE %s' % r.as_string(encoding, kwargs))
-        groups = self.groups
-        if groups is not None:
-            s.append(groups.as_string(encoding, kwargs))
-        having = self.having
-        if having is not None:
-            s.append(having.as_string(encoding, kwargs))
-        return ' '.join(s)
+            as_string = lambda x: x.as_string(encoding, kwargs)
+        s = [','.join(as_string(term) for term in self.selection)]
+        if self.groupby:
+            s.append('GROUPBY ' + ','.join(as_string(term)
+                                           for term in self.groupby))
+        if self.orderby:
+            s.append('ORDERBY ' + ','.join(as_string(term)
+                                           for term in self.orderby))
+        if self.where:
+            s.append('WHERE ' + as_string(self.where))
+        if self.having:
+            s.append('HAVING ' + ','.join(as_string(term)
+                                           for term in self.having))
+        if self.with_:
+            s.append('WITH ' + ','.join(as_string(term)
+                                        for term in self.with_))
+        if self.distinct:
+            return 'DISTINCT Any ' + ' '.join(s)
+        return 'Any ' + ' '.join(s)
                                       
     def copy(self, copy_solutions=True, solutions=None):
         new = Select()
@@ -422,20 +342,73 @@
             new.solutions = solutions
         elif copy_solutions and self.solutions is not None:
             new.solutions = deepcopy(self.solutions)
-        for child in self.from_: # copy subqueries first
-            new.add_subquery(child.copy(), [ca.name for ca in self.subquery_aliases(child)])
-        for child in self.children:
-            new.append(child.copy(new))
-        for child in self.selected:
+        if self.with_:
+            new.set_with([sq.copy(new) for sq in self.with_])
+        for child in self.selection:
             new.append_selected(child.copy(new))
+        if self.groupby:
+            new.set_groupby([sq.copy(new) for sq in self.groupby])
+        if self.orderby:
+            new.set_orderby([sq.copy(new) for sq in self.orderby])
+        if self.where:
+            new.set_where(self.where.copy(new))
+        if self.having:
+            new.set_having([sq.copy(new) for sq in self.having])
         new.distinct = self.distinct
         return new
     
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_select(self, *args, **kwargs)
+    # select specific methods #################################################
+
+    def set_statement_type(self, etype):
+        """set the statement type for this selection
+        this method must be called last (i.e. once selected variables has been
+        added)
+        """
+        assert self.selection
+        # Person P  ->  Any P where P is Person
+        if etype != 'Any':
+            for var in self.get_selected_variables():
+                self.add_type_restriction(var.variable, etype)
+    
+    def set_distinct(self, value):
+        """mark DISTINCT query"""
+        if self.should_register_op and value != self.distinct:
+            from rql.undo import SetDistinctOperation
+            self.undo_manager.add_operation(SetDistinctOperation(self.distinct, self))
+        self.distinct = value
     
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_select(self, *args, **kwargs)
+    def set_orderby(self, terms):
+        self.orderby = terms
+        for node in terms:
+            node.parent = self
+
+    def set_groupby(self, terms):
+        self.groupby = terms
+        for node in terms:
+            node.parent = self
+
+    def set_having(self, terms):
+        self.having = terms
+        for node in terms:
+            node.parent = self
+            
+    def set_with(self, terms, check=True):
+        self.with_ = terms
+        for node in terms:
+            node.parent = self
+            if check and len(node.aliases) != len(node.query.children[0].selection):
+                raise BadRQLQuery('Should have the same number of aliases than '
+                                  'selected terms in sub-query')
+            for i, alias in enumerate(node.aliases):
+                alias = alias.name
+                if alias in self.aliases:
+                    raise BadRQLQuery('Duplicated alias %s' % alias)
+                self.aliases[alias] = nodes.ColumnAlias(alias, i, node.query)
+                # alias may already have been used as a regular variable, replace it
+                if alias in self.defined_vars:
+                    for vref in self.defined_vars.pop(alias).references():
+                        vref.variable = self.aliases[alias]
+
 
     def get_variable(self, name):
         """get a variable instance from its name
@@ -468,122 +441,66 @@
     
     # quick accessors #########################################################
 
-    def subquery_aliases(self, subquery):
-        aliases = [ca for ca in self.aliases.itervalues() if ca.query is subquery]
-        aliases.sort(key=lambda x: x.colnum)
-        return aliases
-    
-    @property 
-    def root(self):
-        """return the root node of the tree"""
-        return self.parent
-
-    @property
-    def groups(self):
-        """return a Group node or None if there is no grouped variable"""
-        for c in self.children:
-            if isinstance(c, nodes.Group):
-                return c
-        return None
-    
-    @property
-    def having(self):
-        """return a Having or None if there is no HAVING clause"""
-        for c in self.children:
-            if isinstance(c, nodes.Having):
-                return c
-        return None
-    
+#     def subquery_aliases(self, subquery):
+#         aliases = [ca for ca in self.aliases.itervalues() if ca.query is subquery]
+#         aliases.sort(key=lambda x: x.colnum)
+#         return aliases
+        
     def get_selected_variables(self):
         """returns all selected variables, including those used in aggregate
         functions
         """
-        for term in self.selected_terms():
+        for term in self.selection:
             for node in term.iget_nodes(nodes.VariableRef):
                 yield node
 
-    def selected_terms(self):
-        """returns selected terms"""
-        return self.selected[:]
-        
     # construction helper methods #############################################
 
     def append_selected(self, term):
         if isinstance(term, nodes.Constant) and term.type == 'etype':
             raise BadRQLQuery('Entity type are not allowed in selection')
         term.parent = self
-        self.selected.append(term)
+        self.selection.append(term)
             
     def replace(self, oldnode, newnode):
-        # XXX no vref handling ?
-        try:
-            Statement.replace(self, oldnode, newnode)
-        except ValueError:
-            i = self.selected.index(oldnode)
-            self.selected.pop(i)
-            self.selected.insert(i, newnode)
-            newnode.parent = self
-            
-    def set_statement_type(self, etype):
-        """set the statement type for this selection
-        this method must be called last (i.e. once selected variables has been
-        added)
-        """
-        assert self.selected
-        # Person P  ->  Any P where P is Person
-        if etype != 'Any':
-            for var in self.get_selected_variables():
-                self.add_type_restriction(var.variable, etype)
-
-    def add_subquery(self, union, aliases, check=True):
-        if check and len(aliases) != len(union.children[0].selected):
-            raise BadRQLQuery('Should have the same number of aliases than '
-                              'selected terms in sub-query')
-        self.from_.append(union)
-        union.parent = self
-        for i, alias in enumerate(aliases):
-            if alias in self.aliases:
-                raise BadRQLQuery('Duplicated alias %s' % alias)
-            self.aliases[alias] = nodes.ColumnAlias(alias, i, union)
-            # alias may already have been used as a regular variable, replace it
-            if alias in self.defined_vars:
-                for vref in self.defined_vars.pop(alias).references():
-                    vref.variable = self.aliases[alias]
-                    
-    def get_description(self):
-        """return the list of types or relations (if not found) associated to
-        selected variables
-        """
-        descr = []
-        for term in self.selected:
-            try:
-                descr.append(term.get_description())
-            except CoercionError:
-                descr.append('Any')
-        return descr
-
-    def set_distinct(self, value):
-        """mark DISTINCT query"""
-        if self.should_register_op and value != self.distinct:
-            from rql.undo import SetDistinctOperation
-            self.undo_manager.add_operation(SetDistinctOperation(self.distinct, self))
-        self.distinct = value
-
+        assert oldnode is self.where
+        self.where = newnode
+        newnode.parent = self
+#         # XXX no vref handling ?
+#         try:
+#             Statement.replace(self, oldnode, newnode)
+#         except ValueError:
+#             i = self.selection.index(oldnode)
+#             self.selection.pop(i)
+#             self.selection.insert(i, newnode)
+        
+    def remove(self, node):
+        if node is self.where:
+            self.where = None
+        elif node in self.orderby:
+            self.remove_sort_term(node)
+        elif node in self.groupby:
+            self.remove_group_var(node)
+        else:
+            raise Exception('duh XXX')
+        node.parent = None
+        
     def undefine_variable(self, var):
         """undefine the given variable and remove all relations where it appears"""
         if hasattr(var, 'variable'):
             var = var.variable
         # remove relations where this variable is referenced
-        for varref in var.references():
-            rel = varref.relation()
+        for vref in var.references():
+            rel = vref.relation()
             if rel is not None:
-                self.parent.remove_node(rel)
-            elif isinstance(varref.parent, nodes.SortTerm):
-                self.parent.remove_sort_term(varref.parent)
-            elif isinstance(varref.parent, nodes.Group):
-                self.remove_group_var(varref)
+                self.remove_node(rel)
+            # XXX may have other nodes between vref and the sort term
+            elif isinstance(vref.parent, nodes.SortTerm):
+                self.remove_sort_term(vref.parent)
+            elif vref in self.groupby:
+                self.remove_group_var(vref)
             else: # selected variable
-                self.remove_selected(varref)
+                self.remove_selected(vref)
         # effective undefine operation
         if self.should_register_op:
             from rql.undo import UndefineVarOperation
@@ -594,7 +511,7 @@
         """get variable index in the list using identity (Variable and VariableRef
         define __cmp__
         """
-        for i, term in enumerate(self.selected):
+        for i, term in enumerate(self.selection):
             if term is var:
                 return i
         raise IndexError()
@@ -606,7 +523,7 @@
         if self.should_register_op:
             from rql.undo import UnselectVarOperation
             self.undo_manager.add_operation(UnselectVarOperation(var, index))
-        for vref in self.selected.pop(index).iget_nodes(nodes.VariableRef):
+        for vref in self.selection.pop(index).iget_nodes(nodes.VariableRef):
             vref.unregister_reference()
 
     def add_selected(self, term, index=None):
@@ -619,7 +536,7 @@
                 var = nodes.variable_ref(var)
                 var.register_reference()
         if index is not None:
-            self.selected.insert(index, term)
+            self.selection.insert(index, term)
             term.parent = self
         else:
             self.append_selected(term)
@@ -631,89 +548,91 @@
         """add var in 'orderby' constraints
         asc is a boolean indicating the group order (ascendent or descendent)
         """
-        var = nodes.variable_ref(var)
-        var.register_reference()
-        groups = self.groups
-        if groups is None:
-            groups = nodes.Group()
-            self.append(groups)
-        groups.append(var)
+        if self.groupby is None:
+            self.groupby = []
+        vref = nodes.variable_ref(var)
+        vref.register_reference()
+        self.groupby.append(vref)
+        vref.parent = self
         if self.should_register_op:
             from rql.undo import AddGroupOperation
-            self.undo_manager.add_operation(AddGroupOperation(var))
+            self.undo_manager.add_operation(AddGroupOperation(vref))
 
-    def remove_group_var(self, var):
+    def remove_group_var(self, vref):
         """remove the group variable and the group node if necessary"""
-        groups = self.groups
-        assert var in groups.children
-        if len(groups.children) == 1:
-            self.parent.remove_node(groups)
-        else:
-            self.parent.remove_node(var)
+        vref.unregister_reference()
+        self.groupby.remove(vref)
+        if self.should_register_op:
+            from rql.undo import RemoveGroupOperation
+            self.undo_manager.add_operation(RemoveGroupOperation(vref))
+        if not self.groupby:
+            self.groupby = None
+
+    def add_sort_var(self, var, asc=True):
+        """add var in 'orderby' constraints
+        asc is a boolean indicating the sort order (ascendent or descendent)
+        """
+        vref = nodes.variable_ref(var)
+        vref.register_reference()
+        term = nodes.SortTerm(vref, asc)
+        self.add_sort_term(term)
+        
+    def add_sort_term(self, term):
+        if self.orderby is None:
+            self.orderby = []
+        self.orderby.append(term)
+        term.parent = self
+        for vref in term.iget_nodes(nodes.VariableRef):
+            try:
+                vref.register_reference()
+            except AssertionError:
+                pass # already referenced
+        if self.should_register_op:
+            from rql.undo import AddSortOperation
+            self.undo_manager.add_operation(AddSortOperation(term))
+
+    def remove_sort_terms(self):
+        if self.orderby:
+            for term in self.orderby:
+                self.remove_sort_term(term)
+        
+    def remove_sort_term(self, term):
+        """remove a sort term and the sort node if necessary"""
+        if self.should_register_op:
+            from rql.undo import RemoveSortOperation
+            self.undo_manager.add_operation(RemoveSortOperation(term))
+        for vref in term.iget_nodes(nodes.VariableRef):
+            vref.unregister_reference()
+        self.orderby.remove(term)
+        if not self.orderby:
+            self.orderby = None
 
     def select_only_variables(self):
-        self.selected = [vref for term in self.selected
+        self.selection = [vref for term in self.selection
                          for vref in term.iget_nodes(nodes.VariableRef)]
+
     
-class Delete(Statement):
+class Delete(Statement, ScopeNode):
     """the Delete node is the root of the syntax tree for deletion statement
     """
     TYPE = 'delete'
     
     def __init__(self):
         Statement.__init__(self)
+        ScopeNode.__init__(self)
         self.main_variables = []
         self.main_relations = []
-    
-    def __repr__(self):
-        result = ['DELETE']
-        if self.main_variables:
-            result.append(', '.join(['%r %r' %(etype, var)
-                                     for etype, var in self.main_variables]))
-        if self.main_relations:
-            if self.main_variables:
-                result.append(',')
-            result.append(', '.join([repr(rel) for rel in self.main_relations]))
-        r = self.get_restriction()
-        if r is not None:
-            result.append('WHERE %r' % r)
-        return ' '.join(result)
 
-    def as_string(self, encoding=None, kwargs=None):
-        """return the tree as an encoded rql string"""
-        result = ['DELETE']
-        if self.main_variables:
-            result.append(', '.join(['%s %s' %(etype, var)
-                                     for etype, var in self.main_variables]))
-        if self.main_relations:
-            if self.main_variables:
-                result.append(',')
-            result.append(', '.join([rel.as_string(encoding, kwargs)
-                                     for rel in self.main_relations]))                
-        r = self.get_restriction()
-        if r is not None:
-            result.append('WHERE %s' % r.as_string(encoding, kwargs))
-        return ' '.join(result)
+    @property
+    def children(self):
+        children = self.selection[:]
+        children += self.main_relations
+        if self.where:
+            children.append(self.where)
+        return children
 
-    def copy(self):
-        new = Statement.copy(self)
-        for etype, var in self.main_variables:
-            vref = nodes.VariableRef(new.get_variable(var.name))
-            new.add_main_variable(etype, vref)
-        for child in self.main_relations:
-            new.add_main_relation(child.copy(new))
-        return new
-
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_delete( self, *args, **kwargs )
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_delete( self, *args, **kwargs )
-    
-    def get_selected_variables(self):
-        return self.selected_terms()
-    
-    def selected_terms(self):
+    @property
+    def selection(self):
         return [vref for et, vref in self.main_variables]
     
     def add_main_variable(self, etype, vref):
@@ -730,61 +649,71 @@
         assert isinstance(relation.children[1].children[0], nodes.VariableRef)
         relation.parent = self
         self.main_relations.append( relation )
+    
+    # repr / as_string / copy #################################################
+
+    def __repr__(self):
+        result = ['DELETE']
+        if self.main_variables:
+            result.append(', '.join(['%r %r' %(etype, var)
+                                     for etype, var in self.main_variables]))
+        if self.main_relations:
+            if self.main_variables:
+                result.append(',')
+            result.append(', '.join([repr(rel) for rel in self.main_relations]))
+        if self.where is not None:
+            result.append(repr(self.where))
+        return ' '.join(result)
+
+    def as_string(self, encoding=None, kwargs=None):
+        """return the tree as an encoded rql string"""
+        result = ['DELETE']
+        if self.main_variables:
+            result.append(', '.join(['%s %s' %(etype, var)
+                                     for etype, var in self.main_variables]))
+        if self.main_relations:
+            if self.main_variables:
+                result.append(',')
+            result.append(', '.join([rel.as_string(encoding, kwargs)
+                                     for rel in self.main_relations]))                
+        if self.where is not None:
+            result.append('WHERE ' + self.where.as_string(encoding, kwargs))
+        return ' '.join(result)
+
+    def copy(self):
+        new = Delete()
+        for etype, var in self.main_variables:
+            vref = nodes.VariableRef(new.get_variable(var.name))
+            new.add_main_variable(etype, vref)
+        for child in self.main_relations:
+            new.add_main_relation(child.copy(new))
+        if self.where:
+            new.set_where(self.where.copy(new))
+        return new
 
 
-class Insert(Statement):
+class Insert(Statement, ScopeNode):
     """the Insert node is the root of the syntax tree for insertion statement
     """
     TYPE = 'insert'
     
     def __init__(self):
         Statement.__init__(self)
+        ScopeNode.__init__(self)
         self.main_variables = []
         self.main_relations = []
         self.inserted_variables = {}
-                              
-    def __repr__(self):
-        result = ['INSERT']
-        result.append(', '.join(['%r %r' % (etype, var)
-                                 for etype, var in self.main_variables]))
-        if self.main_relations:
-            result.append(':')
-            result.append(', '.join([repr(rel) for rel in self.main_relations]))
-        restr = self.get_restriction()
-        if restr is not None:
-            result.append('WHERE %r' % restr)
-        return ' '.join(result)
 
-    def as_string(self, encoding=None, kwargs=None):
-        """return the tree as an encoded rql string"""
-        result = ['INSERT']
-        result.append(', '.join(['%s %s' % (etype, var)
-                                 for etype, var in self.main_variables]))
-        if self.main_relations:
-            result.append(':')
-            result.append(', '.join([rel.as_string(encoding, kwargs)
-                                     for rel in self.main_relations]))
-        restr = self.get_restriction()
-        if restr is not None:
-            result.append('WHERE %s' % restr.as_string(encoding, kwargs))
-        return ' '.join(result)
+    @property
+    def children(self):
+        children = self.selection[:]
+        children += self.main_relations
+        if self.where:
+            children.append(self.where)
+        return children
 
-    def copy(self):
-        new = Statement.copy(self)
-        for etype, var in self.main_variables:
-            vref = nodes.VariableRef(new.get_variable(var.name))
-            new.add_main_variable(etype, vref)
-        for child in self.main_relations:
-            new.add_main_relation(child.copy(new))
-        return new
-
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_insert( self, *args, **kwargs )
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_insert( self, *args, **kwargs )
-
-    def selected_terms(self):
+    @property
+    def selection(self):
         return [vref for et, vref in self.main_variables]
         
     def add_main_variable(self, etype, vref):
@@ -806,43 +735,96 @@
                 raise BadRQLQuery(msg % (var, var))
         relation.parent = self
         self.main_relations.append( relation )
+                              
+    # repr / as_string / copy #################################################
 
-        
-class Update(Statement):
-    """the Update node is the root of the syntax tree for update statement
-    """
-    TYPE = 'update'
-
-    def __init__(self):
-        Statement.__init__(self)
-        self.main_relations = []
+    def __repr__(self):
+        result = ['INSERT']
+        result.append(', '.join(['%r %r' % (etype, var)
+                                 for etype, var in self.main_variables]))
+        if self.main_relations:
+            result.append(':')
+            result.append(', '.join([repr(rel) for rel in self.main_relations]))
+        if self.where is not None:
+            result.append('WHERE ' + repr(self.where))
+        return ' '.join(result)
 
     def as_string(self, encoding=None, kwargs=None):
         """return the tree as an encoded rql string"""
-        result = ['SET']
-        result.append(', '.join([rel.as_string(encoding, kwargs)
-                                 for rel in self.main_relations]))
-        r = self.get_restriction()
-        if r is not None:
-            result.append('WHERE %s' % r.as_string(encoding, kwargs))
+        result = ['INSERT']
+        result.append(', '.join(['%s %s' % (etype, var)
+                                 for etype, var in self.main_variables]))
+        if self.main_relations:
+            result.append(':')
+            result.append(', '.join([rel.as_string(encoding, kwargs)
+                                     for rel in self.main_relations]))
+        if self.where is not None:
+            result.append('WHERE ' + self.where.as_string(encoding, kwargs))
         return ' '.join(result)
 
     def copy(self):
-        new = Statement.copy(self)
+        new = Insert()
+        for etype, var in self.main_variables:
+            vref = nodes.VariableRef(new.get_variable(var.name))
+            new.add_main_variable(etype, vref)
         for child in self.main_relations:
             new.add_main_relation(child.copy(new))
+        if self.where:
+            new.set_where(self.where.copy(new))
         return new
 
-    def accept(self, visitor, *args, **kwargs):
-        return visitor.visit_update( self, *args, **kwargs )
-    
-    def leave(self, visitor, *args, **kwargs):
-        return visitor.leave_update( self, *args, **kwargs )
+        
+class Set(Statement, ScopeNode):
+    """the Set node is the root of the syntax tree for update statement
+    """
+    TYPE = 'set'
 
-    def selected_terms(self):
+    def __init__(self):
+        Statement.__init__(self)
+        ScopeNode.__init__(self)
+        self.main_relations = []
+
+    @property
+    def children(self):
+        children = self.main_relations[:]
+        if self.where:
+            children.append(self.where)
+        return children
+
+    @property
+    def selection(self):
         return []
         
     def add_main_relation(self, relation):
         """add a relation to the list of modified relations"""
         relation.parent = self
         self.main_relations.append( relation )
+
+    # repr / as_string / copy #################################################
+
+    def __repr__(self):
+        result = ['SET']
+        result.append(', '.join(repr(rel) for rel in self.main_relations))
+        if self.where is not None:
+            result.append('WHERE ' + repr(self.where))
+        return ' '.join(result)
+
+    def as_string(self, encoding=None, kwargs=None):
+        """return the tree as an encoded rql string"""
+        result = ['SET']
+        result.append(', '.join(rel.as_string(encoding, kwargs)
+                                for rel in self.main_relations))
+        if self.where is not None:
+            result.append('WHERE ' + self.where.as_string(encoding, kwargs))
+        return ' '.join(result)
+
+    def copy(self):
+        new = Set()
+        for child in self.main_relations:
+            new.add_main_relation(child.copy(new))
+        if self.where:
+            new.set_where(self.where.copy(new))
+        return new
+
+
+build_visitor_stub((Union, Select, Insert, Delete, Set))
diff --git a/test/unittest_analyze.py b/test/unittest_analyze.py
--- a/test/unittest_analyze.py
+++ b/test/unittest_analyze.py
@@ -146,6 +146,8 @@
             if DEBUG:
                 print rql
             node = self.helper.parse(rql)
+            print rql
+            print node
             self.assertRaises(TypeResolverException,
                               self.helper.compute_solutions, node, debug=DEBUG)
         
@@ -161,7 +163,7 @@
     def test_base_2(self):
         node = self.helper.parse('Person X')
         # check constant type of the is relation inserted
-        self.assertEqual(node.children[0].get_restriction().children[1].children[0].type,
+        self.assertEqual(node.children[0].where.children[1].children[0].type,
                          'etype')
         self.helper.compute_solutions(node, debug=DEBUG)
         sols = node.children[0].solutions
@@ -256,14 +258,14 @@
         
         
     def test_base_guess_3(self):
-        node = self.helper.parse('Any Z WHERE X name Z GROUPBY Z')
+        node = self.helper.parse('Any Z GROUPBY Z WHERE X name Z')
         self.helper.compute_solutions(node, debug=DEBUG)
         sols = sorted(node.children[0].solutions)
         self.assertEqual(sols, [{'X': 'Company', 'Z': 'String'},
                                  {'X': 'Person', 'Z': 'String'}])
 
     def test_var_name(self):
-        node = self.helper.parse('Any E1 WHERE E2 is Person, E2 name E1 GROUPBY E1')
+        node = self.helper.parse('Any E1 GROUPBY E1 WHERE E2 is Person, E2 name E1')
         self.helper.compute_solutions(node, debug=DEBUG)
         sols = sorted(node.children[0].solutions)
         self.assertEqual(sols, [{'E2': 'Person', 'E1': 'String'}])
@@ -304,7 +306,7 @@
         self.assertEqual(sols, [{'P': 'Person'}])
         
     def test_union(self):
-        node = self.helper.parse('Any P WHERE X eid 0, NOT X connait P UNION Any E1 WHERE E2 work_for E1, E2 eid 2')
+        node = self.helper.parse('(Any P WHERE X eid 0, NOT X connait P) UNION (Any E1 WHERE E2 work_for E1, E2 eid 2)')
         self.helper.compute_solutions(node, debug=DEBUG)
         sols = sorted(node.children[0].solutions)
         self.assertEqual(sols, [{'P': 'Person', 'X': 'Person'}], [{'E1': 'Company', 'E2': 'Person'}])
@@ -322,15 +324,14 @@
                                  'U': 'Person'}])
 
     def test_subqueries(self):
-        node = self.helper.parse('Any L, Y, F '
-                                 'FROM (Any X,F WHERE X is Person, X firstname F '
-                                 'UNION Any X,F WHERE X is Company, X name F) AS (Y,F) '
-                                 'WHERE Y located L;')
+        node = self.helper.parse('Any L, Y, F WHERE Y located L '
+                                 'WITH Y,F BEING ((Any X,F WHERE X is Person, X firstname F) '
+                                 'UNION (Any X,F WHERE X is Company, X name F))')
         self.helper.compute_solutions(node, debug=DEBUG)
         sols = sorted(node.children[0].solutions)
-        self.assertEqual(node.children[0].from_[0].children[0].solutions, [{'X': 'Person',
+        self.assertEqual(node.children[0].with_[0].query.children[0].solutions, [{'X': 'Person',
                                                                             'F': 'String'}])
-        self.assertEqual(node.children[0].from_[0].children[1].solutions, [{'X': 'Company',
+        self.assertEqual(node.children[0].with_[0].query.children[1].solutions, [{'X': 'Company',
                                                                             'F': 'String'}])
         sols = sorted(node.children[0].solutions)
         self.assertEqual(sols, [{'Y': 'Company', 'L': 'Address',
@@ -339,12 +340,12 @@
                                  'F': 'String'}])
 
     def test_subqueries_aggregat(self):
-        node = self.helper.parse('Any L, SUM(X)*100/Y '
-                                 'FROM (Any SUM(X) WHERE X is Person) AS Y '
-                                 'WHERE X is Person, X located L GROUPBY L;')
+        node = self.helper.parse('Any L, SUM(X)*100/Y GROUPBY L '
+                                 'WHERE X is Person, X located L '
+                                 'WITH Y BEING (Any SUM(X) WHERE X is Person)')
         self.helper.compute_solutions(node, debug=DEBUG)
         sols = sorted(node.children[0].solutions)
-        self.assertEqual(node.children[0].from_[0].children[0].solutions, [{'X': 'Person'}])
+        self.assertEqual(node.children[0].with_[0].query.children[0].solutions, [{'X': 'Person'}])
         self.assertEqual(node.children[0].solutions, [{'X': 'Person', 'Y': 'Person',
                                                        'L': 'Address'}])
         
diff --git a/test/unittest_editextensions.py b/test/unittest_editextensions.py
--- a/test/unittest_editextensions.py
+++ b/test/unittest_editextensions.py
@@ -12,7 +12,7 @@
         rqlst.save_state()
         select = rqlst.children[0]
         var = select.make_variable()
-        select.remove_selected(select.selected[0])
+        select.remove_selected(select.selection[0])
         select.add_selected(var)
         # check operations
         self.assertEquals(rqlst.as_string(), 'Any %s WHERE X is Person' % var.name)
@@ -30,7 +30,7 @@
         rqlst.save_state()
         select = rqlst.children[0]
         var = select.make_variable()
-        select.remove_selected(select.selected[0])
+        select.remove_selected(select.selection[0])
         select.add_selected(var)
         # check operations
         self.assertEquals(rqlst.as_string(), 'Any %s WHERE X is Person, X name N' % var.name)
diff --git a/test/unittest_nodes.py b/test/unittest_nodes.py
--- a/test/unittest_nodes.py
+++ b/test/unittest_nodes.py
@@ -84,20 +84,22 @@
         tree.recover()
         self.assertEquals(tree.offset, 0)
 
-    def test_union_add_sort_var(self):
+    def test_select_add_sort_var(self):
         tree = self._parse('Any X')
         tree.save_state()
-        tree.add_sort_var(tree.get_variable('X'))
+        select = tree.children[0]
+        select.add_sort_var(select.get_variable('X'))
         tree.check_references()
         self.assertEquals(tree.as_string(), 'Any X ORDERBY X')
         tree.recover()
         tree.check_references()
         self.assertEquals(tree.as_string(), 'Any X')
 
-    def test_union_remove_sort_terms(self):
+    def test_select_remove_sort_terms(self):
         tree = self._parse('Any X ORDERBY X')
         tree.save_state()
-        tree.remove_sort_terms()
+        select = tree.children[0]
+        select.remove_sort_terms()
         tree.check_references()
         self.assertEquals(tree.as_string(), 'Any X')
         tree.recover()
@@ -134,7 +136,7 @@
         tree = self._parse('Any X GROUPBY X')
         tree.save_state()
         select = tree.children[0]
-        select.remove_group_var(select.groups.children[0])
+        select.remove_group_var(select.groupby[0])
         tree.check_references()
         self.assertEquals(tree.as_string(), 'Any X')
         tree.recover()
@@ -143,97 +145,94 @@
                              
     def test_select_base_1(self):
         tree = self._parse("Any X WHERE X is Person")
-        self.assertRaises(ValueError, tree.get_restriction)
         self.assertIsInstance(tree, stmts.Union)
         self.assertEqual(tree.limit, None)
         self.assertEqual(tree.offset, 0)
         select = tree.children[0]
         self.assertIsInstance(select, stmts.Select)
         self.assertEqual(select.distinct, False)
-        self.assertEqual(len(select.children), 1)
-        self.assertIsInstance(select.children[0], nodes.Relation)
-        self.assert_(select.children[0] is select.get_restriction())
+        self.assertEqual(len(select.children), 2)
+        self.assert_(select.children[0] is select.selection[0])
+        self.assert_(select.children[1] is select.where)
+        self.assertIsInstance(select.where, nodes.Relation)
         
     def test_select_base_2(self):
         tree = self._simpleparse("Any X WHERE X is Person")
-        self.assertEqual(len(tree.children), 1)
+        self.assertEqual(len(tree.children), 2)
         self.assertEqual(tree.distinct, False)
         
     def test_select_distinct(self):
         tree = self._simpleparse("DISTINCT Any X WHERE X is Person")
-        self.assertEqual(len(tree.children), 1)
+        self.assertEqual(len(tree.children), 2)
         self.assertEqual(tree.distinct, True)
         
     def test_select_null(self):
         tree = self._simpleparse("Any X WHERE X name NULL")
-        constant = tree.children[0].children[1].children[0]
+        constant = tree.where.children[1].children[0]
         self.assertEqual(constant.type, None)
         self.assertEqual(constant.value, None)
         
     def test_select_true(self):
         tree = self._simpleparse("Any X WHERE X name TRUE")
-        constant = tree.children[0].children[1].children[0]
+        constant = tree.where.children[1].children[0]
         self.assertEqual(constant.type, 'Boolean')
         self.assertEqual(constant.value, True)
         
     def test_select_false(self):
         tree = self._simpleparse("Any X WHERE X name FALSE")
-        constant = tree.children[0].children[1].children[0]
+        constant = tree.where.children[1].children[0]
         self.assertEqual(constant.type, 'Boolean')
         self.assertEqual(constant.value, False)
         
     def test_select_date(self):
         tree = self._simpleparse("Any X WHERE X born TODAY")
-        constant = tree.children[0].children[1].children[0]
+        constant = tree.where.children[1].children[0]
         self.assertEqual(constant.type, 'Date')
         self.assertEqual(constant.value, 'TODAY')
         
     def test_select_int(self):
         tree = self._simpleparse("Any X WHERE X name 1")
-        constant = tree.children[0].children[1].children[0]
+        constant = tree.where.children[1].children[0]
         self.assertEqual(constant.type, 'Int')
         self.assertEqual(constant.value, 1)
         
     def test_select_float(self):
         tree = self._simpleparse("Any X WHERE X name 1.0")
-        constant = tree.children[0].children[1].children[0]
+        constant = tree.where.children[1].children[0]
         self.assertEqual(constant.type, 'Float')
         self.assertEqual(constant.value, 1.0)
         
     def test_select_group(self):
-        tree = self._simpleparse("Any X WHERE X is Person, X name N GROUPBY N")
+        tree = self._simpleparse("Any X GROUPBY N WHERE X is Person, X name N")
         self.assertEqual(tree.distinct, False)
-        self.assertEqual(len(tree.children), 2)
-        self.assertIsInstance(tree.children[0], nodes.AND)
-        self.assertIsInstance(tree.children[1], nodes.Group)
-        self.assertIsInstance(tree.children[1].children[0], nodes.VariableRef)
-        self.assertEqual(tree.children[1].children[0].name, 'N')
+        self.assertEqual(len(tree.children), 3)
+        self.assertIsInstance(tree.where, nodes.And)
+        self.assertIsInstance(tree.groupby[0], nodes.VariableRef)
+        self.assertEqual(tree.groupby[0].name, 'N')
 
     def test_select_ord_default(self):
-        tree = self._parse("Any X WHERE X is Person, X name N ORDERBY N")
-        self.assertEqual(tree.sortterms.children[0].asc, 1)
+        tree = self._parse("Any X ORDERBY N WHERE X is Person, X name N")
+        self.assertEqual(tree.children[0].orderby[0].asc, 1)
 
     def test_select_ord_desc(self):
-        tree = self._parse("Any X WHERE X is Person, X name N ORDERBY N DESC")
+        tree = self._parse("Any X ORDERBY N DESC WHERE X is Person, X name N")
         select = tree.children[0]
-        self.assertEqual(len(select.children), 1)
-        self.assertIsInstance(select.children[0], nodes.AND)
-        sort = tree.sortterms
-        self.assertIsInstance(sort, nodes.Sort)
-        self.assertIsInstance(sort.children[0], nodes.SortTerm)
-        self.assertEqual(sort.children[0].term.name, 'N')
-        self.assertEqual(sort.children[0].asc, 0)
+        self.assertEqual(len(select.children), 3)
+        self.assertIsInstance(select.where, nodes.And)
+        sort = select.orderby
+        self.assertIsInstance(sort[0], nodes.SortTerm)
+        self.assertEqual(sort[0].term.name, 'N')
+        self.assertEqual(sort[0].asc, 0)
         self.assertEqual(select.distinct, False)
 
     def test_select_group_ord_asc(self):
-        tree = self._parse("Any X WHERE X is Person, X name N GROUPBY N ORDERBY N ASC",
-                           "Any X WHERE X is Person, X name N GROUPBY N ORDERBY N")
+        tree = self._parse("Any X GROUPBY N ORDERBY N ASC WHERE X is Person, X name N",
+                           "Any X GROUPBY N ORDERBY N WHERE X is Person, X name N")
         select = tree.children[0]
-        self.assertEqual(len(select.children), 2)
-        group = select.children[1]
-        self.assertIsInstance(group, nodes.Group)
-        self.assertIsInstance(group.children[0], nodes.VariableRef)
-        self.assertEqual(group.children[0].name, 'N')
+        self.assertEqual(len(select.children), 4)
+        group = select.groupby
+        self.assertIsInstance(group[0], nodes.VariableRef)
+        self.assertEqual(group[0].name, 'N')
 
     def test_select_limit_offset(self):
         tree = self._parse("Any X WHERE X name 1.0 LIMIT 10 OFFSET 10")
@@ -241,17 +240,18 @@
         self.assertEqual(tree.offset, 10)
         
     def test_copy(self):
-        tree = self._parse("Any X,LOWER(Y) WHERE X is Person, X name N, X date >= TODAY GROUPBY N ORDERBY N")
+        tree = self._parse("Any X,LOWER(Y) GROUPBY N ORDERBY N WHERE X is Person, X name N, X date >= TODAY")
         select = stmts.Select()
-        restriction = tree.children[0].get_restriction()
+        restriction = tree.children[0].where
         self.check_equal_but_not_same(restriction, restriction.copy(select))
-        groups = tree.children[0].groups
-        self.check_equal_but_not_same(groups, groups.copy(select))
-        sorts = tree.sortterms
-        self.check_equal_but_not_same(sorts, sorts.copy(select))
+        group = tree.children[0].groupby[0]
+        self.check_equal_but_not_same(group, group.copy(select))
+        sort = tree.children[0].orderby[0]
+        self.check_equal_but_not_same(sort, sort.copy(select))
 
     def test_selected_index(self):
-        tree = self._simpleparse("Any X WHERE X is Person, X name N ORDERBY N DESC")
+        tree = self._simpleparse("Any X ORDERBY N DESC WHERE X is Person, X name N")
+        annotator.annotate(tree)
         self.assertEquals(tree.defined_vars['X'].selected_index(), 0)
         self.assertEquals(tree.defined_vars['N'].selected_index(), None)
             
@@ -260,7 +260,7 @@
     def test_insert_base_1(self):
         tree = self._parse("INSERT Person X")
         self.assertIsInstance(tree, stmts.Insert)
-        self.assertEqual(tree.children, [])
+        self.assertEqual(len(tree.children), 1)
         # test specific attributes
         self.assertEqual(len(tree.main_relations), 0)
         self.assertEqual(len(tree.main_variables), 1)
@@ -294,8 +294,8 @@
         
     def test_insert_where(self):
         tree = self._parse("INSERT Person X : X name 'bidule', X friend Y WHERE Y name 'chouette'")
-        self.assertEqual(len(tree.children), 1)
-        self.assertIsInstance(tree.children[0], nodes.Relation)
+        self.assertEqual(len(tree.children), 4)
+        self.assertIsInstance(tree.where, nodes.Relation)
         # test specific attributes
         self.assertEqual(len(tree.main_relations), 2)
         for relation in tree.main_relations:
@@ -309,9 +309,9 @@
     
     def test_update_1(self):
         tree = self._parse("SET X name 'toto' WHERE X is Person, X name 'bidule'")
-        self.assertIsInstance(tree, stmts.Update)
-        self.assertEqual(len(tree.children), 1)
-        self.assertIsInstance(tree.children[0], nodes.AND)
+        self.assertIsInstance(tree, stmts.Set)
+        self.assertEqual(len(tree.children), 2)
+        self.assertIsInstance(tree.where, nodes.And)
 
         
     # deletion tests #########################################################
@@ -319,14 +319,14 @@
     def test_delete_1(self):
         tree = self._parse("DELETE Person X WHERE X name 'toto'")
         self.assertIsInstance(tree, stmts.Delete)
-        self.assertEqual(len(tree.children), 1)
-        self.assertIsInstance(tree.children[0], nodes.Relation)
+        self.assertEqual(len(tree.children), 2)
+        self.assertIsInstance(tree.where, nodes.Relation)
         
     def test_delete_2(self):
         tree = self._parse("DELETE X friend Y WHERE X name 'toto'")
         self.assertIsInstance(tree, stmts.Delete)
-        self.assertEqual(len(tree.children), 1)
-        self.assertIsInstance(tree.children[0], nodes.Relation)
+        self.assertEqual(len(tree.children), 2)
+        self.assertIsInstance(tree.where, nodes.Relation)
         
     # as_string tests ####################################################
     
@@ -370,14 +370,14 @@
     # non regression tests ####################################################
     
     def test_get_description_and_get_type(self):
-        tree = parse("Any N,COUNT(X),NOW-D WHERE X name N, X creation_date D GROUPBY N;")
+        tree = parse("Any N,COUNT(X),NOW-D GROUPBY N WHERE X name N, X creation_date D;")
         annotator.annotate(tree)
         tree.schema = schema
         self.assertEqual(tree.get_description(), [['name', 'COUNT(name)', 'creation_date']])
-        self.assertEqual(tree.children[0].selected[0].get_type(), 'Any')
-        self.assertEqual(tree.children[0].selected[1].get_type(), 'Int')
-        self.assertEqual(tree.children[0].defined_vars['D'].get_type({'D': 'Datetime'}), 'Datetime')
-        self.assertEqual(tree.children[0].selected[2].get_type({'D': 'Datetime'}), 'Interval')
+        self.assertEqual(tree.where.selected[0].get_type(), 'Any')
+        self.assertEqual(tree.where.selected[1].get_type(), 'Int')
+        self.assertEqual(tree.where.defined_vars['D'].get_type({'D': 'Datetime'}), 'Datetime')
+        self.assertEqual(tree.where.selected[2].get_type({'D': 'Datetime'}), 'Interval')
 
     def test_repr_encoding(self):
         tree = parse(u'Any N where NOT N has_text "bidüle"')
@@ -394,15 +394,11 @@
     def test_known_values_2(self):
         tree = parse('Any X where X name "turlututu", Y know X, Y name "chapo pointu"').children[0]
         varrefs = tree.get_nodes(nodes.VariableRef)
-        self.assertEquals(len(varrefs), 4)
+        self.assertEquals(len(varrefs), 5)
         for varref in varrefs:
-            self.assertEquals(isinstance(varref, nodes.VariableRef), 1)
-        names = [ x.name for x in varrefs ]
-        names.sort()
-        self.assertEquals(names[0], 'X')
-        self.assertEquals(names[1], 'X')
-        self.assertEquals(names[2], 'Y')
-        self.assertEquals(names[3], 'Y')
+            self.assertIsInstance(varref, nodes.VariableRef)
+        self.assertEquals(sorted(x.name for x in varrefs),
+                          ['X', 'X', 'X', 'Y', 'Y'])    
 
     def test_iknown_values_1(self):
         tree = parse('Any X where X name "turlututu"').children[0]
@@ -414,15 +410,11 @@
     def test_iknown_values_2(self):
         tree = parse('Any X where X name "turlututu", Y know X, Y name "chapo pointu"').children[0]
         varrefs = list(tree.iget_nodes(nodes.VariableRef))
-        self.assertEquals(len(varrefs), 4)
+        self.assertEquals(len(varrefs), 5)
         for varref in varrefs:
-            self.assertEquals(isinstance(varref, nodes.VariableRef), 1)
-        names = [ x.name for x in varrefs ]
-        names.sort()
-        self.assertEquals(names[0], 'X')
-        self.assertEquals(names[1], 'X')
-        self.assertEquals(names[2], 'Y')
-        self.assertEquals(names[3], 'Y')    
+            self.assertIsInstance(varref, nodes.VariableRef)
+        self.assertEquals(sorted(x.name for x in varrefs),
+                          ['X', 'X', 'X', 'Y', 'Y'])    
     
 if __name__ == '__main__':
     unittest_main()
diff --git a/test/unittest_parser.py b/test/unittest_parser.py
--- a/test/unittest_parser.py
+++ b/test/unittest_parser.py
@@ -17,7 +17,8 @@
 #    'SET X travaille Y;',
     "Personne P WHERE OFFSET 200;",
 
-    'Any X WHERE X nom "toto" GROUPBY X ORDERBY X UNION Any X WHERE X firstname "toto" GROUPBY X ORDERBY X;',
+    'Any X GROUPBY X ORDERBY X WHERE X nom "toto" UNION Any X GROUPBY X ORDERBY X WHERE X firstname "toto";',
+    '(Any X GROUPBY X WHERE X nom "toto") UNION (Any X GROUPBY X WHERE X firstname "toto") ORDERBY X;',
 
     'Any X, X/Y FROM (Any SUM(X) WHERE X is Person) WHERE X is Person;', # missing AS for subquery
     
@@ -34,9 +35,10 @@
     'Any X LIMIT -1;',
     'Any X OFFSET -1;',
     'Any X ORDERBY Y;',
-    'Any N,COUNT(X) FROM (Any X, N WHERE X name N, X is State UNION '
-    '                     Any X, N WHERE X name N, X is Transition) AS X,N' # alias should be grouped
-    ' GROUPBY N HAVING COUNT(X)>1',
+    'Any N,COUNT(X) GROUPBY N '
+    ' HAVING COUNT(X)>1 '
+    ' WITH X,N BEING (Any X, N WHERE X name N, X is State UNION '
+    '                 Any X, N WHERE X name N, X is Transition);',
     )
 
 # FIXME: this shoud be generated from the spec file
@@ -73,7 +75,7 @@
 
     "Any X, COUNT(B) where B concerns X GROUPBY X ORDERBY 1;",
     
-    "Any X, COUNT(B) where B concerns X GROUPBY X HAVING COUNT(B) > 2 ORDERBY 1;",
+    "Any X, COUNT(B) GROUPBY X ORDERBY 1 WHERE B concerns X HAVING COUNT(B) > 2;",
     
     'Any X, MAX(COUNT(B)) WHERE B concerns X GROUPBY X;', # syntaxically correct
 
@@ -81,20 +83,20 @@
     'DELETE Any X WHERE X eid > 12;',
 
 #    'Any X WHERE 5 in_state X;',
-    'Any X WHERE X eid > 12 UNION Any X WHERE X eid < 23;',
     '(Any X WHERE X eid > 12) UNION (Any X WHERE X eid < 23);',
-    'Any X WHERE X nom "toto" UNION Any X WHERE X firstname "toto";',
-    'Any X WHERE X nom "toto" GROUPBY X UNION Any X WHERE X firstname "toto" GROUPBY X ORDERBY X;',
+    '(Any X WHERE X nom "toto") UNION (Any X WHERE X firstname "toto");',
+    '(Any X WHERE X nom "toto" GROUPBY X) UNION (Any X WHERE X firstname "toto" GROUPBY X ORDERBY X);',
 
-    'Any X, X/Y FROM (Any SUM(X) WHERE X is Person) AS Y WHERE X is Person;',
-    'Any Y, COUNT(X) FROM (Person X UNION Document X) AS Y WHERE X bla Y GROUPBY Y;',
+    'Any X, X/Y WHERE X is Person WITH Y BEING (Any SUM(X) WHERE X is Person);',
+    'Any Y, COUNT(X) GROUPBY Y WHERE X bla Y WITH Y BEING ((Person X) UNION (Document X));',
     
     'Any T2, COUNT(T1)'
-    '  FROM (Any X,N WHERE X name N, X transition_of E, E name %(name)s'
-    '        UNION '
-    '        Any X,N WHERE X name N, X state_of E, E name %(name)s) AS (T1,T2)'
     ' GROUPBY T1'
-    ' ORDERBY 2 DESC, T2;',
+    ' ORDERBY 2 DESC, T2;'
+    ' WITH T1,T2 BEING ('
+    '      (Any X,N WHERE X name N, X transition_of E, E name %(name)s)'
+    '       UNION '
+    '      (Any X,N WHERE X name N, X state_of E, E name %(name)s))',
     )
 
 class ParserHercule(TestCase):
@@ -117,34 +119,34 @@
         
     def test_precedence_1(self):
         tree = self.parse("Any X WHERE X firstname 'lulu' AND X name 'toto' OR X name 'tutu';")
-        base = tree.children[0].children[0]
-        self.assertEqual(isinstance(base, nodes.OR), 1)
-        self.assertEqual(isinstance(base.children[0], nodes.AND), 1)
+        base = tree.children[0].where
+        self.assertEqual(isinstance(base, nodes.Or), 1)
+        self.assertEqual(isinstance(base.children[0], nodes.And), 1)
         self.assertEqual(isinstance(base.children[1], nodes.Relation), 1)
         self.assertEqual(str(tree), "Any X WHERE (X firstname 'lulu', X name 'toto') OR (X name 'tutu')")
 
     def test_precedence_2(self):
         tree = self.parse("Any X WHERE X firstname 'lulu', X name 'toto' OR X name 'tutu';")
-        base = tree.children[0].children[0]
-        self.assertEqual(isinstance(base, nodes.AND), 1)
+        base = tree.children[0].where
+        self.assertEqual(isinstance(base, nodes.And), 1)
         self.assertEqual(isinstance(base.children[0], nodes.Relation), 1)
-        self.assertEqual(isinstance(base.children[1], nodes.OR), 1)
+        self.assertEqual(isinstance(base.children[1], nodes.Or), 1)
         self.assertEqual(str(tree), "Any X WHERE X firstname 'lulu', (X name 'toto') OR (X name 'tutu')")
 
     def test_precedence_3(self):
         tree = self.parse("Any X WHERE X firstname 'lulu' AND (X name 'toto' or X name 'tutu');")
-        base = tree.children[0].children[0]
-        self.assertEqual(isinstance(base, nodes.AND), 1)
+        base = tree.children[0].where
+        self.assertEqual(isinstance(base, nodes.And), 1)
         self.assertEqual(isinstance(base.children[0], nodes.Relation), 1)
-        self.assertEqual(isinstance(base.children[1], nodes.OR), 1)
+        self.assertEqual(isinstance(base.children[1], nodes.Or), 1)
         self.assertEqual(str(tree), "Any X WHERE X firstname 'lulu', (X name 'toto') OR (X name 'tutu')")
 
     def test_precedence_4(self):
         tree = self.parse("Any X WHERE X firstname 'lulu' OR X name 'toto' AND X name 'tutu';")
-        base = tree.children[0].children[0]
-        self.assertEqual(isinstance(base, nodes.OR), 1)
+        base = tree.children[0].where
+        self.assertEqual(isinstance(base, nodes.Or), 1)
         self.assertEqual(isinstance(base.children[0], nodes.Relation), 1)
-        self.assertEqual(isinstance(base.children[1], nodes.AND), 1)
+        self.assertEqual(isinstance(base.children[1], nodes.And), 1)
         
     def test_not_precedence_0(self):
         tree = self.parse("Any X WHERE NOT X firstname 'lulu', X name 'toto';")
@@ -160,33 +162,33 @@
 
     def test_string_1(self):
         tree = self.parse(r"Any X WHERE X firstname 'lu\"lu';")
-        const = tree.children[0].children[0].children[1].children[0]
+        const = tree.children[0].where.children[1].children[0]
         self.assertEqual(const.value, r'lu\"lu')
 
     def test_string_2(self):
         tree = self.parse(r"Any X WHERE X firstname 'lu\'lu';")
-        const = tree.children[0].children[0].children[1].children[0]
+        const = tree.children[0].where.children[1].children[0]
         self.assertEqual(const.value, 'lu\'lu')
 
     def test_string_3(self):
         tree = self.parse(r'Any X WHERE X firstname "lu\'lu";')
-        const = tree.children[0].children[0].children[1].children[0]
+        const = tree.children[0].where.children[1].children[0]
         self.assertEqual(const.value, r"lu\'lu")
 
     def test_string_4(self):
         tree = self.parse(r'Any X WHERE X firstname "lu\"lu";')
-        const = tree.children[0].children[0].children[1].children[0]
+        const = tree.children[0].where.children[1].children[0]
         self.assertEqual(const.value, "lu\"lu")
 
     def test_math_1(self):
         tree = self.parse(r'Any X WHERE X date (TODAY + 1);')
-        math = tree.children[0].children[0].children[1].children[0]
+        math = tree.children[0].where.children[1].children[0]
         self.assert_(isinstance(math, nodes.MathExpression))
         self.assertEqual(math.operator, '+')
 
     def test_math_2(self):
         tree = self.parse(r'Any X WHERE X date (TODAY + 1 * 2);')
-        math = tree.children[0].children[0].children[1].children[0]
+        math = tree.children[0].where.children[1].children[0]
         self.assert_(isinstance(math, nodes.MathExpression))
         self.assertEqual(math.operator, '+')
         math2 = math.children[1]
@@ -195,7 +197,7 @@
 
     def test_math_3(self):
         tree = self.parse(r'Any X WHERE X date (TODAY + 1) * 2;')
-        math = tree.children[0].children[0].children[1].children[0]
+        math = tree.children[0].where.children[1].children[0]
         self.assert_(isinstance(math, nodes.MathExpression))
         self.assertEqual(math.operator, '*')
         math2 = math.children[0]
@@ -204,23 +206,23 @@
 
     def test_substitute(self):
         tree = self.parse("Any X WHERE X firstname %(firstname)s;")
-        cste = tree.children[0].children[0].children[1].children[0]
+        cste = tree.children[0].where.children[1].children[0]
         self.assert_(isinstance(cste, nodes.Constant))
         self.assertEquals(cste.type, 'Substitute')
         self.assertEquals(cste.value, 'firstname')
 
     def test_optional_relation(self):
         tree = self.parse(r'Any X WHERE X related Y;')
-        related = tree.children[0].children[0]
+        related = tree.children[0].where
         self.assertEquals(related.optional, None)
         tree = self.parse(r'Any X WHERE X? related Y;')
-        related = tree.children[0].children[0]
+        related = tree.children[0].where
         self.assertEquals(related.optional, 'left')
         tree = self.parse(r'Any X WHERE X related Y?;')
-        related = tree.children[0].children[0]
+        related = tree.children[0].where
         self.assertEquals(related.optional, 'right')
         tree = self.parse(r'Any X WHERE X? related Y?;')
-        related = tree.children[0].children[0]
+        related = tree.children[0].where
         self.assertEquals(related.optional, 'both')
 
     def test_exists(self):
@@ -229,7 +231,7 @@
         self.assertEqual(tree.as_string(),
                          "Any X WHERE X firstname 'lulu', "
                          "EXISTS(X owned_by U, U in_group G, (G name 'lulufanclub') OR (G name 'managers'))")
-        exists = tree.get_nodes(nodes.Exists)[0]
+        exists = tree.children[0].where.get_nodes(nodes.Exists)[0]
         self.failUnless(exists.children[0].parent is exists)
         self.failUnless(exists.parent)
 
diff --git a/test/unittest_stcheck.py b/test/unittest_stcheck.py
--- a/test/unittest_stcheck.py
+++ b/test/unittest_stcheck.py
@@ -18,23 +18,20 @@
     
     'Any UPPER(Y) WHERE X name "toto"',
 
-    'Any C where C located P, P eid %(x)s ORDERBY N', #15066
+    'Any C ORDERBY N where C located P, P eid %(x)s', #15066
 
 #    'Any COUNT(X),P WHERE X concerns P', #9726
-    'Any X, MAX(COUNT(B)) WHERE B concerns X GROUPBY X;',
-
-    'Any X WHERE X nom "toto" UNION Any X,F WHERE X firstname F;',
+    'Any X, MAX(COUNT(B)) GROUPBY X WHERE B concerns X;',
 
-    'Any Y WHERE X eid 12, X concerns Y UNION Any Y WHERE X eid 13, X located Y ORDERBY X',
-    
-    'Any X, X/Z FROM (Any SUM(X) WHERE X is Person) AS Y WHERE X is Person;',
+    '(Any X WHERE X nom "toto") UNION (Any X,F WHERE X firstname F);',
+
+    'Any X, X/Z WHERE X is Person; WITH Y BEING (Any SUM(X) WHERE X is Person)',
     )
 
 OK_QUERIES = (
-    'Any N,COUNT(X) WHERE X name N GROUPBY N'
+    '(Any N,COUNT(X) GROUPBY N WHERE X name N)'
     ' UNION '
-    'Any N,COUNT(X) WHERE X firstname N GROUPBY N'
-    ' ORDERBY 2',
+    '(Any N,COUNT(X) GROUPBY N WHERE X firstname N)',
     )
 
 class CheckClassTest(TestCase):
@@ -76,13 +73,14 @@
              'Any X WHERE 12 work_for X'),
             ('Any X WHERE X work_for Y, Y eid IN (12)',
              'Any X WHERE X work_for 12'),
-            ('Any X WHERE X work_for Y, Y eid IN (12) ORDERBY Y',
+            ('Any X ORDERBY Y WHERE X work_for Y, Y eid IN (12)',
              'Any X WHERE X work_for 12'),
             ('Any X WHERE X eid 12',
              'Any 12'),
             ('Any X WHERE X is Person, X eid 12',
              'Any 12'),
-            ('Any X,Y WHERE X eid 0, Y eid 1, X work_for Y', 'Any 0,1 WHERE 0 work_for 1'),
+            ('Any X,Y WHERE X eid 0, Y eid 1, X work_for Y',
+             'Any 0,1 WHERE 0 work_for 1'),
 # no more supported, use outerjoin explicitly
 #            ('Any X,Y WHERE X work_for Y OR NOT X work_for Y', 'Any X,Y WHERE X? work_for Y?'),
 #            ('Any X,Y WHERE NOT X work_for Y OR X work_for Y', 'Any X,Y WHERE X? work_for Y?'),
@@ -90,13 +88,19 @@
             ("DISTINCT Any P WHERE P connait S OR S connait P, S name 'chouette'",
              "DISTINCT Any P WHERE P connait S, S name 'chouette'"),
             # queries that should not be rewritten
-            ('DELETE Person X WHERE X eid 12', 'DELETE Person X WHERE X eid 12'),
-            ('Any X WHERE X work_for Y, Y eid IN (12, 13)', 'Any X WHERE X work_for Y, Y eid IN(12, 13)'),
-            ('Any X WHERE X work_for Y, NOT Y eid 12', 'Any X WHERE X work_for Y, NOT Y eid 12'),
-            ('Any X WHERE NOT X eid 12', 'Any X WHERE NOT X eid 12'),
-            ('Any N WHERE X eid 12, X name N', 'Any N WHERE X eid 12, X name N'),
+            ('DELETE Person X WHERE X eid 12',
+             'DELETE Person X WHERE X eid 12'),
+            ('Any X WHERE X work_for Y, Y eid IN (12, 13)',
+             'Any X WHERE X work_for Y, Y eid IN(12, 13)'),
+            ('Any X WHERE X work_for Y, NOT Y eid 12',
+             'Any X WHERE X work_for Y, NOT Y eid 12'),
+            ('Any X WHERE NOT X eid 12',
+             'Any X WHERE NOT X eid 12'),
+            ('Any N WHERE X eid 12, X name N',
+             'Any N WHERE X eid 12, X name N'),
 
-            ('Any X WHERE X eid > 12', 'Any X WHERE X eid > 12'),
+            ('Any X WHERE X eid > 12',
+             'Any X WHERE X eid > 12'),
             
             ('Any X WHERE X eid 12, X connait P?, X work_for Y',
              'Any X WHERE X eid 12, X connait P?, X work_for Y'),
@@ -111,11 +115,11 @@
             ('Any X WHERE X eid 12, EXISTS(X name "hop" OR X work_for Y?)',
              "Any 12 WHERE EXISTS((A name 'hop') OR (A work_for Y?), 12 identity A)"),
             
-            ('Any X WHERE X eid 12 UNION Any X WHERE X eid 13 ORDERBY X',
+            ('(Any X WHERE X eid 12) UNION (Any X ORDERBY X WHERE X eid 13)',
              'Any 12 UNION Any 13'),
 
-            ('Any X FROM (Any X WHERE X eid 12) AS X',
-             'Any X FROM (Any 12) AS X'),
+            ('Any X WITH X BEING (Any X WHERE X eid 12)',
+             'Any X WITH X BEING (Any 12)'),
             ):
             yield self._test_rewrite, rql, expected
 
@@ -185,13 +189,13 @@
         self.assertEquals(len(C.stinfo['relations']), 2)
 
     def test_subquery_annotation(self):
-        rqlst = self.parse('Any X FROM (Any X WHERE C is Company, EXISTS(X work_for C)) AS X').children[0]
-        C = rqlst.from_[0].children[0].defined_vars['C']
+        rqlst = self.parse('Any X WITH X BEING (Any X WHERE C is Company, EXISTS(X work_for C))').children[0]
+        C = rqlst.with_[0].query.children[0].defined_vars['C']
         self.failIf(C.scope is rqlst, C.scope)
         self.assertEquals(len(C.stinfo['relations']), 1)
-        rqlst = self.parse('Any X,ET FROM (Any X, ET WHERE C is ET, EXISTS(X work_for C)) AS (X,ET)').children[0]
-        C = rqlst.from_[0].children[0].defined_vars['C']
-        self.failUnless(C.scope is rqlst.from_[0].children[0], C.scope)
+        rqlst = self.parse('Any X,ET WITH X,ET BEING (Any X, ET WHERE C is ET, EXISTS(X work_for C))').children[0]
+        C = rqlst.with_[0].query.children[0].defined_vars['C']
+        self.failUnless(C.scope is rqlst.with_[0].query.children[0], C.scope)
         self.assertEquals(len(C.stinfo['relations']), 2)
         
 if __name__ == '__main__':
diff --git a/undo.py b/undo.py
--- a/undo.py
+++ b/undo.py
@@ -8,6 +8,7 @@
 __docformat__ = "restructuredtext en"
 
 from rql.nodes import VariableRef, Variable, BinaryNode
+from rql.stmts import Select
 
 class SelectionManager:
     """manage the operation stacks"""
@@ -124,14 +125,20 @@
     def __init__(self, node):
         NodeOperation.__init__(self, node)
         self.node_parent = node.parent
-        self.index = node.parent.children.index(node)
+        if isinstance(self.node_parent, Select):
+            assert self.node is self.node_parent.where
+        else:
+            self.index = node.parent.children.index(node)
         # XXX FIXME : find a better way to do that
         # needed when removing a BinaryNode's child
         self.binary_remove = isinstance(self.node_parent, BinaryNode)
         if self.binary_remove:
             self.gd_parent = self.node_parent.parent
-            self.parent_index = self.gd_parent.children.index(self.node_parent)
-            
+            if isinstance(self.gd_parent, Select):
+                assert self.node_parent is self.gd_parent.where
+            else:
+                self.parent_index = self.gd_parent.children.index(self.node_parent)
+                
     def undo(self, selection):
         """undo the operation on the selection"""
         if self.binary_remove:
@@ -142,8 +149,13 @@
             # 'node_parent'. We must Reparent it manually !
             node_sibling = self.node_parent.children[0]
             node_sibling.parent = self.node_parent
-            self.node_parent.insert(self.index, self.node) 
-            self.gd_parent.children[self.parent_index] = self.node_parent
+            self.node_parent.insert(self.index, self.node)
+            if isinstance(self.gd_parent, Select):
+                self.gd_parent.where = self.node_parent
+            else:
+                self.gd_parent.children[self.parent_index] = self.node_parent
+        elif isinstance(self.node_parent, Select):
+            self.node_parent.where = self.node
         else:
             self.node_parent.insert(self.index, self.node)
         # register reference from the removed node
@@ -155,14 +167,14 @@
 
     def undo(self, selection):
         """undo the operation on the selection"""
-        selection.remove_sort_term(self.node)
+        self.stmt.remove_sort_term(self.node)
     
 class RemoveSortOperation(NodeOperation):
-    """defines how to undo 'add sort'"""
+    """defines how to undo 'remove sort'"""
 
     def undo(self, selection):
         """undo the operation on the selection"""
-        selection.add_sort_term(self.node)
+        self.stmt.add_sort_term(self.node)
     
 class AddGroupOperation(NodeOperation):
     """defines how to undo 'add group'"""
@@ -170,6 +182,13 @@
     def undo(self, selection):
         """undo the operation on the selection"""
         self.stmt.remove_group_var(self.node)
+    
+class RemoveGroupOperation(NodeOperation):
+    """defines how to undo 'remove group'"""
+
+    def undo(self, selection):
+        """undo the operation on the selection"""
+        self.stmt.add_group_var(self.node)
 
 # misc operations #############################################################
 
diff --git a/utils.py b/utils.py
--- a/utils.py
+++ b/utils.py
@@ -1,8 +1,10 @@
 """Miscellaneous utilities for rql
 
-Copyright (c) 2003-2007 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:organization: Logilab
+:copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
+__docformat__ = "restructuredtext en"
 
 from rql._exceptions import BadRQLQuery
 
@@ -31,10 +33,11 @@
         yield var
 
 KEYWORDS = set(('INSERT', 'SET', 'DELETE',
+                'UNION', 'WITH', 'BEING',
                 'WHERE', 'AND', 'OR', 'NOT'
                 'IN', 'LIKE',
                 'TRUE', 'FALSE', 'NULL', 'TODAY',
-                'GROUPBY', 'ORDERBY', 'ASC', 'DESC',
+                'GROUPBY', 'HAVING', 'ORDERBY', 'ASC', 'DESC',
                 'LIMIT', 'OFFSET'))
     
 
@@ -56,7 +59,6 @@
         except AttributeError, ex:
             yield term    
 
-
 def is_keyword(word):
     """return true if the given word is a RQL keyword"""
     return word.upper() in KEYWORDS
@@ -98,6 +100,13 @@
 
 # Visitor #####################################################################
 
+_accept = 'lambda self, visitor, *args, **kwargs: visitor.visit_%s(self, *args, **kwargs)' 
+_leave = 'lambda self, visitor, *args, **kwargs: visitor.leave_%s(self, *args, **kwargs)' 
+def build_visitor_stub(classes):
+    for cls in classes:
+        cls.accept = eval(_accept % (cls.__name__.lower()))
+        cls.leave = eval(_leave % (cls.__name__.lower()))        
+
 class RQLVisitorHandler:
     """handler providing a dummy implementation of all callbacks necessary
     to visit a RQL syntax tree
@@ -109,18 +118,13 @@
         pass
     def visit_delete(self, delete):
         pass
-    def visit_update(self, update):
+    def visit_set(self, update):
         pass
     
-    def visit_group(self, group):
-        pass
-    def visit_sort(self, sort):
+    def visit_select(self, selection):
         pass
     def visit_sortterm(self, sortterm):
         pass
-
-    def visit_select(self, selection):
-        pass
     
     def visit_and(self, et):
         pass