diff --git a/libbs/__init__.py b/libbs/__init__.py index 5034083..4647494 100644 --- a/libbs/__init__.py +++ b/libbs/__init__.py @@ -1,4 +1,4 @@ -__version__ = "3.4.0" +__version__ = "3.4.1" import logging diff --git a/libbs/api/type_parser.py b/libbs/api/type_parser.py index 145c485..31981be 100644 --- a/libbs/api/type_parser.py +++ b/libbs/api/type_parser.py @@ -4,6 +4,7 @@ from typing import Optional import pycparser +from pycparser import c_ast from pycparser.c_parser import ParseError # pycparser hack to parse type expressions @@ -14,6 +15,31 @@ l = logging.getLogger(__name__) +def _patch_pycparser(): + """ + Adds a `parse_type_with_name` method to pycparser.CParser that parses a bare + type expression (like "int *") rather than a full translation unit. pycparser + 3.0 removed the ability to customize the start production via ply.yacc. + """ + if hasattr(pycparser.CParser, "parse_type_with_name"): + return + + def parse_type_with_name(self, text, filename="", scope_stack=None) -> c_ast.Typename: + self.clex._filename = filename + self.clex._lineno = 1 + self._scope_stack = [{}] if scope_stack is None else scope_stack + + self.clex.input(text, filename) + self._tokens = pycparser.c_parser._TokenStream(self.clex) + + return self._parse_type_name() + + pycparser.CParser.parse_type_with_name = parse_type_with_name + + +_patch_pycparser() + + class CType: def __init__( self, @@ -95,13 +121,6 @@ def __init__( # hack in type parsing self._type_parser_singleton = pycparser.CParser() - self._type_parser_singleton.cparser = pycparser.ply.yacc.yacc( - module=self._type_parser_singleton, - start='parameter_declaration', - debug=False, - optimize=False, - errorlog=errorlog - ) self.ALL_TYPES = {} self.BASIC_TYPES = {} self.STDINT_TYPES = {} @@ -190,8 +209,9 @@ def extract_type_name(self, type_str: str) -> str | None: parsable_type = type_str.replace(";", "").strip() type_name = None try: - node = self._type_parser_singleton.parse(text=parsable_type) - type_name = node.name + ast = self._type_parser_singleton.parse(text=parsable_type + ";") + if ast.ext: + type_name = ast.ext[0].name except ParseError: pass @@ -207,15 +227,15 @@ def extract_type_name(self, type_str: str) -> str | None: return type_name - def parse_type(self, defn, preprocess=True, predefined_types=None, arch=None) -> Optional[CType]: # pylint:disable=unused-argument + def parse_type(self, defn, predefined_types=None, arch=None) -> Optional[CType]: # pylint:disable=unused-argument """ Parse a simple type expression into a SimType >>> self.parse_type('int *') """ - return self.parse_type_with_name(defn, preprocess=preprocess, predefined_types=predefined_types, arch=arch)[0] + return self.parse_type_with_name(defn, predefined_types=predefined_types, arch=arch)[0] - def parse_type_with_name(self, defn, preprocess=True, predefined_types=None, arch=None): # pylint:disable=unused-argument + def parse_type_with_name(self, defn, predefined_types=None, arch=None): # pylint:disable=unused-argument """ Parse a simple type expression into a SimType, returning the a tuple of the type object and any associated name that might be found in the place a name would go in a type declaration. @@ -228,12 +248,12 @@ def parse_type_with_name(self, defn, preprocess=True, predefined_types=None, arc if pycparser is None: raise ImportError("Please install pycparser in order to parse C definitions") - if preprocess: - defn = re.sub(r"/\*.*?\*/", r"", defn) + defn = re.sub(r"/\*.*?\*/", r"", defn, flags=re.DOTALL) + defn = re.sub(r"//.*?$", r"", defn, flags=re.MULTILINE) failed_parse = False try: - node = self._type_parser_singleton.parse(text=defn) + node = self._type_parser_singleton.parse_type_with_name(text=defn) except ParseError: failed_parse = True @@ -243,7 +263,7 @@ def parse_type_with_name(self, defn, preprocess=True, predefined_types=None, arc # if failed_parse: try: - node = self._type_parser_singleton.parse(text="struct " + defn) + node = self._type_parser_singleton.parse_type_with_name(text="struct " + defn) except Exception: return (None, ) diff --git a/pyproject.toml b/pyproject.toml index 3e98d55..57a6630 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ requires-python = ">= 3.10" dependencies = [ "toml", "ply", - "pycparser<3.0", + "pycparser~=3.0", "setuptools", "prompt_toolkit", "tqdm",