Repository: zoofIO/flexx
Pull request #382 (2017-06-14 08:53:45)

Fixes for new listcomp logic


Patch:
diff --git a/flexx/pyscript/parser0.py b/flexx/pyscript/parser0.py
index 479af38..8d77973 100644
--- a/flexx/pyscript/parser0.py
+++ b/flexx/pyscript/parser0.py
@@ -164,7 +164,7 @@ class Parser0:
         self._stack = []
         self._indent = indent
         self._dummy_counter = 0
-        self._name_prefix = ''
+        self._scope_prefix = []  # stack of name prefixes to simulate local scope
         
         # To keep track of std lib usage
         self._std_functions = set()
diff --git a/flexx/pyscript/parser1.py b/flexx/pyscript/parser1.py
index cff1dbc..fa317ba 100644
--- a/flexx/pyscript/parser1.py
+++ b/flexx/pyscript/parser1.py
@@ -223,24 +223,33 @@ class Parser1(Parser0):
     
     ## Variables
     
-    def set_name_prefix(self, prefix):
+    def push_scope_prefix(self, prefix):
         # To avoid name clashes e.g. in comprehensions, which have their own
         # scope in Python, but we want to apply these as a for loop in JS
         # where possible.
-        self._name_prefix = prefix
+        assert prefix
+        self._scope_prefix.append(prefix)
     
+    def pop_scope_prefix(self):
+        self._scope_prefix.pop(-1)
+
     def parse_Name(self, node):
         # node.ctx can be Load, Store, Del -> can be of use somewhere?
         name = node.name
         if self.vars.get(name, None):
-            name = self.with_prefix(name)
-        elif self._name_prefix and (self._name_prefix + name) in self.vars:
-            name = self._name_prefix + name  # apply the current prefix
-        elif name in self.NAME_MAP:
-            name = self.NAME_MAP[name]
-        else:
-            if not (name in self._functions or name in ('undefined', 'window')):
-                self.vars.use(name)  # mark as used (not defined)
+            return self.with_prefix(name)
+        if self._scope_prefix:
+            for stackitem in reversed(self._stack):
+                scope = stackitem[2]
+                for prefix in reversed(self._scope_prefix):
+                    prefixed_name = prefix + name
+                    if prefixed_name in scope:
+                        return prefixed_name
+        if name in self.NAME_MAP:
+            return self.NAME_MAP[name]
+        # Else ...
+        if not (name in self._functions or name in ('undefined', 'window')):
+            self.vars.use(name)  # mark as used (not defined)
         return name
     
     def parse_Starred(self, node):
diff --git a/flexx/pyscript/parser2.py b/flexx/pyscript/parser2.py
index 30b98ce..be22418 100644
--- a/flexx/pyscript/parser2.py
+++ b/flexx/pyscript/parser2.py
@@ -645,17 +645,17 @@ class Parser2(Parser1):
     def parse_ListComp_funtionless(self, node, result_name):
         
         prefix = result_name
-        self.set_name_prefix(prefix)
+        self.push_scope_prefix(prefix)
         code = []
         
         for iter, comprehension in enumerate(node.comp_nodes):
             cc = []
             # Get target (can be multiple vars)
             if isinstance(comprehension.target_node, ast.Tuple):
-                target = [''.join(self.parse(t)) for t in 
+                target = [namenode.name for namenode in
                           comprehension.target_node.element_nodes]
             else:
-                target = [''.join(self.parse(comprehension.target_node))]
+                target = [comprehension.target_node.name]
             target = [prefix + t for t in target]
             for t in target:
                 self.vars.add(t)
@@ -686,7 +686,7 @@ class Parser2(Parser1):
         for comprehension in node.comp_nodes:
             code.append('}')  # end for
         
-        self.set_name_prefix('')
+        self.pop_scope_prefix()
         return code
     
     def parse_ListComp(self, node):


Test patch:
diff --git a/flexx/pyscript/tests/test_parser2.py b/flexx/pyscript/tests/test_parser2.py
index a47e357..23fb4b0 100644
--- a/flexx/pyscript/tests/test_parser2.py
+++ b/flexx/pyscript/tests/test_parser2.py
@@ -218,6 +218,18 @@ class TestConrolFlow:
         assert evalpy(code2) == 'true'
     
     
+    def test_listcomp_regressions(self):
+        
+        code1 = 'a = [i for i in range(the_iter)]'
+        js = py2js(code1)
+        assert 'the_iter' in js.meta['vars_unknown']
+        assert 'i' not in js.meta['vars_unknown']
+        
+        code1 = 'corners = [[(p[i] + 0.5) for i in range(3)] for p in corners_local]'
+        js = py2js(code1)
+        assert 'p' not in js.meta['vars_unknown']
+    
+    
     def xx_test_list_comprehension_speed(self):
         # https://developers.google.com/speed/articles/optimizing-javascript
         # ~ 0.029 when comprehension transpile to closures