1.1 --- a/libxml2macro.py Fri May 27 20:31:30 2005 +0000
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,256 +0,0 @@
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=None):
1.40 -
1.41 - """
1.42 - Under the 'root_node', process all suitable expression nodes which employ
1.43 - special names using the given 'prefix'.
1.44 - """
1.45 -
1.46 - for node in root_node.getChildNodes():
1.47 -
1.48 - # Find imports and discover if they are really used to define the
1.49 - # special prefix.
1.50 -
1.51 - if isinstance(node, compiler.ast.Import):
1.52 - prefix = process_import(node, root_node) or prefix
1.53 -
1.54 - # Identify suitable names and add replacement nodes.
1.55 -
1.56 - elif isinstance(node, compiler.ast.CallFunc):
1.57 - process_callfunc(node, prefix)
1.58 - elif isinstance(node, compiler.ast.Getattr):
1.59 - process_getattr(node, prefix, root_node)
1.60 - else:
1.61 - process_nodes(node, prefix)
1.62 -
1.63 -def process_import(node, parent):
1.64 -
1.65 - """
1.66 - Process the Import 'node' searching for the special incantation required to
1.67 - set the prefix. Remove compliant nodes from their 'parent' node. If no new
1.68 - prefix is found, return None.
1.69 - """
1.70 -
1.71 - for name, alias in node.names:
1.72 - if name == "libxml2macro":
1.73 -
1.74 - # Remove this node from its parent.
1.75 -
1.76 - parent.nodes.remove(node)
1.77 - return alias
1.78 -
1.79 - return None
1.80 -
1.81 -def process_callfunc(node, prefix):
1.82 -
1.83 - """
1.84 - Process the CallFunc 'node' searching for special names with the given
1.85 - 'prefix'.
1.86 - """
1.87 -
1.88 - # Check the prefix.
1.89 -
1.90 - if prefix is None:
1.91 - return
1.92 -
1.93 - # Check the target.
1.94 -
1.95 - target = node.node
1.96 - if isinstance(target, compiler.ast.Getattr):
1.97 - if process_getattr(target, prefix, node):
1.98 -
1.99 - # Process all sibling arguments of the new first argument, too.
1.100 -
1.101 - process_callfunc_args(node, prefix, start_index=1)
1.102 -
1.103 - else:
1.104 - process_callfunc_args(node, prefix)
1.105 -
1.106 - else:
1.107 - process_callfunc_args(node, prefix)
1.108 -
1.109 -def process_callfunc_args(node, prefix, start_index=0):
1.110 -
1.111 - """
1.112 - Process the arguments of the given CallFunc 'node' searching for special
1.113 - names with the given 'prefix'. The optional 'start_index' is used to
1.114 - indicate the first argument to be processed.
1.115 - """
1.116 -
1.117 - for index in range(start_index, len(node.args)):
1.118 - arg = node.args[index]
1.119 - if isinstance(arg, compiler.ast.Getattr):
1.120 - process_getattr(arg, prefix, node, index=index)
1.121 -
1.122 -def process_getattr(node, prefix, parent, index=None):
1.123 -
1.124 - """
1.125 - Process the Getattr 'node' searching for special names with the given
1.126 - 'prefix', using the given 'parent' to add transformed expressions in place
1.127 - of the original ones.
1.128 -
1.129 - The optional 'index' is used when arguments of CallFunc nodes are being
1.130 - processed and where a replacement of such arguments is occurring.
1.131 - """
1.132 -
1.133 - # Check the prefix.
1.134 -
1.135 - if prefix is None:
1.136 - return
1.137 -
1.138 - # Detected cases:
1.139 - # node.attr plus node.attr.attr
1.140 - # (obj.node).attr plus (obj.node).attr.attr
1.141 - # Note that the deep cases are dealt with first using a recursive call.
1.142 -
1.143 - if getattr_has_prefix(node, prefix) or \
1.144 - isinstance(node.expr, compiler.ast.Getattr) and propagated_prefix(process_getattr(node.expr, prefix, node)):
1.145 -
1.146 - # Replace CallFunc plus Getattr occurrences:
1.147 - # node.attr(args) -> Node_attr(node, args)
1.148 - # fn(node.attr) -> fn(Node_attr(node))
1.149 -
1.150 - if isinstance(parent, compiler.ast.CallFunc):
1.151 -
1.152 - # If this node is not an argument, transform the call.
1.153 -
1.154 - if index is None:
1.155 - parent.node = compiler.ast.Name("Node_%s" % node.attrname)
1.156 - parent.args.insert(0, node.expr)
1.157 -
1.158 - else:
1.159 - replacement = compiler.ast.CallFunc(
1.160 - compiler.ast.Name("Node_%s" % node.attrname),
1.161 - [node.expr]
1.162 - )
1.163 - parent.args[index] = replacement
1.164 -
1.165 - # Replace plain Getattr nodes:
1.166 - # node.attr -> Node_attr(node)
1.167 - # NOTE: Nasty but necessary rewiring of the parent node required.
1.168 -
1.169 - else:
1.170 - replacement = compiler.ast.CallFunc(
1.171 - compiler.ast.Name("Node_%s" % node.attrname),
1.172 - [node.expr]
1.173 - )
1.174 - for key, value in parent.__dict__.items():
1.175 - # Detect lists.
1.176 - if hasattr(value, "__len__") and node in value:
1.177 - index = value.index(node)
1.178 - value[index] = replacement
1.179 - elif value is node:
1.180 - parent.__dict__[key] = replacement
1.181 -
1.182 - # Propagate whether the kind of result might need transforming itself.
1.183 -
1.184 - return node.attrname
1.185 -
1.186 - else:
1.187 - process_nodes(node, prefix)
1.188 - return None
1.189 -
1.190 -def propagated_prefix(attrname):
1.191 -
1.192 - """
1.193 - Return whether the given 'attrname' used in a transformation should be
1.194 - considered significant at the parent level.
1.195 - """
1.196 -
1.197 - return attrname in ("ownerElement", "ownerDocument")
1.198 -
1.199 -def getattr_has_prefix(node, prefix):
1.200 -
1.201 - """
1.202 - Determine whether the given Getattr 'node' employs the special 'prefix' in a
1.203 - number of ways.
1.204 - """
1.205 -
1.206 - # Check the expression as a simple name:
1.207 - # node.attr
1.208 -
1.209 - if isinstance(node.expr, compiler.ast.Name) and node.expr.name.startswith(prefix):
1.210 - return 1
1.211 -
1.212 - # Check the attribute name of child expressions:
1.213 - # (obj.node).attr
1.214 -
1.215 - elif isinstance(node.expr, compiler.ast.Getattr) and node.expr.attrname.startswith(prefix):
1.216 - return 1
1.217 - else:
1.218 - return 0
1.219 -
1.220 -def include_import(module):
1.221 -
1.222 - """
1.223 - Include an import statement in 'module' to make the macro library available.
1.224 - """
1.225 -
1.226 - module.node.nodes.insert(0, compiler.ast.From("libxml2dom.macrolib", [("*", None)]))
1.227 -
1.228 -def process_file(filename):
1.229 -
1.230 - """
1.231 - Process the module given by the specified 'filename'. The optional special
1.232 - 'prefix' marks those variables to be processed.
1.233 - """
1.234 -
1.235 - # Open the module as an AST.
1.236 -
1.237 - module = compiler.parseFile(filename)
1.238 -
1.239 - # Find references to special variables.
1.240 -
1.241 - process_nodes(module)
1.242 -
1.243 - # Add necessary imports.
1.244 -
1.245 - include_import(module)
1.246 -
1.247 - # Write the module.
1.248 -
1.249 - write_module(module, filename, os.path.splitext(filename)[0] + ".pyc")
1.250 - return module
1.251 -
1.252 -if __name__ == "__main__":
1.253 - import sys
1.254 - if len(sys.argv) < 2:
1.255 - print "libxml2macro.py <module-filename>"
1.256 - sys.exit(1)
1.257 - process_file(sys.argv[1])
1.258 -
1.259 -# vim: tabstop=4 expandtab shiftwidth=4