docs: def to_node(self, exp, schema):
          """
          Return a Node that is the root of the parse tree for the the specified
          expression.
  
          :param exp: A list that represents a relational algebra expression.
          Assumes that this list was generated by pyparsing.
          :param schema: A dictionary of relation names to attribute names used
          for verification and generating attributes.
          :return: A Node.
          """
          # A relation node.
          if len(exp) == 1 and isinstance(exp[0], str):
              node = RelationNode(name=exp[0], schema=schema)
  
          # An expression.
          elif len(exp) == 1 and isinstance(exp[0], list):
              node = self.to_node(exp[0], schema)
  
          # Unary operators.
          elif isinstance(exp[0], str) and self.grammar.is_unary(exp[0]):
              child = self.to_node(exp[2:], schema)
              node = self.create_unary_node(operator=exp[0], child=child,
                                            param=exp[1], schema=schema)
  
          # Assignment.
          elif exp[1] is self.grammar.syntax.assign_op:
              child = self.to_node(exp[2:], schema)
              node = self.create_unary_node(operator=exp[1], child=child,
                                            param=exp[0], schema=schema)
  
          # Binary operators.
          elif self.grammar.is_binary(exp[1]):
              # Pyparsing will put different operators with the same precedence
              # in the same list. This can be a problem when we mix operators with
              # and without parameters (for example join). We avoid this below
              # and build from right to left, to create the correct syntax tree.
              if isinstance(exp[-2], str):
                  # Operator without parameters
                  op_pos = -2
                  param = None
              else:
                  op_pos = -3
                  param = exp[-2]
  
              operator = exp[op_pos]
              left = self.to_node(exp[:op_pos], schema)
              right = self.to_node(exp[-1], schema)
              node = self.create_binary_node(operator=operator, left=left,
                                             right=right, param=param)
  
          else:
              raise ValueError
  
          return node