summaryrefslogtreecommitdiff
path: root/sys/src/cmd/python/Demo/parser
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@localhost>2011-05-03 11:25:13 +0000
committercinap_lenrek <cinap_lenrek@localhost>2011-05-03 11:25:13 +0000
commit458120dd40db6b4df55a4e96b650e16798ef06a0 (patch)
tree8f82685be24fef97e715c6f5ca4c68d34d5074ee /sys/src/cmd/python/Demo/parser
parent3a742c699f6806c1145aea5149bf15de15a0afd7 (diff)
add hg and python
Diffstat (limited to 'sys/src/cmd/python/Demo/parser')
-rw-r--r--sys/src/cmd/python/Demo/parser/FILES6
-rw-r--r--sys/src/cmd/python/Demo/parser/README31
-rw-r--r--sys/src/cmd/python/Demo/parser/docstring.py2
-rw-r--r--sys/src/cmd/python/Demo/parser/example.py190
-rw-r--r--sys/src/cmd/python/Demo/parser/simple.py1
-rw-r--r--sys/src/cmd/python/Demo/parser/source.py27
-rwxr-xr-xsys/src/cmd/python/Demo/parser/test_parser.py48
-rw-r--r--sys/src/cmd/python/Demo/parser/texipre.dat100
-rw-r--r--sys/src/cmd/python/Demo/parser/unparse.py519
9 files changed, 924 insertions, 0 deletions
diff --git a/sys/src/cmd/python/Demo/parser/FILES b/sys/src/cmd/python/Demo/parser/FILES
new file mode 100644
index 000000000..1ff59a31a
--- /dev/null
+++ b/sys/src/cmd/python/Demo/parser/FILES
@@ -0,0 +1,6 @@
+Demo/parser
+Doc/libparser.tex
+Lib/AST.py
+Lib/symbol.py
+Lib/token.py
+Modules/parsermodule.c
diff --git a/sys/src/cmd/python/Demo/parser/README b/sys/src/cmd/python/Demo/parser/README
new file mode 100644
index 000000000..a576d33b0
--- /dev/null
+++ b/sys/src/cmd/python/Demo/parser/README
@@ -0,0 +1,31 @@
+These files are from the large example of using the `parser' module. Refer
+to the Python Library Reference for more information.
+
+It also contains examples for the AST parser.
+
+Files:
+------
+
+ FILES -- list of files associated with the parser module.
+
+ README -- this file.
+
+ example.py -- module that uses the `parser' module to extract
+ information from the parse tree of Python source
+ code.
+
+ docstring.py -- sample source file containing only a module docstring.
+
+ simple.py -- sample source containing a "short form" definition.
+
+ source.py -- sample source code used to demonstrate ability to
+ handle nested constructs easily using the functions
+ and classes in example.py.
+
+ test_parser.py program to put the parser module through its paces.
+
+ unparse.py AST (2.5) based example to recreate source code
+ from an AST. This is incomplete; contributions
+ are welcome.
+
+Enjoy!
diff --git a/sys/src/cmd/python/Demo/parser/docstring.py b/sys/src/cmd/python/Demo/parser/docstring.py
new file mode 100644
index 000000000..45a261b61
--- /dev/null
+++ b/sys/src/cmd/python/Demo/parser/docstring.py
@@ -0,0 +1,2 @@
+"""Some documentation.
+"""
diff --git a/sys/src/cmd/python/Demo/parser/example.py b/sys/src/cmd/python/Demo/parser/example.py
new file mode 100644
index 000000000..2aa9ec285
--- /dev/null
+++ b/sys/src/cmd/python/Demo/parser/example.py
@@ -0,0 +1,190 @@
+"""Simple code to extract class & function docstrings from a module.
+
+This code is used as an example in the library reference manual in the
+section on using the parser module. Refer to the manual for a thorough
+discussion of the operation of this code.
+"""
+
+import os
+import parser
+import symbol
+import token
+import types
+
+from types import ListType, TupleType
+
+
+def get_docs(fileName):
+ """Retrieve information from the parse tree of a source file.
+
+ fileName
+ Name of the file to read Python source code from.
+ """
+ source = open(fileName).read()
+ basename = os.path.basename(os.path.splitext(fileName)[0])
+ ast = parser.suite(source)
+ return ModuleInfo(ast.totuple(), basename)
+
+
+class SuiteInfoBase:
+ _docstring = ''
+ _name = ''
+
+ def __init__(self, tree = None):
+ self._class_info = {}
+ self._function_info = {}
+ if tree:
+ self._extract_info(tree)
+
+ def _extract_info(self, tree):
+ # extract docstring
+ if len(tree) == 2:
+ found, vars = match(DOCSTRING_STMT_PATTERN[1], tree[1])
+ else:
+ found, vars = match(DOCSTRING_STMT_PATTERN, tree[3])
+ if found:
+ self._docstring = eval(vars['docstring'])
+ # discover inner definitions
+ for node in tree[1:]:
+ found, vars = match(COMPOUND_STMT_PATTERN, node)
+ if found:
+ cstmt = vars['compound']
+ if cstmt[0] == symbol.funcdef:
+ name = cstmt[2][1]
+ self._function_info[name] = FunctionInfo(cstmt)
+ elif cstmt[0] == symbol.classdef:
+ name = cstmt[2][1]
+ self._class_info[name] = ClassInfo(cstmt)
+
+ def get_docstring(self):
+ return self._docstring
+
+ def get_name(self):
+ return self._name
+
+ def get_class_names(self):
+ return self._class_info.keys()
+
+ def get_class_info(self, name):
+ return self._class_info[name]
+
+ def __getitem__(self, name):
+ try:
+ return self._class_info[name]
+ except KeyError:
+ return self._function_info[name]
+
+
+class SuiteFuncInfo:
+ # Mixin class providing access to function names and info.
+
+ def get_function_names(self):
+ return self._function_info.keys()
+
+ def get_function_info(self, name):
+ return self._function_info[name]
+
+
+class FunctionInfo(SuiteInfoBase, SuiteFuncInfo):
+ def __init__(self, tree = None):
+ self._name = tree[2][1]
+ SuiteInfoBase.__init__(self, tree and tree[-1] or None)
+
+
+class ClassInfo(SuiteInfoBase):
+ def __init__(self, tree = None):
+ self._name = tree[2][1]
+ SuiteInfoBase.__init__(self, tree and tree[-1] or None)
+
+ def get_method_names(self):
+ return self._function_info.keys()
+
+ def get_method_info(self, name):
+ return self._function_info[name]
+
+
+class ModuleInfo(SuiteInfoBase, SuiteFuncInfo):
+ def __init__(self, tree = None, name = "<string>"):
+ self._name = name
+ SuiteInfoBase.__init__(self, tree)
+ if tree:
+ found, vars = match(DOCSTRING_STMT_PATTERN, tree[1])
+ if found:
+ self._docstring = vars["docstring"]
+
+
+def match(pattern, data, vars=None):
+ """Match `data' to `pattern', with variable extraction.
+
+ pattern
+ Pattern to match against, possibly containing variables.
+
+ data
+ Data to be checked and against which variables are extracted.
+
+ vars
+ Dictionary of variables which have already been found. If not
+ provided, an empty dictionary is created.
+
+ The `pattern' value may contain variables of the form ['varname'] which
+ are allowed to match anything. The value that is matched is returned as
+ part of a dictionary which maps 'varname' to the matched value. 'varname'
+ is not required to be a string object, but using strings makes patterns
+ and the code which uses them more readable.
+
+ This function returns two values: a boolean indicating whether a match
+ was found and a dictionary mapping variable names to their associated
+ values.
+ """
+ if vars is None:
+ vars = {}
+ if type(pattern) is ListType: # 'variables' are ['varname']
+ vars[pattern[0]] = data
+ return 1, vars
+ if type(pattern) is not TupleType:
+ return (pattern == data), vars
+ if len(data) != len(pattern):
+ return 0, vars
+ for pattern, data in map(None, pattern, data):
+ same, vars = match(pattern, data, vars)
+ if not same:
+ break
+ return same, vars
+
+
+# This pattern identifies compound statements, allowing them to be readily
+# differentiated from simple statements.
+#
+COMPOUND_STMT_PATTERN = (
+ symbol.stmt,
+ (symbol.compound_stmt, ['compound'])
+ )
+
+
+# This pattern will match a 'stmt' node which *might* represent a docstring;
+# docstrings require that the statement which provides the docstring be the
+# first statement in the class or function, which this pattern does not check.
+#
+DOCSTRING_STMT_PATTERN = (
+ symbol.stmt,
+ (symbol.simple_stmt,
+ (symbol.small_stmt,
+ (symbol.expr_stmt,
+ (symbol.testlist,
+ (symbol.test,
+ (symbol.and_test,
+ (symbol.not_test,
+ (symbol.comparison,
+ (symbol.expr,
+ (symbol.xor_expr,
+ (symbol.and_expr,
+ (symbol.shift_expr,
+ (symbol.arith_expr,
+ (symbol.term,
+ (symbol.factor,
+ (symbol.power,
+ (symbol.atom,
+ (token.STRING, ['docstring'])
+ )))))))))))))))),
+ (token.NEWLINE, '')
+ ))
diff --git a/sys/src/cmd/python/Demo/parser/simple.py b/sys/src/cmd/python/Demo/parser/simple.py
new file mode 100644
index 000000000..184e2fe5d
--- /dev/null
+++ b/sys/src/cmd/python/Demo/parser/simple.py
@@ -0,0 +1 @@
+def f(): "maybe a docstring"
diff --git a/sys/src/cmd/python/Demo/parser/source.py b/sys/src/cmd/python/Demo/parser/source.py
new file mode 100644
index 000000000..b90062851
--- /dev/null
+++ b/sys/src/cmd/python/Demo/parser/source.py
@@ -0,0 +1,27 @@
+"""Exmaple file to be parsed for the parsermodule example.
+
+The classes and functions in this module exist only to exhibit the ability
+of the handling information extraction from nested definitions using parse
+trees. They shouldn't interest you otherwise!
+"""
+
+class Simple:
+ "This class does very little."
+
+ def method(self):
+ "This method does almost nothing."
+ return 1
+
+ class Nested:
+ "This is a nested class."
+
+ def nested_method(self):
+ "Method of Nested class."
+ def nested_function():
+ "Function in method of Nested class."
+ pass
+ return nested_function
+
+def function():
+ "This function lives at the module level."
+ return 0
diff --git a/sys/src/cmd/python/Demo/parser/test_parser.py b/sys/src/cmd/python/Demo/parser/test_parser.py
new file mode 100755
index 000000000..be39bca7f
--- /dev/null
+++ b/sys/src/cmd/python/Demo/parser/test_parser.py
@@ -0,0 +1,48 @@
+#! /usr/bin/env python
+# (Force the script to use the latest build.)
+#
+# test_parser.py
+
+import parser, traceback
+
+_numFailed = 0
+
+def testChunk(t, fileName):
+ global _numFailed
+ print '----', fileName,
+ try:
+ ast = parser.suite(t)
+ tup = parser.ast2tuple(ast)
+ # this discards the first AST; a huge memory savings when running
+ # against a large source file like Tkinter.py.
+ ast = None
+ new = parser.tuple2ast(tup)
+ except parser.ParserError, err:
+ print
+ print 'parser module raised exception on input file', fileName + ':'
+ traceback.print_exc()
+ _numFailed = _numFailed + 1
+ else:
+ if tup != parser.ast2tuple(new):
+ print
+ print 'parser module failed on input file', fileName
+ _numFailed = _numFailed + 1
+ else:
+ print 'o.k.'
+
+def testFile(fileName):
+ t = open(fileName).read()
+ testChunk(t, fileName)
+
+def test():
+ import sys
+ args = sys.argv[1:]
+ if not args:
+ import glob
+ args = glob.glob("*.py")
+ args.sort()
+ map(testFile, args)
+ sys.exit(_numFailed != 0)
+
+if __name__ == '__main__':
+ test()
diff --git a/sys/src/cmd/python/Demo/parser/texipre.dat b/sys/src/cmd/python/Demo/parser/texipre.dat
new file mode 100644
index 000000000..8ad03a617
--- /dev/null
+++ b/sys/src/cmd/python/Demo/parser/texipre.dat
@@ -0,0 +1,100 @@
+\input texinfo @c -*-texinfo-*-
+@c %**start of header
+@setfilename parser.info
+@settitle Python Parser Module Reference
+@setchapternewpage odd
+@footnotestyle end
+@c %**end of header
+
+@ifinfo
+This file describes the interfaces
+published by the optional @code{parser} module and gives examples of
+how they may be used. It contains the same text as the chapter on the
+@code{parser} module in the @cite{Python Library Reference}, but is
+presented as a separate document.
+
+Copyright 1995-1996 by Fred L. Drake, Jr., Reston, Virginia, USA, and
+Virginia Polytechnic Institute and State University, Blacksburg,
+Virginia, USA. Portions of the software copyright 1991-1995 by
+Stichting Mathematisch Centrum, Amsterdam, The Netherlands. Copying is
+permitted under the terms associated with the main Python distribution,
+with the additional restriction that this additional notice be included
+and maintained on all distributed copies.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the names of Fred L. Drake, Jr. and
+Virginia Polytechnic Institute and State University not be used in
+advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+FRED L. DRAKE, JR. AND VIRGINIA POLYTECHNIC INSTITUTE AND STATE
+UNIVERSITY DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL FRED L. DRAKE, JR. OR VIRGINIA POLYTECHNIC INSTITUTE AND
+STATE UNIVERSITY BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+@end ifinfo
+
+@titlepage
+@title Python Parser Module Reference
+@author Fred L. Drake, Jr.
+
+@c The following two commands start the copyright page.
+@page
+@vskip 0pt plus 1filll
+Copyright 1995-1996 by Fred L. Drake, Jr., Reston, Virginia, USA, and
+Virginia Polytechnic Institute and State University, Blacksburg,
+Virginia, USA. Portions of the software copyright 1991-1995 by
+Stichting Mathematisch Centrum, Amsterdam, The Netherlands. Copying is
+permitted under the terms associated with the main Python distribution,
+with the additional restriction that this additional notice be included
+and maintained on all distributed copies.
+
+@center All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the names of Fred L. Drake, Jr. and
+Virginia Polytechnic Institute and State University not be used in
+advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+FRED L. DRAKE, JR. AND VIRGINIA POLYTECHNIC INSTITUTE AND STATE
+UNIVERSITY DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL FRED L. DRAKE, JR. OR VIRGINIA POLYTECHNIC INSTITUTE AND
+STATE UNIVERSITY BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+@end titlepage
+
+
+@node Top, Overview, (dir), (dir)
+@top The Python Parser Module
+
+@ifinfo
+This file describes the interfaces
+published by the optional @code{parser} module and gives examples of
+how they may be used. It contains the same text as the chapter on the
+@code{parser} module in the @cite{Python Library Reference}, but is
+presented as a separate document.
+
+This version corresponds to Python version 1.4 (1 Sept. 1996).
+
+@end ifinfo
+
+@c placeholder for the master menu -- patched by texinfo-all-menus-update
+@menu
+@end menu
diff --git a/sys/src/cmd/python/Demo/parser/unparse.py b/sys/src/cmd/python/Demo/parser/unparse.py
new file mode 100644
index 000000000..f4dd90c95
--- /dev/null
+++ b/sys/src/cmd/python/Demo/parser/unparse.py
@@ -0,0 +1,519 @@
+"Usage: unparse.py <path to source file>"
+import sys
+import _ast
+import cStringIO
+import os
+
+class Unparser:
+ """Methods in this class recursively traverse an AST and
+ output source code for the abstract syntax; original formatting
+ is disregarged. """
+
+ def __init__(self, tree, file = sys.stdout):
+ """Unparser(tree, file=sys.stdout) -> None.
+ Print the source for tree to file."""
+ self.f = file
+ self._indent = 0
+ self.dispatch(tree)
+ print >>self.f,""
+ self.f.flush()
+
+ def fill(self, text = ""):
+ "Indent a piece of text, according to the current indentation level"
+ self.f.write("\n"+" "*self._indent + text)
+
+ def write(self, text):
+ "Append a piece of text to the current line."
+ self.f.write(text)
+
+ def enter(self):
+ "Print ':', and increase the indentation."
+ self.write(":")
+ self._indent += 1
+
+ def leave(self):
+ "Decrease the indentation level."
+ self._indent -= 1
+
+ def dispatch(self, tree):
+ "Dispatcher function, dispatching tree type T to method _T."
+ if isinstance(tree, list):
+ for t in tree:
+ self.dispatch(t)
+ return
+ meth = getattr(self, "_"+tree.__class__.__name__)
+ meth(tree)
+
+
+ ############### Unparsing methods ######################
+ # There should be one method per concrete grammar type #
+ # Constructors should be grouped by sum type. Ideally, #
+ # this would follow the order in the grammar, but #
+ # currently doesn't. #
+ ########################################################
+
+ def _Module(self, tree):
+ for stmt in tree.body:
+ self.dispatch(stmt)
+
+ # stmt
+ def _Expr(self, tree):
+ self.fill()
+ self.dispatch(tree.value)
+
+ def _Import(self, t):
+ self.fill("import ")
+ first = True
+ for a in t.names:
+ if first:
+ first = False
+ else:
+ self.write(", ")
+ self.write(a.name)
+ if a.asname:
+ self.write(" as "+a.asname)
+
+ def _ImportFrom(self, t):
+ self.fill("from ")
+ self.write(t.module)
+ self.write(" import ")
+ for i, a in enumerate(t.names):
+ if i == 0:
+ self.write(", ")
+ self.write(a.name)
+ if a.asname:
+ self.write(" as "+a.asname)
+ # XXX(jpe) what is level for?
+
+ def _Assign(self, t):
+ self.fill()
+ for target in t.targets:
+ self.dispatch(target)
+ self.write(" = ")
+ self.dispatch(t.value)
+
+ def _AugAssign(self, t):
+ self.fill()
+ self.dispatch(t.target)
+ self.write(" "+self.binop[t.op.__class__.__name__]+"= ")
+ self.dispatch(t.value)
+
+ def _Return(self, t):
+ self.fill("return ")
+ if t.value:
+ self.dispatch(t.value)
+
+ def _Pass(self, t):
+ self.fill("pass")
+
+ def _Break(self, t):
+ self.fill("break")
+
+ def _Continue(self, t):
+ self.fill("continue")
+
+ def _Delete(self, t):
+ self.fill("del ")
+ self.dispatch(t.targets)
+
+ def _Assert(self, t):
+ self.fill("assert ")
+ self.dispatch(t.test)
+ if t.msg:
+ self.write(", ")
+ self.dispatch(t.msg)
+
+ def _Exec(self, t):
+ self.fill("exec ")
+ self.dispatch(t.body)
+ if t.globals:
+ self.write(" in ")
+ self.dispatch(t.globals)
+ if t.locals:
+ self.write(", ")
+ self.dispatch(t.locals)
+
+ def _Print(self, t):
+ self.fill("print ")
+ do_comma = False
+ if t.dest:
+ self.write(">>")
+ self.dispatch(t.dest)
+ do_comma = True
+ for e in t.values:
+ if do_comma:self.write(", ")
+ else:do_comma=True
+ self.dispatch(e)
+ if not t.nl:
+ self.write(",")
+
+ def _Global(self, t):
+ self.fill("global")
+ for i, n in enumerate(t.names):
+ if i != 0:
+ self.write(",")
+ self.write(" " + n)
+
+ def _Yield(self, t):
+ self.fill("yield")
+ if t.value:
+ self.write(" (")
+ self.dispatch(t.value)
+ self.write(")")
+
+ def _Raise(self, t):
+ self.fill('raise ')
+ if t.type:
+ self.dispatch(t.type)
+ if t.inst:
+ self.write(", ")
+ self.dispatch(t.inst)
+ if t.tback:
+ self.write(", ")
+ self.dispatch(t.tback)
+
+ def _TryExcept(self, t):
+ self.fill("try")
+ self.enter()
+ self.dispatch(t.body)
+ self.leave()
+
+ for ex in t.handlers:
+ self.dispatch(ex)
+ if t.orelse:
+ self.fill("else")
+ self.enter()
+ self.dispatch(t.orelse)
+ self.leave()
+
+ def _TryFinally(self, t):
+ self.fill("try")
+ self.enter()
+ self.dispatch(t.body)
+ self.leave()
+
+ self.fill("finally")
+ self.enter()
+ self.dispatch(t.finalbody)
+ self.leave()
+
+ def _excepthandler(self, t):
+ self.fill("except ")
+ if t.type:
+ self.dispatch(t.type)
+ if t.name:
+ self.write(", ")
+ self.dispatch(t.name)
+ self.enter()
+ self.dispatch(t.body)
+ self.leave()
+
+ def _ClassDef(self, t):
+ self.write("\n")
+ self.fill("class "+t.name)
+ if t.bases:
+ self.write("(")
+ for a in t.bases:
+ self.dispatch(a)
+ self.write(", ")
+ self.write(")")
+ self.enter()
+ self.dispatch(t.body)
+ self.leave()
+
+ def _FunctionDef(self, t):
+ self.write("\n")
+ for deco in t.decorators:
+ self.fill("@")
+ self.dispatch(deco)
+ self.fill("def "+t.name + "(")
+ self.dispatch(t.args)
+ self.write(")")
+ self.enter()
+ self.dispatch(t.body)
+ self.leave()
+
+ def _For(self, t):
+ self.fill("for ")
+ self.dispatch(t.target)
+ self.write(" in ")
+ self.dispatch(t.iter)
+ self.enter()
+ self.dispatch(t.body)
+ self.leave()
+ if t.orelse:
+ self.fill("else")
+ self.enter()
+ self.dispatch(t.orelse)
+ self.leave
+
+ def _If(self, t):
+ self.fill("if ")
+ self.dispatch(t.test)
+ self.enter()
+ # XXX elif?
+ self.dispatch(t.body)
+ self.leave()
+ if t.orelse:
+ self.fill("else")
+ self.enter()
+ self.dispatch(t.orelse)
+ self.leave()
+
+ def _While(self, t):
+ self.fill("while ")
+ self.dispatch(t.test)
+ self.enter()
+ self.dispatch(t.body)
+ self.leave()
+ if t.orelse:
+ self.fill("else")
+ self.enter()
+ self.dispatch(t.orelse)
+ self.leave
+
+ def _With(self, t):
+ self.fill("with ")
+ self.dispatch(t.context_expr)
+ if t.optional_vars:
+ self.write(" as ")
+ self.dispatch(t.optional_vars)
+ self.enter()
+ self.dispatch(t.body)
+ self.leave()
+
+ # expr
+ def _Str(self, tree):
+ self.write(repr(tree.s))
+
+ def _Name(self, t):
+ self.write(t.id)
+
+ def _Repr(self, t):
+ self.write("`")
+ self.dispatch(t.value)
+ self.write("`")
+
+ def _Num(self, t):
+ self.write(repr(t.n))
+
+ def _List(self, t):
+ self.write("[")
+ for e in t.elts:
+ self.dispatch(e)
+ self.write(", ")
+ self.write("]")
+
+ def _ListComp(self, t):
+ self.write("[")
+ self.dispatch(t.elt)
+ for gen in t.generators:
+ self.dispatch(gen)
+ self.write("]")
+
+ def _GeneratorExp(self, t):
+ self.write("(")
+ self.dispatch(t.elt)
+ for gen in t.generators:
+ self.dispatch(gen)
+ self.write(")")
+
+ def _comprehension(self, t):
+ self.write(" for ")
+ self.dispatch(t.target)
+ self.write(" in ")
+ self.dispatch(t.iter)
+ for if_clause in t.ifs:
+ self.write(" if ")
+ self.dispatch(if_clause)
+
+ def _IfExp(self, t):
+ self.dispatch(t.body)
+ self.write(" if ")
+ self.dispatch(t.test)
+ if t.orelse:
+ self.write(" else ")
+ self.dispatch(t.orelse)
+
+ def _Dict(self, t):
+ self.write("{")
+ for k,v in zip(t.keys, t.values):
+ self.dispatch(k)
+ self.write(" : ")
+ self.dispatch(v)
+ self.write(", ")
+ self.write("}")
+
+ def _Tuple(self, t):
+ if not t.elts:
+ self.write("()")
+ return
+ self.write("(")
+ for e in t.elts:
+ self.dispatch(e)
+ self.write(", ")
+ self.write(")")
+
+ unop = {"Invert":"~", "Not": "not", "UAdd":"+", "USub":"-"}
+ def _UnaryOp(self, t):
+ self.write(self.unop[t.op.__class__.__name__])
+ self.write("(")
+ self.dispatch(t.operand)
+ self.write(")")
+
+ binop = { "Add":"+", "Sub":"-", "Mult":"*", "Div":"/", "Mod":"%",
+ "LShift":">>", "RShift":"<<", "BitOr":"|", "BitXor":"^", "BitAnd":"&",
+ "FloorDiv":"//", "Pow": "**"}
+ def _BinOp(self, t):
+ self.write("(")
+ self.dispatch(t.left)
+ self.write(")" + self.binop[t.op.__class__.__name__] + "(")
+ self.dispatch(t.right)
+ self.write(")")
+
+ cmpops = {"Eq":"==", "NotEq":"!=", "Lt":"<", "LtE":"<=", "Gt":">", "GtE":">=",
+ "Is":"is", "IsNot":"is not", "In":"in", "NotIn":"not in"}
+ def _Compare(self, t):
+ self.write("(")
+ self.dispatch(t.left)
+ for o, e in zip(t.ops, t.comparators):
+ self.write(") " +self.cmpops[o.__class__.__name__] + " (")
+ self.dispatch(e)
+ self.write(")")
+
+ boolops = {_ast.And: 'and', _ast.Or: 'or'}
+ def _BoolOp(self, t):
+ self.write("(")
+ self.dispatch(t.values[0])
+ for v in t.values[1:]:
+ self.write(" %s " % self.boolops[t.op.__class__])
+ self.dispatch(v)
+ self.write(")")
+
+ def _Attribute(self,t):
+ self.dispatch(t.value)
+ self.write(".")
+ self.write(t.attr)
+
+ def _Call(self, t):
+ self.dispatch(t.func)
+ self.write("(")
+ comma = False
+ for e in t.args:
+ if comma: self.write(", ")
+ else: comma = True
+ self.dispatch(e)
+ for e in t.keywords:
+ if comma: self.write(", ")
+ else: comma = True
+ self.dispatch(e)
+ if t.starargs:
+ if comma: self.write(", ")
+ else: comma = True
+ self.write("*")
+ self.dispatch(t.starargs)
+ if t.kwargs:
+ if comma: self.write(", ")
+ else: comma = True
+ self.write("**")
+ self.dispatch(t.kwargs)
+ self.write(")")
+
+ def _Subscript(self, t):
+ self.dispatch(t.value)
+ self.write("[")
+ self.dispatch(t.slice)
+ self.write("]")
+
+ # slice
+ def _Ellipsis(self, t):
+ self.write("...")
+
+ def _Index(self, t):
+ self.dispatch(t.value)
+
+ def _Slice(self, t):
+ if t.lower:
+ self.dispatch(t.lower)
+ self.write(":")
+ if t.upper:
+ self.dispatch(t.upper)
+ if t.step:
+ self.write(":")
+ self.dispatch(t.step)
+
+ def _ExtSlice(self, t):
+ for i, d in enumerate(t.dims):
+ if i != 0:
+ self.write(': ')
+ self.dispatch(d)
+
+ # others
+ def _arguments(self, t):
+ first = True
+ nonDef = len(t.args)-len(t.defaults)
+ for a in t.args[0:nonDef]:
+ if first:first = False
+ else: self.write(", ")
+ self.dispatch(a)
+ for a,d in zip(t.args[nonDef:], t.defaults):
+ if first:first = False
+ else: self.write(", ")
+ self.dispatch(a),
+ self.write("=")
+ self.dispatch(d)
+ if t.vararg:
+ if first:first = False
+ else: self.write(", ")
+ self.write("*"+t.vararg)
+ if t.kwarg:
+ if first:first = False
+ else: self.write(", ")
+ self.write("**"+t.kwarg)
+
+ def _keyword(self, t):
+ self.write(t.arg)
+ self.write("=")
+ self.dispatch(t.value)
+
+ def _Lambda(self, t):
+ self.write("lambda ")
+ self.dispatch(t.args)
+ self.write(": ")
+ self.dispatch(t.body)
+
+def roundtrip(filename, output=sys.stdout):
+ source = open(filename).read()
+ tree = compile(source, filename, "exec", 0x400)
+ Unparser(tree, output)
+
+
+
+def testdir(a):
+ try:
+ names = [n for n in os.listdir(a) if n.endswith('.py')]
+ except OSError:
+ print >> sys.stderr, "Directory not readable: %s" % a
+ else:
+ for n in names:
+ fullname = os.path.join(a, n)
+ if os.path.isfile(fullname):
+ output = cStringIO.StringIO()
+ print 'Testing %s' % fullname
+ try:
+ roundtrip(fullname, output)
+ except Exception, e:
+ print ' Failed to compile, exception is %s' % repr(e)
+ elif os.path.isdir(fullname):
+ testdir(fullname)
+
+def main(args):
+ if args[0] == '--testdir':
+ for a in args[1:]:
+ testdir(a)
+ else:
+ for a in args:
+ roundtrip(a)
+
+if __name__=='__main__':
+ main(sys.argv[1:])