[Python-checkins] r52955 - sandbox/trunk/2to3/PatternGrammar.txt sandbox/trunk/2to3/example.py sandbox/trunk/2to3/fix_apply.py sandbox/trunk/2to3/patcomp.py sandbox/trunk/2to3/pytree.py

guido.van.rossum python-checkins at python.org
Thu Dec 7 17:55:43 CET 2006


Author: guido.van.rossum
Date: Thu Dec  7 17:55:42 2006
New Revision: 52955

Modified:
   sandbox/trunk/2to3/PatternGrammar.txt
   sandbox/trunk/2to3/example.py
   sandbox/trunk/2to3/fix_apply.py
   sandbox/trunk/2to3/patcomp.py
   sandbox/trunk/2to3/pytree.py
Log:
Added a negated pattern.  Syntax appears sub-optimal, but it works.
(It works like a negative look-ahead in regexes.)


Modified: sandbox/trunk/2to3/PatternGrammar.txt
==============================================================================
--- sandbox/trunk/2to3/PatternGrammar.txt	(original)
+++ sandbox/trunk/2to3/PatternGrammar.txt	Thu Dec  7 17:55:42 2006
@@ -13,7 +13,7 @@
 
 Alternatives: Alternative ('|' Alternative)*
 
-Alternative: Unit+
+Alternative: (Unit | NegatedUnit)+
 
 Unit: [NAME '='] ( STRING [Repeater]
                  | NAME [Details] [Repeater]
@@ -21,6 +21,8 @@
                  | '[' Alternatives ']'
 		 )
 
+NegatedUnit: 'not' (STRING | NAME [Details] | '(' Alternatives ')')
+
 Repeater: '*' | '+' | '{' NUMBER ',' NUMBER '}'
 
 Details: '<' Alternatives '>'

Modified: sandbox/trunk/2to3/example.py
==============================================================================
--- sandbox/trunk/2to3/example.py	(original)
+++ sandbox/trunk/2to3/example.py	Thu Dec  7 17:55:42 2006
@@ -17,6 +17,34 @@
     z = apply(fs[0], g or h, h or g)
     # Hello
     apply(f, (x, y) + t)
+    apply(f, args,)
+    apply(f, args, kwds,)
+    # Test that complex functions are parenthesized
+    x = apply(f+g, args)
+    x = apply(f*g, args)
+    x = apply(f**g, args)
+    # But dotted names etc. not
+    x = apply(f.g, args)
+    x = apply(f[x], args)
+    x = apply(f(), args)
+    # Extreme case
+    x = apply(a.b.c.d.e.f, args, kwds)
+    # XXX Comments in weird places still get lost
+    apply(   # foo
+          f, # bar
+          args)
+
+def bad_apply_examples():
+    # These should *not* be touched
+    apply()
+    apply(f)
+    apply(f,)
+    apply(f, args, kwds, extras)
+    apply(f, *args, **kwds)
+    apply(f, *args)
+    apply(func=f, args=args, kwds=kwds)
+    apply(f, args=args, kwds=kwds)
+    apply(f, args, kwds=kwds)
 
 def print_examples():
     # plain vanilla

Modified: sandbox/trunk/2to3/fix_apply.py
==============================================================================
--- sandbox/trunk/2to3/fix_apply.py	(original)
+++ sandbox/trunk/2to3/fix_apply.py	Thu Dec  7 17:55:42 2006
@@ -64,40 +64,60 @@
 
 # Tree matching patterns
 pat_compile = patcomp.PatternCompiler().compile_pattern
-p_apply = pat_compile("power< 'apply' trailer<'(' args=any ')'> >")
+p_apply = pat_compile("""(
+power< 'apply'
+    trailer<
+        '('
+        arglist<
+            (not argument<NAME '=' any>) func=any ','
+            (not argument<NAME '=' any>) args=any [','
+            (not argument<NAME '=' any>) kwds=any] [',']
+        >
+        ')'
+    >
+>
+)"""
+    )
 
 
 def fix_apply(node):
-    if not p_apply.match(node):
+    results = {}
+    if not p_apply.match(node, results):
         return
     n_arglist = node.children[1].children[1]
-    if n_arglist.type != syms.arglist:
-        return # apply() with only one argument?!
-    l_args = []
-    for arg in n_arglist.children:
-        if arg == n_comma:
-            continue
-        if arg == n_star or arg == n_doublestar:
-            return # apply() with a * or ** in its argument list?!
-        arg.set_prefix("")
-        l_args.append(arg)
-    if not 2 <= len(l_args) <= 3:
-        return # too few or too many arguments to handle
+    assert n_arglist.type
+    func = results["func"]
+    args = results["args"]
+    kwds = results.get("kwds")
     prefix = node.get_prefix()
-    l_args[0].replace(None)
-    node.children[0].replace(l_args[0])
+    func.replace(None)
+    if (func.type not in (token.NAME, syms.atom) and
+        (func.type != syms.power or
+         func.children[-2].type == token.DOUBLESTAR)):
+        # Need to parenthesize
+        func = pytree.Node(syms.atom,
+                           (pytree.Leaf(token.LPAR, "("),
+                            func,
+                            pytree.Leaf(token.RPAR, ")")))
+    func.set_prefix("")
+    args.replace(None)
+    args.set_prefix("")
+    if kwds is not None:
+        kwds.replace(None)
+        kwds.set_prefix("")
+    node.children[0].replace(func)
     node.set_prefix(prefix)
-    l_newargs = [pytree.Leaf(token.STAR, "*"), l_args[1]]
-    if l_args[2:]:
+    l_newargs = [pytree.Leaf(token.STAR, "*"), args]
+    if kwds is not None:
         l_newargs.extend([pytree.Leaf(token.COMMA, ","),
                           pytree.Leaf(token.DOUBLESTAR, "**"),
-                          l_args[2]])
-        l_newargs[-2].set_prefix(" ")
+                          kwds])
+        l_newargs[-2].set_prefix(" ") # that's the ** token
     for n in l_newargs:
         if n.parent is not None:
             n.replace(None) # Force parent to None
     n_arglist.replace(pytree.Node(syms.arglist, l_newargs))
-    # XXX Sometimes we can be cleverer, e.g. apply(f, (x, y) + t)
+    # XXX Sometimes we could be cleverer, e.g. apply(f, (x, y) + t)
     # can be translated into f(x, y, *t) instead of f(*(x, y) + t)
 
 

Modified: sandbox/trunk/2to3/patcomp.py
==============================================================================
--- sandbox/trunk/2to3/patcomp.py	(original)
+++ sandbox/trunk/2to3/patcomp.py	Thu Dec  7 17:55:42 2006
@@ -71,6 +71,10 @@
             units = [self.compile_node(ch) for ch in node.children]
             return pytree.WildcardPattern([units], min=1, max=1)
 
+        if node.type == self.syms.NegatedUnit:
+            pattern = self.compile_basic(node.children[1:])
+            return pytree.NegatedPattern(pattern)
+
         assert node.type == self.syms.Unit
 
         name = None
@@ -82,32 +86,10 @@
         if len(nodes) >= 2 and nodes[-1].type == self.syms.Repeater:
             repeat = nodes[-1]
             nodes = nodes[:-1]
+
         # Now we've reduced it to: STRING | NAME [Details] | (...) | [...]
-        assert len(nodes) >= 1
-        node = nodes[0]
-        if node.type == token.STRING:
-            value = literals.evalString(node.value)
-            pattern = pytree.LeafPattern(content=value)
-        elif node.type == token.NAME:
-            value = node.value
-            if value.isupper():
-                pattern = TOKEN_MAP[value]
-            else:
-                if value == "any":
-                    type = None
-                elif not value.startswith("_"):
-                    type = getattr(self.pysyms, value) # XXX KeyError
-                if nodes[1:]: # Details present
-                    content = [self.compile_node(nodes[1].children[1])]
-                else:
-                    content = None
-                pattern = pytree.NodePattern(type, content)
-        elif node.value == "(":
-            pattern = self.compile_node(nodes[1])
-        elif node.value == "[":
-            assert repeat is None
-            subpattern = self.compile_node(nodes[1])
-            pattern = pytree.WildcardPattern([[subpattern]], min=0, max=1)
+        pattern = self.compile_basic(nodes, repeat)
+
         if repeat is not None:
             assert repeat.type == self.syms.Repeater
             children = repeat.children
@@ -128,6 +110,39 @@
             pattern.name = name
         return pattern
 
+    def compile_basic(self, nodes, repeat=None):
+        # Compile STRING | NAME [Details] | (...) | [...]
+        assert len(nodes) >= 1
+        node = nodes[0]
+        if node.type == token.STRING:
+            value = literals.evalString(node.value)
+            return pytree.LeafPattern(content=value)
+        elif node.type == token.NAME:
+            value = node.value
+            if value.isupper():
+                if value not in TOKEN_MAP:
+                    raise SyntaxError("Invalid token: %r" % value)
+                return pytree.LeafPattern(TOKEN_MAP[value])
+            else:
+                if value == "any":
+                    type = None
+                elif not value.startswith("_"):
+                    type = getattr(self.pysyms, value, None)
+                    if type is None:
+                        raise SyntaxError("Invalid symbol: %r" % value)
+                if nodes[1:]: # Details present
+                    content = [self.compile_node(nodes[1].children[1])]
+                else:
+                    content = None
+                return pytree.NodePattern(type, content)
+        elif node.value == "(":
+            return self.compile_node(nodes[1])
+        elif node.value == "[":
+            assert repeat is None
+            subpattern = self.compile_node(nodes[1])
+            return pytree.WildcardPattern([[subpattern]], min=0, max=1)
+        assert False, node
+
     def get_int(self, node):
         assert node.type == token.NUMBER
         return int(node.value)
@@ -151,7 +166,7 @@
 
 def test():
     pc = PatternCompiler()
-    pat = pc.compile_pattern("a=power< 'apply' trailer<'(' b=any ')'> >")
+    pat = pc.compile_pattern("a=power< 'apply' trailer<'(' b=(not STRING) ')'> >")
     print pat
 
 

Modified: sandbox/trunk/2to3/pytree.py
==============================================================================
--- sandbox/trunk/2to3/pytree.py	(original)
+++ sandbox/trunk/2to3/pytree.py	Thu Dec  7 17:55:42 2006
@@ -501,6 +501,40 @@
                         yield c0 + c1, r
 
 
+class NegatedPattern(BasePattern):
+
+    def __init__(self, content=None):
+        """Initializer.
+
+        The argument is either a pattern or None.  If it is None, this
+        only matches an empty sequence (effectively '$' in regex
+        lingo).  If it is not None, this matches whenever the argument
+        pattern doesn't have any matches.
+        """
+        if content is not None:
+            assert isinstance(content, BasePattern), repr(content)
+        self.content = content
+
+    def match(self, node):
+        # We never match a node in its entirety
+        return False
+
+    def match_seq(self, nodes):
+        # We only match an empty sequence of nodes in its entirety
+        return len(nodes) == 0
+
+    def generate_matches(self, nodes):
+        if self.content is None:
+            # Return a match if there is an empty sequence
+            if len(nodes) == 0:
+                yield 0, {}
+        else:
+            # Return a match if the argument pattern has no matches
+            for c, r in self.content.generate_matches(nodes):
+                return
+            yield 0, {}
+
+
 def generate_matches(patterns, nodes):
     """Generator yielding matches for a sequence of patterns and nodes.
 


More information about the Python-checkins mailing list