Repository: edgedb/edgedb

Issue:
Get rid of AST.parent
![Screenshot_20191205_121201](https://user-images.githubusercontent.com/347119/70257591-8f7b3a00-1758-11ea-82d1-914a4813affa.png)

`AST.parent` is a historical vestige that is used just in a couple of places, most notably, EdgeQL codegen and the AST transformer (which itself isn't used very much).  Look into getting rid of this, as it's up there in the function cost chart (`AST.__init__`)


Discussion:
Specifically, we'll need to rewrite `edb/edgeql/codegen.py` to use singledispatch, and a simple context to replicate the current `_needs_parentheses` logic.

**Upd:** since codegens call a helper to fix the `parent` links before they begin their tree traversal, they should continue working even without rewriting them to singledispatch.
This was not worth the effort. Parent attaching before:
```
./compile_edgeql_script_webapp_insert_review.pstats
48:     2128    0.010    0.000    0.042    0.000 edb/common/ast/base.py:189(__init__)

./compile_edgeql_script_webapp_get_movie.pstats
57:     1677    0.008    0.000    0.036    0.000 edb/common/ast/base.py:189(__init__)

./compile_edgeql_script_simple.pstats
33:     1216    0.006    0.000    0.029    0.000 edb/common/ast/base.py:189(__init__)

./compile_edgeql_script_webapp_insert_movie.pstats
48:     1712    0.009    0.000    0.038    0.000 edb/common/ast/base.py:189(__init__)

./compile_edgeql_script_webapp_insert_person.pstats
38:     1688    0.007    0.000    0.033    0.000 edb/common/ast/base.py:189(__init__)

./compile_edgeql_script_webapp_configure_system.pstats
31:     1512    0.008    0.000    0.033    0.000 edb/common/ast/base.py:189(__init__)

./compile_edgeql_script_webapp_get_user.pstats
63:      702    0.003    0.000    0.015    0.000 edb/common/ast/base.py:189(__init__)

./compile_edgeql_script_webapp_get_person.pstats
65:     1415    0.007    0.000    0.029    0.000 edb/common/ast/base.py:189(__init__)
```

After:
```
./compile_edgeql_script_webapp_insert_review.pstats
52:     2128    0.003    0.000    0.036    0.000 edb/common/ast/base.py:192(__init__)

./compile_edgeql_script_webapp_get_movie.pstats
67:     1677    0.002    0.000    0.022    0.000 edb/common/ast/base.py:192(__init__)

./compile_edgeql_script_simple.pstats
37:     1408    0.002    0.000    0.022    0.000 edb/common/ast/base.py:192(__init__)

./compile_edgeql_script_webapp_insert_movie.pstats
53:     1712    0.002    0.000    0.024    0.000 edb/common/ast/base.py:192(__init__)

./compile_edgeql_script_webapp_insert_person.pstats
47:     1688    0.002    0.000    0.023    0.000 edb/common/ast/base.py:192(__init__)

./compile_edgeql_script_webapp_configure_system.pstats
46:     2016    0.002    0.000    0.028    0.000 edb/common/ast/base.py:192(__init__)

./compile_edgeql_script_webapp_get_user.pstats
78:      702    0.001    0.000    0.011    0.000 edb/common/ast/base.py:192(__init__)

./compile_edgeql_script_webapp_get_person.pstats
76:     1415    0.002    0.000    0.019    0.000 edb/common/ast/base.py:192(__init__)
```

Gross results below but note that there's always +- noise.

Before:
```
compile_edgeql_script: Mean +- std dev: 2.29 ms +- 0.09 ms
compile_edgeql_script_webapp_configure_system: Mean +- std dev: 35.6 ms +- 1.0 ms
compile_edgeql_script_webapp_get_movie: Mean +- std dev: 190 ms +- 11 ms
compile_edgeql_script_webapp_get_person: Mean +- std dev: 192 ms +- 5 ms
compile_edgeql_script_webapp_get_user: Mean +- std dev: 107 ms +- 11 ms
compile_edgeql_script_webapp_insert_movie: Mean +- std dev: 154 ms +- 13 ms
compile_edgeql_script_webapp_insert_person: Mean +- std dev: 54.6 ms +- 4.1 ms
compile_edgeql_script_webapp_insert_review: Mean +- std dev: 79.4 ms +- 6.5 ms
```

After:
```
compile_edgeql_script: Mean +- std dev: 2.21 ms +- 0.09 ms
compile_edgeql_script_webapp_configure_system: Mean +- std dev: 33.8 ms +- 1.1 ms
compile_edgeql_script_webapp_get_movie: Mean +- std dev: 176 ms +- 5 ms
compile_edgeql_script_webapp_get_person: Mean +- std dev: 185 ms +- 5 ms
compile_edgeql_script_webapp_get_user: Mean +- std dev: 101 ms +- 13 ms
compile_edgeql_script_webapp_insert_movie: Mean +- std dev: 152 ms +- 20 ms
compile_edgeql_script_webapp_insert_person: Mean +- std dev: 57.5 ms +- 6.0 ms
compile_edgeql_script_webapp_insert_review: Mean +- std dev: 89.8 ms +- 8.5 ms
```

Resolving patch:
diff --git a/edb/common/ast/base.py b/edb/common/ast/base.py
--- a/edb/common/ast/base.py
+++ b/edb/common/ast/base.py
@@ -192,30 +192,9 @@ def __init__(self, **kwargs):
                 f'cannot instantiate abstract AST node '
                 f'{self.__class__.__name__!r}')
 
-        object.__setattr__(self, 'parent', None)
+        object.__setattr__(self, 'parent', kwargs.get('parent', None))
         self._init_fields(kwargs)
 
-        # XXX: use weakref here
-        for arg, value in kwargs.items():
-            if hasattr(self, arg):
-                if is_ast_node(value):
-                    object.__setattr__(value, 'parent', self)
-                elif isinstance(value, list):
-                    for v in value:
-                        if is_ast_node(v):
-                            object.__setattr__(v, 'parent', self)
-                elif isinstance(value, dict):
-                    for v in value.values():
-                        if is_ast_node(v):
-                            object.__setattr__(v, 'parent', self)
-            else:
-                raise ASTError(
-                    'cannot set attribute "%s" in ast class "%s"' %
-                    (arg, self.__class__.__name__))
-
-        if 'parent' in kwargs:
-            object.__setattr__(self, 'parent', kwargs['parent'])
-
     def _init_fields(self, values):
         for field_name, field in self.__class__._fields.items():
             if field_name in values:


Test patch: