123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- #-----------------------------------------------------------------
- # _ast_gen.py
- #
- # Generates the AST Node classes from a specification given in
- # a configuration file
- #
- # The design of this module was inspired by astgen.py from the
- # Python 2.5 code-base.
- #
- # Eli Bendersky [https://eli.thegreenplace.net/]
- # License: BSD
- #-----------------------------------------------------------------
- import pprint
- from string import Template
- class ASTCodeGenerator(object):
- def __init__(self, cfg_filename='_c_ast.cfg'):
- """ Initialize the code generator from a configuration
- file.
- """
- self.cfg_filename = cfg_filename
- self.node_cfg = [NodeCfg(name, contents)
- for (name, contents) in self.parse_cfgfile(cfg_filename)]
- def generate(self, file=None):
- """ Generates the code into file, an open file buffer.
- """
- src = Template(_PROLOGUE_COMMENT).substitute(
- cfg_filename=self.cfg_filename)
- src += _PROLOGUE_CODE
- for node_cfg in self.node_cfg:
- src += node_cfg.generate_source() + '\n\n'
- file.write(src)
- def parse_cfgfile(self, filename):
- """ Parse the configuration file and yield pairs of
- (name, contents) for each node.
- """
- with open(filename, "r") as f:
- for line in f:
- line = line.strip()
- if not line or line.startswith('#'):
- continue
- colon_i = line.find(':')
- lbracket_i = line.find('[')
- rbracket_i = line.find(']')
- if colon_i < 1 or lbracket_i <= colon_i or rbracket_i <= lbracket_i:
- raise RuntimeError("Invalid line in %s:\n%s\n" % (filename, line))
- name = line[:colon_i]
- val = line[lbracket_i + 1:rbracket_i]
- vallist = [v.strip() for v in val.split(',')] if val else []
- yield name, vallist
- class NodeCfg(object):
- """ Node configuration.
- name: node name
- contents: a list of contents - attributes and child nodes
- See comment at the top of the configuration file for details.
- """
- def __init__(self, name, contents):
- self.name = name
- self.all_entries = []
- self.attr = []
- self.child = []
- self.seq_child = []
- for entry in contents:
- clean_entry = entry.rstrip('*')
- self.all_entries.append(clean_entry)
- if entry.endswith('**'):
- self.seq_child.append(clean_entry)
- elif entry.endswith('*'):
- self.child.append(clean_entry)
- else:
- self.attr.append(entry)
- def generate_source(self):
- src = self._gen_init()
- src += '\n' + self._gen_children()
- src += '\n' + self._gen_iter()
- src += '\n' + self._gen_attr_names()
- return src
- def _gen_init(self):
- src = "class %s(Node):\n" % self.name
- if self.all_entries:
- args = ', '.join(self.all_entries)
- slots = ', '.join("'{0}'".format(e) for e in self.all_entries)
- slots += ", 'coord', '__weakref__'"
- arglist = '(self, %s, coord=None)' % args
- else:
- slots = "'coord', '__weakref__'"
- arglist = '(self, coord=None)'
- src += " __slots__ = (%s)\n" % slots
- src += " def __init__%s:\n" % arglist
- for name in self.all_entries + ['coord']:
- src += " self.%s = %s\n" % (name, name)
- return src
- def _gen_children(self):
- src = ' def children(self):\n'
- if self.all_entries:
- src += ' nodelist = []\n'
- for child in self.child:
- src += (
- ' if self.%(child)s is not None:' +
- ' nodelist.append(("%(child)s", self.%(child)s))\n') % (
- dict(child=child))
- for seq_child in self.seq_child:
- src += (
- ' for i, child in enumerate(self.%(child)s or []):\n'
- ' nodelist.append(("%(child)s[%%d]" %% i, child))\n') % (
- dict(child=seq_child))
- src += ' return tuple(nodelist)\n'
- else:
- src += ' return ()\n'
- return src
- def _gen_iter(self):
- src = ' def __iter__(self):\n'
- if self.all_entries:
- for child in self.child:
- src += (
- ' if self.%(child)s is not None:\n' +
- ' yield self.%(child)s\n') % (dict(child=child))
- for seq_child in self.seq_child:
- src += (
- ' for child in (self.%(child)s or []):\n'
- ' yield child\n') % (dict(child=seq_child))
- if not (self.child or self.seq_child):
- # Empty generator
- src += (
- ' return\n' +
- ' yield\n')
- else:
- # Empty generator
- src += (
- ' return\n' +
- ' yield\n')
- return src
- def _gen_attr_names(self):
- src = " attr_names = (" + ''.join("%r, " % nm for nm in self.attr) + ')'
- return src
- _PROLOGUE_COMMENT = \
- r'''#-----------------------------------------------------------------
- # ** ATTENTION **
- # This code was automatically generated from the file:
- # $cfg_filename
- #
- # Do not modify it directly. Modify the configuration file and
- # run the generator again.
- # ** ** *** ** **
- #
- # pycparser: c_ast.py
- #
- # AST Node classes.
- #
- # Eli Bendersky [https://eli.thegreenplace.net/]
- # License: BSD
- #-----------------------------------------------------------------
- '''
- _PROLOGUE_CODE = r'''
- import sys
- def _repr(obj):
- """
- Get the representation of an object, with dedicated pprint-like format for lists.
- """
- if isinstance(obj, list):
- return '[' + (',\n '.join((_repr(e).replace('\n', '\n ') for e in obj))) + '\n]'
- else:
- return repr(obj)
- class Node(object):
- __slots__ = ()
- """ Abstract base class for AST nodes.
- """
- def __repr__(self):
- """ Generates a python representation of the current node
- """
- result = self.__class__.__name__ + '('
-
- indent = ''
- separator = ''
- for name in self.__slots__[:-2]:
- result += separator
- result += indent
- result += name + '=' + (_repr(getattr(self, name)).replace('\n', '\n ' + (' ' * (len(name) + len(self.__class__.__name__)))))
-
- separator = ','
- indent = '\n ' + (' ' * len(self.__class__.__name__))
-
- result += indent + ')'
-
- return result
- def children(self):
- """ A sequence of all children that are Nodes
- """
- pass
- def show(self, buf=sys.stdout, offset=0, attrnames=False, nodenames=False, showcoord=False, _my_node_name=None):
- """ Pretty print the Node and all its attributes and
- children (recursively) to a buffer.
- buf:
- Open IO buffer into which the Node is printed.
- offset:
- Initial offset (amount of leading spaces)
- attrnames:
- True if you want to see the attribute names in
- name=value pairs. False to only see the values.
- nodenames:
- True if you want to see the actual node names
- within their parents.
- showcoord:
- Do you want the coordinates of each Node to be
- displayed.
- """
- lead = ' ' * offset
- if nodenames and _my_node_name is not None:
- buf.write(lead + self.__class__.__name__+ ' <' + _my_node_name + '>: ')
- else:
- buf.write(lead + self.__class__.__name__+ ': ')
- if self.attr_names:
- if attrnames:
- nvlist = [(n, getattr(self,n)) for n in self.attr_names]
- attrstr = ', '.join('%s=%s' % nv for nv in nvlist)
- else:
- vlist = [getattr(self, n) for n in self.attr_names]
- attrstr = ', '.join('%s' % v for v in vlist)
- buf.write(attrstr)
- if showcoord:
- buf.write(' (at %s)' % self.coord)
- buf.write('\n')
- for (child_name, child) in self.children():
- child.show(
- buf,
- offset=offset + 2,
- attrnames=attrnames,
- nodenames=nodenames,
- showcoord=showcoord,
- _my_node_name=child_name)
- class NodeVisitor(object):
- """ A base NodeVisitor class for visiting c_ast nodes.
- Subclass it and define your own visit_XXX methods, where
- XXX is the class name you want to visit with these
- methods.
- For example:
- class ConstantVisitor(NodeVisitor):
- def __init__(self):
- self.values = []
- def visit_Constant(self, node):
- self.values.append(node.value)
- Creates a list of values of all the constant nodes
- encountered below the given node. To use it:
- cv = ConstantVisitor()
- cv.visit(node)
- Notes:
- * generic_visit() will be called for AST nodes for which
- no visit_XXX method was defined.
- * The children of nodes for which a visit_XXX was
- defined will not be visited - if you need this, call
- generic_visit() on the node.
- You can use:
- NodeVisitor.generic_visit(self, node)
- * Modeled after Python's own AST visiting facilities
- (the ast module of Python 3.0)
- """
- _method_cache = None
- def visit(self, node):
- """ Visit a node.
- """
- if self._method_cache is None:
- self._method_cache = {}
- visitor = self._method_cache.get(node.__class__.__name__, None)
- if visitor is None:
- method = 'visit_' + node.__class__.__name__
- visitor = getattr(self, method, self.generic_visit)
- self._method_cache[node.__class__.__name__] = visitor
- return visitor(node)
- def generic_visit(self, node):
- """ Called if no explicit visitor function exists for a
- node. Implements preorder visiting of the node.
- """
- for c in node:
- self.visit(c)
- '''
|