1.1 --- a/libxml2macro.py Sun Jan 16 01:15:01 2005 +0000
1.2 +++ b/libxml2macro.py Sun Jan 16 01:15:37 2005 +0000
1.3 @@ -1,12 +1,43 @@
1.4 #!/usr/bin/env python
1.5
1.6 import compiler
1.7 +import marshal, imp, os, stat, struct
1.8 +
1.9 +# Originally from Analysis/Writers/Python.
1.10 +
1.11 +def write_module(module, source_filename, target_filename, syntax_check=1):
1.12 +
1.13 + """
1.14 + Write the given 'module', associated with the given 'source_filename', to a
1.15 + file with the given 'target_filename'. The optional 'syntax_check' flag (set
1.16 + by default) ensures that the module is syntactically correct.
1.17 + """
1.18 +
1.19 + if syntax_check:
1.20 + compiler.syntax.check(module)
1.21 + compiler.misc.set_filename(source_filename, module)
1.22 + generator = compiler.pycodegen.ModuleCodeGenerator(module)
1.23 + f = open(target_filename, "wb")
1.24 + f.write(get_header(source_filename))
1.25 + marshal.dump(generator.getCode(), f)
1.26 + f.close()
1.27 +
1.28 +def get_header(filename):
1.29 +
1.30 + "Taken from compiler.pycodegen. Prepare the compiled module header."
1.31 +
1.32 + MAGIC = imp.get_magic()
1.33 + mtime = os.stat(filename)[stat.ST_MTIME]
1.34 + mtime = struct.pack('<i', mtime)
1.35 + return MAGIC + mtime
1.36 +
1.37 +# Processing functions.
1.38
1.39 def process_nodes(root_node, prefix):
1.40
1.41 """
1.42 Under the 'root_node', process all suitable expression nodes which employ
1.43 - Name nodes using the given 'prefix'.
1.44 + special names using the given 'prefix'.
1.45 """
1.46
1.47 for node in root_node.getChildNodes():
1.48 @@ -19,24 +50,77 @@
1.49 process_nodes(node, prefix)
1.50
1.51 def process_callfunc(node, prefix):
1.52 +
1.53 + """
1.54 + Process the CallFunc 'node' searching for special names with the given
1.55 + 'prefix'.
1.56 + """
1.57 +
1.58 # Check the target.
1.59 target = node.node
1.60 if isinstance(target, compiler.ast.Getattr):
1.61 - process_getattr(target, prefix, node)
1.62 + if process_getattr(target, prefix, node):
1.63 +
1.64 + # Process all sibling arguments of the new first argument, too.
1.65 +
1.66 + process_callfunc_args(node, prefix, start_index=1)
1.67 +
1.68 + else:
1.69 + process_callfunc_args(node, prefix)
1.70 +
1.71 else:
1.72 - process_nodes(node, prefix)
1.73 + process_callfunc_args(node, prefix)
1.74 +
1.75 +def process_callfunc_args(node, prefix, start_index=0):
1.76 +
1.77 + """
1.78 + Process the arguments of the given CallFunc 'node' searching for special
1.79 + names with the given 'prefix'. The optional 'start_index' is used to
1.80 + indicate the first argument to be processed.
1.81 + """
1.82
1.83 -def process_getattr(node, prefix, parent):
1.84 - # Check the target.
1.85 - target = node.expr
1.86 - if isinstance(target, compiler.ast.Name) and target.name.startswith(prefix):
1.87 + for index in range(start_index, len(node.args)):
1.88 + arg = node.args[index]
1.89 + if isinstance(arg, compiler.ast.Getattr):
1.90 + process_getattr(arg, prefix, node, index=index)
1.91 +
1.92 +def process_getattr(node, prefix, parent, index=None):
1.93 +
1.94 + """
1.95 + Process the Getattr 'node' searching for special names with the given
1.96 + 'prefix', using the given 'parent' to add transformed expressions in place
1.97 + of the original ones.
1.98 +
1.99 + The optional 'index' is used when arguments of CallFunc nodes are being
1.100 + processed and where a replacement of such arguments is occurring.
1.101 + """
1.102 +
1.103 + # Detected cases:
1.104 + # node.attr plus node.attr.attr
1.105 + # (obj.node).attr plus (obj.node).attr.attr
1.106 + # Note that the deep cases are dealt with first using a recursive call.
1.107 +
1.108 + if getattr_has_prefix(node, prefix) or \
1.109 + isinstance(node.expr, compiler.ast.Getattr) and propagated_prefix(process_getattr(node.expr, prefix, node)):
1.110
1.111 # Replace CallFunc plus Getattr occurrences:
1.112 # node.attr(args) -> Node_attr(node, args)
1.113 + # fn(node.attr) -> fn(Node_attr(node))
1.114
1.115 if isinstance(parent, compiler.ast.CallFunc):
1.116 - parent.node = compiler.ast.Name("Node_%s" % node.attrname)
1.117 - parent.args.insert(0, compiler.ast.Name(target.name))
1.118 +
1.119 + # If this node is not an argument, transform the call.
1.120 +
1.121 + if index is None:
1.122 + parent.node = compiler.ast.Name("Node_%s" % node.attrname)
1.123 + parent.args.insert(0, node.expr)
1.124 +
1.125 + else:
1.126 + replacement = compiler.ast.CallFunc(
1.127 + compiler.ast.Name("Node_%s" % node.attrname),
1.128 + [node.expr]
1.129 + )
1.130 + parent.args[index] = replacement
1.131
1.132 # Replace plain Getattr nodes:
1.133 # node.attr -> Node_attr(node)
1.134 @@ -56,8 +140,41 @@
1.135 except TypeError:
1.136 if value is node:
1.137 parent.__dict__[key] = replacement
1.138 +
1.139 + # Propagate whether the kind of result might need transforming itself.
1.140 +
1.141 + return node.attrname
1.142 +
1.143 else:
1.144 process_nodes(node, prefix)
1.145 + return None
1.146 +
1.147 +def propagated_prefix(attrname):
1.148 +
1.149 + """
1.150 + Return whether the given 'attrname' used in a transformation should be
1.151 + considered significant at the parent level.
1.152 + """
1.153 +
1.154 + return attrname in ("ownerElement", "ownerDocument")
1.155 +
1.156 +def getattr_has_prefix(node, prefix):
1.157 +
1.158 + """
1.159 + Determine whether the given Getattr 'node' employs the special 'prefix' in a
1.160 + number of ways.
1.161 + """
1.162 +
1.163 + # Check the expression as a simple name:
1.164 + # node.attr
1.165 + if isinstance(node.expr, compiler.ast.Name) and node.expr.name.startswith(prefix):
1.166 + return 1
1.167 + # Check the attribute name of child expressions:
1.168 + # (obj.node).attr
1.169 + elif isinstance(node.expr, compiler.ast.Getattr) and node.expr.attrname.startswith(prefix):
1.170 + return 1
1.171 + else:
1.172 + return 0
1.173
1.174 def process_file(filename, prefix="x2_"):
1.175
1.176 @@ -73,6 +190,17 @@
1.177 # Find references to special variables.
1.178
1.179 process_nodes(module, prefix)
1.180 +
1.181 + # Write the module.
1.182 +
1.183 + write_module(module, filename, os.path.splitext(filename)[0] + ".pyc")
1.184 return module
1.185
1.186 +if __name__ == "__main__":
1.187 + import sys
1.188 + if len(sys.argv) < 2:
1.189 + print "libxml2macro.py <module-filename>"
1.190 + sys.exit(1)
1.191 + process_file(sys.argv[1])
1.192 +
1.193 # vim: tabstop=4 expandtab shiftwidth=4