Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
_ast_gen.py356 linesDownload Raw Back to pycparser
1# -----------------------------------------------------------------2# _ast_gen.py3#4# Generates the AST Node classes from a specification given in5# a configuration file. This module can also be run as a script to6# regenerate c_ast.py from _c_ast.cfg (from the repo root or the7# pycparser/ directory). Use 'make check' to reformat the generated8# file after running this script.9#10# The design of this module was inspired by astgen.py from the11# Python 2.5 code-base.12#13# Eli Bendersky [https://eli.thegreenplace.net/]14# License: BSD15# -----------------------------------------------------------------16from string import Template17import os18from typing import IO19 20 21class ASTCodeGenerator:22    def __init__(self, cfg_filename="_c_ast.cfg"):23        """Initialize the code generator from a configuration24        file.25        """26        self.cfg_filename = cfg_filename27        self.node_cfg = [28            NodeCfg(name, contents)29            for (name, contents) in self.parse_cfgfile(cfg_filename)30        ]31 32    def generate(self, file: IO[str]) -> None:33        """Generates the code into file, an open file buffer."""34        src = Template(_PROLOGUE_COMMENT).substitute(cfg_filename=self.cfg_filename)35 36        src += _PROLOGUE_CODE37        for node_cfg in self.node_cfg:38            src += node_cfg.generate_source() + "\n\n"39 40        file.write(src)41 42    def parse_cfgfile(self, filename):43        """Parse the configuration file and yield pairs of44        (name, contents) for each node.45        """46        with open(filename, "r") as f:47            for line in f:48                line = line.strip()49                if not line or line.startswith("#"):50                    continue51                colon_i = line.find(":")52                lbracket_i = line.find("[")53                rbracket_i = line.find("]")54                if colon_i < 1 or lbracket_i <= colon_i or rbracket_i <= lbracket_i:55                    raise RuntimeError(f"Invalid line in {filename}:\n{line}\n")56 57                name = line[:colon_i]58                val = line[lbracket_i + 1 : rbracket_i]59                vallist = [v.strip() for v in val.split(",")] if val else []60                yield name, vallist61 62 63class NodeCfg:64    """Node configuration.65 66    name: node name67    contents: a list of contents - attributes and child nodes68    See comment at the top of the configuration file for details.69    """70 71    def __init__(self, name, contents):72        self.name = name73        self.all_entries = []74        self.attr = []75        self.child = []76        self.seq_child = []77 78        for entry in contents:79            clean_entry = entry.rstrip("*")80            self.all_entries.append(clean_entry)81 82            if entry.endswith("**"):83                self.seq_child.append(clean_entry)84            elif entry.endswith("*"):85                self.child.append(clean_entry)86            else:87                self.attr.append(entry)88 89    def generate_source(self):90        src = self._gen_init()91        src += "\n" + self._gen_children()92        src += "\n" + self._gen_iter()93        src += "\n" + self._gen_attr_names()94        return src95 96    def _gen_init(self):97        src = f"class {self.name}(Node):\n"98 99        if self.all_entries:100            args = ", ".join(self.all_entries)101            slots = ", ".join(f"'{e}'" for e in self.all_entries)102            slots += ", 'coord', '__weakref__'"103            arglist = f"(self, {args}, coord=None)"104        else:105            slots = "'coord', '__weakref__'"106            arglist = "(self, coord=None)"107 108        src += f"    __slots__ = ({slots})\n"109        src += f"    def __init__{arglist}:\n"110 111        for name in self.all_entries + ["coord"]:112            src += f"        self.{name} = {name}\n"113 114        return src115 116    def _gen_children(self):117        src = "    def children(self):\n"118 119        if self.all_entries:120            src += "        nodelist = []\n"121 122            for child in self.child:123                src += f"        if self.{child} is not None:\n"124                src += f'            nodelist.append(("{child}", self.{child}))\n'125 126            for seq_child in self.seq_child:127                src += f"        for i, child in enumerate(self.{seq_child} or []):\n"128                src += f'            nodelist.append((f"{seq_child}[{{i}}]", child))\n'129 130            src += "        return tuple(nodelist)\n"131        else:132            src += "        return ()\n"133 134        return src135 136    def _gen_iter(self):137        src = "    def __iter__(self):\n"138 139        if self.all_entries:140            for child in self.child:141                src += f"        if self.{child} is not None:\n"142                src += f"            yield self.{child}\n"143 144            for seq_child in self.seq_child:145                src += f"        for child in (self.{seq_child} or []):\n"146                src += "            yield child\n"147 148            if not (self.child or self.seq_child):149                # Empty generator150                src += "        return\n" + "        yield\n"151        else:152            # Empty generator153            src += "        return\n" + "        yield\n"154 155        return src156 157    def _gen_attr_names(self):158        src = "    attr_names = (" + "".join(f"{nm!r}, " for nm in self.attr) + ")"159        return src160 161 162_PROLOGUE_COMMENT = r"""#-----------------------------------------------------------------163# ** ATTENTION **164# This code was automatically generated from _c_ast.cfg165#166# Do not modify it directly. Modify the configuration file and167# run the generator again.168# ** ** *** ** **169#170# pycparser: c_ast.py171#172# AST Node classes.173#174# Eli Bendersky [https://eli.thegreenplace.net/]175# License: BSD176#-----------------------------------------------------------------177 178"""179_PROLOGUE_CODE = r'''180import sys181from typing import Any, ClassVar, IO, Optional182 183def _repr(obj):184    """185    Get the representation of an object, with dedicated pprint-like format for lists.186    """187    if isinstance(obj, list):188        return '[' + (',\n '.join((_repr(e).replace('\n', '\n ') for e in obj))) + '\n]'189    else:190        return repr(obj)191 192class Node:193    __slots__ = ()194    """ Abstract base class for AST nodes.195    """196    attr_names: ClassVar[tuple[str, ...]] = ()197    coord: Optional[Any]198    def __repr__(self):199        """ Generates a python representation of the current node200        """201        result = self.__class__.__name__ + '('202 203        indent = ''204        separator = ''205        for name in self.__slots__[:-2]:206            result += separator207            result += indent208            result += name + '=' + (_repr(getattr(self, name)).replace('\n', '\n  ' + (' ' * (len(name) + len(self.__class__.__name__)))))209 210            separator = ','211            indent = '\n ' + (' ' * len(self.__class__.__name__))212 213        result += indent + ')'214 215        return result216 217    def children(self):218        """ A sequence of all children that are Nodes219        """220        pass221 222    def show(223        self,224        buf: IO[str] = sys.stdout,225        offset: int = 0,226        attrnames: bool = False,227        showemptyattrs: bool = True,228        nodenames: bool = False,229        showcoord: bool = False,230        _my_node_name: Optional[str] = None,231    ):232        """ Pretty print the Node and all its attributes and233            children (recursively) to a buffer.234 235            buf:236                Open IO buffer into which the Node is printed.237 238            offset:239                Initial offset (amount of leading spaces)240 241            attrnames:242                True if you want to see the attribute names in243                name=value pairs. False to only see the values.244 245            showemptyattrs:246                False if you want to suppress printing empty attributes.247 248            nodenames:249                True if you want to see the actual node names250                within their parents.251 252            showcoord:253                Do you want the coordinates of each Node to be254                displayed.255        """256        lead = ' ' * offset257        if nodenames and _my_node_name is not None:258            buf.write(lead + self.__class__.__name__+ ' <' + _my_node_name + '>: ')259        else:260            buf.write(lead + self.__class__.__name__+ ': ')261 262        if self.attr_names:263            def is_empty(v):264                v is None or (hasattr(v, '__len__') and len(v) == 0)265            nvlist = [(n, getattr(self,n)) for n in self.attr_names \266                        if showemptyattrs or not is_empty(getattr(self,n))]267            if attrnames:268                attrstr = ', '.join(f'{name}={value}' for name, value in nvlist)269            else:270                attrstr = ', '.join(f'{value}' for _, value in nvlist)271            buf.write(attrstr)272 273        if showcoord:274            buf.write(f' (at {self.coord})')275        buf.write('\n')276 277        for (child_name, child) in self.children():278            child.show(279                buf,280                offset=offset + 2,281                attrnames=attrnames,282                showemptyattrs=showemptyattrs,283                nodenames=nodenames,284                showcoord=showcoord,285                _my_node_name=child_name)286 287 288class NodeVisitor:289    """ A base NodeVisitor class for visiting c_ast nodes.290        Subclass it and define your own visit_XXX methods, where291        XXX is the class name you want to visit with these292        methods.293 294        For example:295 296        class ConstantVisitor(NodeVisitor):297            def __init__(self):298                self.values = []299 300            def visit_Constant(self, node):301                self.values.append(node.value)302 303        Creates a list of values of all the constant nodes304        encountered below the given node. To use it:305 306        cv = ConstantVisitor()307        cv.visit(node)308 309        Notes:310 311        *   generic_visit() will be called for AST nodes for which312            no visit_XXX method was defined.313        *   The children of nodes for which a visit_XXX was314            defined will not be visited - if you need this, call315            generic_visit() on the node.316            You can use:317                NodeVisitor.generic_visit(self, node)318        *   Modeled after Python's own AST visiting facilities319            (the ast module of Python 3.0)320    """321 322    _method_cache = None323 324    def visit(self, node: Node):325        """ Visit a node.326        """327 328        if self._method_cache is None:329            self._method_cache = {}330 331        visitor = self._method_cache.get(node.__class__.__name__, None)332        if visitor is None:333            method = 'visit_' + node.__class__.__name__334            visitor = getattr(self, method, self.generic_visit)335            self._method_cache[node.__class__.__name__] = visitor336 337        return visitor(node)338 339    def generic_visit(self, node: Node):340        """ Called if no explicit visitor function exists for a341            node. Implements preorder visiting of the node.342        """343        for _, c in node.children():344            self.visit(c)345 346'''347 348 349if __name__ == "__main__":350    base_dir = os.path.dirname(os.path.abspath(__file__))351    cfg_path = os.path.join(base_dir, "_c_ast.cfg")352    out_path = os.path.join(base_dir, "c_ast.py")353    ast_gen = ASTCodeGenerator(cfg_path)354    with open(out_path, "w") as out:355        ast_gen.generate(out)356 
codekingpro/portable-devtools · Team Ai