[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