Repository: davidjamesca/ctypesgen Branch: master Commit: a90952dd6056 Files: 70 Total size: 711.8 KB Directory structure: gitextract_dfid13aw/ ├── .flake8 ├── .github/ │ └── workflows/ │ ├── black.yml │ ├── flake8.yml │ ├── publish.yml │ └── test.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING ├── LICENSE ├── MANIFEST.in ├── README.md ├── ctypesgen/ │ ├── .gitignore │ ├── __init__.py │ ├── __main__.py │ ├── ctypedescs.py │ ├── descriptions.py │ ├── expressions.py │ ├── libraryloader.py │ ├── messages.py │ ├── options.py │ ├── parser/ │ │ ├── .gitignore │ │ ├── __init__.py │ │ ├── cdeclarations.py │ │ ├── cgrammar.py │ │ ├── cparser.py │ │ ├── ctypesparser.py │ │ ├── datacollectingparser.py │ │ ├── lex.py │ │ ├── lextab.py │ │ ├── parsetab.py │ │ ├── pplexer.py │ │ ├── preprocessor.py │ │ └── yacc.py │ ├── printer_json/ │ │ ├── __init__.py │ │ └── printer.py │ ├── printer_python/ │ │ ├── __init__.py │ │ ├── defaultheader.py │ │ ├── preamble.py │ │ └── printer.py │ ├── processor/ │ │ ├── __init__.py │ │ ├── dependencies.py │ │ ├── operations.py │ │ └── pipeline.py │ └── version.py ├── debian/ │ ├── .gitignore │ ├── compat │ ├── control │ ├── ctypesgen.docs │ ├── ctypesgen.manpages │ ├── mk_changelog │ ├── mk_manpage │ └── rules ├── demo/ │ ├── .gitignore │ ├── README.md │ ├── demoapp.c │ ├── demoapp.py │ ├── demolib.c │ ├── demolib.h │ └── pydemolib.py ├── docs/ │ └── publishing.md ├── pyproject.toml ├── run.py ├── setup.py ├── tests/ │ ├── .gitignore │ ├── __init__.py │ ├── ctypesgentest.py │ └── testsuite.py ├── todo.txt └── tox.ini ================================================ FILE CONTENTS ================================================ ================================================ FILE: .flake8 ================================================ [flake8] ignore = # whitespace before ':' (Black) E203, # line break before binary operator (Black) W503, per-file-ignores = # Files and directories which need fixes or specific exceptions. # # Description of codes: # E401 multiple imports on one line # E501 line too long # ctypesgen/__init__.py: F401 ctypesgen/parser/cgrammar.py: E501 max-line-length = 100 exclude = ctypesgen/parser/parsetab.py, ctypesgen/parser/lextab.py, ctypesgen/parser/yacc.py, ctypesgen/parser/lex.py, demo/pydemolib.py, .git, __pycache__, debian builtins = _, ================================================ FILE: .github/workflows/black.yml ================================================ --- name: Python Black Formatting on: - push - pull_request - fork jobs: black: name: Black runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.10' - name: Install run: | python -m pip install --upgrade pip pip install black==23.3.0 - name: Run Black run: | black --check --diff setup.py run.py ctypesgen/ \ --exclude='${{ env.EXCLUDE }}' env: EXCLUDE: ".*tab.py|ctypesgen/parser/cgrammar.py|\ ctypesgen/parser/lex.py|ctypesgen/parser/yacc.py" ================================================ FILE: .github/workflows/flake8.yml ================================================ --- name: Python Flake8 Code Quality on: - push - pull_request - fork jobs: flake8: name: ${{ matrix.directory }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install run: | python -m pip install --upgrade pip pip install flake8==3.8.4 - name: Run Flake8 run: | flake8 --count --statistics --show-source --jobs=$(nproc) . ================================================ FILE: .github/workflows/publish.yml ================================================ --- name: Publish Python distributions to PyPI on: release: types: [published] jobs: build-n-publish: name: Build and publish Python distributions to PyPI runs-on: ubuntu-latest steps: - name: Check out repository uses: actions/checkout@v3 with: ref: ${{ github.ref }} - name: Set up Python 3.10 uses: actions/setup-python@v3 with: python-version: '3.10' - name: Install pypa/build run: python -m pip install build --user - name: Build a binary wheel and a source tarball run: python -m build - name: Publish distribution to GitHub uses: softprops/action-gh-release@v1 with: files: | dist/* - name: Publish distribution to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: password: ${{ secrets.PYPI_API_TOKEN }} ================================================ FILE: .github/workflows/test.yml ================================================ --- name: Test on: - push - pull_request - fork jobs: setup-and-test: name: Python-${{ matrix.python }} ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: include: # Linux - os: ubuntu-latest python: 3.7 - os: ubuntu-latest python: 3.8 - os: ubuntu-latest python: 3.9 - os: ubuntu-latest python: '3.10' # macOS - os: macos-latest python: '3.10' # Windows - os: windows-latest python: '3.10' fail-fast: false steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} - name: Install Python dependencies run: | python -m pip install --upgrade pip pip install pytest - name: Run Test run: | pytest -v --showlocals tests/testsuite.py ================================================ FILE: .gitignore ================================================ # precompiled python files *.pyc # generated by distutils MANIFEST dist/ # generated by setuptools build/ ctypesgen.egg-info/ .eggs/ .python-version tests/.python-version # Swap/backup editor files *.swp *~ .pybuild/ .tox/ .idea/ core ================================================ FILE: .travis.yml ================================================ dist: bionic language: python python: 3.7.8 install: - pip install tox script: - tox stages: - name: tox - name: publish to test.pypi.org if: env(publish) = true AND type = api # to publish, go to https://travis-ci.com/Alan-R/ctypesgen/ # click more options -> trigger build # in textbox enter this: # env: # publish: true - name: verify if: env(publish) = true AND type = api jobs: include: - stage: tox env: TOXENV=py37 - stage: tox env: TOXENV=black - stage: publish to test.pypi.org install: - pip install --upgrade build twine script: - python -m build - python -m twine upload --repository-url https://test.pypi.org/legacy/ dist/* - stage: verify install: - pip install --index-url https://test.pypi.org/simple/ --no-deps ctypesgen script: - python -c 'import ctypesgen; print(ctypesgen.VERSION)' ================================================ FILE: CHANGELOG.md ================================================ ## Change Log ### Unreleased ### v1.1.1 - Fixed inconsistency in version output in released packages ### v1.1.0 This release has a number of bug fixes in addition to a few new features. Following a complete transition to Python 3, with dropped Python 2 support, major work was made towards code modernization and quality. - The code is now Black formatted and Flake8 tested - Greatly improved unittest framework - Embedded PLY version updated to 3.11 - New option: `--no-embed-preamble` create separate files for preamble and loader instead of embedding in each output file - New option: `--allow-gnu-c` do not undefine `__GNUC__` - Fixed library loader search path on macOS - Fixed rare bug, processing (legacy) header files with MacRoman encoding on macOS - Added full support for floating and integer constants - Added support for sized integer types on Windows - Added support to handle `restrict` and `_Noreturn` keywords - Added name formats to posix library loader - Fixed mapping of 'short int' to c_short - Git tags are now using `x.y.z` format ### v1.0.2 Many issues fixed. Parse gcc attributes more Implements automatic calling convention selection based on gcc attributes for stdcall/cdecl. - Simplify and unify library loader for various platforms. Improve library path searches on Linux (parsed ld.so.conf includes now). - First implementaion of #pragma pack - First implemenation of #undef - Adds several command line options: `-D` `--define` `-U` `--undefine` `--no-undefs` `-P` `--strip-prefix` `--debug-level` ### v1.0.1 Fix handling of function prototypes Other minor improvments included. ### v1.0.0 Py2/Py3 support Various development branches merged back In addition to the various developments from the different branches, this tag also represents a code state that: - ties in with Travis CI to watch code developments - improves testsuite, including moving all JSON tests to testsuite - includes a decent Debian package build configuration - automatically creates a man page to be included in the Debian package ================================================ FILE: CONTRIBUTING ================================================ The best way to document a bug is to create a new test which demonstrates it. You should do that by adding a new test to: ctypesgen/test/testsuite.py This is *required* for any patches you might provide. You must provide tests to demonstrate your bug fix or enhancement. All patches will be have to pass unit tests. You can run the tests by running "tox" with no options. All our Python code is formatted with using the "black" command with a 100 character line length. black --line-length 100 You can verify your patch formatting before you submit it by running "tox -e black". ================================================ FILE: LICENSE ================================================ Copyright (c) 2007-2022, Ctypesgen Developers All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: MANIFEST.in ================================================ graft ctypesgen recursive-exclude ctypesgen .gitignore global-exclude *.py[cod] include ctypesgen/VERSION ================================================ FILE: README.md ================================================ ctypesgen --------- (c) Ctypesgen developers 2007-2022 https://github.com/ctypesgen/ctypesgen _ctypesgen_ is a pure-python ctypes wrapper generator. It parses C header files and creates a wrapper for libraries based on what it finds. Preprocessor macros are handled in a manner consistent with typical C code. Preprocessor macro functions are translated into Python functions that are then made available to the user of the newly-generated Python wrapper library. It can also output JSON, which can be used with Mork, which generates bindings for Lua, using the alien module (which binds libffi to Lua). ## Documentation See https://github.com/ctypesgen/ctypesgen/wiki for full documentation. Run `ctypesgen --help` for full range of available options. ## Installation _ctypesgen_ can be installed by `pip install ctypesgen`. It requires Python 3.7 to run. ## Basic Usage This project automatically generates ctypes wrappers for header files written in C. For example, if you'd like to generate Neon bindings, you can do so using this recipe (using a standard pip install): ```sh ctypesgen -lneon /usr/local/include/neon/ne_*.h -o neon.py ``` Some libraries, such as APR, need special flags to compile. You can pass these flags in on the command line. For example: ```sh FLAGS = `apr-1-config --cppflags --includes` ctypesgen $FLAGS -llibapr-1.so $HOME/include/apr-1/apr*.h -o apr.py ``` Sometimes, libraries will depend on each other. You can specify these dependencies using -mmodule, where module is the name of the dependency module. Here's an example for apr_util: ```sh ctypesgen $FLAGS -llibaprutil-1.so $HOME/include/apr-1/ap[ru]*.h \ -mapr -o apr_util.py ``` If you want JSON output (e.g. for generating Lua bindings), use ``` --output-language=json ``` When outputting JSON, you will probably also want to use ``` --all-headers --builtin-symbols --no-stddef-types --no-gnu-types --no-python-types ``` ## Related Software of Interest _libffi_ is a portable Foreign Function Interface library: http://sources.redhat.com/libffi/ _Mork_, the friendly alien, can be found at: https://github.com/rrthomas/mork ## License _ctypesgen_ is distributed under the New (2-clause) BSD License: http://www.opensource.org/licenses/bsd-license.php ================================================ FILE: ctypesgen/.gitignore ================================================ VERSION ================================================ FILE: ctypesgen/__init__.py ================================================ """ Ctypesgencore is the module that contains the main body of ctypesgen - in fact, it contains everything but the command-line interface. ctypesgen's job is divided into three steps: Step 1: Parse Ctypesgen reads the input header files and parses them. It generates a list of function, variable, struct, union, enum, constant, typedef, and macro descriptions from the input files. These descriptions are encapsulated as ctypesgen.descriptions.Description objects. The package ctypesgen.parser is responsible for the parsing stage. Step 2: Process Ctypesgen processes the list of descriptions from the parsing stage. This is the stage where ctypesgen resolves name conflicts and filters descriptions using the regexes specified on the command line. Other processing steps take place at this stage, too. When processing is done, ctypesgen finalizes which descriptions will be included in the output file. The package ctypesgen.processor is responsible for the processing stage. Step 3: Print Ctypesgen writes the descriptions to the output file, along with a header. The package ctypesgen.printer is responsible for the printing stage. There are three modules in ctypesgen that describe the format that the parser, processor, and printer modules use to pass information. They are: * descriptions: Classes to represent the descriptions. * ctypedecls: Classes to represent C types. * expressions: Classes to represent an expression in a language-independent format. """ __all__ = [ "parser", "processor", "printer", "descriptions", "ctypedescs", "expressions", "messages", "options", "version", ] # Workhorse modules from . import parser from . import processor from . import printer_python from . import version # Modules describing internal format from . import descriptions from . import ctypedescs from . import expressions # Helper modules from . import messages from . import options try: from . import printer_json except ImportError: pass __version__ = version.VERSION.partition("-")[-1] VERSION = __version__ printer = printer_python # Default the printer to generating Python ================================================ FILE: ctypesgen/__main__.py ================================================ """ Command-line interface for ctypesgen """ import argparse from ctypesgen import ( messages as msgs, options as core_options, parser as core_parser, printer_python, printer_json, processor, version, ) def find_names_in_modules(modules): names = set() for module in modules: try: mod = __import__(module) except Exception: pass else: names.update(dir(mod)) return names def main(givenargs=None): # TODO(geisserml) In the future, convert action="append" to nargs="*" - that's nicer to use parser = argparse.ArgumentParser() parser.add_argument( "--version", action="version", version=version.VERSION_NUMBER, ) # Parameters parser.add_argument("headers", nargs="+", help="Sequence of header files") parser.add_argument( "-o", "--output", metavar="FILE", help="write wrapper to FILE [default stdout]", ) parser.add_argument( "-l", "--library", dest="libraries", action="append", default=[], metavar="LIBRARY", help="link to LIBRARY", ) parser.add_argument( "--include", dest="other_headers", action="append", default=[], metavar="HEADER", help="include system header HEADER (e.g. stdio.h or stdlib.h)", ) parser.add_argument( "-m", "--module", "--link-module", action="append", dest="modules", metavar="MODULE", default=[], help="use symbols from Python module MODULE", ) parser.add_argument( "-I", "--includedir", action="append", dest="include_search_paths", default=[], metavar="INCLUDEDIR", help="add INCLUDEDIR as a directory to search for headers", ) parser.add_argument( "-L", "-R", "--rpath", "--libdir", action="append", dest="universal_libdirs", default=[], metavar="LIBDIR", help="Add LIBDIR to the search path (both compile-time and run-time)", ) parser.add_argument( "--compile-libdir", action="append", dest="compile_libdirs", metavar="LIBDIR", default=[], help="Add LIBDIR to the compile-time library search path.", ) parser.add_argument( "--runtime-libdir", action="append", dest="runtime_libdirs", metavar="LIBDIR", default=[], help="Add LIBDIR to the run-time library search path.", ) parser.add_argument( "--no-embed-preamble", action="store_false", dest="embed_preamble", default=True, help="Do not embed preamble and loader in output file. " "Defining --output as a file and --output-language to " "Python is a prerequisite.", ) # Parser options parser.add_argument( "--cpp", dest="cpp", default="gcc -E", help="The command to invoke the c preprocessor, including any " "necessary options (default: gcc -E)", ) parser.add_argument( "--allow-gnu-c", action="store_true", dest="allow_gnu_c", default=False, help="Specify whether to undefine the '__GNUC__' macro, " "while invoking the C preprocessor.\n" "(default: False. i.e. ctypesgen adds an implicit undefine using '-U __GNUC__'.)\n" "Specify this flag to avoid ctypesgen undefining '__GNUC__' as shown above.", ) parser.add_argument( "-D", "--define", action="append", dest="cpp_defines", metavar="MACRO", default=[], help="Add a definition to the preprocessor via commandline", ) parser.add_argument( "-U", "--undefine", action="append", dest="cpp_undefines", metavar="NAME", default=[], help="Instruct the preprocessor to undefine the specified macro via commandline", ) parser.add_argument( "--save-preprocessed-headers", metavar="FILENAME", dest="save_preprocessed_headers", default=None, help="Save the preprocessed headers to the specified FILENAME", ) parser.add_argument( "--optimize-lexer", dest="optimize_lexer", action="store_true", default=False, help="Run the lexer in optimized mode. This mode requires write " "access to lextab.py file stored within the ctypesgen package.", ) # Processor options parser.add_argument( "-a", "--all-headers", action="store_true", dest="all_headers", default=False, help="include symbols from all headers, including system headers", ) parser.add_argument( "--builtin-symbols", action="store_true", dest="builtin_symbols", default=False, help="include symbols automatically generated by the preprocessor", ) parser.add_argument( "--no-macros", action="store_false", dest="include_macros", default=True, help="Don't output macros.", ) parser.add_argument( "--no-undefs", action="store_false", dest="include_undefs", default=True, help="Do not remove macro definitions as per #undef directives", ) parser.add_argument( "-i", "--include-symbols", action="append", dest="include_symbols", metavar="REGEXPR", default=[], help="Regular expression for symbols to always include. Multiple " "instances of this option will be combined into a single expression " "doing something like '(expr1|expr2|expr3)'.", ) parser.add_argument( "-x", "--exclude-symbols", action="append", dest="exclude_symbols", metavar="REGEXPR", default=[], help="Regular expression for symbols to exclude. Multiple instances " "of this option will be combined into a single expression doing " "something like '(expr1|expr2|expr3)'.", ) parser.add_argument( "--no-stddef-types", action="store_true", dest="no_stddef_types", default=False, help="Do not support extra C types from stddef.h", ) parser.add_argument( "--no-gnu-types", action="store_true", dest="no_gnu_types", default=False, help="Do not support extra GNU C types", ) parser.add_argument( "--no-python-types", action="store_true", dest="no_python_types", default=False, help="Do not support extra C types built in to Python", ) parser.add_argument( "--no-load-library", action="store_true", dest="no_load_library", default=False, help="Do not try to load library during the processing", ) # Printer options parser.add_argument( "--header-template", dest="header_template", default=None, metavar="TEMPLATE", help="Use TEMPLATE as the header template in the output file.", ) parser.add_argument( "--strip-build-path", dest="strip_build_path", default=None, metavar="BUILD_PATH", help="Strip build path from header paths in the wrapper file.", ) parser.add_argument( "--insert-file", dest="inserted_files", default=[], action="append", metavar="FILENAME", help="Add the contents of FILENAME to the end of the wrapper file.", ) parser.add_argument( "--output-language", dest="output_language", metavar="LANGUAGE", default="py", choices=("py", "json"), help="Choose output language", ) parser.add_argument( "-P", "--strip-prefix", dest="strip_prefixes", default=[], action="append", metavar="REGEXPR", help="Regular expression to match prefix to strip from all symbols. " "Multiple instances of this option will be combined into a single " "expression doing something like '(expr1|expr2|expr3)'.", ) # Error options parser.add_argument( "--all-errors", action="store_true", default=False, dest="show_all_errors", help="Display all warnings and errors even if they would not affect output.", ) parser.add_argument( "--show-long-errors", action="store_true", default=False, dest="show_long_errors", help="Display long error messages instead of abbreviating error messages.", ) parser.add_argument( "--no-macro-warnings", action="store_false", default=True, dest="show_macro_warnings", help="Do not print macro warnings.", ) parser.add_argument( "--debug-level", dest="debug_level", default=0, type=int, help="Run ctypesgen with specified debug level (also applies to yacc parser)", ) parser.set_defaults(**core_options.default_values) args = parser.parse_args(givenargs) # Important: don't use +=, it modifies the original list instead of # creating a new one. This can be problematic with repeated API calls. args.compile_libdirs = args.compile_libdirs + args.universal_libdirs args.runtime_libdirs = args.runtime_libdirs + args.universal_libdirs # Figure out what names will be defined by imported Python modules args.other_known_names = find_names_in_modules(args.modules) if len(args.libraries) == 0: msgs.warning_message("No libraries specified", cls="usage") # Fetch printer for the requested output language if args.output_language == "py": printer = printer_python.WrapperPrinter elif args.output_language == "json": printer = printer_json.WrapperPrinter else: assert False # handled by argparse choices # Step 1: Parse descriptions = core_parser.parse(args.headers, args) # Step 2: Process processor.process(descriptions, args) # Step 3: Print printer(args.output, args, descriptions) msgs.status_message("Wrapping complete.") # Correct what may be a common mistake if descriptions.all == []: if not args.all_headers: msgs.warning_message( "There wasn't anything of use in the " "specified header file(s). Perhaps you meant to run with " "--all-headers to include objects from included sub-headers? ", cls="usage", ) if __name__ == "__main__": main() ================================================ FILE: ctypesgen/ctypedescs.py ================================================ """ ctypesgen.ctypedescs contains classes to represent a C type. All of them classes are subclasses of CtypesType. Unlike in previous versions of ctypesgen, CtypesType and its subclasses are completely independent of the parser module. The most important method of CtypesType and its subclasses is the py_string method. str(ctype) returns a string which, when evaluated in the wrapper at runtime, results in a ctypes type object. For example, a CtypesType representing an array of four integers could be created using: >>> ctype = CtypesArray(CtypesSimple("int",True,0),4) str(ctype) would evaluate to "c_int * 4". """ __docformat__ = "restructuredtext" ctypes_type_map = { # typename signed longs ("void", True, 0): "None", ("int", True, 0): "c_int", ("int", False, 0): "c_uint", ("int", True, 1): "c_long", ("int", False, 1): "c_ulong", ("char", True, 0): "c_char", ("char", False, 0): "c_ubyte", ("short", True, 0): "c_short", ("short", False, 0): "c_ushort", ("float", True, 0): "c_float", ("double", True, 0): "c_double", ("double", True, 1): "c_longdouble", ("int8_t", True, 0): "c_int8", ("__int8_t", True, 0): "c_int8", ("__int8", True, 0): "c_int8", ("int16_t", True, 0): "c_int16", ("__int16_t", True, 0): "c_int16", ("__int16", True, 0): "c_int16", ("int32_t", True, 0): "c_int32", ("__int32_t", True, 0): "c_int32", ("__int32", True, 0): "c_int32", ("int64_t", True, 0): "c_int64", ("__int64", True, 0): "c_int64", ("__int64_t", True, 0): "c_int64", ("uint8_t", False, 0): "c_uint8", ("__uint8", False, 0): "c_uint8", ("__uint8_t", False, 0): "c_uint8", ("uint16_t", False, 0): "c_uint16", ("__uint16", False, 0): "c_uint16", ("__uint16_t", False, 0): "c_uint16", ("uint32_t", False, 0): "c_uint32", ("__uint32", False, 0): "c_uint32", ("__uint32_t", False, 0): "c_uint32", ("uint64_t", False, 0): "c_uint64", ("__uint64", False, 0): "c_uint64", ("__uint64_t", False, 0): "c_uint64", ("_Bool", True, 0): "c_bool", ("bool", True, 0): "c_bool", } ctypes_type_map_python_builtin = { ("int", True, -1): "c_short", ("int", False, -1): "c_ushort", ("int", True, 2): "c_longlong", ("int", False, 2): "c_ulonglong", ("size_t", True, 0): "c_size_t", ("off64_t", True, 0): "c_int64", ("wchar_t", True, 0): "c_wchar", ("ptrdiff_t", True, 0): "c_ptrdiff_t", # Requires definition in preamble ("ssize_t", True, 0): "c_ptrdiff_t", # Requires definition in preamble ("va_list", True, 0): "c_void_p", } # This protocol is used for walking type trees. class CtypesTypeVisitor(object): def visit_struct(self, struct): pass def visit_enum(self, enum): pass def visit_typedef(self, name): pass def visit_error(self, error, cls): pass def visit_identifier(self, identifier): # This one comes from inside ExpressionNodes. There may be # ExpressionNode objects in array count expressions. pass def visit_type_and_collect_info(ctype): class Visitor(CtypesTypeVisitor): def visit_struct(self, struct): structs.append(struct) def visit_enum(self, enum): enums.append(enum) def visit_typedef(self, typedef): typedefs.append(typedef) def visit_error(self, error, cls): errors.append((error, cls)) def visit_identifier(self, identifier): identifiers.append(identifier) structs = [] enums = [] typedefs = [] errors = [] identifiers = [] v = Visitor() ctype.visit(v) return structs, enums, typedefs, errors, identifiers # Remove one level of indirection from function pointer; needed for typedefs # and function parameters. def remove_function_pointer(t): if type(t) == CtypesPointer and type(t.destination) == CtypesFunction: return t.destination elif type(t) == CtypesPointer: t.destination = remove_function_pointer(t.destination) return t else: return t class CtypesType(object): def __init__(self): super(CtypesType, self).__init__() self.errors = [] def __repr__(self): return '' % (type(self).__name__, self.py_string()) def error(self, message, cls=None): self.errors.append((message, cls)) def visit(self, visitor): for error, cls in self.errors: visitor.visit_error(error, cls) class CtypesSimple(CtypesType): """Represents a builtin type, like "char" or "int".""" def __init__(self, name, signed, longs): super(CtypesSimple, self).__init__() self.name = name self.signed = signed self.longs = longs def py_string(self, ignore_can_be_ctype=None): return ctypes_type_map[(self.name, self.signed, self.longs)] class CtypesSpecial(CtypesType): def __init__(self, name): super(CtypesSpecial, self).__init__() self.name = name def py_string(self, ignore_can_be_ctype=None): return self.name class CtypesTypedef(CtypesType): """Represents a type defined by a typedef.""" def __init__(self, name): super(CtypesTypedef, self).__init__() self.name = name def visit(self, visitor): if not self.errors: visitor.visit_typedef(self.name) super(CtypesTypedef, self).visit(visitor) def py_string(self, ignore_can_be_ctype=None): return self.name class CtypesBitfield(CtypesType): def __init__(self, base, bitfield): super(CtypesBitfield, self).__init__() self.base = base self.bitfield = bitfield def visit(self, visitor): self.base.visit(visitor) super(CtypesBitfield, self).visit(visitor) def py_string(self, ignore_can_be_ctype=None): return self.base.py_string() class CtypesPointer(CtypesType): def __init__(self, destination, qualifiers): super(CtypesPointer, self).__init__() self.destination = destination self.qualifiers = qualifiers def visit(self, visitor): if self.destination: self.destination.visit(visitor) super(CtypesPointer, self).visit(visitor) def py_string(self, ignore_can_be_ctype=None): return "POINTER(%s)" % self.destination.py_string() class CtypesArray(CtypesType): def __init__(self, base, count): super(CtypesArray, self).__init__() self.base = base self.count = count def visit(self, visitor): self.base.visit(visitor) if self.count: self.count.visit(visitor) super(CtypesArray, self).visit(visitor) def py_string(self, ignore_can_be_ctype=None): if self.count is None: return "POINTER(%s)" % self.base.py_string() if type(self.base) == CtypesArray: return "(%s) * int(%s)" % (self.base.py_string(), self.count.py_string(False)) else: return "%s * int(%s)" % (self.base.py_string(), self.count.py_string(False)) class CtypesNoErrorCheck(object): def py_string(self, ignore_can_be_ctype=None): return "None" def __bool__(self): return False __nonzero__ = __bool__ class CtypesPointerCast(object): def __init__(self, target): self.target = target def py_string(self, ignore_can_be_ctype=None): return "lambda v,*a : cast(v, {})".format(self.target.py_string()) class CtypesFunction(CtypesType): def __init__(self, restype, parameters, variadic, attrib=dict()): super(CtypesFunction, self).__init__() self.restype = restype self.errcheck = CtypesNoErrorCheck() # Don't allow POINTER(None) (c_void_p) as a restype... causes errors # when ctypes automagically returns it as an int. # Instead, convert to POINTER(c_void). c_void is not a ctypes type, # you can make it any arbitrary type. if ( type(self.restype) == CtypesPointer and type(self.restype.destination) == CtypesSimple and self.restype.destination.name == "void" ): # we will provide a means of converting this to a c_void_p self.restype = CtypesPointer(CtypesSpecial("c_ubyte"), ()) self.errcheck = CtypesPointerCast(CtypesSpecial("c_void_p")) # Return "String" instead of "POINTER(c_char)" if self.restype.py_string() == "POINTER(c_char)": if "const" in self.restype.qualifiers: self.restype = CtypesSpecial("c_char_p") else: self.restype = CtypesSpecial("String") self.argtypes = [remove_function_pointer(p) for p in parameters] self.variadic = variadic self.attrib = attrib def visit(self, visitor): self.restype.visit(visitor) for a in self.argtypes: a.visit(visitor) super(CtypesFunction, self).visit(visitor) def py_string(self, ignore_can_be_ctype=None): return "CFUNCTYPE(UNCHECKED(%s), %s)" % ( self.restype.py_string(), ", ".join([a.py_string() for a in self.argtypes]), ) last_tagnum = 0 def anonymous_struct_tagnum(): global last_tagnum last_tagnum += 1 return last_tagnum def fmt_anonymous_struct_tag(num): return "anon_%d" % num def anonymous_struct_tag(): return fmt_anonymous_struct_tag(anonymous_struct_tagnum()) class CtypesStruct(CtypesType): def __init__(self, tag, attrib, variety, members, src=None): super(CtypesStruct, self).__init__() self.tag = tag self.attrib = attrib self.variety = variety # "struct" or "union" self.members = members if type(self.tag) == int or not self.tag: if type(self.tag) == int: self.tag = fmt_anonymous_struct_tag(self.tag) else: self.tag = anonymous_struct_tag() self.anonymous = True else: self.anonymous = False if self.members is None: self.opaque = True else: self.opaque = False self.src = src def get_required_types(self): types = super(CtypesStruct, self).get_required_types() types.add((self.variety, self.tag)) return types def visit(self, visitor): visitor.visit_struct(self) if not self.opaque: for name, ctype in self.members: ctype.visit(visitor) super(CtypesStruct, self).visit(visitor) def get_subtypes(self): if self.opaque: return set() else: return set([m[1] for m in self.members]) def py_string(self, ignore_can_be_ctype=None): return "%s_%s" % (self.variety, self.tag) last_tagnum = 0 def anonymous_enum_tag(): global last_tagnum last_tagnum += 1 return "anon_%d" % last_tagnum class CtypesEnum(CtypesType): def __init__(self, tag, enumerators, src=None): super(CtypesEnum, self).__init__() self.tag = tag self.enumerators = enumerators if not self.tag: self.tag = anonymous_enum_tag() self.anonymous = True else: self.anonymous = False if self.enumerators is None: self.opaque = True else: self.opaque = False self.src = src def visit(self, visitor): visitor.visit_enum(self) super(CtypesEnum, self).visit(visitor) def py_string(self, ignore_can_be_ctype=None): return "enum_%s" % self.tag ================================================ FILE: ctypesgen/descriptions.py ================================================ """ ctypesgen.descriptions contains classes to represent a description of a struct, union, enum, function, constant, variable, or macro. All the description classes are subclassed from an abstract base class, Description. The descriptions module also contains a class, DescriptionCollection, to hold lists of Description objects. """ class DescriptionCollection(object): """Represents a collection of Descriptions.""" def __init__( self, constants, typedefs, structs, enums, functions, variables, macros, all, output_order ): self.constants = constants self.typedefs = typedefs self.structs = structs self.enums = enums self.functions = functions self.variables = variables self.macros = macros self.all = all self.output_order = output_order class Description(object): """Represents a constant, typedef, struct, function, variable, enum, or macro description. Description is an abstract base class.""" def __init__(self, src=None): super(Description, self).__init__() self.src = src # A tuple of (filename, lineno) # If object will be included in output file. Values are "yes", "never", # and "if_needed". self.include_rule = "yes" # A word about requirements, and dependents: # If X requires Y, Y is in X.requirements. # If X is in Y.requirements, then Y is in X.dependents. self.requirements = set() self.dependents = set() # If the processor module finds a fatal error that prevents a # a description from being output, then it appends a string describing # the problem to 'errors'. If it finds a nonfatal error, it appends a # string to 'warnings'. If the description would have been output, then # the errors and warnings are printed. # If there is anything in 'errors' after processing is complete, the # description is not output. self.errors = [] self.warnings = [] def add_requirements(self, reqs): self.requirements = self.requirements.union(reqs) for req in reqs: req.dependents.add(self) def error(self, msg, cls=None): self.errors.append((msg, cls)) def warning(self, msg, cls=None): self.warnings.append((msg, cls)) def __repr__(self): return "" % self.casual_name() def casual_name(self): """Return a name to show the user.""" def py_name(self): """Return the name associated with this description in Python code.""" def c_name(self): """Return the name associated with this description in C code.""" class ConstantDescription(Description): """Simple class to contain information about a constant.""" def __init__(self, name, value, src=None): super(ConstantDescription, self).__init__(src) # Name of constant, a string self.name = name # Value of constant, as an ExpressionNode object self.value = value def casual_name(self): return 'Constant "%s"' % self.name def py_name(self): return self.name def c_name(self): return self.name class TypedefDescription(Description): """Simple container class for a type definition.""" def __init__(self, name, ctype, src=None): super(TypedefDescription, self).__init__(src) self.name = name # Name, a string self.ctype = ctype # The base type as a ctypedescs.CtypeType object def casual_name(self): return 'Typedef "%s"' % self.name def py_name(self): return self.name def c_name(self): return self.name class StructDescription(Description): """Simple container class for a structure or union definition.""" def __init__(self, tag, attrib, variety, members, opaque, ctype, src=None): super(StructDescription, self).__init__(src) # The name of the structure minus the "struct" or "union" self.tag = tag self.attrib = attrib # A string "struct" or "union" self.variety = variety # A list of pairs of (name,ctype) self.members = members # True if struct body was not specified in header file self.opaque = opaque # The original CtypeStruct that created the struct self.ctype = ctype def casual_name(self): return '%s "%s"' % (self.variety.capitalize(), self.tag) def py_name(self): return "%s_%s" % (self.variety, self.tag) def c_name(self): return "%s %s" % (self.variety, self.tag) class EnumDescription(Description): """Simple container class for an enum definition.""" def __init__(self, tag, members, ctype, src=None): super(EnumDescription, self).__init__(src) # The name of the enum, minus the "enum" self.tag = tag # A list of (name,value) pairs where value is a number self.members = members # The original CtypeEnum that created the enum self.ctype = ctype def casual_name(self): return 'Enum "%s"' % self.tag def py_name(self): return "enum_%s" % self.tag def c_name(self): return "enum %s" % self.tag class FunctionDescription(Description): """Simple container class for a C function.""" def __init__(self, name, restype, argtypes, errcheck, variadic, attrib, src): super(FunctionDescription, self).__init__(src) # Name, a string self.name = name # Name according to C - stored in case description is renamed self.cname = name # A ctype representing return type self.restype = restype # A list of ctypes representing the argument types self.argtypes = argtypes # An optional error checker/caster self.errcheck = errcheck # Does this function accept a variable number of arguments? self.variadic = variadic # The set of attributes applied to the function (e.g. stdcall) self.attrib = attrib def casual_name(self): return 'Function "%s"' % self.name def py_name(self): return self.name def c_name(self): return self.cname class VariableDescription(Description): """Simple container class for a C variable declaration.""" def __init__(self, name, ctype, src=None): super(VariableDescription, self).__init__(src) # Name, a string self.name = name # Name according to C - stored in case description is renamed self.cname = name # The type of the variable self.ctype = ctype def casual_name(self): return 'Variable "%s"' % self.name def py_name(self): return self.name def c_name(self): return self.cname class MacroDescription(Description): """Simple container class for a C macro.""" def __init__(self, name, params, expr, src=None): super(MacroDescription, self).__init__(src) self.name = name self.params = params self.expr = expr # ExpressionNode for the macro's body def casual_name(self): return 'Macro "%s"' % self.name def py_name(self): return self.name def c_name(self): return self.name class UndefDescription(Description): """Simple container class for a preprocessor #undef directive.""" def __init__(self, macro, src=None): super(UndefDescription, self).__init__(src) self.include_rule = "if_needed" self.macro = macro def casual_name(self): return 'Undef "%s"' % self.macro.name def py_name(self): return "#undef:%s" % self.macro.name def c_name(self): return "#undef %s" % self.macro.name ================================================ FILE: ctypesgen/expressions.py ================================================ """ The expressions module contains classes to represent an expression. The main class is ExpressionNode. ExpressionNode's most useful method is py_string(), which returns a Python string representing that expression. """ import warnings import keyword from ctypesgen.ctypedescs import ( CtypesPointer, CtypesSimple, CtypesStruct, CtypesType, ) # Right now, the objects in this module are all oriented toward evaluation. # However, they don't have to be, since ctypes objects are mutable. For example, # shouldn't it be possible to translate the macro: # # #define INCREMENT(x) ++x # # into Python? The resulting code should be: # # def INCREMENT(x): # x.value+=1 # return x.value # # On the other hand, this would be a challenge to write. class EvaluationContext(object): """Interface for evaluating expression nodes.""" def evaluate_identifier(self, name): warnings.warn('Attempt to evaluate identifier "%s" failed' % name) return 0 def evaluate_sizeof(self, object): warnings.warn('Attempt to evaluate sizeof object "%s" failed' % str(object)) return 0 def evaluate_parameter(self, name): warnings.warn('Attempt to evaluate parameter "%s" failed' % name) return 0 class ExpressionNode(object): def __init__(self): self.errors = [] def error(self, message, cls=None): self.errors.append((message, cls)) def __repr__(self): try: string = repr(self.py_string(True)) except ValueError: string = "" return "<%s: %s>" % (type(self).__name__, string) def visit(self, visitor): for error, cls in self.errors: visitor.visit_error(error, cls) class ConstantExpressionNode(ExpressionNode): def __init__(self, value, is_literal=False): ExpressionNode.__init__(self) self.value = value self.is_literal = is_literal def evaluate(self, context): return self.value def py_string(self, can_be_ctype): if self.is_literal: return self.value if self.value == float("inf"): return "float('inf')" elif self.value == float("-inf"): return "float('-inf')" return repr(self.value) class IdentifierExpressionNode(ExpressionNode): def __init__(self, name): ExpressionNode.__init__(self) self.name = name def evaluate(self, context): return context.evaluate_identifier(self.name) def visit(self, visitor): visitor.visit_identifier(self.name) ExpressionNode.visit(self, visitor) def py_string(self, can_be_ctype): # Errors will be thrown in generated code if identifier evaluates # to a ctypes object, and can_be_ctype is False. return self.name class ParameterExpressionNode(ExpressionNode): def __init__(self, name): ExpressionNode.__init__(self) self.name = name def evaluate(self, context): return context.evaluate_parameter(self.name) def visit(self, visitor): ExpressionNode.visit(self, visitor) def py_string(self, can_be_ctype): # Errors will be thrown in generated code if parameter is # a ctypes object, and can_be_ctype is False. return self.name class UnaryExpressionNode(ExpressionNode): def __init__(self, name, op, format, child_can_be_ctype, child): ExpressionNode.__init__(self) self.name = name self.op = op self.format = format self.child_can_be_ctype = child_can_be_ctype self.child = child def visit(self, visitor): self.child.visit(visitor) ExpressionNode.visit(self, visitor) def evaluate(self, context): if self.op: return self.op(self.child.evaluate(context)) else: raise ValueError('The C operator "%s" can\'t be evaluated right now' % self.name) def py_string(self, can_be_ctype): return self.format % self.child.py_string(self.child_can_be_ctype and can_be_ctype) class SizeOfExpressionNode(ExpressionNode): def __init__(self, child): ExpressionNode.__init__(self) self.child = child def visit(self, visitor): self.child.visit(visitor) ExpressionNode.visit(self, visitor) def evaluate(self, context): if isinstance(self.child, CtypesType): return context.evaluate_sizeof(self.child) else: return context.evaluate_sizeof_object(self.child) def py_string(self, can_be_ctype): if isinstance(self.child, CtypesType): return "sizeof(%s)" % self.child.py_string() else: return "sizeof(%s)" % self.child.py_string(True) class BinaryExpressionNode(ExpressionNode): def __init__(self, name, op, format, can_be_ctype, left, right): ExpressionNode.__init__(self) self.name = name self.op = op self.format = format self.can_be_ctype = can_be_ctype self.left = left self.right = right def visit(self, visitor): self.left.visit(visitor) self.right.visit(visitor) ExpressionNode.visit(self, visitor) def evaluate(self, context): if self.op: return self.op(self.left.evaluate(context), self.right.evaluate(context)) else: raise ValueError('The C operator "%s" can\'t be evaluated right now' % self.name) def py_string(self, can_be_ctype): return self.format % ( self.left.py_string(self.can_be_ctype[0] and can_be_ctype), self.right.py_string(self.can_be_ctype[0] and can_be_ctype), ) class ConditionalExpressionNode(ExpressionNode): def __init__(self, cond, yes, no): ExpressionNode.__init__(self) self.cond = cond self.yes = yes self.no = no def visit(self, visitor): self.cond.visit(visitor) self.yes.visit(visitor) self.no.visit(visitor) ExpressionNode.visit(self, visitor) def evaluate(self, context): if self.cond.evaluate(context): return self.yes.evaluate(context) else: return self.no.evaluate(context) def py_string(self, can_be_ctype): return "%s and %s or %s" % ( self.cond.py_string(True), self.yes.py_string(can_be_ctype), self.no.py_string(can_be_ctype), ) class AttributeExpressionNode(ExpressionNode): def __init__(self, op, format, base, attribute): ExpressionNode.__init__(self) self.op = op self.format = format self.base = base self.attribute = attribute # Attribute access will raise parse errors if you don't do this. # Fortunately, the processor module does the same thing to # the struct member name. if self.attribute in keyword.kwlist: self.attribute = "_" + self.attribute def visit(self, visitor): self.base.visit(visitor) ExpressionNode.visit(self, visitor) def evaluate(self, context): return self.op(self.base.evaluate(context), self.attribute) def py_string(self, can_be_ctype): if can_be_ctype: return self.format % (self.base.py_string(can_be_ctype), self.attribute) else: return "(%s.value)" % ( self.format % (self.base.py_string(can_be_ctype), self.attribute) ) class CallExpressionNode(ExpressionNode): def __init__(self, function, arguments): ExpressionNode.__init__(self) self.function = function self.arguments = arguments def visit(self, visitor): self.function.visit(visitor) for arg in self.arguments: arg.visit(visitor) ExpressionNode.visit(self, visitor) def evaluate(self, context): arguments = [arg.evaluate(context) for arg in self.arguments] return self.function.evaluate(context)(*arguments) def py_string(self, can_be_ctype): function = self.function.py_string(can_be_ctype) arguments = [x.py_string(can_be_ctype) for x in self.arguments] return "(%s (%s))" % (function, ", ".join(arguments)) class TypeCastExpressionNode(ExpressionNode): """ Type cast expressions as handled by ctypesgen. There is a strong possibility that this does not support all types of casts. """ def __init__(self, base, ctype): ExpressionNode.__init__(self) self.base = base self.ctype = ctype def visit(self, visitor): self.base.visit(visitor) self.ctype.visit(visitor) ExpressionNode.visit(self, visitor) def evaluate(self, context): return self.base.evaluate(context) def py_string(self, can_be_ctype): if isinstance(self.ctype, CtypesPointer): return "cast({}, {})".format(self.base.py_string(True), self.ctype.py_string()) elif isinstance(self.ctype, CtypesStruct): raise TypeError( "conversion to non-scalar type ({}) requested from {}".format( self.ctype, self.base.py_string(False) ) ) else: # In reality, this conversion should only really work if the types # are scalar types. We won't work really hard to test if the types # are indeed scalar. # To be backwards compatible, we always return literals for builtin types. # We use a function to convert to integer for c_char types since # c_char can take integer or byte types, but the others can *only* # take non-char arguments. # ord_if_char must be provided by preambles if isinstance(self.ctype, CtypesSimple) and ( self.ctype.name, self.ctype.signed, ) == ( "char", True, ): ord_if_char = "" elif isinstance(self.ctype, CtypesSimple) and self.ctype.name == "void": # This is a very simple type cast: cast everything to (void) # At least one macro from mingw does this return "None" else: ord_if_char = "ord_if_char" return "({to} ({ord_if_char}({frm}))).value".format( to=self.ctype.py_string(), ord_if_char=ord_if_char, frm=self.base.py_string(False), ) class UnsupportedExpressionNode(ExpressionNode): def __init__(self, message): ExpressionNode.__init__(self) self.message = message self.error(message, "unsupported-type") def evaluate(self, context): raise ValueError("Tried to evaluate an unsupported expression " "node: %s" % self.message) def __repr__(self): return "" def py_string(self, can_be_ctype): raise ValueError("Called py_string() an unsupported expression " "node: %s" % self.message) ================================================ FILE: ctypesgen/libraryloader.py ================================================ """ Load libraries - appropriately for all our supported platforms """ # ---------------------------------------------------------------------------- # Copyright (c) 2008 David James # Copyright (c) 2006-2008 Alex Holkner # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # * Neither the name of pyglet nor the names of its # contributors may be used to endorse or promote products # derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- import ctypes import ctypes.util import glob import os.path import platform import re import sys def _environ_path(name): """Split an environment variable into a path-like list elements""" if name in os.environ: return os.environ[name].split(":") return [] class LibraryLoader: """ A base class For loading of libraries ;-) Subclasses load libraries for specific platforms. """ # library names formatted specifically for platforms name_formats = ["%s"] class Lookup: """Looking up calling conventions for a platform""" mode = ctypes.DEFAULT_MODE def __init__(self, path): super(LibraryLoader.Lookup, self).__init__() self.access = dict(cdecl=ctypes.CDLL(path, self.mode)) def get(self, name, calling_convention="cdecl"): """Return the given name according to the selected calling convention""" if calling_convention not in self.access: raise LookupError( "Unknown calling convention '{}' for function '{}'".format( calling_convention, name ) ) return getattr(self.access[calling_convention], name) def has(self, name, calling_convention="cdecl"): """Return True if this given calling convention finds the given 'name'""" if calling_convention not in self.access: return False return hasattr(self.access[calling_convention], name) def __getattr__(self, name): return getattr(self.access["cdecl"], name) def __init__(self): self.other_dirs = [] def __call__(self, libname): """Given the name of a library, load it.""" paths = self.getpaths(libname) for path in paths: # noinspection PyBroadException try: return self.Lookup(path) except Exception: # pylint: disable=broad-except pass raise ImportError("Could not load %s." % libname) def getpaths(self, libname): """Return a list of paths where the library might be found.""" if os.path.isabs(libname): yield libname else: # search through a prioritized series of locations for the library # we first search any specific directories identified by user for dir_i in self.other_dirs: for fmt in self.name_formats: # dir_i should be absolute already yield os.path.join(dir_i, fmt % libname) # check if this code is even stored in a physical file try: this_file = __file__ except NameError: this_file = None # then we search the directory where the generated python interface is stored if this_file is not None: for fmt in self.name_formats: yield os.path.abspath(os.path.join(os.path.dirname(__file__), fmt % libname)) # now, use the ctypes tools to try to find the library for fmt in self.name_formats: path = ctypes.util.find_library(fmt % libname) if path: yield path # then we search all paths identified as platform-specific lib paths for path in self.getplatformpaths(libname): yield path # Finally, we'll try the users current working directory for fmt in self.name_formats: yield os.path.abspath(os.path.join(os.path.curdir, fmt % libname)) def getplatformpaths(self, _libname): # pylint: disable=no-self-use """Return all the library paths available in this platform""" return [] # Darwin (Mac OS X) class DarwinLibraryLoader(LibraryLoader): """Library loader for MacOS""" name_formats = [ "lib%s.dylib", "lib%s.so", "lib%s.bundle", "%s.dylib", "%s.so", "%s.bundle", "%s", ] class Lookup(LibraryLoader.Lookup): """ Looking up library files for this platform (Darwin aka MacOS) """ # Darwin requires dlopen to be called with mode RTLD_GLOBAL instead # of the default RTLD_LOCAL. Without this, you end up with # libraries not being loadable, resulting in "Symbol not found" # errors mode = ctypes.RTLD_GLOBAL def getplatformpaths(self, libname): if os.path.pathsep in libname: names = [libname] else: names = [fmt % libname for fmt in self.name_formats] for directory in self.getdirs(libname): for name in names: yield os.path.join(directory, name) @staticmethod def getdirs(libname): """Implements the dylib search as specified in Apple documentation: http://developer.apple.com/documentation/DeveloperTools/Conceptual/ DynamicLibraries/Articles/DynamicLibraryUsageGuidelines.html Before commencing the standard search, the method first checks the bundle's ``Frameworks`` directory if the application is running within a bundle (OS X .app). """ dyld_fallback_library_path = _environ_path("DYLD_FALLBACK_LIBRARY_PATH") if not dyld_fallback_library_path: dyld_fallback_library_path = [ os.path.expanduser("~/lib"), "/usr/local/lib", "/usr/lib", ] dirs = [] if "/" in libname: dirs.extend(_environ_path("DYLD_LIBRARY_PATH")) else: dirs.extend(_environ_path("LD_LIBRARY_PATH")) dirs.extend(_environ_path("DYLD_LIBRARY_PATH")) dirs.extend(_environ_path("LD_RUN_PATH")) if hasattr(sys, "frozen") and getattr(sys, "frozen") == "macosx_app": dirs.append(os.path.join(os.environ["RESOURCEPATH"], "..", "Frameworks")) dirs.extend(dyld_fallback_library_path) return dirs # Posix class PosixLibraryLoader(LibraryLoader): """Library loader for POSIX-like systems (including Linux)""" _ld_so_cache = None _include = re.compile(r"^\s*include\s+(?P.*)") name_formats = ["lib%s.so", "%s.so", "%s"] class _Directories(dict): """Deal with directories""" def __init__(self): dict.__init__(self) self.order = 0 def add(self, directory): """Add a directory to our current set of directories""" if len(directory) > 1: directory = directory.rstrip(os.path.sep) # only adds and updates order if exists and not already in set if not os.path.exists(directory): return order = self.setdefault(directory, self.order) if order == self.order: self.order += 1 def extend(self, directories): """Add a list of directories to our set""" for a_dir in directories: self.add(a_dir) def ordered(self): """Sort the list of directories""" return (i[0] for i in sorted(self.items(), key=lambda d: d[1])) def _get_ld_so_conf_dirs(self, conf, dirs): """ Recursive function to help parse all ld.so.conf files, including proper handling of the `include` directive. """ try: with open(conf) as fileobj: for dirname in fileobj: dirname = dirname.strip() if not dirname: continue match = self._include.match(dirname) if not match: dirs.add(dirname) else: for dir2 in glob.glob(match.group("pattern")): self._get_ld_so_conf_dirs(dir2, dirs) except IOError: pass def _create_ld_so_cache(self): # Recreate search path followed by ld.so. This is going to be # slow to build, and incorrect (ld.so uses ld.so.cache, which may # not be up-to-date). Used only as fallback for distros without # /sbin/ldconfig. # # We assume the DT_RPATH and DT_RUNPATH binary sections are omitted. directories = self._Directories() for name in ( "LD_LIBRARY_PATH", "SHLIB_PATH", # HP-UX "LIBPATH", # OS/2, AIX "LIBRARY_PATH", # BE/OS ): if name in os.environ: directories.extend(os.environ[name].split(os.pathsep)) self._get_ld_so_conf_dirs("/etc/ld.so.conf", directories) bitage = platform.architecture()[0] unix_lib_dirs_list = [] if bitage.startswith("64"): # prefer 64 bit if that is our arch unix_lib_dirs_list += ["/lib64", "/usr/lib64"] # must include standard libs, since those paths are also used by 64 bit # installs unix_lib_dirs_list += ["/lib", "/usr/lib"] if sys.platform.startswith("linux"): # Try and support multiarch work in Ubuntu # https://wiki.ubuntu.com/MultiarchSpec if bitage.startswith("32"): # Assume Intel/AMD x86 compat unix_lib_dirs_list += ["/lib/i386-linux-gnu", "/usr/lib/i386-linux-gnu"] elif bitage.startswith("64"): # Assume Intel/AMD x86 compatible unix_lib_dirs_list += [ "/lib/x86_64-linux-gnu", "/usr/lib/x86_64-linux-gnu", ] else: # guess... unix_lib_dirs_list += glob.glob("/lib/*linux-gnu") directories.extend(unix_lib_dirs_list) cache = {} lib_re = re.compile(r"lib(.*)\.s[ol]") # ext_re = re.compile(r"\.s[ol]$") for our_dir in directories.ordered(): try: for path in glob.glob("%s/*.s[ol]*" % our_dir): file = os.path.basename(path) # Index by filename cache_i = cache.setdefault(file, set()) cache_i.add(path) # Index by library name match = lib_re.match(file) if match: library = match.group(1) cache_i = cache.setdefault(library, set()) cache_i.add(path) except OSError: pass self._ld_so_cache = cache def getplatformpaths(self, libname): if self._ld_so_cache is None: self._create_ld_so_cache() result = self._ld_so_cache.get(libname, set()) for i in result: # we iterate through all found paths for library, since we may have # actually found multiple architectures or other library types that # may not load yield i # Windows class WindowsLibraryLoader(LibraryLoader): """Library loader for Microsoft Windows""" name_formats = ["%s.dll", "lib%s.dll", "%slib.dll", "%s"] class Lookup(LibraryLoader.Lookup): """Lookup class for Windows libraries...""" def __init__(self, path): super(WindowsLibraryLoader.Lookup, self).__init__(path) self.access["stdcall"] = ctypes.windll.LoadLibrary(path) # Platform switching # If your value of sys.platform does not appear in this dict, please contact # the Ctypesgen maintainers. loaderclass = { "darwin": DarwinLibraryLoader, "cygwin": WindowsLibraryLoader, "win32": WindowsLibraryLoader, "msys": WindowsLibraryLoader, } load_library = loaderclass.get(sys.platform, PosixLibraryLoader)() def add_library_search_dirs(other_dirs): """ Add libraries to search paths. If library paths are relative, convert them to absolute with respect to this file's directory """ for path in other_dirs: if not os.path.isabs(path): path = os.path.abspath(path) load_library.other_dirs.append(path) del loaderclass ================================================ FILE: ctypesgen/messages.py ================================================ """ ctypesgen.messages contains functions to display status, error, or warning messages to the user. Warning and error messages are also associated with a "message class", which is a string, which currently has no effect. Error classes are: 'usage' - there was something funny about the command-line parameters 'cparser' - there was a syntax error in the header file 'missing-library' - a library could not be loaded 'macro' - a macro could not be translated to Python 'unsupported-type' - there was a type in the header that ctypes cannot use, like "long double". 'other' - catchall. Warning classes are: 'usage' - there was something funny about the command-line parameters 'rename' - a description has been renamed to avoid a name conflict 'other' - catchall. """ import logging __all__ = ["error_message", "warning_message", "status_message"] log = logging.getLogger("ctypesgen") ch = logging.StreamHandler() # use stdio logging_fmt_str = "%(levelname)s: %(message)s" formatter = logging.Formatter(logging_fmt_str) ch.setFormatter(formatter) log.addHandler(ch) log.setLevel(logging.INFO) # default level that ctypesgen was using with original version def error_message(msg, cls=None): log.error("%s", msg) def warning_message(msg, cls=None): log.warning("%s", msg) def status_message(msg): log.info("Status: %s", msg) ================================================ FILE: ctypesgen/options.py ================================================ """ All of the components of ctypegencore require an argument called "options". In command-line usage, this would be an argparse.Namespace object. However, if ctypesgen is used as a standard Python module, constructing this object would be a pain. So this module exists to provide a "default" options object for convenience. """ import argparse import copy default_values = { "other_headers": [], "modules": [], "include_search_paths": [], "compile_libdirs": [], "runtime_libdirs": [], "cpp": "gcc -E", "allow_gnu_c": False, "cpp_defines": [], "cpp_undefines": [], "save_preprocessed_headers": None, "all_headers": False, "builtin_symbols": False, "include_symbols": [], "exclude_symbols": [], "show_all_errors": False, "show_long_errors": False, "show_macro_warnings": True, "header_template": None, "inserted_files": [], "other_known_names": [], "include_macros": True, "include_undefs": True, "libraries": [], "strip_build_path": None, "output_language": "py", "no_stddef_types": False, "no_gnu_types": False, "no_python_types": False, "debug_level": 0, "strip_prefixes": [], "embed_preamble": True, "no_load_library": False, } def get_default_options(): return argparse.Namespace(**copy.deepcopy(default_values)) ================================================ FILE: ctypesgen/parser/.gitignore ================================================ new_parsetab.py parser.out ================================================ FILE: ctypesgen/parser/__init__.py ================================================ """ This package parses C header files and generates lists of functions, typedefs, variables, structs, unions, enums, macros, and constants. This package knows nothing about the libraries themselves. The public interface for this package is the function "parse". Use as follows: >>> descriptions = parse(["inputfile1.h","inputfile2.h"], options) where "options" is an argparse.Namespace object. parse() returns a DescriptionCollection object. See ctypesgen.descriptions for more information. """ from .datacollectingparser import DataCollectingParser def parse(headers, options): parser = DataCollectingParser(headers, options) parser.parse() return parser.data() __all__ = ["parse"] ================================================ FILE: ctypesgen/parser/cdeclarations.py ================================================ """ This file contains classes that represent C declarations. cparser produces declarations in this format, and ctypesparser reformats them into a format that is not C-specific. The other modules don't need to touch these. """ __docformat__ = "restructuredtext" # -------------------------------------------------------------------------- # C Object Model # -------------------------------------------------------------------------- class Declaration(object): def __init__(self): self.declarator = None self.type = Type() self.storage = None self.attrib = Attrib() def __repr__(self): d = {"declarator": self.declarator, "type": self.type} if self.storage: d["storage"] = self.storage li = ["%s=%r" % (k, v) for k, v in d.items()] return "Declaration(%s)" % ", ".join(li) class Declarator(object): pointer = None def __init__(self): self.identifier = None self.initializer = None self.array = None self.parameters = None self.bitfield = None self.attrib = Attrib() # make pointer read-only to catch mistakes early pointer = property(lambda self: None) def __repr__(self): s = self.identifier or "" if self.bitfield: s += f":{self.bitfield.value}" if self.array: s += repr(self.array) if self.initializer: s += " = %r" % self.initializer if self.parameters is not None: s += "(" + ", ".join([repr(p) for p in self.parameters]) + ")" return s class Pointer(Declarator): pointer = None def __init__(self): super(Pointer, self).__init__() self.qualifiers = [] def __repr__(self): q = "" if self.qualifiers: q = "<%s>" % " ".join(self.qualifiers) return "POINTER%s(%r)" % (q, self.pointer) + super(Pointer, self).__repr__() class Array(object): def __init__(self): self.size = None self.array = None def __repr__(self): if self.size: a = "[%r]" % self.size else: a = "[]" if self.array: return repr(self.array) + a else: return a class Parameter(object): def __init__(self): self.type = Type() self.storage = None self.declarator = None self.attrib = Attrib() def __repr__(self): d = {"type": self.type} if self.declarator: d["declarator"] = self.declarator if self.storage: d["storage"] = self.storage li = ["%s=%r" % (k, v) for k, v in d.items()] return "Parameter(%s)" % ", ".join(li) class Type(object): def __init__(self): self.qualifiers = [] self.specifiers = [] def __repr__(self): return " ".join(self.qualifiers + [str(s) for s in self.specifiers]) # These are used only internally. class StorageClassSpecifier(str): def __repr__(self): return "StorageClassSpecifier({})".format(str(self)) class TypeSpecifier(str): def __repr__(self): return "TypeSpecifier({})".format(str(self)) class StructTypeSpecifier(object): def __init__(self, is_union, attrib, tag, declarations): self.is_union = is_union self.attrib = attrib self.tag = tag self.declarations = declarations self.filename = None self.lineno = -1 def __repr__(self): if self.is_union: s = "union" else: s = "struct" if self.attrib: attrs = list() for attr, val in self.attrib.items(): if val and type(val) == str: attrs.append("{}({})".format(attr, val)) elif val: attrs.append(attr) s += " __attribute__(({}))".format(",".join(attrs)) if self.tag and type(self.tag) != int: s += " %s" % self.tag if self.declarations: s += " {%s}" % "; ".join([repr(d) for d in self.declarations]) return s class EnumSpecifier(object): def __init__(self, tag, enumerators, src=None): self.tag = tag self.enumerators = enumerators self.filename = None self.lineno = -1 def __repr__(self): s = "enum" if self.tag: s += " %s" % self.tag if self.enumerators: s += " {%s}" % ", ".join([repr(e) for e in self.enumerators]) return s class Enumerator(object): def __init__(self, name, expression): self.name = name self.expression = expression def __repr__(self): s = self.name if self.expression: s += " = %r" % self.expression return s class TypeQualifier(str): def __repr__(self): return "TypeQualifier({})".format(str(self)) class PragmaPack(object): DEFAULT = None def __init__(self): self.current = self.DEFAULT self.stack = list() def set_default(self): self.current = self.DEFAULT def push(self, id=None, value=None): item = (id, self.current) self.stack.append(item) if value is not None: self.current = value def pop(self, id=None): if not self.stack: if id: return ( "#pragma pack(pop, {id}) encountered without matching " "#pragma pack(push, {id})".format(id=id), ) else: return "#pragma pack(pop) encountered without matching #pragma pack(push)" item = None err = None if id is not None: i = len(self.stack) - 1 while i >= 0 and self.stack[i][0] != id: i -= 1 if i >= 0: item = self.stack[i] self.stack = self.stack[:i] else: err = ( "#pragma pack(pop, {id}) encountered without matching " "#pragma pack(push, {id}); popped last".format(id=id) ) if item is None: item = self.stack.pop() self.current = item[1] return err pragma_pack = PragmaPack() class Attrib(dict): def __init__(self, *a, **kw): if pragma_pack.current: super(Attrib, self).__init__(packed=True, aligned=[pragma_pack.current]) super(Attrib, self).update(*a, **kw) else: super(Attrib, self).__init__(*a, **kw) self._unalias() def __repr__(self): return "Attrib({})".format(dict(self)) def update(self, *a, **kw): super(Attrib, self).update(*a, **kw) self._unalias() def _unalias(self): """ Check for any attribute aliases and remove leading/trailing '__' According to https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html, an attribute can also be preceded/followed by a double underscore ('__'). """ self.pop(None, None) # remove dummy empty attribute fixes = [attr for attr in self if attr.startswith("__") and attr.endswith("__")] for attr in fixes: self[attr[2 : (len(attr) - 2)]] = self.pop(attr) def apply_specifiers(specifiers, declaration): """Apply specifiers to the declaration (declaration may be a Parameter instead).""" for s in specifiers: if type(s) == StorageClassSpecifier: if declaration.storage: # Multiple storage classes, technically an error... ignore it pass declaration.storage = s elif type(s) in (TypeSpecifier, StructTypeSpecifier, EnumSpecifier): declaration.type.specifiers.append(s) elif type(s) == TypeQualifier: declaration.type.qualifiers.append(s) elif type(s) == Attrib: declaration.attrib.update(s) ================================================ FILE: ctypesgen/parser/cgrammar.py ================================================ #!/usr/bin/env python3 """This is a yacc grammar for C. Derived from ANSI C grammar: * Lexicon: http://www.lysator.liu.se/c/ANSI-C-grammar-l.html http://www.quut.com/c/ANSI-C-grammar-l-2011.html * Grammar: http://www.lysator.liu.se/c/ANSI-C-grammar-y.html http://www.quut.com/c/ANSI-C-grammar-y-2011.html Reference is C99: * http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1124.pdf Parts of C2X (C23) is included: * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2731.pdf """ __docformat__ = "restructuredtext" if __name__ == "__main__": # NOTE if this file is modified, run to generate a new parsetab.py # E.g.: # env PYTHONPATH=. python ctypesgen/parser/cgrammar.py # new_parsetab.py is generated in the current directory and needs to be # manually copied (after inspection) to ctypesgen/parser/parsetab.py import sys import os sys.path.insert(0, os.path.join(os.path.pardir, os.path.pardir)) from ctypesgen.parser.cgrammar import main main() sys.exit() import os.path import sys from ctypesgen import expressions from ctypesgen.ctypedescs import anonymous_struct_tagnum from ctypesgen.parser import cdeclarations, yacc reserved_keyword_tokens = ( "SIZEOF", "TYPEDEF", "EXTERN", "STATIC", "AUTO", "REGISTER", "INLINE", "CONST", "RESTRICT", "VOLATILE", "CHAR", "SHORT", "INT", "LONG", "SIGNED", "UNSIGNED", "FLOAT", "DOUBLE", "VOID", "STRUCT", "UNION", "ENUM", "CASE", "DEFAULT", "IF", "ELSE", "SWITCH", "WHILE", "DO", "FOR", "GOTO", "CONTINUE", "BREAK", "RETURN", ) reserved_keyword_tokens_new = ( "_BOOL", "_NORETURN", # "_ALIGNAS", "_ALIGNOF", "_ATOMIC", "_COMPLEX", # "_DECIMAL128", "_DECIMAL32", "_DECIMAL64", # "_GENERIC", "_IMAGINARY", "_STATIC_ASSERT", "_THREAD_LOCAL", ) extra_keywords_with_alias = { "__asm__": "__ASM__", "__attribute__": "__ATTRIBUTE__", "__restrict": "RESTRICT", "__inline__": "INLINE", "__inline": "INLINE", } keyword_map = {} for keyword in reserved_keyword_tokens: keyword_map[keyword.lower()] = keyword for keyword in reserved_keyword_tokens_new: keyword_map[keyword[:2].upper() + keyword[2:].lower()] = keyword keyword_map[keyword[1:].lower()] = keyword keyword_map.update(extra_keywords_with_alias) keywords = tuple(keyword_map.keys()) tokens = reserved_keyword_tokens + reserved_keyword_tokens_new + ( # Identifier "IDENTIFIER", # Type identifiers "TYPE_NAME", # "FUNC_NAME", "TYPEDEF_NAME", # Constants "STRING_LITERAL", "CHARACTER_CONSTANT", # "ENUMERATION_CONSTANT", "I_CONST_HEX", "I_CONST_DEC", "I_CONST_OCT", "I_CONST_BIN", "F_CONST_1", "F_CONST_2", "F_CONST_3", "F_CONST_4", "F_CONST_5", "F_CONST_6", # Operators "PLUS", "MINUS", "TIMES", "DIVIDE", "MOD", "AND", "OR", "NOT", "XOR", "LNOT", "LT", "GT", "CONDOP", "PTR_OP", "INC_OP", "DEC_OP", "LEFT_OP", "RIGHT_OP", "LE_OP", "GE_OP", "EQ_OP", "NE_OP", "AND_OP", "OR_OP", # Assignment "MUL_ASSIGN", "DIV_ASSIGN", "MOD_ASSIGN", "ADD_ASSIGN", "SUB_ASSIGN", "LEFT_ASSIGN", "RIGHT_ASSIGN", "AND_ASSIGN", "XOR_ASSIGN", "OR_ASSIGN", "EQUALS", # Preprocessor "PP_DEFINE", "PP_DEFINE_MACRO_NAME", "PP_DEFINE_NAME", "PP_END_DEFINE", "PP_IDENTIFIER_PASTE", "PP_MACRO_PARAM", "PP_STRINGIFY", "PP_UNDEFINE", # "PP_NUMBER", # Pragma "PRAGMA", "PRAGMA_END", "PRAGMA_PACK", # Delimiters "PERIOD", "ELLIPSIS", "LPAREN", "RPAREN", "LBRACKET", "RBRACKET", "LBRACE", "RBRACE", "COMMA", "SEMI", "COLON", "__ASM__", "__ATTRIBUTE__", ) precedence = (("nonassoc", "IF"), ("nonassoc", "ELSE")) def p_translation_unit(p): """ translation_unit : | translation_unit external_declaration | translation_unit directive """ # Starting production. # Allow empty production so that files with no declarations are still # valid. # Intentionally empty def p_identifier(p): """ identifier : IDENTIFIER | IDENTIFIER PP_IDENTIFIER_PASTE identifier | PP_MACRO_PARAM PP_IDENTIFIER_PASTE identifier | IDENTIFIER PP_IDENTIFIER_PASTE PP_MACRO_PARAM | PP_MACRO_PARAM PP_IDENTIFIER_PASTE PP_MACRO_PARAM """ if len(p) == 2: p[0] = expressions.IdentifierExpressionNode(p[1]) else: # Should it be supported? It wouldn't be very hard to add support. # Basically, it would involve a new ExpressionNode called # an IdentifierPasteExpressionNode that took a list of strings and # ParameterExpressionNodes. Then it would generate code like # "locals()['%s' + '%s' + ...]" where %s was substituted with the # elements of the list. I haven't supported it yet because I think # it's unnecessary and a little too powerful. p[0] = expressions.UnsupportedExpressionNode( "Identifier pasting is not supported by ctypesgen." ) def p_constant_integer(p): """ constant : I_CONST_HEX | I_CONST_DEC | I_CONST_OCT | I_CONST_BIN """ constant = p[1] is_literal = True if constant.isdigit(): is_literal = False constant = int(p[1]) p[0] = expressions.ConstantExpressionNode(constant, is_literal=is_literal) def p_constant_float(p): """ constant : F_CONST_1 | F_CONST_2 | F_CONST_3 | F_CONST_4 | F_CONST_5 | F_CONST_6 """ p[0] = expressions.ConstantExpressionNode(p[1], is_literal=True) def p_constant_character(p): """ constant : CHARACTER_CONSTANT """ constant_char = p[1] p[0] = expressions.ConstantExpressionNode(constant_char) def p_string_literal(p): """ string_literal : STRING_LITERAL """ p[0] = expressions.ConstantExpressionNode(p[1]) def p_multi_string_literal(p): """ multi_string_literal : string_literal | macro_param | multi_string_literal string_literal | multi_string_literal macro_param """ if len(p) == 2: p[0] = p[1] else: p[0] = expressions.BinaryExpressionNode( "string concatenation", (lambda x, y: x + y), "(%s + %s)", (False, False), p[1], p[2] ) def p_macro_param(p): """ macro_param : PP_MACRO_PARAM | PP_STRINGIFY PP_MACRO_PARAM """ if len(p) == 2: p[0] = expressions.ParameterExpressionNode(p[1]) else: p[0] = expressions.ParameterExpressionNode(p[2]) def p_primary_expression(p): """ primary_expression : identifier | constant | multi_string_literal | LPAREN expression RPAREN """ if p[1] == "(": p[0] = p[2] else: p[0] = p[1] def p_postfix_expression(p): """ postfix_expression : primary_expression | postfix_expression LBRACKET expression RBRACKET | postfix_expression LPAREN RPAREN | postfix_expression LPAREN argument_expression_list RPAREN | postfix_expression PERIOD IDENTIFIER | postfix_expression PTR_OP IDENTIFIER | postfix_expression INC_OP | postfix_expression DEC_OP """ if len(p) == 2: p[0] = p[1] elif p[2] == "[": p[0] = expressions.BinaryExpressionNode( "array access", (lambda a, b: a[b]), "(%s [%s])", (True, False), p[1], p[3] ) elif p[2] == "(": if p[3] == ")": p[0] = expressions.CallExpressionNode(p[1], []) else: p[0] = expressions.CallExpressionNode(p[1], p[3]) elif p[2] == ".": p[0] = expressions.AttributeExpressionNode( (lambda x, a: getattr(x, a)), "(%s.%s)", p[1], p[3] ) elif p[2] == "->": p[0] = expressions.AttributeExpressionNode( (lambda x, a: getattr(x.contents, a)), "(%s.contents.%s)", p[1], p[3] ) elif p[2] == "++": p[0] = expressions.UnaryExpressionNode( "increment", (lambda x: x + 1), "(%s + 1)", False, p[1] ) elif p[2] == "--": p[0] = expressions.UnaryExpressionNode( "decrement", (lambda x: x - 1), "(%s - 1)", False, p[1] ) def p_argument_expression_list(p): """ argument_expression_list : assignment_expression | argument_expression_list COMMA assignment_expression | type_name | argument_expression_list COMMA type_name """ if len(p) == 4: p[1].append(p[3]) p[0] = p[1] else: p[0] = [p[1]] def p_asm_expression(p): """ asm_expression : __ASM__ volatile_opt LPAREN string_literal RPAREN | __ASM__ volatile_opt LPAREN string_literal COLON str_opt_expr_pair_list RPAREN | __ASM__ volatile_opt LPAREN string_literal COLON str_opt_expr_pair_list COLON str_opt_expr_pair_list RPAREN | __ASM__ volatile_opt LPAREN string_literal COLON str_opt_expr_pair_list COLON str_opt_expr_pair_list COLON str_opt_expr_pair_list RPAREN """ # Definitely not ISO C, adapted from example ANTLR GCC parser at # http://www.antlr.org/grammar/cgram//grammars/GnuCParser.g # but more lenient (expressions permitted in optional final part, when # they shouldn't be -- avoids shift/reduce conflict with # str_opt_expr_pair_list). p[0] = expressions.UnsupportedExpressionNode("This node is ASM assembler.") def p_str_opt_expr_pair_list(p): """ str_opt_expr_pair_list : | str_opt_expr_pair | str_opt_expr_pair_list COMMA str_opt_expr_pair """ def p_str_opt_expr_pair(p): """ str_opt_expr_pair : string_literal | string_literal LPAREN expression RPAREN """ def p_volatile_opt(p): """ volatile_opt : | VOLATILE """ prefix_ops_dict = { "++": ("increment", (lambda x: x + 1), "(%s + 1)", False), "--": ("decrement", (lambda x: x - 1), "(%s - 1)", False), "&": ("reference ('&')", None, "pointer(%s)", True), "*": ("dereference ('*')", None, "(%s[0])", True), "+": ("unary '+'", (lambda x: x), "%s", True), "-": ("negation", (lambda x: -x), "(-%s)", False), "~": ("inversion", (lambda x: ~x), "(~%s)", False), "!": ("logical not", (lambda x: not x), "(not %s)", True), } def p_unary_expression(p): """ unary_expression : postfix_expression | INC_OP unary_expression | DEC_OP unary_expression | unary_operator cast_expression | SIZEOF unary_expression | SIZEOF LPAREN type_name RPAREN | asm_expression """ if len(p) == 2: p[0] = p[1] elif p[1] == "sizeof": if len(p) == 5: p[0] = expressions.SizeOfExpressionNode(p[3]) else: p[0] = expressions.SizeOfExpressionNode(p[2]) else: name, op, format, can_be_ctype = prefix_ops_dict[p[1]] p[0] = expressions.UnaryExpressionNode(name, op, format, can_be_ctype, p[2]) def p_unary_operator(p): """ unary_operator : AND | TIMES | PLUS | MINUS | NOT | LNOT """ p[0] = p[1] def p_cast_expression(p): """ cast_expression : unary_expression | LPAREN type_name RPAREN cast_expression """ if len(p) == 2: p[0] = p[1] else: p[0] = expressions.TypeCastExpressionNode(p[4], p[2]) mult_ops_dict = { "*": ("multiplication", (lambda x, y: x * y), "(%s * %s)"), "/": ("division", (lambda x, y: x / y), "(%s / %s)"), "%": ("modulo", (lambda x, y: x % y), "(%s %% %s)"), } def p_multiplicative_expression(p): """ multiplicative_expression : cast_expression | multiplicative_expression TIMES cast_expression | multiplicative_expression DIVIDE cast_expression | multiplicative_expression MOD cast_expression """ if len(p) == 2: p[0] = p[1] else: name, op, format = mult_ops_dict[p[2]] p[0] = expressions.BinaryExpressionNode(name, op, format, (False, False), p[1], p[3]) add_ops_dict = { "+": ("addition", (lambda x, y: x + y), "(%s + %s)"), "-": ("subtraction", (lambda x, y: x - y), "(%s - %s)"), } def p_additive_expression(p): """ additive_expression : multiplicative_expression | additive_expression PLUS multiplicative_expression | additive_expression MINUS multiplicative_expression """ if len(p) == 2: p[0] = p[1] else: name, op, format = add_ops_dict[p[2]] p[0] = expressions.BinaryExpressionNode(name, op, format, (False, False), p[1], p[3]) shift_ops_dict = { ">>": ("right shift", (lambda x, y: x >> y), "(%s >> %s)"), "<<": ("left shift", (lambda x, y: x << y), "(%s << %s)"), } def p_shift_expression(p): """ shift_expression : additive_expression | shift_expression LEFT_OP additive_expression | shift_expression RIGHT_OP additive_expression """ if len(p) == 2: p[0] = p[1] else: name, op, format = shift_ops_dict[p[2]] p[0] = expressions.BinaryExpressionNode(name, op, format, (False, False), p[1], p[3]) rel_ops_dict = { ">": ("greater-than", (lambda x, y: x > y), "(%s > %s)"), "<": ("less-than", (lambda x, y: x < y), "(%s < %s)"), ">=": ("greater-than-equal", (lambda x, y: x >= y), "(%s >= %s)"), "<=": ("less-than-equal", (lambda x, y: x <= y), "(%s <= %s)"), } def p_relational_expression(p): """ relational_expression : shift_expression | relational_expression LT shift_expression | relational_expression GT shift_expression | relational_expression LE_OP shift_expression | relational_expression GE_OP shift_expression """ if len(p) == 2: p[0] = p[1] else: name, op, format = rel_ops_dict[p[2]] p[0] = expressions.BinaryExpressionNode(name, op, format, (False, False), p[1], p[3]) equality_ops_dict = { "==": ("equals", (lambda x, y: x == y), "(%s == %s)"), "!=": ("not equals", (lambda x, y: x != y), "(%s != %s)"), } def p_equality_expression(p): """ equality_expression : relational_expression | equality_expression EQ_OP relational_expression | equality_expression NE_OP relational_expression """ if len(p) == 2: p[0] = p[1] else: name, op, format = equality_ops_dict[p[2]] p[0] = expressions.BinaryExpressionNode(name, op, format, (False, False), p[1], p[3]) def p_and_expression(p): """ and_expression : equality_expression | and_expression AND equality_expression """ if len(p) == 2: p[0] = p[1] else: p[0] = expressions.BinaryExpressionNode( "bitwise and", (lambda x, y: x & y), "(%s & %s)", (False, False), p[1], p[3] ) def p_exclusive_or_expression(p): """ exclusive_or_expression : and_expression | exclusive_or_expression XOR and_expression """ if len(p) == 2: p[0] = p[1] else: p[0] = expressions.BinaryExpressionNode( "bitwise xor", (lambda x, y: x ^ y), "(%s ^ %s)", (False, False), p[1], p[3] ) def p_inclusive_or_expression(p): """ inclusive_or_expression : exclusive_or_expression | inclusive_or_expression OR exclusive_or_expression """ if len(p) == 2: p[0] = p[1] else: p[0] = expressions.BinaryExpressionNode( "bitwise or", (lambda x, y: x | y), "(%s | %s)", (False, False), p[1], p[3] ) def p_logical_and_expression(p): """ logical_and_expression : inclusive_or_expression | logical_and_expression AND_OP inclusive_or_expression """ if len(p) == 2: p[0] = p[1] else: p[0] = expressions.BinaryExpressionNode( "logical and", (lambda x, y: x and y), "(%s and %s)", (True, True), p[1], p[3] ) def p_logical_or_expression(p): """ logical_or_expression : logical_and_expression | logical_or_expression OR_OP logical_and_expression """ if len(p) == 2: p[0] = p[1] else: p[0] = expressions.BinaryExpressionNode( "logical and", (lambda x, y: x or y), "(%s or %s)", (True, True), p[1], p[3] ) def p_conditional_expression(p): """ conditional_expression : logical_or_expression | logical_or_expression CONDOP expression COLON conditional_expression """ if len(p) == 2: p[0] = p[1] else: p[0] = expressions.ConditionalExpressionNode(p[1], p[3], p[5]) assign_ops_dict = { "*=": ("multiply", (lambda x, y: x * y), "(%s * %s)"), "/=": ("divide", (lambda x, y: x / y), "(%s / %s)"), "%=": ("modulus", (lambda x, y: x % y), "(%s % %s)"), "+=": ("addition", (lambda x, y: x + y), "(%s + %s)"), "-=": ("subtraction", (lambda x, y: x - y), "(%s - %s)"), "<<=": ("left shift", (lambda x, y: x << y), "(%s << %s)"), ">>=": ("right shift", (lambda x, y: x >> y), "(%s >> %s)"), "&=": ("bitwise and", (lambda x, y: x & y), "(%s & %s)"), "^=": ("bitwise xor", (lambda x, y: x ^ y), "(%s ^ %s)"), "|=": ("bitwise or", (lambda x, y: x | y), "(%s | %s)"), } def p_assignment_expression(p): """ assignment_expression : conditional_expression | unary_expression assignment_operator assignment_expression """ if len(p) == 2: p[0] = p[1] else: # In C, the value of (x*=3) is the same as (x*3). We support that here. # However, we don't support the change in the value of x. if p[2] == "=": p[0] = p[3] else: name, op, format = assign_ops_dict[p[2]] p[0] = expressions.BinaryExpressionNode(name, op, format, (True, True), p[1], p[3]) def p_assignment_operator(p): """ assignment_operator : EQUALS | MUL_ASSIGN | DIV_ASSIGN | MOD_ASSIGN | ADD_ASSIGN | SUB_ASSIGN | LEFT_ASSIGN | RIGHT_ASSIGN | AND_ASSIGN | XOR_ASSIGN | OR_ASSIGN """ p[0] = p[1] def p_expression(p): """ expression : assignment_expression | expression COMMA assignment_expression """ p[0] = p[1] # We don't need to support sequence expressions... def p_constant_expression(p): """ constant_expression : conditional_expression """ p[0] = p[1] def p_declaration(p): """ declaration : declaration_impl SEMI """ # The ';' must be here, not in 'declaration', as declaration needs to # be executed before the ';' is shifted (otherwise the next lookahead will # be read, which may be affected by this declaration if its a typedef. def p_declaration_impl(p): """ declaration_impl : declaration_specifier_list | declaration_specifier_list init_declarator_list """ declaration = cdeclarations.Declaration() cdeclarations.apply_specifiers(p[1], declaration) if len(p) == 2: filename = p.slice[1].filename lineno = p.slice[1].lineno p.parser.cparser.impl_handle_declaration(declaration, filename, lineno) return filename = p.slice[2].filename lineno = p.slice[2].lineno for declarator in p[2]: declaration.declarator = declarator p.parser.cparser.impl_handle_declaration(declaration, filename, lineno) def p_declaration_specifier_list(p): """ declaration_specifier_list : gcc_attributes declaration_specifier gcc_attributes | declaration_specifier_list declaration_specifier gcc_attributes """ if type(p[1]) == cdeclarations.Attrib: p[0] = (p[1], p[2], p[3]) p.slice[0].filename = p.slice[2].filename p.slice[0].lineno = p.slice[2].lineno else: p[0] = p[1] + (p[2], p[3]) p.slice[0].filename = p.slice[1].filename p.slice[0].lineno = p.slice[1].lineno def p_declaration_specifier(p): """ declaration_specifier : storage_class_specifier | type_specifier | type_qualifier | function_specifier """ p[0] = p[1] def p_init_declarator_list(p): """ init_declarator_list : init_declarator | init_declarator_list COMMA init_declarator """ if len(p) > 2: p[0] = p[1] + (p[3],) else: p[0] = (p[1],) def p_init_declarator(p): """ init_declarator : declarator gcc_attributes | declarator gcc_attributes EQUALS initializer """ p[0] = p[1] p[0].attrib.update(p[2]) p.slice[0].filename = p.slice[1].filename p.slice[0].lineno = p.slice[1].lineno if len(p) > 3: p[0].initializer = p[4] def p_storage_class_specifier(p): """ storage_class_specifier : TYPEDEF | EXTERN | STATIC | AUTO | REGISTER """ p[0] = cdeclarations.StorageClassSpecifier(p[1]) def p_type_specifier(p): """ type_specifier : VOID | _BOOL | CHAR | SHORT | INT | LONG | FLOAT | DOUBLE | SIGNED | UNSIGNED | struct_or_union_specifier | enum_specifier | TYPE_NAME """ if type(p[1]) in (cdeclarations.StructTypeSpecifier, cdeclarations.EnumSpecifier): p[0] = p[1] else: p[0] = cdeclarations.TypeSpecifier(p[1]) def p_struct_or_union_specifier(p): """ struct_or_union_specifier : struct_or_union gcc_attributes IDENTIFIER LBRACE member_declaration_list RBRACE | struct_or_union gcc_attributes TYPE_NAME LBRACE member_declaration_list RBRACE | struct_or_union gcc_attributes LBRACE member_declaration_list RBRACE | struct_or_union gcc_attributes IDENTIFIER | struct_or_union gcc_attributes TYPE_NAME """ # format of grammar for gcc_attributes taken from c-parser.c in GCC source. # The TYPE_NAME ones are dodgy, needed for Apple headers # CoreServices.framework/Frameworks/CarbonCore.framework/Headers/Files.h. # CoreServices.framework/Frameworks/OSServices.framework/Headers/Power.h tag = None decl = None if len(p) == 4: # struct [attributes] tag = p[3] elif p[3] == "{": tag, decl = anonymous_struct_tagnum(), p[4] else: tag, decl = p[3], p[5] p[0] = cdeclarations.StructTypeSpecifier(p[1], p[2], tag, decl) p.slice[0].filename = p.slice[1].filename p.slice[0].lineno = p.slice[1].lineno p[0].filename = p.slice[1].filename p[0].lineno = p.slice[1].lineno def p_struct_or_union(p): """ struct_or_union : STRUCT | UNION """ p[0] = p[1] == "union" def p_gcc_attributes(p): """ gcc_attributes : | gcc_attributes gcc_attribute """ # Allow empty production on attributes (take from c-parser.c in GCC source) if len(p) == 1: p[0] = cdeclarations.Attrib() else: p[0] = p[1] p[0].update(p[2]) def p_gcc_attribute(p): """ gcc_attribute : __ATTRIBUTE__ LPAREN LPAREN gcc_attrib_list RPAREN RPAREN """ p[0] = cdeclarations.Attrib() p[0].update(p[4]) def p_gcc_attrib_list(p): """ gcc_attrib_list : gcc_attrib | gcc_attrib_list COMMA gcc_attrib """ if len(p) == 2: p[0] = (p[1],) else: p[0] = p[1] + (p[3],) def p_gcc_attrib(p): """ gcc_attrib : | IDENTIFIER | IDENTIFIER LPAREN argument_expression_list RPAREN """ if len(p) == 1: p[0] = (None, None) elif len(p) == 2: p[0] = (p[1], True) elif len(p) == 5: p[0] = (p[1], p[3]) else: raise RuntimeError("Should never reach this part of the grammar") def p_member_declaration_list(p): """ member_declaration_list : member_declaration | member_declaration_list member_declaration """ if len(p) == 2: p[0] = p[1] else: p[0] = p[1] + p[2] def p_member_declaration(p): """ member_declaration : specifier_qualifier_list member_declarator_list SEMI | specifier_qualifier_list SEMI """ # p[0] returned is a tuple, to handle multiple declarators in one # declaration. r = () if len(p) >= 4: for declarator in p[2]: declaration = cdeclarations.Declaration() cdeclarations.apply_specifiers(p[1], declaration) declaration.declarator = declarator r += (declaration,) else: # anonymous field (C11/GCC extension) declaration = cdeclarations.Declaration() cdeclarations.apply_specifiers(p[1], declaration) r = (declaration,) p[0] = r def p_specifier_qualifier_list(p): """ specifier_qualifier_list : gcc_attributes specifier_qualifier gcc_attributes | specifier_qualifier_list specifier_qualifier gcc_attributes """ if type(p[1]) == cdeclarations.Attrib: p[0] = (p[1], p[2], p[3]) else: p[0] = p[1] + (p[2], p[3]) def p_specifier_qualifier(p): """ specifier_qualifier : type_specifier | type_qualifier """ p[0] = p[1] def p_member_declarator_list(p): """ member_declarator_list : member_declarator | member_declarator_list COMMA member_declarator """ if len(p) == 2: p[0] = (p[1],) else: p[0] = p[1] + (p[3],) def p_member_declarator(p): """ member_declarator : declarator gcc_attributes | COLON constant_expression gcc_attributes | declarator COLON constant_expression gcc_attributes """ if p[1] == ":": p[0] = cdeclarations.Declarator() p[0].bitfield = p[2] else: p[0] = p[1] # Bitfield support if p[2] == ":": p[0].bitfield = p[3] p[0].attrib.update(p[len(p) - 1]) def p_enum_specifier(p): """ enum_specifier : ENUM LBRACE enumerator_list RBRACE | ENUM IDENTIFIER LBRACE enumerator_list RBRACE | ENUM IDENTIFIER """ if len(p) == 5: p[0] = cdeclarations.EnumSpecifier(None, p[3]) elif len(p) == 6: p[0] = cdeclarations.EnumSpecifier(p[2], p[4]) else: p[0] = cdeclarations.EnumSpecifier(p[2], ()) p[0].filename = p.slice[0].filename p[0].lineno = p.slice[0].lineno def p_enumerator_list(p): """ enumerator_list : enumerator_list_iso | enumerator_list_iso COMMA """ # Apple headers sometimes have trailing ',' after enumerants, which is # not ISO C. p[0] = p[1] def p_enumerator_list_iso(p): """ enumerator_list_iso : enumerator | enumerator_list_iso COMMA enumerator """ if len(p) == 2: p[0] = (p[1],) else: p[0] = p[1] + (p[3],) def p_enumerator(p): """ enumerator : IDENTIFIER | IDENTIFIER EQUALS constant_expression """ if len(p) == 2: p[0] = cdeclarations.Enumerator(p[1], None) else: p[0] = cdeclarations.Enumerator(p[1], p[3]) def p_type_qualifier(p): """ type_qualifier : CONST | VOLATILE | RESTRICT """ p[0] = cdeclarations.TypeQualifier(p[1]) def p_function_specifier(p): """ function_specifier : INLINE | _NORETURN """ def p_declarator(p): """ declarator : pointer direct_declarator | direct_declarator """ if len(p) > 2: p[0] = p[1] ptr = p[1] while ptr.pointer: ptr = ptr.pointer ptr.pointer = p[2] p[2].attrib.update(p[1].attrib) else: p[0] = p[1] def p_direct_declarator(p): """ direct_declarator : IDENTIFIER | LPAREN gcc_attributes declarator RPAREN | direct_declarator LBRACKET constant_expression RBRACKET | direct_declarator LBRACKET RBRACKET | direct_declarator LPAREN parameter_type_list RPAREN | direct_declarator LPAREN identifier_list RPAREN | direct_declarator LPAREN RPAREN """ if isinstance(p[1], cdeclarations.Declarator): p[0] = p[1] if p[2] == "[": a = cdeclarations.Array() a.array = p[0].array p[0].array = a if p[3] != "]": a.size = p[3] else: if p[3] == ")": p[0].parameters = () else: p[0].parameters = p[3] elif p[1] == "(": p[0] = p[3] p[3].attrib.update(p[2]) else: p[0] = cdeclarations.Declarator() p[0].identifier = p[1] # Check parameters for (void) and simplify to empty tuple. if p[0].parameters and len(p[0].parameters) == 1: param = p[0].parameters[0] if param.type.specifiers == ["void"] and not param.declarator: p[0].parameters = () def p_pointer(p): """ pointer : TIMES | TIMES type_qualifier_list | TIMES pointer | TIMES type_qualifier_list pointer """ if len(p) == 2: p[0] = cdeclarations.Pointer() elif len(p) == 3 and isinstance(p[2], cdeclarations.Pointer): p[0] = cdeclarations.Pointer() p[0].pointer = p[2] p[0].attrib.update(p[2].attrib) else: p[0] = cdeclarations.Pointer() for tq in p[2]: if isinstance(tq, cdeclarations.Attrib): p[0].attrib.update(tq) else: p[0].qualifiers += (tq,) if len(p) == 4: p[0].pointer = p[3] p[0].attrib.update(p[3].attrib) def p_type_qualifier_list(p): """ type_qualifier_list : type_qualifier | gcc_attribute | type_qualifier_list type_qualifier | type_qualifier_list gcc_attribute """ if len(p) > 2: p[0] = p[1] + (p[2],) else: p[0] = (p[1],) def p_parameter_type_list(p): """ parameter_type_list : parameter_list | parameter_list COMMA ELLIPSIS """ if len(p) > 2: p[0] = p[1] + (p[3],) else: p[0] = p[1] def p_parameter_list(p): """ parameter_list : parameter_declaration | parameter_list COMMA parameter_declaration """ if len(p) > 2: p[0] = p[1] + (p[3],) else: p[0] = (p[1],) def p_parameter_declaration(p): """ parameter_declaration : declaration_specifier_list declarator gcc_attributes | declaration_specifier_list abstract_declarator | declaration_specifier_list """ p[0] = cdeclarations.Parameter() specs = p[1] if len(p) == 4: # add the attributes as a final specifier specs += (p[3],) p[0].declarator = p[2] elif len(p) == 3: p[0].declarator = p[2] cdeclarations.apply_specifiers(specs, p[0]) def p_identifier_list(p): """ identifier_list : IDENTIFIER | identifier_list COMMA IDENTIFIER """ param = cdeclarations.Parameter() param.declarator = cdeclarations.Declarator() if len(p) > 2: param.declarator.identifier = p[3] p[0] = p[1] + (param,) else: param.declarator.identifier = p[1] p[0] = (param,) def p_type_name(p): """ type_name : specifier_qualifier_list | specifier_qualifier_list abstract_declarator """ typ = p[1] if len(p) == 3: declarator = p[2] else: declarator = None declaration = cdeclarations.Declaration() declaration.declarator = declarator cdeclarations.apply_specifiers(typ, declaration) ctype = p.parser.cparser.get_ctypes_type(declaration.type, declaration.declarator) p[0] = ctype def p_abstract_declarator(p): """ abstract_declarator : pointer | direct_abstract_declarator gcc_attributes | pointer direct_abstract_declarator gcc_attributes """ if len(p) == 2: p[0] = p[1] ptr = p[0] while ptr.pointer: ptr = ptr.pointer # Only if doesn't already terminate in a declarator if type(ptr) == cdeclarations.Pointer: ptr.pointer = cdeclarations.Declarator() ptr.pointer.attrib.update(p[1].attrib) else: ptr.attrib.update(p[1].attrib) elif len(p) == 3: p[0] = p[1] p[1].attrib.update(p[2]) else: p[0] = p[1] ptr = p[0] while ptr.pointer: ptr = ptr.pointer ptr.pointer = p[2] p[2].attrib.update(p[1].attrib) p[2].attrib.update(p[3]) def p_direct_abstract_declarator(p): """ direct_abstract_declarator : LPAREN gcc_attributes abstract_declarator RPAREN | LBRACKET RBRACKET | LBRACKET constant_expression RBRACKET | direct_abstract_declarator LBRACKET RBRACKET | direct_abstract_declarator LBRACKET constant_expression RBRACKET | LPAREN RPAREN | LPAREN parameter_type_list RPAREN | direct_abstract_declarator LPAREN RPAREN | direct_abstract_declarator LPAREN parameter_type_list RPAREN """ if p[1] == "(" and isinstance(p[3], cdeclarations.Declarator): p[0] = p[3] p[3].attrib.update(p[2]) else: if isinstance(p[1], cdeclarations.Declarator): p[0] = p[1] if p[2] == "[": a = cdeclarations.Array() a.array = p[0].array p[0].array = a if p[3] != "]": p[0].array.size = p[3] elif p[2] == "(": if p[3] == ")": p[0].parameters = () else: p[0].parameters = p[3] else: p[0] = cdeclarations.Declarator() if p[1] == "[": p[0].array = cdeclarations.Array() if p[2] != "]": p[0].array.size = p[2] elif p[1] == "(": if p[2] == ")": p[0].parameters = () else: p[0].parameters = p[2] # Check parameters for (void) and simplify to empty tuple. if p[0].parameters and len(p[0].parameters) == 1: param = p[0].parameters[0] if param.type.specifiers == ["void"] and not param.declarator: p[0].parameters = () def p_initializer(p): """ initializer : assignment_expression | LBRACE initializer_list RBRACE | LBRACE initializer_list COMMA RBRACE """ def p_initializer_list(p): """ initializer_list : initializer | initializer_list COMMA initializer """ def p_statement(p): """ statement : labeled_statement | compound_statement | expression_statement | selection_statement | iteration_statement | jump_statement """ def p_labeled_statement(p): """ labeled_statement : IDENTIFIER COLON statement | CASE constant_expression COLON statement | DEFAULT COLON statement """ def p_compound_statement(p): """ compound_statement : LBRACE RBRACE | LBRACE statement_list RBRACE | LBRACE declaration_list RBRACE | LBRACE declaration_list statement_list RBRACE """ def p_compound_statement_error(p): """ compound_statement : LBRACE error RBRACE """ # Error resynchronisation catch-all def p_declaration_list(p): """ declaration_list : declaration | declaration_list declaration """ def p_statement_list(p): """ statement_list : statement | statement_list statement """ def p_expression_statement(p): """ expression_statement : SEMI | expression SEMI """ def p_expression_statement_error(p): """ expression_statement : error SEMI """ # Error resynchronisation catch-all def p_selection_statement(p): """ selection_statement : IF LPAREN expression RPAREN statement %prec IF | IF LPAREN expression RPAREN statement ELSE statement | SWITCH LPAREN expression RPAREN statement """ def p_iteration_statement(p): """ iteration_statement : WHILE LPAREN expression RPAREN statement | DO statement WHILE LPAREN expression RPAREN SEMI | FOR LPAREN expression_statement expression_statement RPAREN statement | FOR LPAREN expression_statement expression_statement expression RPAREN statement """ def p_jump_statement(p): """ jump_statement : GOTO IDENTIFIER SEMI | CONTINUE SEMI | BREAK SEMI | RETURN SEMI | RETURN expression SEMI """ def p_external_declaration(p): """ external_declaration : declaration | function_definition """ # Intentionally empty def p_function_definition(p): """ function_definition : declaration_specifier_list declarator declaration_list compound_statement | declaration_specifier_list declarator compound_statement | declarator declaration_list compound_statement | declarator compound_statement """ # No impl of function defs def p_directive(p): """ directive : define | undefine | pragma """ def p_define(p): """ define : PP_DEFINE PP_DEFINE_NAME PP_END_DEFINE | PP_DEFINE PP_DEFINE_NAME type_name PP_END_DEFINE | PP_DEFINE PP_DEFINE_NAME constant_expression PP_END_DEFINE | PP_DEFINE PP_DEFINE_MACRO_NAME LPAREN RPAREN PP_END_DEFINE | PP_DEFINE PP_DEFINE_MACRO_NAME LPAREN RPAREN constant_expression PP_END_DEFINE | PP_DEFINE PP_DEFINE_MACRO_NAME LPAREN macro_parameter_list RPAREN PP_END_DEFINE | PP_DEFINE PP_DEFINE_MACRO_NAME LPAREN macro_parameter_list RPAREN constant_expression PP_END_DEFINE """ filename = p.slice[1].filename lineno = p.slice[1].lineno if p[3] != "(": if len(p) == 4: p.parser.cparser.handle_define_constant(p[2], None, filename, lineno) else: p.parser.cparser.handle_define_constant(p[2], p[3], filename, lineno) else: if p[4] == ")": params = [] if len(p) == 6: expr = None elif len(p) == 7: expr = p[5] else: params = p[4] if len(p) == 7: expr = None elif len(p) == 8: expr = p[6] filename = p.slice[1].filename lineno = p.slice[1].lineno p.parser.cparser.handle_define_macro(p[2], params, expr, filename, lineno) def p_define_error(p): """ define : PP_DEFINE error PP_END_DEFINE """ lexer = p[2].lexer clexdata = lexer.tokens start = end = p[2].clexpos while clexdata[start].type != "PP_DEFINE": start -= 1 while clexdata[end].type != "PP_END_DEFINE": end += 1 name = clexdata[start + 1].value if clexdata[start + 1].type == "PP_DEFINE_NAME": params = None contents = [t.value for t in clexdata[start + 2 : end]] else: end_of_param_list = start while clexdata[end_of_param_list].value != ")" and end_of_param_list < end: end_of_param_list += 1 params = [t.value for t in clexdata[start + 3 : end_of_param_list] if t.value != ","] contents = [t.value for t in clexdata[end_of_param_list + 1 : end]] filename = p.slice[1].filename lineno = p.slice[1].lineno p[2].lexer.cparser.handle_define_unparseable(name, params, contents, filename, lineno) def p_undefine(p): """ undefine : PP_UNDEFINE PP_DEFINE_NAME PP_END_DEFINE """ filename = p.slice[1].filename lineno = p.slice[1].lineno macro = expressions.IdentifierExpressionNode(p[2]) p.parser.cparser.handle_undefine(macro, filename, lineno) def p_macro_parameter_list(p): """ macro_parameter_list : PP_MACRO_PARAM | macro_parameter_list COMMA PP_MACRO_PARAM """ if len(p) == 2: p[0] = [p[1]] else: p[1].append(p[3]) p[0] = p[1] def p_error(t): if t.lexer.in_define: # p_define_error will generate an error message. pass else: if t.type == "$end": t.parser.cparser.handle_error("Syntax error at end of file.", t.filename, 0) else: t.lexer.cparser.handle_error("Syntax error at %r" % t.value, t.filename, t.lineno) # Don't alter lexer: default behaviour is to pass error production # up until it hits the catch-all at declaration, at which point # parsing continues (synchronisation). def p_pragma(p): """ pragma : pragma_pack | PRAGMA pragma_directive_list PRAGMA_END """ def p_pragma_pack(p): """ pragma_pack : PRAGMA PRAGMA_PACK LPAREN RPAREN PRAGMA_END | PRAGMA PRAGMA_PACK LPAREN constant RPAREN PRAGMA_END | PRAGMA PRAGMA_PACK LPAREN pragma_pack_stack_args RPAREN PRAGMA_END """ err = None if len(p) == 6: cdeclarations.pragma_pack.set_default() elif isinstance(p[4], tuple): op, id, n = p[4] if op == "push": err = cdeclarations.pragma_pack.push(id, n) elif op == "pop": err = cdeclarations.pragma_pack.pop(id) else: err = "Syntax error for #pragma pack at {}:{}".format( p.slice[1].filename, p.slice[1].lineno ) else: cdeclarations.pragma_pack.current = p[4] if err: p.lexer.cparser.handle_error(err, p.slice[1].filename, p.slice[1].lineno) def p_pragma_pack_stack_args(p): """ pragma_pack_stack_args : IDENTIFIER | IDENTIFIER COMMA IDENTIFIER | IDENTIFIER COMMA IDENTIFIER COMMA constant | IDENTIFIER COMMA constant COMMA IDENTIFIER | IDENTIFIER COMMA constant """ op, id, n = p[1], None, None if len(p) > 2: if isinstance(p[3], expressions.ConstantExpressionNode): n = p[3].value if len(p) > 4: id = p[5] else: id = p[3] if len(p) > 4: n = p[5].value p[0] = (op, id, n) def p_pragma_directive_list(p): """ pragma_directive_list : pragma_directive | pragma_directive_list pragma_directive """ if len(p) == 3: p[0] = p[1] + (p[2],) else: p[0] = (p[1],) def p_pragma_directive(p): """ pragma_directive : IDENTIFIER | string_literal """ p[0] = p[1] def main(): yacc.yacc(tabmodule="new_parsetab") ================================================ FILE: ctypesgen/parser/cparser.py ================================================ """ Parse a C source file. To use, subclass CParser and override its handle_* methods. Then instantiate the class with a string to parse. """ __docformat__ = "restructuredtext" import os.path import sys from ctypesgen.parser import cgrammar, preprocessor, yacc # -------------------------------------------------------------------------- # Lexer # -------------------------------------------------------------------------- class CLexer(object): def __init__(self, cparser): self.cparser = cparser self.type_names = set() self.in_define = False self.lineno = -1 self.lexpos = -1 def input(self, tokens): self.tokens = tokens self.pos = 0 def token(self): while self.pos < len(self.tokens): t = self.tokens[self.pos] self.pos += 1 if not t: break if t.type == "PP_DEFINE": self.in_define = True elif t.type == "PP_END_DEFINE": self.in_define = False # Transform PP tokens into C tokens elif t.type == "IDENTIFIER" and t.value in cgrammar.keywords: t.type = cgrammar.keyword_map[t.value] elif t.type == "IDENTIFIER" and t.value in self.type_names: if self.pos < 2 or self.tokens[self.pos - 2].type not in ( "VOID", "_BOOL", "CHAR", "SHORT", "INT", "LONG", "FLOAT", "DOUBLE", "SIGNED", "UNSIGNED", "ENUM", "STRUCT", "UNION", "TYPE_NAME", ): t.type = "TYPE_NAME" t.lexer = self t.clexpos = self.pos - 1 return t return None # -------------------------------------------------------------------------- # Parser # -------------------------------------------------------------------------- class CParser(object): """Parse a C source file. Subclass and override the handle_* methods. Call `parse` with a string to parse. """ def __init__(self, options): super(CParser, self).__init__() self.preprocessor_parser = preprocessor.PreprocessorParser(options, self) self.parser = yacc.yacc( method="LALR", debug=False, module=cgrammar, write_tables=True, outputdir=os.path.dirname(__file__), optimize=True, ) self.parser.errorfunc = cgrammar.p_error self.parser.cparser = self self.lexer = CLexer(self) if not options.no_stddef_types: self.lexer.type_names.add("wchar_t") self.lexer.type_names.add("ptrdiff_t") self.lexer.type_names.add("size_t") if not options.no_gnu_types: self.lexer.type_names.add("__builtin_va_list") if sys.platform == "win32" and not options.no_python_types: self.lexer.type_names.add("__int64") def parse(self, filename, debug=False): """Parse a file. If `debug` is True, parsing state is dumped to stdout. """ self.handle_status("Preprocessing %s" % filename) self.preprocessor_parser.parse(filename) self.lexer.input(self.preprocessor_parser.output) self.handle_status("Parsing %s" % filename) self.parser.parse(lexer=self.lexer, debug=debug, tracking=True) # ---------------------------------------------------------------------- # Parser interface. Override these methods in your subclass. # ---------------------------------------------------------------------- def handle_error(self, message, filename, lineno): """A parse error occurred. The default implementation prints `lineno` and `message` to stderr. The parser will try to recover from errors by synchronising at the next semicolon. """ sys.stderr.write("%s:%s %s\n" % (filename, lineno, message)) def handle_pp_error(self, message): """The C preprocessor emitted an error. The default implementation prints the error to stderr. If processing can continue, it will. """ sys.stderr.write("Preprocessor: {}\n".format(message)) def handle_status(self, message): """Progress information. The default implementationg prints message to stderr. """ sys.stderr.write("{}\n".format(message)) def handle_define(self, name, params, value, filename, lineno): """#define `name` `value` or #define `name`(`params`) `value` name is a string params is None or a list of strings value is a ...? """ def handle_define_constant(self, name, value, filename, lineno): """#define `name` `value` name is a string value is an ExpressionNode or None """ def handle_define_macro(self, name, params, value, filename, lineno): """#define `name`(`params`) `value` name is a string params is a list of strings value is an ExpressionNode or None """ def handle_undefine(self, name, filename, lineno): """#undef `name` name is a string """ def impl_handle_declaration(self, declaration, filename, lineno): """Internal method that calls `handle_declaration`. This method also adds any new type definitions to the lexer's list of valid type names, which affects the parsing of subsequent declarations. """ if declaration.storage == "typedef": declarator = declaration.declarator if not declarator: # XXX TEMPORARY while struct etc not filled return while declarator.pointer: declarator = declarator.pointer self.lexer.type_names.add(declarator.identifier) self.handle_declaration(declaration, filename, lineno) def handle_declaration(self, declaration, filename, lineno): """A declaration was encountered. `declaration` is an instance of Declaration. Where a declaration has multiple initialisers, each is returned as a separate declaration. """ pass class DebugCParser(CParser): """A convenience class that prints each invocation of a handle_* method to stdout. """ def handle_define(self, name, value, filename, lineno): print("#define name=%r, value=%r" % (name, value)) def handle_define_constant(self, name, value, filename, lineno): print("#define constant name=%r, value=%r" % (name, value)) def handle_declaration(self, declaration, filename, lineno): print(declaration) def get_ctypes_type(self, typ, declarator): return typ def handle_define_unparseable(self, name, params, value, filename, lineno): if params: original_string = "#define %s(%s) %s" % (name, ",".join(params), " ".join(value)) else: original_string = "#define %s %s" % (name, " ".join(value)) print(original_string) if __name__ == "__main__": DebugCParser().parse(sys.argv[1], debug=True) ================================================ FILE: ctypesgen/parser/ctypesparser.py ================================================ """ ctypesgen.parser.ctypesparser contains a class, CtypesParser, which is a subclass of ctypesgen.parser.cparser.CParser. CtypesParser overrides the handle_declaration() method of CParser. It turns the low-level type declarations produced by CParser into CtypesType instances and breaks the parser's general declarations into function, variable, typedef, constant, and type descriptions. """ __docformat__ = "restructuredtext" __all__ = ["CtypesParser"] from ctypesgen.ctypedescs import ( CtypesArray, CtypesBitfield, CtypesEnum, CtypesFunction, CtypesPointer, CtypesSimple, CtypesSpecial, CtypesStruct, CtypesTypedef, ctypes_type_map, ctypes_type_map_python_builtin, remove_function_pointer, ) from ctypesgen.expressions import ( BinaryExpressionNode, ConstantExpressionNode, IdentifierExpressionNode, ) from ctypesgen.parser.cdeclarations import ( Attrib, EnumSpecifier, Pointer, StructTypeSpecifier, ) from ctypesgen.parser.cparser import CParser def make_enum_from_specifier(specifier): tag = specifier.tag enumerators = [] last_name = None for e in specifier.enumerators: if e.expression: value = e.expression else: if last_name: value = BinaryExpressionNode( "addition", (lambda x, y: x + y), "(%s + %s)", (False, False), IdentifierExpressionNode(last_name), ConstantExpressionNode(1), ) else: value = ConstantExpressionNode(0) enumerators.append((e.name, value)) last_name = e.name return CtypesEnum(tag, enumerators, src=(specifier.filename, specifier.lineno)) def get_decl_id(decl): """Return the identifier of a given declarator""" while isinstance(decl, Pointer): decl = decl.pointer p_name = "" if decl is not None and decl.identifier is not None: p_name = decl.identifier return p_name class CtypesParser(CParser): """Parse a C file for declarations that can be used by ctypes. Subclass and override the handle_ctypes_* methods. """ def __init__(self, options): super(CtypesParser, self).__init__(options) self.type_map = ctypes_type_map if not options.no_python_types: self.type_map.update(ctypes_type_map_python_builtin) def make_struct_from_specifier(self, specifier): variety = {True: "union", False: "struct"}[specifier.is_union] tag = specifier.tag if specifier.declarations: members = [] for declaration in specifier.declarations: t = self.get_ctypes_type( declaration.type, declaration.declarator, check_qualifiers=True ) declarator = declaration.declarator if declarator is None: # Anonymous field in nested union/struct (C11/GCC). name = None else: while declarator.pointer: declarator = declarator.pointer name = declarator.identifier members.append((name, remove_function_pointer(t))) else: members = None return CtypesStruct( tag, specifier.attrib, variety, members, src=(specifier.filename, specifier.lineno) ) def get_ctypes_type(self, typ, declarator, check_qualifiers=False): signed = True typename = "int" longs = 0 t = None for specifier in typ.specifiers: if isinstance(specifier, StructTypeSpecifier): t = self.make_struct_from_specifier(specifier) elif isinstance(specifier, EnumSpecifier): t = make_enum_from_specifier(specifier) elif specifier == "signed": signed = True elif specifier == "unsigned": signed = False elif specifier == "long": longs += 1 elif specifier == "short": longs = -1 else: typename = str(specifier) if not t: # It is a numeric type of some sort if (typename, signed, longs) in self.type_map: t = CtypesSimple(typename, signed, longs) elif signed and not longs: t = CtypesTypedef(typename) else: name = " ".join(typ.specifiers) if typename in [x[0] for x in self.type_map.keys()]: # It's an unsupported variant of a builtin type error = 'Ctypes does not support the type "%s".' % name else: error = ( "Ctypes does not support adding additional " 'specifiers to typedefs, such as "%s"' % name ) t = CtypesTypedef(name) t.error(error, cls="unsupported-type") if declarator and declarator.bitfield: t = CtypesBitfield(t, declarator.bitfield) qualifiers = [] qualifiers.extend(typ.qualifiers) while declarator and declarator.pointer: if declarator.parameters is not None: variadic = "..." in declarator.parameters params = [] for param in declarator.parameters: if param == "...": break param_name = get_decl_id(param.declarator) ct = self.get_ctypes_type(param.type, param.declarator) ct.identifier = param_name params.append(ct) t = CtypesFunction(t, params, variadic) a = declarator.array while a: t = CtypesArray(t, a.size) a = a.array qualifiers.extend(declarator.qualifiers) t = CtypesPointer(t, tuple(typ.qualifiers) + tuple(declarator.qualifiers)) declarator = declarator.pointer if declarator and declarator.parameters is not None: variadic = "..." in declarator.parameters params = [] for param in declarator.parameters: if param == "...": break param_name = get_decl_id(param.declarator) ct = self.get_ctypes_type(param.type, param.declarator) ct.identifier = param_name params.append(ct) t = CtypesFunction(t, params, variadic, declarator.attrib) if declarator: a = declarator.array while a: t = CtypesArray(t, a.size) a = a.array if ( isinstance(t, CtypesPointer) and isinstance(t.destination, CtypesSimple) and t.destination.name == "char" and t.destination.signed ): t = CtypesSpecial("String") return t def handle_declaration(self, declaration, filename, lineno): t = self.get_ctypes_type(declaration.type, declaration.declarator) if type(t) in (CtypesStruct, CtypesEnum): self.handle_ctypes_new_type(remove_function_pointer(t), filename, lineno) declarator = declaration.declarator if declarator is None: # XXX TEMPORARY while struct with no typedef not filled in return while declarator.pointer: declarator = declarator.pointer name = declarator.identifier if declaration.storage == "typedef": self.handle_ctypes_typedef(name, remove_function_pointer(t), filename, lineno) elif type(t) == CtypesFunction: attrib = Attrib(t.attrib) attrib.update(declaration.attrib) self.handle_ctypes_function( name, t.restype, t.argtypes, t.errcheck, t.variadic, attrib, filename, lineno ) elif declaration.storage != "static": self.handle_ctypes_variable(name, t, filename, lineno) # ctypes parser interface. Override these methods in your subclass. def handle_ctypes_new_type(self, ctype, filename, lineno): pass def handle_ctypes_typedef(self, name, ctype, filename, lineno): pass def handle_ctypes_function( self, name, restype, argtypes, errcheck, variadic, attrib, filename, lineno ): pass def handle_ctypes_variable(self, name, ctype, filename, lineno): pass ================================================ FILE: ctypesgen/parser/datacollectingparser.py ================================================ """ DataCollectingParser subclasses ctypesparser.CtypesParser and builds Description objects from the CtypesType objects and other information from CtypesParser. After parsing is complete, a DescriptionCollection object can be retrieved by calling DataCollectingParser.data(). """ import os from tempfile import mkstemp from ctypesgen.ctypedescs import CtypesEnum, CtypesType, CtypesTypeVisitor from ctypesgen.descriptions import ( ConstantDescription, DescriptionCollection, EnumDescription, FunctionDescription, MacroDescription, StructDescription, TypedefDescription, UndefDescription, VariableDescription, ) from ctypesgen.expressions import ConstantExpressionNode from ctypesgen.messages import error_message, status_message from ctypesgen.parser import ctypesparser class DataCollectingParser(ctypesparser.CtypesParser, CtypesTypeVisitor): """Main class for the Parser component. Steps for use: p=DataCollectingParser(names_of_header_files,options) p.parse() data=p.data() #A dictionary of constants, enums, structs, functions, etc. """ def __init__(self, headers, options): super(DataCollectingParser, self).__init__(options) self.headers = headers self.options = options self.constants = [] self.typedefs = [] self.structs = [] self.enums = [] self.functions = [] self.variables = [] self.macros = [] self.all = [] self.output_order = [] # NULL is a useful macro to have defined null = ConstantExpressionNode(None) nullmacro = ConstantDescription("NULL", null, ("", 1)) self.constants.append(nullmacro) self.all.append(nullmacro) self.output_order.append(("constant", nullmacro)) # A list of tuples describing macros; saved to be processed after # everything else has been parsed self.saved_macros = [] # A set of structs that are already known self.already_seen_structs = set() # A dict of structs that have only been seen in opaque form self.already_seen_opaque_structs = {} # A set of enums that are already known self.already_seen_enums = set() # A dict of enums that have only been seen in opaque form self.already_seen_opaque_enums = {} def parse(self): fd, fname = mkstemp(suffix=".h") with os.fdopen(fd, "w") as f: for header in self.options.other_headers: f.write("#include <%s>\n" % header) for header in self.headers: f.write('#include "%s"\n' % os.path.abspath(header)) f.flush() try: super(DataCollectingParser, self).parse(fname, self.options.debug_level) finally: os.unlink(fname) for name, params, expr, (filename, lineno) in self.saved_macros: self.handle_macro(name, params, expr, filename, lineno) def handle_define_constant(self, name, expr, filename, lineno): # Called by CParser # Save to handle later self.saved_macros.append((name, None, expr, (filename, lineno))) def handle_define_unparseable(self, name, params, value, filename, lineno): # Called by CParser if params: original_string = "#define %s(%s) %s" % (name, ",".join(params), " ".join(value)) else: original_string = "#define %s %s" % (name, " ".join(value)) macro = MacroDescription(name, params, None, src=(filename, lineno)) macro.error('Could not parse macro "%s"' % original_string, cls="macro") macro.original_string = original_string self.macros.append(macro) self.all.append(macro) self.output_order.append(("macro", macro)) def handle_define_macro(self, name, params, expr, filename, lineno): # Called by CParser # Save to handle later self.saved_macros.append((name, params, expr, (filename, lineno))) def handle_undefine(self, macro, filename, lineno): # save to handle later to get order correct self.saved_macros.append(("#undef", None, macro, (filename, lineno))) def handle_ctypes_typedef(self, name, ctype, filename, lineno): # Called by CtypesParser ctype.visit(self) typedef = TypedefDescription(name, ctype, src=(filename, repr(lineno))) self.typedefs.append(typedef) self.all.append(typedef) self.output_order.append(("typedef", typedef)) def handle_ctypes_new_type(self, ctype, filename, lineno): # Called by CtypesParser if isinstance(ctype, CtypesEnum): self.handle_enum(ctype, filename, lineno) else: self.handle_struct(ctype, filename, lineno) def handle_ctypes_function( self, name, restype, argtypes, errcheck, variadic, attrib, filename, lineno ): # Called by CtypesParser restype.visit(self) for argtype in argtypes: argtype.visit(self) function = FunctionDescription( name, restype, argtypes, errcheck, variadic, attrib, src=(filename, repr(lineno)) ) self.functions.append(function) self.all.append(function) self.output_order.append(("function", function)) def handle_ctypes_variable(self, name, ctype, filename, lineno): # Called by CtypesParser ctype.visit(self) variable = VariableDescription(name, ctype, src=(filename, repr(lineno))) self.variables.append(variable) self.all.append(variable) self.output_order.append(("variable", variable)) def handle_struct(self, ctypestruct, filename, lineno): # Called from within DataCollectingParser # When we find an opaque struct, we make a StructDescription for it # and record it in self.already_seen_opaque_structs. If we later # find a transparent struct with the same tag, we fill in the # opaque struct with the information from the transparent struct and # move the opaque struct to the end of the struct list. name = "%s %s" % (ctypestruct.variety, ctypestruct.tag) if name in self.already_seen_structs: return if ctypestruct.opaque: if name not in self.already_seen_opaque_structs: struct = StructDescription( ctypestruct.tag, ctypestruct.attrib, ctypestruct.variety, None, # No members True, # Opaque ctypestruct, src=(filename, str(lineno)), ) self.already_seen_opaque_structs[name] = struct self.structs.append(struct) self.all.append(struct) self.output_order.append(("struct", struct)) else: for membername, ctype in ctypestruct.members: ctype.visit(self) if name in self.already_seen_opaque_structs: # Fill in older version struct = self.already_seen_opaque_structs[name] struct.opaque = False struct.members = ctypestruct.members struct.ctype = ctypestruct struct.src = ctypestruct.src self.output_order.append(("struct-body", struct)) del self.already_seen_opaque_structs[name] else: struct = StructDescription( ctypestruct.tag, ctypestruct.attrib, ctypestruct.variety, ctypestruct.members, False, # Not opaque src=(filename, str(lineno)), ctype=ctypestruct, ) self.structs.append(struct) self.all.append(struct) self.output_order.append(("struct", struct)) self.output_order.append(("struct-body", struct)) self.already_seen_structs.add(name) def handle_enum(self, ctypeenum, filename, lineno): # Called from within DataCollectingParser. # Process for handling opaque enums is the same as process for opaque # structs. See handle_struct() for more details. tag = ctypeenum.tag if tag in self.already_seen_enums: return if ctypeenum.opaque: if tag not in self.already_seen_opaque_enums: enum = EnumDescription(ctypeenum.tag, None, ctypeenum, src=(filename, str(lineno))) enum.opaque = True self.already_seen_opaque_enums[tag] = enum self.enums.append(enum) self.all.append(enum) self.output_order.append(("enum", enum)) else: if tag in self.already_seen_opaque_enums: # Fill in older opaque version enum = self.already_seen_opaque_enums[tag] enum.opaque = False enum.ctype = ctypeenum enum.src = ctypeenum.src enum.members = ctypeenum.enumerators del self.already_seen_opaque_enums[tag] else: enum = EnumDescription( ctypeenum.tag, ctypeenum.enumerators, src=(filename, str(lineno)), ctype=ctypeenum, ) enum.opaque = False self.enums.append(enum) self.all.append(enum) self.output_order.append(("enum", enum)) self.already_seen_enums.add(tag) for enumname, expr in ctypeenum.enumerators: constant = ConstantDescription(enumname, expr, src=(filename, lineno)) self.constants.append(constant) self.all.append(constant) self.output_order.append(("constant", constant)) def handle_macro(self, name, params, expr, filename, lineno): # Called from within DataCollectingParser src = (filename, lineno) if expr is None: expr = ConstantExpressionNode(True) constant = ConstantDescription(name, expr, src) self.constants.append(constant) self.all.append(constant) return expr.visit(self) if isinstance(expr, CtypesType): if params: macro = MacroDescription(name, "", src) macro.error( "%s has parameters but evaluates to a type. " "Ctypesgen does not support it." % macro.casual_name(), cls="macro", ) self.macros.append(macro) self.all.append(macro) self.output_order.append(("macro", macro)) else: typedef = TypedefDescription(name, expr, src) self.typedefs.append(typedef) self.all.append(typedef) self.output_order.append(("typedef", typedef)) elif name == "#undef": undef = UndefDescription(expr, src) self.all.append(undef) self.output_order.append(("undef", undef)) else: macro = MacroDescription(name, params, expr, src) self.macros.append(macro) self.all.append(macro) self.output_order.append(("macro", macro)) # Macros could possibly contain things like __FILE__, __LINE__, etc... # This could be supported, but it would be a lot of work. It would # probably also bloat the Preamble considerably. def handle_error(self, message, filename, lineno): # Called by CParser error_message("%s:%d: %s" % (filename, lineno, message), cls="cparser") def handle_pp_error(self, message): # Called by PreprocessorParser error_message("%s: %s" % (self.options.cpp, message), cls="cparser") def handle_status(self, message): # Called by CParser status_message(message) def visit_struct(self, struct): self.handle_struct(struct, struct.src[0], struct.src[1]) def visit_enum(self, enum): self.handle_enum(enum, enum.src[0], enum.src[1]) def data(self): return DescriptionCollection( self.constants, self.typedefs, self.structs, self.enums, self.functions, self.variables, self.macros, self.all, self.output_order, ) ================================================ FILE: ctypesgen/parser/lex.py ================================================ # ----------------------------------------------------------------------------- # ply: lex.py # # Copyright (C) 2001-2018 # David M. Beazley (Dabeaz LLC) # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the David Beazley or Dabeaz LLC may be used to # endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- __version__ = '3.11' __tabversion__ = '3.10' import re import sys import types import copy import os import inspect # This tuple contains known string types try: # Python 2.6 StringTypes = (types.StringType, types.UnicodeType) except AttributeError: # Python 3.0 StringTypes = (str, bytes) # This regular expression is used to match valid token names _is_identifier = re.compile(r'^[a-zA-Z0-9_]+$') # Exception thrown when invalid token encountered and no default error # handler is defined. class LexError(Exception): def __init__(self, message, s): self.args = (message,) self.text = s # Token class. This class is used to represent the tokens produced. class LexToken(object): def __str__(self): return 'LexToken(%s,%r,%d,%d)' % (self.type, self.value, self.lineno, self.lexpos) def __repr__(self): return str(self) # This object is a stand-in for a logging object created by the # logging module. class PlyLogger(object): def __init__(self, f): self.f = f def critical(self, msg, *args, **kwargs): self.f.write((msg % args) + '\n') def warning(self, msg, *args, **kwargs): self.f.write('WARNING: ' + (msg % args) + '\n') def error(self, msg, *args, **kwargs): self.f.write('ERROR: ' + (msg % args) + '\n') info = critical debug = critical # Null logger is used when no output is generated. Does nothing. class NullLogger(object): def __getattribute__(self, name): return self def __call__(self, *args, **kwargs): return self # ----------------------------------------------------------------------------- # === Lexing Engine === # # The following Lexer class implements the lexer runtime. There are only # a few public methods and attributes: # # input() - Store a new string in the lexer # token() - Get the next token # clone() - Clone the lexer # # lineno - Current line number # lexpos - Current position in the input string # ----------------------------------------------------------------------------- class Lexer: def __init__(self): self.lexre = None # Master regular expression. This is a list of # tuples (re, findex) where re is a compiled # regular expression and findex is a list # mapping regex group numbers to rules self.lexretext = None # Current regular expression strings self.lexstatere = {} # Dictionary mapping lexer states to master regexs self.lexstateretext = {} # Dictionary mapping lexer states to regex strings self.lexstaterenames = {} # Dictionary mapping lexer states to symbol names self.lexstate = 'INITIAL' # Current lexer state self.lexstatestack = [] # Stack of lexer states self.lexstateinfo = None # State information self.lexstateignore = {} # Dictionary of ignored characters for each state self.lexstateerrorf = {} # Dictionary of error functions for each state self.lexstateeoff = {} # Dictionary of eof functions for each state self.lexreflags = 0 # Optional re compile flags self.lexdata = None # Actual input data (as a string) self.lexpos = 0 # Current position in input text self.lexlen = 0 # Length of the input text self.lexerrorf = None # Error rule (if any) self.lexeoff = None # EOF rule (if any) self.lextokens = None # List of valid tokens self.lexignore = '' # Ignored characters self.lexliterals = '' # Literal characters that can be passed through self.lexmodule = None # Module self.lineno = 1 # Current line number self.lexoptimize = False # Optimized mode def clone(self, object=None): c = copy.copy(self) # If the object parameter has been supplied, it means we are attaching the # lexer to a new object. In this case, we have to rebind all methods in # the lexstatere and lexstateerrorf tables. if object: newtab = {} for key, ritem in self.lexstatere.items(): newre = [] for cre, findex in ritem: newfindex = [] for f in findex: if not f or not f[0]: newfindex.append(f) continue newfindex.append((getattr(object, f[0].__name__), f[1])) newre.append((cre, newfindex)) newtab[key] = newre c.lexstatere = newtab c.lexstateerrorf = {} for key, ef in self.lexstateerrorf.items(): c.lexstateerrorf[key] = getattr(object, ef.__name__) c.lexmodule = object return c # ------------------------------------------------------------ # writetab() - Write lexer information to a table file # ------------------------------------------------------------ def writetab(self, lextab, outputdir=''): if isinstance(lextab, types.ModuleType): raise IOError("Won't overwrite existing lextab module") basetabmodule = lextab.split('.')[-1] filename = os.path.join(outputdir, basetabmodule) + '.py' with open(filename, 'w') as tf: tf.write('# %s.py. This file automatically created by PLY (version %s). Don\'t edit!\n' % (basetabmodule, __version__)) tf.write('_tabversion = %s\n' % repr(__tabversion__)) tf.write('_lextokens = set(%s)\n' % repr(tuple(sorted(self.lextokens)))) tf.write('_lexreflags = %s\n' % repr(int(self.lexreflags))) tf.write('_lexliterals = %s\n' % repr(self.lexliterals)) tf.write('_lexstateinfo = %s\n' % repr(self.lexstateinfo)) # Rewrite the lexstatere table, replacing function objects with function names tabre = {} for statename, lre in self.lexstatere.items(): titem = [] for (pat, func), retext, renames in zip(lre, self.lexstateretext[statename], self.lexstaterenames[statename]): titem.append((retext, _funcs_to_names(func, renames))) tabre[statename] = titem tf.write('_lexstatere = %s\n' % repr(tabre)) tf.write('_lexstateignore = %s\n' % repr(self.lexstateignore)) taberr = {} for statename, ef in self.lexstateerrorf.items(): taberr[statename] = ef.__name__ if ef else None tf.write('_lexstateerrorf = %s\n' % repr(taberr)) tabeof = {} for statename, ef in self.lexstateeoff.items(): tabeof[statename] = ef.__name__ if ef else None tf.write('_lexstateeoff = %s\n' % repr(tabeof)) # ------------------------------------------------------------ # readtab() - Read lexer information from a tab file # ------------------------------------------------------------ def readtab(self, tabfile, fdict): if isinstance(tabfile, types.ModuleType): lextab = tabfile else: exec('import %s' % tabfile) lextab = sys.modules[tabfile] if getattr(lextab, '_tabversion', '0.0') != __tabversion__: raise ImportError('Inconsistent PLY version') self.lextokens = lextab._lextokens self.lexreflags = lextab._lexreflags self.lexliterals = lextab._lexliterals self.lextokens_all = self.lextokens | set(self.lexliterals) self.lexstateinfo = lextab._lexstateinfo self.lexstateignore = lextab._lexstateignore self.lexstatere = {} self.lexstateretext = {} for statename, lre in lextab._lexstatere.items(): titem = [] txtitem = [] for pat, func_name in lre: titem.append((re.compile(pat, lextab._lexreflags), _names_to_funcs(func_name, fdict))) self.lexstatere[statename] = titem self.lexstateretext[statename] = txtitem self.lexstateerrorf = {} for statename, ef in lextab._lexstateerrorf.items(): self.lexstateerrorf[statename] = fdict[ef] self.lexstateeoff = {} for statename, ef in lextab._lexstateeoff.items(): self.lexstateeoff[statename] = fdict[ef] self.begin('INITIAL') # ------------------------------------------------------------ # input() - Push a new string into the lexer # ------------------------------------------------------------ def input(self, s): # Pull off the first character to see if s looks like a string c = s[:1] if not isinstance(c, StringTypes): raise ValueError('Expected a string') self.lexdata = s self.lexpos = 0 self.lexlen = len(s) # ------------------------------------------------------------ # begin() - Changes the lexing state # ------------------------------------------------------------ def begin(self, state): if state not in self.lexstatere: raise ValueError('Undefined state') self.lexre = self.lexstatere[state] self.lexretext = self.lexstateretext[state] self.lexignore = self.lexstateignore.get(state, '') self.lexerrorf = self.lexstateerrorf.get(state, None) self.lexeoff = self.lexstateeoff.get(state, None) self.lexstate = state # ------------------------------------------------------------ # push_state() - Changes the lexing state and saves old on stack # ------------------------------------------------------------ def push_state(self, state): self.lexstatestack.append(self.lexstate) self.begin(state) # ------------------------------------------------------------ # pop_state() - Restores the previous state # ------------------------------------------------------------ def pop_state(self): self.begin(self.lexstatestack.pop()) # ------------------------------------------------------------ # current_state() - Returns the current lexing state # ------------------------------------------------------------ def current_state(self): return self.lexstate # ------------------------------------------------------------ # skip() - Skip ahead n characters # ------------------------------------------------------------ def skip(self, n): self.lexpos += n # ------------------------------------------------------------ # opttoken() - Return the next token from the Lexer # # Note: This function has been carefully implemented to be as fast # as possible. Don't make changes unless you really know what # you are doing # ------------------------------------------------------------ def token(self): # Make local copies of frequently referenced attributes lexpos = self.lexpos lexlen = self.lexlen lexignore = self.lexignore lexdata = self.lexdata while lexpos < lexlen: # This code provides some short-circuit code for whitespace, tabs, and other ignored characters if lexdata[lexpos] in lexignore: lexpos += 1 continue # Look for a regular expression match for lexre, lexindexfunc in self.lexre: m = lexre.match(lexdata, lexpos) if not m: continue # Create a token for return tok = LexToken() tok.value = m.group() tok.lineno = self.lineno tok.lexpos = lexpos i = m.lastindex func, tok.type = lexindexfunc[i] if not func: # If no token type was set, it's an ignored token if tok.type: self.lexpos = m.end() return tok else: lexpos = m.end() break lexpos = m.end() # If token is processed by a function, call it tok.lexer = self # Set additional attributes useful in token rules self.lexmatch = m self.lexpos = lexpos newtok = func(tok) # Every function must return a token, if nothing, we just move to next token if not newtok: lexpos = self.lexpos # This is here in case user has updated lexpos. lexignore = self.lexignore # This is here in case there was a state change break # Verify type of the token. If not in the token map, raise an error if not self.lexoptimize: if newtok.type not in self.lextokens_all: raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % ( func.__code__.co_filename, func.__code__.co_firstlineno, func.__name__, newtok.type), lexdata[lexpos:]) return newtok else: # No match, see if in literals if lexdata[lexpos] in self.lexliterals: tok = LexToken() tok.value = lexdata[lexpos] tok.lineno = self.lineno tok.type = tok.value tok.lexpos = lexpos self.lexpos = lexpos + 1 return tok # No match. Call t_error() if defined. if self.lexerrorf: tok = LexToken() tok.value = self.lexdata[lexpos:] tok.lineno = self.lineno tok.type = 'error' tok.lexer = self tok.lexpos = lexpos self.lexpos = lexpos newtok = self.lexerrorf(tok) if lexpos == self.lexpos: # Error method didn't change text position at all. This is an error. raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:]) lexpos = self.lexpos if not newtok: continue return newtok self.lexpos = lexpos raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos], lexpos), lexdata[lexpos:]) if self.lexeoff: tok = LexToken() tok.type = 'eof' tok.value = '' tok.lineno = self.lineno tok.lexpos = lexpos tok.lexer = self self.lexpos = lexpos newtok = self.lexeoff(tok) return newtok self.lexpos = lexpos + 1 if self.lexdata is None: raise RuntimeError('No input string given with input()') return None # Iterator interface def __iter__(self): return self def next(self): t = self.token() if t is None: raise StopIteration return t __next__ = next # ----------------------------------------------------------------------------- # ==== Lex Builder === # # The functions and classes below are used to collect lexing information # and build a Lexer object from it. # ----------------------------------------------------------------------------- # ----------------------------------------------------------------------------- # _get_regex(func) # # Returns the regular expression assigned to a function either as a doc string # or as a .regex attribute attached by the @TOKEN decorator. # ----------------------------------------------------------------------------- def _get_regex(func): return getattr(func, 'regex', func.__doc__) # ----------------------------------------------------------------------------- # get_caller_module_dict() # # This function returns a dictionary containing all of the symbols defined within # a caller further down the call stack. This is used to get the environment # associated with the yacc() call if none was provided. # ----------------------------------------------------------------------------- def get_caller_module_dict(levels): f = sys._getframe(levels) ldict = f.f_globals.copy() if f.f_globals != f.f_locals: ldict.update(f.f_locals) return ldict # ----------------------------------------------------------------------------- # _funcs_to_names() # # Given a list of regular expression functions, this converts it to a list # suitable for output to a table file # ----------------------------------------------------------------------------- def _funcs_to_names(funclist, namelist): result = [] for f, name in zip(funclist, namelist): if f and f[0]: result.append((name, f[1])) else: result.append(f) return result # ----------------------------------------------------------------------------- # _names_to_funcs() # # Given a list of regular expression function names, this converts it back to # functions. # ----------------------------------------------------------------------------- def _names_to_funcs(namelist, fdict): result = [] for n in namelist: if n and n[0]: result.append((fdict[n[0]], n[1])) else: result.append(n) return result # ----------------------------------------------------------------------------- # _form_master_re() # # This function takes a list of all of the regex components and attempts to # form the master regular expression. Given limitations in the Python re # module, it may be necessary to break the master regex into separate expressions. # ----------------------------------------------------------------------------- def _form_master_re(relist, reflags, ldict, toknames): if not relist: return [] regex = '|'.join(relist) try: lexre = re.compile(regex, reflags) # Build the index to function map for the matching engine lexindexfunc = [None] * (max(lexre.groupindex.values()) + 1) lexindexnames = lexindexfunc[:] for f, i in lexre.groupindex.items(): handle = ldict.get(f, None) if type(handle) in (types.FunctionType, types.MethodType): lexindexfunc[i] = (handle, toknames[f]) lexindexnames[i] = f elif handle is not None: lexindexnames[i] = f if f.find('ignore_') > 0: lexindexfunc[i] = (None, None) else: lexindexfunc[i] = (None, toknames[f]) return [(lexre, lexindexfunc)], [regex], [lexindexnames] except Exception: m = int(len(relist)/2) if m == 0: m = 1 llist, lre, lnames = _form_master_re(relist[:m], reflags, ldict, toknames) rlist, rre, rnames = _form_master_re(relist[m:], reflags, ldict, toknames) return (llist+rlist), (lre+rre), (lnames+rnames) # ----------------------------------------------------------------------------- # def _statetoken(s,names) # # Given a declaration name s of the form "t_" and a dictionary whose keys are # state names, this function returns a tuple (states,tokenname) where states # is a tuple of state names and tokenname is the name of the token. For example, # calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM') # ----------------------------------------------------------------------------- def _statetoken(s, names): parts = s.split('_') for i, part in enumerate(parts[1:], 1): if part not in names and part != 'ANY': break if i > 1: states = tuple(parts[1:i]) else: states = ('INITIAL',) if 'ANY' in states: states = tuple(names) tokenname = '_'.join(parts[i:]) return (states, tokenname) # ----------------------------------------------------------------------------- # LexerReflect() # # This class represents information needed to build a lexer as extracted from a # user's input file. # ----------------------------------------------------------------------------- class LexerReflect(object): def __init__(self, ldict, log=None, reflags=0): self.ldict = ldict self.error_func = None self.tokens = [] self.reflags = reflags self.stateinfo = {'INITIAL': 'inclusive'} self.modules = set() self.error = False self.log = PlyLogger(sys.stderr) if log is None else log # Get all of the basic information def get_all(self): self.get_tokens() self.get_literals() self.get_states() self.get_rules() # Validate all of the information def validate_all(self): self.validate_tokens() self.validate_literals() self.validate_rules() return self.error # Get the tokens map def get_tokens(self): tokens = self.ldict.get('tokens', None) if not tokens: self.log.error('No token list is defined') self.error = True return if not isinstance(tokens, (list, tuple)): self.log.error('tokens must be a list or tuple') self.error = True return if not tokens: self.log.error('tokens is empty') self.error = True return self.tokens = tokens # Validate the tokens def validate_tokens(self): terminals = {} for n in self.tokens: if not _is_identifier.match(n): self.log.error("Bad token name '%s'", n) self.error = True if n in terminals: self.log.warning("Token '%s' multiply defined", n) terminals[n] = 1 # Get the literals specifier def get_literals(self): self.literals = self.ldict.get('literals', '') if not self.literals: self.literals = '' # Validate literals def validate_literals(self): try: for c in self.literals: if not isinstance(c, StringTypes) or len(c) > 1: self.log.error('Invalid literal %s. Must be a single character', repr(c)) self.error = True except TypeError: self.log.error('Invalid literals specification. literals must be a sequence of characters') self.error = True def get_states(self): self.states = self.ldict.get('states', None) # Build statemap if self.states: if not isinstance(self.states, (tuple, list)): self.log.error('states must be defined as a tuple or list') self.error = True else: for s in self.states: if not isinstance(s, tuple) or len(s) != 2: self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')", repr(s)) self.error = True continue name, statetype = s if not isinstance(name, StringTypes): self.log.error('State name %s must be a string', repr(name)) self.error = True continue if not (statetype == 'inclusive' or statetype == 'exclusive'): self.log.error("State type for state %s must be 'inclusive' or 'exclusive'", name) self.error = True continue if name in self.stateinfo: self.log.error("State '%s' already defined", name) self.error = True continue self.stateinfo[name] = statetype # Get all of the symbols with a t_ prefix and sort them into various # categories (functions, strings, error functions, and ignore characters) def get_rules(self): tsymbols = [f for f in self.ldict if f[:2] == 't_'] # Now build up a list of functions and a list of strings self.toknames = {} # Mapping of symbols to token names self.funcsym = {} # Symbols defined as functions self.strsym = {} # Symbols defined as strings self.ignore = {} # Ignore strings by state self.errorf = {} # Error functions by state self.eoff = {} # EOF functions by state for s in self.stateinfo: self.funcsym[s] = [] self.strsym[s] = [] if len(tsymbols) == 0: self.log.error('No rules of the form t_rulename are defined') self.error = True return for f in tsymbols: t = self.ldict[f] states, tokname = _statetoken(f, self.stateinfo) self.toknames[f] = tokname if hasattr(t, '__call__'): if tokname == 'error': for s in states: self.errorf[s] = t elif tokname == 'eof': for s in states: self.eoff[s] = t elif tokname == 'ignore': line = t.__code__.co_firstlineno file = t.__code__.co_filename self.log.error("%s:%d: Rule '%s' must be defined as a string", file, line, t.__name__) self.error = True else: for s in states: self.funcsym[s].append((f, t)) elif isinstance(t, StringTypes): if tokname == 'ignore': for s in states: self.ignore[s] = t if '\\' in t: self.log.warning("%s contains a literal backslash '\\'", f) elif tokname == 'error': self.log.error("Rule '%s' must be defined as a function", f) self.error = True else: for s in states: self.strsym[s].append((f, t)) else: self.log.error('%s not defined as a function or string', f) self.error = True # Sort the functions by line number for f in self.funcsym.values(): f.sort(key=lambda x: x[1].__code__.co_firstlineno) # Sort the strings by regular expression length for s in self.strsym.values(): s.sort(key=lambda x: len(x[1]), reverse=True) # Validate all of the t_rules collected def validate_rules(self): for state in self.stateinfo: # Validate all rules defined by functions for fname, f in self.funcsym[state]: line = f.__code__.co_firstlineno file = f.__code__.co_filename module = inspect.getmodule(f) self.modules.add(module) tokname = self.toknames[fname] if isinstance(f, types.MethodType): reqargs = 2 else: reqargs = 1 nargs = f.__code__.co_argcount if nargs > reqargs: self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__) self.error = True continue if nargs < reqargs: self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__) self.error = True continue if not _get_regex(f): self.log.error("%s:%d: No regular expression defined for rule '%s'", file, line, f.__name__) self.error = True continue try: c = re.compile('(?P<%s>%s)' % (fname, _get_regex(f)), self.reflags) if c.match(''): self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file, line, f.__name__) self.error = True except re.error as e: self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file, line, f.__name__, e) if '#' in _get_regex(f): self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'", file, line, f.__name__) self.error = True # Validate all rules defined by strings for name, r in self.strsym[state]: tokname = self.toknames[name] if tokname == 'error': self.log.error("Rule '%s' must be defined as a function", name) self.error = True continue if tokname not in self.tokens and tokname.find('ignore_') < 0: self.log.error("Rule '%s' defined for an unspecified token %s", name, tokname) self.error = True continue try: c = re.compile('(?P<%s>%s)' % (name, r), self.reflags) if (c.match('')): self.log.error("Regular expression for rule '%s' matches empty string", name) self.error = True except re.error as e: self.log.error("Invalid regular expression for rule '%s'. %s", name, e) if '#' in r: self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'", name) self.error = True if not self.funcsym[state] and not self.strsym[state]: self.log.error("No rules defined for state '%s'", state) self.error = True # Validate the error function efunc = self.errorf.get(state, None) if efunc: f = efunc line = f.__code__.co_firstlineno file = f.__code__.co_filename module = inspect.getmodule(f) self.modules.add(module) if isinstance(f, types.MethodType): reqargs = 2 else: reqargs = 1 nargs = f.__code__.co_argcount if nargs > reqargs: self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__) self.error = True if nargs < reqargs: self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__) self.error = True for module in self.modules: self.validate_module(module) # ----------------------------------------------------------------------------- # validate_module() # # This checks to see if there are duplicated t_rulename() functions or strings # in the parser input file. This is done using a simple regular expression # match on each line in the source code of the given module. # ----------------------------------------------------------------------------- def validate_module(self, module): try: lines, linen = inspect.getsourcelines(module) except IOError: return fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(') sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=') counthash = {} linen += 1 for line in lines: m = fre.match(line) if not m: m = sre.match(line) if m: name = m.group(1) prev = counthash.get(name) if not prev: counthash[name] = linen else: filename = inspect.getsourcefile(module) self.log.error('%s:%d: Rule %s redefined. Previously defined on line %d', filename, linen, name, prev) self.error = True linen += 1 # ----------------------------------------------------------------------------- # lex(module) # # Build all of the regular expression rules from definitions in the supplied module # ----------------------------------------------------------------------------- def lex(module=None, object=None, debug=False, optimize=False, lextab='lextab', reflags=int(re.VERBOSE), nowarn=False, outputdir=None, debuglog=None, errorlog=None, cls=Lexer): # if lextab is None: lextab = 'lextab' global lexer ldict = None stateinfo = {'INITIAL': 'inclusive'} lexobj = cls() # lexobj.lexoptimize = optimize global token, input if errorlog is None: errorlog = PlyLogger(sys.stderr) if debug: if debuglog is None: debuglog = PlyLogger(sys.stderr) # Get the module dictionary used for the lexer if object: module = object # Get the module dictionary used for the parser if module: _items = [(k, getattr(module, k)) for k in dir(module)] ldict = dict(_items) # If no __file__ attribute is available, try to obtain it from the __module__ instead if '__file__' not in ldict: ldict['__file__'] = sys.modules[ldict['__module__']].__file__ else: ldict = get_caller_module_dict(2) # Determine if the module is package of a package or not. # If so, fix the tabmodule setting so that tables load correctly pkg = ldict.get('__package__') if pkg and isinstance(lextab, str): if '.' not in lextab: lextab = pkg + '.' + lextab # Collect parser information from the dictionary linfo = LexerReflect(ldict, log=errorlog, reflags=reflags) linfo.get_all() if not optimize: if linfo.validate_all(): raise SyntaxError("Can't build lexer") if optimize and lextab: try: lexobj.readtab(lextab, ldict) token = lexobj.token input = lexobj.input lexer = lexobj return lexobj except ImportError: pass # Dump some basic debugging information if debug: debuglog.info('lex: tokens = %r', linfo.tokens) debuglog.info('lex: literals = %r', linfo.literals) debuglog.info('lex: states = %r', linfo.stateinfo) # Build a dictionary of valid token names lexobj.lextokens = set() for n in linfo.tokens: lexobj.lextokens.add(n) # Get literals specification if isinstance(linfo.literals, (list, tuple)): lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals) else: lexobj.lexliterals = linfo.literals lexobj.lextokens_all = lexobj.lextokens | set(lexobj.lexliterals) # Get the stateinfo dictionary stateinfo = linfo.stateinfo regexs = {} # Build the master regular expressions for state in stateinfo: regex_list = [] # Add rules defined by functions first for fname, f in linfo.funcsym[state]: regex_list.append('(?P<%s>%s)' % (fname, _get_regex(f))) if debug: debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", fname, _get_regex(f), state) # Now add all of the simple rules for name, r in linfo.strsym[state]: regex_list.append('(?P<%s>%s)' % (name, r)) if debug: debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", name, r, state) regexs[state] = regex_list # Build the master regular expressions if debug: debuglog.info('lex: ==== MASTER REGEXS FOLLOW ====') for state in regexs: lexre, re_text, re_names = _form_master_re(regexs[state], reflags, ldict, linfo.toknames) lexobj.lexstatere[state] = lexre lexobj.lexstateretext[state] = re_text lexobj.lexstaterenames[state] = re_names if debug: for i, text in enumerate(re_text): debuglog.info("lex: state '%s' : regex[%d] = '%s'", state, i, text) # For inclusive states, we need to add the regular expressions from the INITIAL state for state, stype in stateinfo.items(): if state != 'INITIAL' and stype == 'inclusive': lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL']) lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL']) lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL']) lexobj.lexstateinfo = stateinfo lexobj.lexre = lexobj.lexstatere['INITIAL'] lexobj.lexretext = lexobj.lexstateretext['INITIAL'] lexobj.lexreflags = reflags # Set up ignore variables lexobj.lexstateignore = linfo.ignore lexobj.lexignore = lexobj.lexstateignore.get('INITIAL', '') # Set up error functions lexobj.lexstateerrorf = linfo.errorf lexobj.lexerrorf = linfo.errorf.get('INITIAL', None) if not lexobj.lexerrorf: errorlog.warning('No t_error rule is defined') # Set up eof functions lexobj.lexstateeoff = linfo.eoff lexobj.lexeoff = linfo.eoff.get('INITIAL', None) # Check state information for ignore and error rules for s, stype in stateinfo.items(): if stype == 'exclusive': if s not in linfo.errorf: errorlog.warning("No error rule is defined for exclusive state '%s'", s) if s not in linfo.ignore and lexobj.lexignore: errorlog.warning("No ignore rule is defined for exclusive state '%s'", s) elif stype == 'inclusive': if s not in linfo.errorf: linfo.errorf[s] = linfo.errorf.get('INITIAL', None) if s not in linfo.ignore: linfo.ignore[s] = linfo.ignore.get('INITIAL', '') # Create global versions of the token() and input() functions token = lexobj.token input = lexobj.input lexer = lexobj # If in optimize mode, we write the lextab if lextab and optimize: if outputdir is None: # If no output directory is set, the location of the output files # is determined according to the following rules: # - If lextab specifies a package, files go into that package directory # - Otherwise, files go in the same directory as the specifying module if isinstance(lextab, types.ModuleType): srcfile = lextab.__file__ else: if '.' not in lextab: srcfile = ldict['__file__'] else: parts = lextab.split('.') pkgname = '.'.join(parts[:-1]) exec('import %s' % pkgname) srcfile = getattr(sys.modules[pkgname], '__file__', '') outputdir = os.path.dirname(srcfile) try: lexobj.writetab(lextab, outputdir) if lextab in sys.modules: del sys.modules[lextab] except IOError as e: errorlog.warning("Couldn't write lextab module %r. %s" % (lextab, e)) return lexobj # ----------------------------------------------------------------------------- # runmain() # # This runs the lexer as a main program # ----------------------------------------------------------------------------- def runmain(lexer=None, data=None): if not data: try: filename = sys.argv[1] f = open(filename) data = f.read() f.close() except IndexError: sys.stdout.write('Reading from standard input (type EOF to end):\n') data = sys.stdin.read() if lexer: _input = lexer.input else: _input = input _input(data) if lexer: _token = lexer.token else: _token = token while True: tok = _token() if not tok: break sys.stdout.write('(%s,%r,%d,%d)\n' % (tok.type, tok.value, tok.lineno, tok.lexpos)) # ----------------------------------------------------------------------------- # @TOKEN(regex) # # This decorator function can be used to set the regex expression on a function # when its docstring might need to be set in an alternative way # ----------------------------------------------------------------------------- def TOKEN(r): def set_regex(f): if hasattr(r, '__call__'): f.regex = _get_regex(r) else: f.regex = r return f return set_regex # Alternative spelling of the TOKEN decorator Token = TOKEN ================================================ FILE: ctypesgen/parser/lextab.py ================================================ # lextab.py. This file automatically created by PLY (version 3.11). Don't edit! _tabversion = '3.10' _lextokens = set(('ADD_ASSIGN', 'AND', 'AND_ASSIGN', 'AND_OP', 'AUTO', 'BREAK', 'CASE', 'CHAR', 'CHARACTER_CONSTANT', 'COLON', 'COMMA', 'CONDOP', 'CONST', 'CONTINUE', 'DEC_OP', 'DEFAULT', 'DIVIDE', 'DIV_ASSIGN', 'DO', 'DOUBLE', 'ELLIPSIS', 'ELSE', 'ENUM', 'EQUALS', 'EQ_OP', 'EXTERN', 'FLOAT', 'FOR', 'F_CONST_1', 'F_CONST_2', 'F_CONST_3', 'F_CONST_4', 'F_CONST_5', 'F_CONST_6', 'GE_OP', 'GOTO', 'GT', 'IDENTIFIER', 'IF', 'INC_OP', 'INT', 'I_CONST_BIN', 'I_CONST_DEC', 'I_CONST_HEX', 'I_CONST_OCT', 'LBRACE', 'LBRACKET', 'LEFT_ASSIGN', 'LEFT_OP', 'LE_OP', 'LNOT', 'LONG', 'LPAREN', 'LT', 'MINUS', 'MOD', 'MOD_ASSIGN', 'MUL_ASSIGN', 'NE_OP', 'NOT', 'OR', 'OR_ASSIGN', 'OR_OP', 'PERIOD', 'PLUS', 'PP_DEFINE', 'PP_DEFINE_MACRO_NAME', 'PP_DEFINE_NAME', 'PP_END_DEFINE', 'PP_IDENTIFIER_PASTE', 'PP_MACRO_PARAM', 'PP_STRINGIFY', 'PP_UNDEFINE', 'PRAGMA', 'PRAGMA_END', 'PRAGMA_PACK', 'PTR_OP', 'RBRACE', 'RBRACKET', 'REGISTER', 'RESTRICT', 'RETURN', 'RIGHT_ASSIGN', 'RIGHT_OP', 'RPAREN', 'SEMI', 'SHORT', 'SIGNED', 'SIZEOF', 'STATIC', 'STRING_LITERAL', 'STRUCT', 'SUB_ASSIGN', 'SWITCH', 'TIMES', 'TYPEDEF', 'TYPE_NAME', 'UNION', 'UNSIGNED', 'VOID', 'VOLATILE', 'WHILE', 'XOR', 'XOR_ASSIGN', '_BOOL', '__ASM__', '__ATTRIBUTE__')) _lexreflags = 64 _lexliterals = '' _lexstateinfo = {'INITIAL': 'inclusive', 'DEFINE': 'exclusive', 'PRAGMA': 'exclusive'} _lexstatere = {'INITIAL': [('(?P\\#\\s+(?P\\d+)\\s+"(?P[^"]+)"[ \\d]*\\n)|(?P(?P[0-9]+)(?P([Ee][+-]?[0-9]+))(f|F|l|L)?)', [None, ('t_ANY_directive', 'directive'), None, None, ('t_ANY_f_const_1', 'f_const_1'), None, None]), ('(?P(?P[0-9]*\\.[0-9]+)(?P([Ee][+-]?[0-9]+)?)(f|F|l|L)?)', [None, ('t_ANY_f_const_2', 'f_const_2'), None, None]), ('(?P(?P[0-9]+\\.)(?P([Ee][+-]?[0-9]+)?)(f|F|l|L)?)', [None, ('t_ANY_f_const_3', 'f_const_3'), None, None]), ('(?P(?P0[xX][a-fA-F0-9]+([Pp][+-]?[0-9]+))(f|F|l|L)?)', [None, ('t_ANY_f_const_4', 'f_const_4'), None]), ('(?P(?P0[xX][a-fA-F0-9]*\\.[a-fA-F0-9]+([Pp][+-]?[0-9]+))(f|F|l|L)?)', [None, ('t_ANY_f_const_5', 'f_const_5'), None]), ('(?P(?P0[xX][a-fA-F0-9]+\\.([Pp][+-]?[0-9]+))(f|F|l|L)?)|(?P(?P0[bB][0-1]*)(((u|U)(ll|LL|l|L)?)|((ll|LL|l|L)(u|U)?))?)', [None, ('t_ANY_f_const_6', 'f_const_6'), None, None, None, ('t_ANY_i_const_bin', 'i_const_bin'), None]), ('(?P(?P0[xX][a-fA-F0-9]+)(((u|U)(ll|LL|l|L)?)|((ll|LL|l|L)(u|U)?))?)', [None, ('t_ANY_i_const_hex', 'i_const_hex'), None]), ('(?P(?P[1-9][0-9]*)(((u|U)(ll|LL|l|L)?)|((ll|LL|l|L)(u|U)?))?)', [None, ('t_ANY_i_const_dec', 'i_const_dec'), None]), ('(?P0(?P[0-7]*)(((u|U)(ll|LL|l|L)?)|((ll|LL|l|L)(u|U)?))?)', [None, ('t_ANY_i_const_oct', 'i_const_oct'), None]), ("(?P(u8|u|U|L)?'(?P\\\\.|[^\\\\'])+')", [None, ('t_ANY_character_constant', 'character_constant'), None, None]), ('(?P"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P[a-zA-Z_][a-zA-Z_0-9]*)|(?P\\n)|(?P\\#undef)', [None, ('t_ANY_string_literal', 'string_literal'), None, None, ('t_INITIAL_identifier', 'identifier'), ('t_INITIAL_newline', 'newline'), ('t_INITIAL_pp_undefine', 'pp_undefine')]), ('(?P\\#define)|(?P\\#pragma)|(?P\\.\\.\\.)|(?P\\+\\+)|(?P\\|\\|)|(?P\\+=)|(?P<<=)|(?P\\*=)|(?P\\|=)|(?P>>=)|(?P\\^=)|(?P&=)|(?P&&)|(?P\\?)|(?P--)|(?P/=)', [None, ('t_INITIAL_pp_define', 'pp_define'), ('t_INITIAL_pragma', 'pragma'), (None, 'ELLIPSIS'), (None, 'INC_OP'), (None, 'OR_OP'), (None, 'ADD_ASSIGN'), (None, 'LEFT_ASSIGN'), (None, 'MUL_ASSIGN'), (None, 'OR_ASSIGN'), (None, 'RIGHT_ASSIGN'), (None, 'XOR_ASSIGN'), (None, 'AND_ASSIGN'), (None, 'AND_OP'), (None, 'CONDOP'), (None, 'DEC_OP'), (None, 'DIV_ASSIGN')]), ('(?P==)|(?P>=)|(?P\\{)|(?P\\[)|(?P<<)|(?P<=)|(?P\\()|(?P%=)|(?P!=)|(?P\\|)|(?P\\.)|(?P\\+)|(?P->)|(?P\\})|(?P\\])|(?P>>)|(?P\\))|(?P-=)|(?P\\*)|(?P\\^)|(?P&)|(?P:)|(?P,)|(?P/)|(?P=)|(?P>)|(?P!)|(?P<)|(?P-)|(?P%)|(?P~)|(?P;)', [None, (None, 'EQ_OP'), (None, 'GE_OP'), (None, 'LBRACE'), (None, 'LBRACKET'), (None, 'LEFT_OP'), (None, 'LE_OP'), (None, 'LPAREN'), (None, 'MOD_ASSIGN'), (None, 'NE_OP'), (None, 'OR'), (None, 'PERIOD'), (None, 'PLUS'), (None, 'PTR_OP'), (None, 'RBRACE'), (None, 'RBRACKET'), (None, 'RIGHT_OP'), (None, 'RPAREN'), (None, 'SUB_ASSIGN'), (None, 'TIMES'), (None, 'XOR'), (None, 'AND'), (None, 'COLON'), (None, 'COMMA'), (None, 'DIVIDE'), (None, 'EQUALS'), (None, 'GT'), (None, 'LNOT'), (None, 'LT'), (None, 'MINUS'), (None, 'MOD'), (None, 'NOT'), (None, 'SEMI')])], 'DEFINE': [('(?P\\#\\s+(?P\\d+)\\s+"(?P[^"]+)"[ \\d]*\\n)', [None, ('t_ANY_directive', 'directive'), None, None]), ('(?P(?P[0-9]+)(?P([Ee][+-]?[0-9]+))(f|F|l|L)?)', [None, ('t_ANY_f_const_1', 'f_const_1'), None, None]), ('(?P(?P[0-9]*\\.[0-9]+)(?P([Ee][+-]?[0-9]+)?)(f|F|l|L)?)', [None, ('t_ANY_f_const_2', 'f_const_2'), None, None]), ('(?P(?P[0-9]+\\.)(?P([Ee][+-]?[0-9]+)?)(f|F|l|L)?)|(?P(?P0[xX][a-fA-F0-9]+([Pp][+-]?[0-9]+))(f|F|l|L)?)', [None, ('t_ANY_f_const_3', 'f_const_3'), None, None, None, None, ('t_ANY_f_const_4', 'f_const_4'), None]), ('(?P(?P0[xX][a-fA-F0-9]*\\.[a-fA-F0-9]+([Pp][+-]?[0-9]+))(f|F|l|L)?)', [None, ('t_ANY_f_const_5', 'f_const_5'), None]), ('(?P(?P0[xX][a-fA-F0-9]+\\.([Pp][+-]?[0-9]+))(f|F|l|L)?)', [None, ('t_ANY_f_const_6', 'f_const_6'), None]), ('(?P(?P0[bB][0-1]*)(((u|U)(ll|LL|l|L)?)|((ll|LL|l|L)(u|U)?))?)', [None, ('t_ANY_i_const_bin', 'i_const_bin'), None]), ('(?P(?P0[xX][a-fA-F0-9]+)(((u|U)(ll|LL|l|L)?)|((ll|LL|l|L)(u|U)?))?)', [None, ('t_ANY_i_const_hex', 'i_const_hex'), None]), ('(?P(?P[1-9][0-9]*)(((u|U)(ll|LL|l|L)?)|((ll|LL|l|L)(u|U)?))?)', [None, ('t_ANY_i_const_dec', 'i_const_dec'), None]), ('(?P0(?P[0-7]*)(((u|U)(ll|LL|l|L)?)|((ll|LL|l|L)(u|U)?))?)', [None, ('t_ANY_i_const_oct', 'i_const_oct'), None]), ('(?P(u8|u|U|L)?\'(?P\\\\.|[^\\\\\'])+\')|(?P"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P[a-zA-Z_][a-zA-Z_0-9]*)|(?P\\n)', [None, ('t_ANY_character_constant', 'character_constant'), None, None, ('t_ANY_string_literal', 'string_literal'), None, None, ('t_DEFINE_identifier', 'identifier'), ('t_DEFINE_newline', 'newline')]), ('(?P(\\#\\#)|(\\#))|(?P\\.\\.\\.)|(?P\\+\\+)|(?P\\|\\|)|(?P\\+=)|(?P<<=)|(?P\\*=)|(?P\\|=)|(?P>>=)|(?P\\^=)|(?P&=)|(?P&&)|(?P\\?)|(?P--)|(?P/=)|(?P==)', [None, ('t_DEFINE_pp_param_op', 'pp_param_op'), None, None, (None, 'ELLIPSIS'), (None, 'INC_OP'), (None, 'OR_OP'), (None, 'ADD_ASSIGN'), (None, 'LEFT_ASSIGN'), (None, 'MUL_ASSIGN'), (None, 'OR_ASSIGN'), (None, 'RIGHT_ASSIGN'), (None, 'XOR_ASSIGN'), (None, 'AND_ASSIGN'), (None, 'AND_OP'), (None, 'CONDOP'), (None, 'DEC_OP'), (None, 'DIV_ASSIGN'), (None, 'EQ_OP')]), ('(?P>=)|(?P\\{)|(?P\\[)|(?P<<)|(?P<=)|(?P\\()|(?P%=)|(?P!=)|(?P\\|)|(?P\\.)|(?P\\+)|(?P->)|(?P\\})|(?P\\])|(?P>>)|(?P\\))|(?P-=)|(?P\\*)|(?P\\^)|(?P&)|(?P:)|(?P,)|(?P/)|(?P=)|(?P>)|(?P!)|(?P<)|(?P-)|(?P%)|(?P~)|(?P;)', [None, (None, 'GE_OP'), (None, 'LBRACE'), (None, 'LBRACKET'), (None, 'LEFT_OP'), (None, 'LE_OP'), (None, 'LPAREN'), (None, 'MOD_ASSIGN'), (None, 'NE_OP'), (None, 'OR'), (None, 'PERIOD'), (None, 'PLUS'), (None, 'PTR_OP'), (None, 'RBRACE'), (None, 'RBRACKET'), (None, 'RIGHT_OP'), (None, 'RPAREN'), (None, 'SUB_ASSIGN'), (None, 'TIMES'), (None, 'XOR'), (None, 'AND'), (None, 'COLON'), (None, 'COMMA'), (None, 'DIVIDE'), (None, 'EQUALS'), (None, 'GT'), (None, 'LNOT'), (None, 'LT'), (None, 'MINUS'), (None, 'MOD'), (None, 'NOT'), (None, 'SEMI')])], 'PRAGMA': [('(?P\\#\\s+(?P\\d+)\\s+"(?P[^"]+)"[ \\d]*\\n)', [None, ('t_ANY_directive', 'directive'), None, None]), ('(?P(?P[0-9]+)(?P([Ee][+-]?[0-9]+))(f|F|l|L)?)', [None, ('t_ANY_f_const_1', 'f_const_1'), None, None]), ('(?P(?P[0-9]*\\.[0-9]+)(?P([Ee][+-]?[0-9]+)?)(f|F|l|L)?)', [None, ('t_ANY_f_const_2', 'f_const_2'), None, None]), ('(?P(?P[0-9]+\\.)(?P([Ee][+-]?[0-9]+)?)(f|F|l|L)?)|(?P(?P0[xX][a-fA-F0-9]+([Pp][+-]?[0-9]+))(f|F|l|L)?)', [None, ('t_ANY_f_const_3', 'f_const_3'), None, None, None, None, ('t_ANY_f_const_4', 'f_const_4'), None]), ('(?P(?P0[xX][a-fA-F0-9]*\\.[a-fA-F0-9]+([Pp][+-]?[0-9]+))(f|F|l|L)?)', [None, ('t_ANY_f_const_5', 'f_const_5'), None]), ('(?P(?P0[xX][a-fA-F0-9]+\\.([Pp][+-]?[0-9]+))(f|F|l|L)?)', [None, ('t_ANY_f_const_6', 'f_const_6'), None]), ('(?P(?P0[bB][0-1]*)(((u|U)(ll|LL|l|L)?)|((ll|LL|l|L)(u|U)?))?)', [None, ('t_ANY_i_const_bin', 'i_const_bin'), None]), ('(?P(?P0[xX][a-fA-F0-9]+)(((u|U)(ll|LL|l|L)?)|((ll|LL|l|L)(u|U)?))?)', [None, ('t_ANY_i_const_hex', 'i_const_hex'), None]), ('(?P(?P[1-9][0-9]*)(((u|U)(ll|LL|l|L)?)|((ll|LL|l|L)(u|U)?))?)', [None, ('t_ANY_i_const_dec', 'i_const_dec'), None]), ('(?P0(?P[0-7]*)(((u|U)(ll|LL|l|L)?)|((ll|LL|l|L)(u|U)?))?)', [None, ('t_ANY_i_const_oct', 'i_const_oct'), None]), ('(?P(u8|u|U|L)?\'(?P\\\\.|[^\\\\\'])+\')|(?P"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?Ppack)|(?P\\n)', [None, ('t_ANY_character_constant', 'character_constant'), None, None, ('t_ANY_string_literal', 'string_literal'), None, None, ('t_PRAGMA_pack', 'pack'), ('t_PRAGMA_newline', 'newline')]), ('(?P[a-zA-Z_][a-zA-Z_0-9]*)|(?P\\.\\.\\.)|(?P\\+\\+)|(?P\\|\\|)|(?P\\+=)|(?P<<=)|(?P\\*=)|(?P\\|=)|(?P>>=)|(?P\\^=)|(?P&=)|(?P&&)|(?P\\?)|(?P--)|(?P/=)|(?P==)', [None, ('t_PRAGMA_identifier', 'identifier'), (None, 'ELLIPSIS'), (None, 'INC_OP'), (None, 'OR_OP'), (None, 'ADD_ASSIGN'), (None, 'LEFT_ASSIGN'), (None, 'MUL_ASSIGN'), (None, 'OR_ASSIGN'), (None, 'RIGHT_ASSIGN'), (None, 'XOR_ASSIGN'), (None, 'AND_ASSIGN'), (None, 'AND_OP'), (None, 'CONDOP'), (None, 'DEC_OP'), (None, 'DIV_ASSIGN'), (None, 'EQ_OP')]), ('(?P>=)|(?P\\{)|(?P\\[)|(?P<<)|(?P<=)|(?P\\()|(?P%=)|(?P!=)|(?P\\|)|(?P\\.)|(?P\\+)|(?P->)|(?P\\})|(?P\\])|(?P>>)|(?P\\))|(?P-=)|(?P\\*)|(?P\\^)|(?P&)|(?P:)|(?P,)|(?P/)|(?P=)|(?P>)|(?P!)|(?P<)|(?P-)|(?P%)|(?P~)|(?P;)', [None, (None, 'GE_OP'), (None, 'LBRACE'), (None, 'LBRACKET'), (None, 'LEFT_OP'), (None, 'LE_OP'), (None, 'LPAREN'), (None, 'MOD_ASSIGN'), (None, 'NE_OP'), (None, 'OR'), (None, 'PERIOD'), (None, 'PLUS'), (None, 'PTR_OP'), (None, 'RBRACE'), (None, 'RBRACKET'), (None, 'RIGHT_OP'), (None, 'RPAREN'), (None, 'SUB_ASSIGN'), (None, 'TIMES'), (None, 'XOR'), (None, 'AND'), (None, 'COLON'), (None, 'COMMA'), (None, 'DIVIDE'), (None, 'EQUALS'), (None, 'GT'), (None, 'LNOT'), (None, 'LT'), (None, 'MINUS'), (None, 'MOD'), (None, 'NOT'), (None, 'SEMI')])]} _lexstateignore = {'INITIAL': ' \t\x0b\x0c\r', 'DEFINE': ' \t\x0b\x0c\r', 'PRAGMA': ' \t\x0b\x0c\r'} _lexstateerrorf = {'DEFINE': 't_DEFINE_error', 'INITIAL': 't_INITIAL_error', 'PRAGMA': 't_PRAGMA_error'} _lexstateeoff = {} ================================================ FILE: ctypesgen/parser/parsetab.py ================================================ # new_parsetab.py # This file is automatically generated. Do not edit. # pylint: disable=W,C,R _tabversion = '3.10' _lr_method = 'LALR' _lr_signature = 'nonassocIFnonassocELSEADD_ASSIGN AND AND_ASSIGN AND_OP AUTO BREAK CASE CHAR CHARACTER_CONSTANT COLON COMMA CONDOP CONST CONTINUE DEC_OP DEFAULT DIVIDE DIV_ASSIGN DO DOUBLE ELLIPSIS ELSE ENUM EQUALS EQ_OP EXTERN FLOAT FOR F_CONST_1 F_CONST_2 F_CONST_3 F_CONST_4 F_CONST_5 F_CONST_6 GE_OP GOTO GT IDENTIFIER IF INC_OP INLINE INT I_CONST_BIN I_CONST_DEC I_CONST_HEX I_CONST_OCT LBRACE LBRACKET LEFT_ASSIGN LEFT_OP LE_OP LNOT LONG LPAREN LT MINUS MOD MOD_ASSIGN MUL_ASSIGN NE_OP NOT OR OR_ASSIGN OR_OP PERIOD PLUS PP_DEFINE PP_DEFINE_MACRO_NAME PP_DEFINE_NAME PP_END_DEFINE PP_IDENTIFIER_PASTE PP_MACRO_PARAM PP_STRINGIFY PP_UNDEFINE PRAGMA PRAGMA_END PRAGMA_PACK PTR_OP RBRACE RBRACKET REGISTER RESTRICT RETURN RIGHT_ASSIGN RIGHT_OP RPAREN SEMI SHORT SIGNED SIZEOF STATIC STRING_LITERAL STRUCT SUB_ASSIGN SWITCH TIMES TYPEDEF TYPE_NAME UNION UNSIGNED VOID VOLATILE WHILE XOR XOR_ASSIGN _BOOL _NORETURN __ASM__ __ATTRIBUTE__ translation_unit :\n | translation_unit external_declaration\n | translation_unit directive\n identifier : IDENTIFIER\n | IDENTIFIER PP_IDENTIFIER_PASTE identifier\n | PP_MACRO_PARAM PP_IDENTIFIER_PASTE identifier\n | IDENTIFIER PP_IDENTIFIER_PASTE PP_MACRO_PARAM\n | PP_MACRO_PARAM PP_IDENTIFIER_PASTE PP_MACRO_PARAM\n constant : I_CONST_HEX\n | I_CONST_DEC\n | I_CONST_OCT\n | I_CONST_BIN\n constant : F_CONST_1\n | F_CONST_2\n | F_CONST_3\n | F_CONST_4\n | F_CONST_5\n | F_CONST_6\n constant : CHARACTER_CONSTANT\n string_literal : STRING_LITERAL\n multi_string_literal : string_literal\n | macro_param\n | multi_string_literal string_literal\n | multi_string_literal macro_param\n macro_param : PP_MACRO_PARAM\n | PP_STRINGIFY PP_MACRO_PARAM\n primary_expression : identifier\n | constant\n | multi_string_literal\n | LPAREN expression RPAREN\n postfix_expression : primary_expression\n | postfix_expression LBRACKET expression RBRACKET\n | postfix_expression LPAREN RPAREN\n | postfix_expression LPAREN argument_expression_list RPAREN\n | postfix_expression PERIOD IDENTIFIER\n | postfix_expression PTR_OP IDENTIFIER\n | postfix_expression INC_OP\n | postfix_expression DEC_OP\n argument_expression_list : assignment_expression\n | argument_expression_list COMMA assignment_expression\n | type_name\n | argument_expression_list COMMA type_name\n asm_expression : __ASM__ volatile_opt LPAREN string_literal RPAREN\n | __ASM__ volatile_opt LPAREN string_literal COLON str_opt_expr_pair_list RPAREN\n | __ASM__ volatile_opt LPAREN string_literal COLON str_opt_expr_pair_list COLON str_opt_expr_pair_list RPAREN\n | __ASM__ volatile_opt LPAREN string_literal COLON str_opt_expr_pair_list COLON str_opt_expr_pair_list COLON str_opt_expr_pair_list RPAREN\n str_opt_expr_pair_list :\n | str_opt_expr_pair\n | str_opt_expr_pair_list COMMA str_opt_expr_pair\n str_opt_expr_pair : string_literal\n | string_literal LPAREN expression RPAREN\n volatile_opt :\n | VOLATILE\n unary_expression : postfix_expression\n | INC_OP unary_expression\n | DEC_OP unary_expression\n | unary_operator cast_expression\n | SIZEOF unary_expression\n | SIZEOF LPAREN type_name RPAREN\n | asm_expression\n unary_operator : AND\n | TIMES\n | PLUS\n | MINUS\n | NOT\n | LNOT\n cast_expression : unary_expression\n | LPAREN type_name RPAREN cast_expression\n multiplicative_expression : cast_expression\n | multiplicative_expression TIMES cast_expression\n | multiplicative_expression DIVIDE cast_expression\n | multiplicative_expression MOD cast_expression\n additive_expression : multiplicative_expression\n | additive_expression PLUS multiplicative_expression\n | additive_expression MINUS multiplicative_expression\n shift_expression : additive_expression\n | shift_expression LEFT_OP additive_expression\n | shift_expression RIGHT_OP additive_expression\n relational_expression : shift_expression\n | relational_expression LT shift_expression\n | relational_expression GT shift_expression\n | relational_expression LE_OP shift_expression\n | relational_expression GE_OP shift_expression\n equality_expression : relational_expression\n | equality_expression EQ_OP relational_expression\n | equality_expression NE_OP relational_expression\n and_expression : equality_expression\n | and_expression AND equality_expression\n exclusive_or_expression : and_expression\n | exclusive_or_expression XOR and_expression\n inclusive_or_expression : exclusive_or_expression\n | inclusive_or_expression OR exclusive_or_expression\n logical_and_expression : inclusive_or_expression\n | logical_and_expression AND_OP inclusive_or_expression\n logical_or_expression : logical_and_expression\n | logical_or_expression OR_OP logical_and_expression\n conditional_expression : logical_or_expression\n | logical_or_expression CONDOP expression COLON conditional_expression\n assignment_expression : conditional_expression\n | unary_expression assignment_operator assignment_expression\n assignment_operator : EQUALS\n | MUL_ASSIGN\n | DIV_ASSIGN\n | MOD_ASSIGN\n | ADD_ASSIGN\n | SUB_ASSIGN\n | LEFT_ASSIGN\n | RIGHT_ASSIGN\n | AND_ASSIGN\n | XOR_ASSIGN\n | OR_ASSIGN\n expression : assignment_expression\n | expression COMMA assignment_expression\n constant_expression : conditional_expression\n declaration : declaration_impl SEMI\n declaration_impl : declaration_specifier_list\n | declaration_specifier_list init_declarator_list\n declaration_specifier_list : gcc_attributes declaration_specifier gcc_attributes\n | declaration_specifier_list declaration_specifier gcc_attributes\n declaration_specifier : storage_class_specifier\n | type_specifier\n | type_qualifier\n | function_specifier\n init_declarator_list : init_declarator\n | init_declarator_list COMMA init_declarator\n init_declarator : declarator gcc_attributes\n | declarator gcc_attributes EQUALS initializer\n storage_class_specifier : TYPEDEF\n | EXTERN\n | STATIC\n | AUTO\n | REGISTER\n type_specifier : VOID\n | _BOOL\n | CHAR\n | SHORT\n | INT\n | LONG\n | FLOAT\n | DOUBLE\n | SIGNED\n | UNSIGNED\n | struct_or_union_specifier\n | enum_specifier\n | TYPE_NAME\n struct_or_union_specifier : struct_or_union gcc_attributes IDENTIFIER LBRACE member_declaration_list RBRACE\n | struct_or_union gcc_attributes TYPE_NAME LBRACE member_declaration_list RBRACE\n | struct_or_union gcc_attributes LBRACE member_declaration_list RBRACE\n | struct_or_union gcc_attributes IDENTIFIER\n | struct_or_union gcc_attributes TYPE_NAME\n struct_or_union : STRUCT\n | UNION\n gcc_attributes :\n | gcc_attributes gcc_attribute\n gcc_attribute : __ATTRIBUTE__ LPAREN LPAREN gcc_attrib_list RPAREN RPAREN\n gcc_attrib_list : gcc_attrib\n | gcc_attrib_list COMMA gcc_attrib\n gcc_attrib :\n | IDENTIFIER\n | IDENTIFIER LPAREN argument_expression_list RPAREN\n member_declaration_list : member_declaration\n | member_declaration_list member_declaration\n member_declaration : specifier_qualifier_list member_declarator_list SEMI\n | specifier_qualifier_list SEMI\n specifier_qualifier_list : gcc_attributes specifier_qualifier gcc_attributes\n | specifier_qualifier_list specifier_qualifier gcc_attributes\n specifier_qualifier : type_specifier\n | type_qualifier\n member_declarator_list : member_declarator\n | member_declarator_list COMMA member_declarator\n member_declarator : declarator gcc_attributes\n | COLON constant_expression gcc_attributes\n | declarator COLON constant_expression gcc_attributes\n enum_specifier : ENUM LBRACE enumerator_list RBRACE\n | ENUM IDENTIFIER LBRACE enumerator_list RBRACE\n | ENUM IDENTIFIER\n enumerator_list : enumerator_list_iso\n | enumerator_list_iso COMMA\n enumerator_list_iso : enumerator\n | enumerator_list_iso COMMA enumerator\n enumerator : IDENTIFIER\n | IDENTIFIER EQUALS constant_expression\n type_qualifier : CONST\n | VOLATILE\n | RESTRICT\n function_specifier : INLINE\n | _NORETURN\n declarator : pointer direct_declarator\n | direct_declarator\n direct_declarator : IDENTIFIER\n | LPAREN gcc_attributes declarator RPAREN\n | direct_declarator LBRACKET constant_expression RBRACKET\n | direct_declarator LBRACKET RBRACKET\n | direct_declarator LPAREN parameter_type_list RPAREN\n | direct_declarator LPAREN identifier_list RPAREN\n | direct_declarator LPAREN RPAREN\n pointer : TIMES\n | TIMES type_qualifier_list\n | TIMES pointer\n | TIMES type_qualifier_list pointer\n type_qualifier_list : type_qualifier\n | gcc_attribute\n | type_qualifier_list type_qualifier\n | type_qualifier_list gcc_attribute\n parameter_type_list : parameter_list\n | parameter_list COMMA ELLIPSIS\n parameter_list : parameter_declaration\n | parameter_list COMMA parameter_declaration\n parameter_declaration : declaration_specifier_list declarator gcc_attributes\n | declaration_specifier_list abstract_declarator\n | declaration_specifier_list\n identifier_list : IDENTIFIER\n | identifier_list COMMA IDENTIFIER\n type_name : specifier_qualifier_list\n | specifier_qualifier_list abstract_declarator\n abstract_declarator : pointer\n | direct_abstract_declarator gcc_attributes\n | pointer direct_abstract_declarator gcc_attributes\n direct_abstract_declarator : LPAREN gcc_attributes abstract_declarator RPAREN\n | LBRACKET RBRACKET\n | LBRACKET constant_expression RBRACKET\n | direct_abstract_declarator LBRACKET RBRACKET\n | direct_abstract_declarator LBRACKET constant_expression RBRACKET\n | LPAREN RPAREN\n | LPAREN parameter_type_list RPAREN\n | direct_abstract_declarator LPAREN RPAREN\n | direct_abstract_declarator LPAREN parameter_type_list RPAREN\n initializer : assignment_expression\n | LBRACE initializer_list RBRACE\n | LBRACE initializer_list COMMA RBRACE\n initializer_list : initializer\n | initializer_list COMMA initializer\n statement : labeled_statement\n | compound_statement\n | expression_statement\n | selection_statement\n | iteration_statement\n | jump_statement\n labeled_statement : IDENTIFIER COLON statement\n | CASE constant_expression COLON statement\n | DEFAULT COLON statement\n compound_statement : LBRACE RBRACE\n | LBRACE statement_list RBRACE\n | LBRACE declaration_list RBRACE\n | LBRACE declaration_list statement_list RBRACE\n compound_statement : LBRACE error RBRACE\n declaration_list : declaration\n | declaration_list declaration\n statement_list : statement\n | statement_list statement\n expression_statement : SEMI\n | expression SEMI\n expression_statement : error SEMI\n selection_statement : IF LPAREN expression RPAREN statement %prec IF\n | IF LPAREN expression RPAREN statement ELSE statement\n | SWITCH LPAREN expression RPAREN statement\n iteration_statement : WHILE LPAREN expression RPAREN statement\n | DO statement WHILE LPAREN expression RPAREN SEMI\n | FOR LPAREN expression_statement expression_statement RPAREN statement\n | FOR LPAREN expression_statement expression_statement expression RPAREN statement\n jump_statement : GOTO IDENTIFIER SEMI\n | CONTINUE SEMI\n | BREAK SEMI\n | RETURN SEMI\n | RETURN expression SEMI\n external_declaration : declaration\n | function_definition\n function_definition : declaration_specifier_list declarator declaration_list compound_statement\n | declaration_specifier_list declarator compound_statement\n | declarator declaration_list compound_statement\n | declarator compound_statement\n directive : define\n | undefine\n | pragma\n define : PP_DEFINE PP_DEFINE_NAME PP_END_DEFINE\n | PP_DEFINE PP_DEFINE_NAME type_name PP_END_DEFINE\n | PP_DEFINE PP_DEFINE_NAME constant_expression PP_END_DEFINE\n | PP_DEFINE PP_DEFINE_MACRO_NAME LPAREN RPAREN PP_END_DEFINE\n | PP_DEFINE PP_DEFINE_MACRO_NAME LPAREN RPAREN constant_expression PP_END_DEFINE\n | PP_DEFINE PP_DEFINE_MACRO_NAME LPAREN macro_parameter_list RPAREN PP_END_DEFINE\n | PP_DEFINE PP_DEFINE_MACRO_NAME LPAREN macro_parameter_list RPAREN constant_expression PP_END_DEFINE\n define : PP_DEFINE error PP_END_DEFINE\n undefine : PP_UNDEFINE PP_DEFINE_NAME PP_END_DEFINE\n macro_parameter_list : PP_MACRO_PARAM\n | macro_parameter_list COMMA PP_MACRO_PARAM\n pragma : pragma_pack\n | PRAGMA pragma_directive_list PRAGMA_END\n pragma_pack : PRAGMA PRAGMA_PACK LPAREN RPAREN PRAGMA_END\n | PRAGMA PRAGMA_PACK LPAREN constant RPAREN PRAGMA_END\n | PRAGMA PRAGMA_PACK LPAREN pragma_pack_stack_args RPAREN PRAGMA_END\n pragma_pack_stack_args : IDENTIFIER\n | IDENTIFIER COMMA IDENTIFIER\n | IDENTIFIER COMMA IDENTIFIER COMMA constant\n | IDENTIFIER COMMA constant COMMA IDENTIFIER\n | IDENTIFIER COMMA constant\n pragma_directive_list : pragma_directive\n | pragma_directive_list pragma_directive\n pragma_directive : IDENTIFIER\n | string_literal\n ' _lr_action_items = {'PP_DEFINE':([0,1,2,3,4,5,6,7,8,15,22,59,85,92,94,167,177,179,180,197,208,211,213,284,285,325,383,387,434,435,438,439,468,],[-1,12,-2,-3,-266,-267,-272,-273,-274,-286,-115,-271,-269,-270,-242,-275,-282,-283,-287,-268,-243,-244,-246,-276,-277,-245,-278,-288,-279,-280,-289,-290,-281,]),'PP_UNDEFINE':([0,1,2,3,4,5,6,7,8,15,22,59,85,92,94,167,177,179,180,197,208,211,213,284,285,325,383,387,434,435,438,439,468,],[-1,14,-2,-3,-266,-267,-272,-273,-274,-286,-115,-271,-269,-270,-242,-275,-282,-283,-287,-268,-243,-244,-246,-276,-277,-245,-278,-288,-279,-280,-289,-290,-281,]),'PRAGMA':([0,1,2,3,4,5,6,7,8,15,22,59,85,92,94,167,177,179,180,197,208,211,213,284,285,325,383,387,434,435,438,439,468,],[-1,16,-2,-3,-266,-267,-272,-273,-274,-286,-115,-271,-269,-270,-242,-275,-282,-283,-287,-268,-243,-244,-246,-276,-277,-245,-278,-288,-279,-280,-289,-290,-281,]),'TIMES':([0,1,2,3,4,5,6,7,8,10,13,15,20,22,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,59,60,61,62,63,66,73,74,75,78,80,82,83,85,87,88,91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107,111,114,119,122,124,125,126,127,128,129,130,132,133,134,135,136,137,138,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,165,167,170,173,174,175,177,179,180,183,193,195,196,197,198,200,202,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,251,252,253,254,255,256,257,258,259,262,263,264,265,266,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,284,285,287,290,291,292,293,294,295,312,315,319,321,323,325,326,327,328,329,330,333,334,338,339,340,345,349,350,356,357,367,368,369,370,371,372,375,377,382,383,385,387,398,402,408,412,413,414,415,416,417,418,419,420,421,422,423,424,434,435,438,439,444,446,447,449,451,453,454,455,456,458,463,468,471,479,481,482,490,491,492,493,495,502,504,],[-1,20,-2,-3,-266,-267,-272,-273,-274,20,-153,-286,20,-115,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-271,-247,134,20,134,20,-20,-153,-154,134,20,-201,-202,-269,20,-119,-176,-270,-248,-242,134,134,-249,-233,-234,-235,-236,-237,-238,-251,-4,134,134,134,134,-67,-54,134,134,134,-69,134,-60,-31,-61,-62,-63,-64,-65,-66,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,280,-275,20,-67,134,-4,-282,-283,-287,-118,20,-203,-204,-268,134,-149,-150,-243,-250,-244,134,-246,-253,134,134,-252,134,134,134,134,134,-262,-263,-264,134,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,134,134,134,134,-37,-38,-55,134,-56,-57,-58,134,134,134,-23,-24,-25,134,134,-26,134,134,134,134,134,134,134,134,134,134,134,134,134,-276,-277,-153,-167,-168,-153,134,-153,134,-153,134,20,-174,134,-245,-239,-5,-7,134,-241,-30,134,134,-261,-265,-33,-35,-36,-8,-6,280,280,-70,-71,-72,-166,134,20,-165,-278,134,-288,20,-148,134,-175,-240,134,-68,134,134,134,134,134,-32,-34,134,-59,-279,-280,-289,-290,134,134,-146,20,134,-147,-254,-256,-257,134,-43,-281,-155,134,-259,134,-255,-258,-260,134,-44,-45,-46,]),'IDENTIFIER':([0,1,2,3,4,5,6,7,8,10,13,15,16,18,20,22,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,59,60,61,62,63,66,68,70,71,72,73,74,75,78,79,80,81,82,83,85,87,88,89,90,91,92,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,116,119,125,126,127,129,133,134,135,136,137,138,167,174,177,179,180,181,182,183,193,194,195,196,197,198,200,202,207,208,209,211,212,213,214,215,216,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,254,258,259,262,266,267,268,270,271,272,273,274,275,276,277,278,279,280,281,282,284,285,287,290,291,293,294,295,303,307,311,312,315,319,321,322,323,325,326,329,330,334,338,339,340,372,375,382,383,385,387,390,398,402,408,412,413,414,416,417,418,419,420,423,434,435,438,439,443,444,446,447,449,451,453,454,455,456,458,468,470,471,479,481,482,490,491,492,493,],[-1,21,-2,-3,-266,-267,-272,-273,-274,21,-153,-286,71,21,-197,-115,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,91,-151,-152,-271,-247,106,21,175,21,71,-296,-298,-299,-20,-153,-154,175,191,-198,-199,-201,-202,-269,21,-119,200,206,-176,-270,-248,-242,106,106,-249,-233,-234,-235,-236,-237,-238,-251,175,175,106,228,175,175,175,175,175,-61,-62,-63,-64,-65,-66,-275,175,-282,-283,-287,-297,302,-118,21,-200,-203,-204,-268,175,-149,-150,206,-243,-250,-244,106,-246,-253,106,175,106,-252,175,175,175,175,175,-262,-263,-264,175,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,175,175,175,175,349,350,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,-276,-277,-153,-167,-168,175,-153,175,393,394,21,-153,175,21,-174,206,175,-245,-239,106,-241,175,175,-261,-265,-166,175,-165,-278,175,-288,440,21,-148,175,-175,-240,106,106,106,175,175,175,175,-279,-280,-289,-290,393,175,175,-146,21,175,-147,-254,-256,-257,106,-281,487,-155,106,-259,106,-255,-258,-260,175,]),'LPAREN':([0,1,2,3,4,5,6,7,8,10,13,15,18,19,20,21,22,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,59,60,61,62,63,64,66,69,73,74,75,76,77,78,80,81,82,83,85,87,88,91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107,110,111,112,113,114,115,119,124,125,126,127,129,132,133,134,135,136,137,138,139,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,167,170,174,175,177,179,180,183,184,186,188,193,194,195,196,197,198,200,202,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,251,252,254,258,259,260,261,262,263,264,265,266,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,284,285,287,288,289,290,291,292,293,294,295,298,304,305,306,311,312,315,319,321,323,325,326,327,328,329,330,333,334,337,338,339,340,345,349,350,356,357,372,373,375,377,378,380,382,383,385,387,393,398,402,408,412,413,414,416,417,418,419,420,421,422,423,427,429,432,433,434,435,438,439,444,446,447,449,451,453,454,455,456,458,465,466,467,468,471,479,481,482,483,490,491,492,493,],[-1,13,-2,-3,-266,-267,-272,-273,-274,13,-153,-286,13,79,-197,-190,-115,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-271,-247,111,13,174,176,13,182,-20,-153,-154,184,79,174,-198,-199,-201,-202,-269,13,-119,-176,-270,-248,-242,111,111,-249,-233,-234,-235,-236,-237,-238,-251,-4,174,221,111,224,225,111,227,111,248,254,254,174,258,-31,-61,-62,-63,-64,-65,-66,-52,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-275,292,111,-4,-282,-283,-287,-118,303,-193,-196,312,-200,-203,-204,-268,111,-149,-150,-243,-250,-244,111,-246,-253,111,111,-252,111,111,111,111,111,-262,-263,-264,111,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,111,174,111,111,-37,-38,111,111,174,353,-53,174,-23,-24,-25,174,174,-26,174,174,174,174,174,174,174,174,174,174,174,174,174,-276,-277,-153,292,376,-167,-168,-153,174,-153,174,-191,-192,-194,-195,312,-153,111,13,-174,174,-245,-239,-5,-7,111,-241,-30,174,418,111,-261,-265,-33,-35,-36,-8,-6,-166,376,174,292,-224,-220,-165,-278,174,-288,444,312,-148,174,-175,-240,111,111,111,111,111,174,-32,-34,111,-222,-226,-225,-221,-279,-280,-289,-290,111,111,-146,13,174,-147,-254,-256,-257,111,-223,-227,-219,-281,-155,111,-259,111,493,-255,-258,-260,111,]),'$end':([0,1,2,3,4,5,6,7,8,15,22,59,85,92,94,167,177,179,180,197,208,211,213,284,285,325,383,387,434,435,438,439,468,],[-1,0,-2,-3,-266,-267,-272,-273,-274,-286,-115,-271,-269,-270,-242,-275,-282,-283,-287,-268,-243,-244,-246,-276,-277,-245,-278,-288,-279,-280,-289,-290,-281,]),'__ATTRIBUTE__':([0,1,2,3,4,5,6,7,8,11,13,15,17,19,20,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,56,57,58,59,60,61,63,66,73,74,75,77,79,80,82,83,84,85,86,88,89,91,92,93,94,96,111,123,124,128,130,131,132,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,161,162,163,164,165,166,167,171,172,173,174,175,177,179,180,183,186,188,195,196,197,200,201,202,208,211,213,248,251,252,253,255,256,257,258,263,264,265,269,283,284,285,287,289,290,291,292,294,298,304,305,306,308,309,312,316,317,318,320,321,325,327,328,333,343,345,349,350,352,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,376,377,378,380,382,383,387,397,398,401,402,403,405,407,409,412,415,421,422,423,424,426,427,429,432,433,434,435,438,439,444,447,448,450,452,453,460,463,465,466,467,468,471,477,478,489,495,502,504,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,-153,-153,-286,76,-189,76,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-151,-152,-153,-271,-247,-153,-153,76,-20,-153,-154,-188,-153,76,-201,-202,-153,-269,76,76,76,-176,-270,-248,-242,-153,-153,-97,-54,-69,-60,-95,-31,-21,-93,-27,-28,-29,-91,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-89,-87,-84,-79,-76,-73,-153,-275,-114,76,-67,-153,-4,-282,-283,-287,76,-193,-196,-203,-204,-268,-149,-153,-150,-243,-244,-246,-153,-37,-38,-55,-56,-57,-58,-153,-23,-24,-25,-26,76,-276,-277,-153,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-153,-161,-153,-174,-245,-5,-7,-30,-96,-33,-35,-36,-94,-92,-90,-8,-6,-88,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,76,-153,76,-153,76,-224,-220,76,-278,-288,76,76,-153,-148,-162,-164,-153,-153,-175,-68,-32,-34,-153,-59,76,-222,-226,-225,-221,-279,-280,-289,-290,-153,-146,-163,76,-153,-147,-98,-43,-223,-227,-219,-281,-155,-153,76,76,-44,-45,-46,]),'TYPEDEF':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,74,75,77,79,84,85,86,88,91,92,93,94,96,167,177,179,180,183,186,188,193,197,200,202,208,211,213,284,285,292,298,304,305,306,308,312,321,325,376,377,383,387,398,402,412,434,435,438,439,447,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,31,-153,-286,31,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,31,-153,-154,-188,-153,-153,-269,31,-119,-176,-270,-248,-242,-153,-275,-282,-283,-287,-118,-193,-196,31,-268,-149,-150,-243,-244,-246,-276,-277,-153,-191,-192,-194,-195,-153,-153,-174,-245,-153,31,-278,-288,31,-148,-175,-279,-280,-289,-290,-146,-147,-281,-155,]),'EXTERN':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,74,75,77,79,84,85,86,88,91,92,93,94,96,167,177,179,180,183,186,188,193,197,200,202,208,211,213,284,285,292,298,304,305,306,308,312,321,325,376,377,383,387,398,402,412,434,435,438,439,447,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,32,-153,-286,32,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,32,-153,-154,-188,-153,-153,-269,32,-119,-176,-270,-248,-242,-153,-275,-282,-283,-287,-118,-193,-196,32,-268,-149,-150,-243,-244,-246,-276,-277,-153,-191,-192,-194,-195,-153,-153,-174,-245,-153,32,-278,-288,32,-148,-175,-279,-280,-289,-290,-146,-147,-281,-155,]),'STATIC':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,74,75,77,79,84,85,86,88,91,92,93,94,96,167,177,179,180,183,186,188,193,197,200,202,208,211,213,284,285,292,298,304,305,306,308,312,321,325,376,377,383,387,398,402,412,434,435,438,439,447,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,33,-153,-286,33,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,33,-153,-154,-188,-153,-153,-269,33,-119,-176,-270,-248,-242,-153,-275,-282,-283,-287,-118,-193,-196,33,-268,-149,-150,-243,-244,-246,-276,-277,-153,-191,-192,-194,-195,-153,-153,-174,-245,-153,33,-278,-288,33,-148,-175,-279,-280,-289,-290,-146,-147,-281,-155,]),'AUTO':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,74,75,77,79,84,85,86,88,91,92,93,94,96,167,177,179,180,183,186,188,193,197,200,202,208,211,213,284,285,292,298,304,305,306,308,312,321,325,376,377,383,387,398,402,412,434,435,438,439,447,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,34,-153,-286,34,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,34,-153,-154,-188,-153,-153,-269,34,-119,-176,-270,-248,-242,-153,-275,-282,-283,-287,-118,-193,-196,34,-268,-149,-150,-243,-244,-246,-276,-277,-153,-191,-192,-194,-195,-153,-153,-174,-245,-153,34,-278,-288,34,-148,-175,-279,-280,-289,-290,-146,-147,-281,-155,]),'REGISTER':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,74,75,77,79,84,85,86,88,91,92,93,94,96,167,177,179,180,183,186,188,193,197,200,202,208,211,213,284,285,292,298,304,305,306,308,312,321,325,376,377,383,387,398,402,412,434,435,438,439,447,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,35,-153,-286,35,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,35,-153,-154,-188,-153,-153,-269,35,-119,-176,-270,-248,-242,-153,-275,-282,-283,-287,-118,-193,-196,35,-268,-149,-150,-243,-244,-246,-276,-277,-153,-191,-192,-194,-195,-153,-153,-174,-245,-153,35,-278,-288,35,-148,-175,-279,-280,-289,-290,-146,-147,-281,-155,]),'VOID':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,84,85,86,88,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,36,-153,-286,36,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,36,-153,-153,-154,-188,-153,-153,-269,36,-119,-176,-270,-248,-242,-153,-153,-275,36,36,-153,-282,-283,-287,-118,-193,-196,36,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,36,-153,-174,-245,-166,-153,36,-165,-278,-288,36,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'_BOOL':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,84,85,86,88,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,37,-153,-286,37,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,37,-153,-153,-154,-188,-153,-153,-269,37,-119,-176,-270,-248,-242,-153,-153,-275,37,37,-153,-282,-283,-287,-118,-193,-196,37,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,37,-153,-174,-245,-166,-153,37,-165,-278,-288,37,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'CHAR':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,84,85,86,88,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,38,-153,-286,38,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,38,-153,-153,-154,-188,-153,-153,-269,38,-119,-176,-270,-248,-242,-153,-153,-275,38,38,-153,-282,-283,-287,-118,-193,-196,38,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,38,-153,-174,-245,-166,-153,38,-165,-278,-288,38,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'SHORT':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,84,85,86,88,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,39,-153,-286,39,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,39,-153,-153,-154,-188,-153,-153,-269,39,-119,-176,-270,-248,-242,-153,-153,-275,39,39,-153,-282,-283,-287,-118,-193,-196,39,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,39,-153,-174,-245,-166,-153,39,-165,-278,-288,39,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'INT':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,84,85,86,88,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,40,-153,-286,40,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,40,-153,-153,-154,-188,-153,-153,-269,40,-119,-176,-270,-248,-242,-153,-153,-275,40,40,-153,-282,-283,-287,-118,-193,-196,40,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,40,-153,-174,-245,-166,-153,40,-165,-278,-288,40,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'LONG':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,84,85,86,88,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,41,-153,-286,41,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,41,-153,-153,-154,-188,-153,-153,-269,41,-119,-176,-270,-248,-242,-153,-153,-275,41,41,-153,-282,-283,-287,-118,-193,-196,41,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,41,-153,-174,-245,-166,-153,41,-165,-278,-288,41,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'FLOAT':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,84,85,86,88,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,42,-153,-286,42,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,42,-153,-153,-154,-188,-153,-153,-269,42,-119,-176,-270,-248,-242,-153,-153,-275,42,42,-153,-282,-283,-287,-118,-193,-196,42,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,42,-153,-174,-245,-166,-153,42,-165,-278,-288,42,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'DOUBLE':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,84,85,86,88,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,43,-153,-286,43,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,43,-153,-153,-154,-188,-153,-153,-269,43,-119,-176,-270,-248,-242,-153,-153,-275,43,43,-153,-282,-283,-287,-118,-193,-196,43,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,43,-153,-174,-245,-166,-153,43,-165,-278,-288,43,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'SIGNED':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,84,85,86,88,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,44,-153,-286,44,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,44,-153,-153,-154,-188,-153,-153,-269,44,-119,-176,-270,-248,-242,-153,-153,-275,44,44,-153,-282,-283,-287,-118,-193,-196,44,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,44,-153,-174,-245,-166,-153,44,-165,-278,-288,44,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'UNSIGNED':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,84,85,86,88,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,45,-153,-286,45,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,45,-153,-153,-154,-188,-153,-153,-269,45,-119,-176,-270,-248,-242,-153,-153,-275,45,45,-153,-282,-283,-287,-118,-193,-196,45,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,45,-153,-174,-245,-166,-153,45,-165,-278,-288,45,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'TYPE_NAME':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,56,57,58,59,60,61,62,63,74,75,77,79,84,85,86,88,89,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,48,-153,-286,48,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-151,-152,-153,-271,-247,-153,48,-153,-153,-154,-188,-153,-153,-269,48,-119,202,-176,-270,-248,-242,-153,-153,-275,48,48,-153,-282,-283,-287,-118,-193,-196,48,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,48,-153,-174,-245,-166,-153,48,-165,-278,-288,48,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'CONST':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,20,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,80,82,83,84,85,86,88,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,195,196,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,49,-153,-286,49,-189,49,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,49,-153,-153,-154,-188,-153,49,-201,-202,-153,-269,49,-119,-176,-270,-248,-242,-153,-153,-275,49,49,-153,-282,-283,-287,-118,-193,-196,49,-203,-204,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,49,-153,-174,-245,-166,-153,49,-165,-278,-288,49,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'VOLATILE':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,20,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,80,82,83,84,85,86,88,91,92,93,94,96,111,139,167,170,172,174,177,179,180,183,186,188,193,195,196,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,50,-153,-286,50,-189,50,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,50,-153,-153,-154,-188,-153,50,-201,-202,-153,-269,50,-119,-176,-270,-248,-242,-153,-153,261,-275,50,50,-153,-282,-283,-287,-118,-193,-196,50,-203,-204,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,50,-153,-174,-245,-166,-153,50,-165,-278,-288,50,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'RESTRICT':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,20,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,80,82,83,84,85,86,88,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,195,196,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,51,-153,-286,51,-189,51,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,51,-153,-153,-154,-188,-153,51,-201,-202,-153,-269,51,-119,-176,-270,-248,-242,-153,-153,-275,51,51,-153,-282,-283,-287,-118,-193,-196,51,-203,-204,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,51,-153,-174,-245,-166,-153,51,-165,-278,-288,51,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'INLINE':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,74,75,77,79,84,85,86,88,91,92,93,94,96,167,177,179,180,183,186,188,193,197,200,202,208,211,213,284,285,292,298,304,305,306,308,312,321,325,376,377,383,387,398,402,412,434,435,438,439,447,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,52,-153,-286,52,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,52,-153,-154,-188,-153,-153,-269,52,-119,-176,-270,-248,-242,-153,-275,-282,-283,-287,-118,-193,-196,52,-268,-149,-150,-243,-244,-246,-276,-277,-153,-191,-192,-194,-195,-153,-153,-174,-245,-153,52,-278,-288,52,-148,-175,-279,-280,-289,-290,-146,-147,-281,-155,]),'_NORETURN':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,74,75,77,79,84,85,86,88,91,92,93,94,96,167,177,179,180,183,186,188,193,197,200,202,208,211,213,284,285,292,298,304,305,306,308,312,321,325,376,377,383,387,398,402,412,434,435,438,439,447,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,53,-153,-286,53,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,53,-153,-154,-188,-153,-153,-269,53,-119,-176,-270,-248,-242,-153,-275,-282,-283,-287,-118,-193,-196,53,-268,-149,-150,-243,-244,-246,-276,-277,-153,-191,-192,-194,-195,-153,-153,-174,-245,-153,53,-278,-288,53,-148,-175,-279,-280,-289,-290,-146,-147,-281,-155,]),'ENUM':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,84,85,86,88,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,55,-153,-286,55,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,55,-153,-153,-154,-188,-153,-153,-269,55,-119,-176,-270,-248,-242,-153,-153,-275,55,55,-153,-282,-283,-287,-118,-193,-196,55,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,55,-153,-174,-245,-166,-153,55,-165,-278,-288,55,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'STRUCT':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,84,85,86,88,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,56,-153,-286,56,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,56,-153,-153,-154,-188,-153,-153,-269,56,-119,-176,-270,-248,-242,-153,-153,-275,56,56,-153,-282,-283,-287,-118,-193,-196,56,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,56,-153,-174,-245,-166,-153,56,-165,-278,-288,56,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'UNION':([0,1,2,3,4,5,6,7,8,10,11,15,17,19,21,22,23,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,58,59,60,61,62,63,74,75,77,79,84,85,86,88,91,92,93,94,96,111,167,170,172,174,177,179,180,183,186,188,193,197,200,201,202,208,211,213,248,258,284,285,287,290,291,292,294,298,304,305,306,308,312,316,317,318,319,320,321,325,372,376,377,382,383,387,398,401,402,403,405,409,412,423,434,435,438,439,444,447,448,453,468,471,],[-1,-153,-2,-3,-266,-267,-272,-273,-274,57,-153,-286,57,-189,-190,-115,-153,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-153,-271,-247,-153,57,-153,-153,-154,-188,-153,-153,-269,57,-119,-176,-270,-248,-242,-153,-153,-275,57,57,-153,-282,-283,-287,-118,-193,-196,57,-268,-149,-153,-150,-243,-244,-246,-153,-153,-276,-277,-153,-167,-168,-153,-153,-191,-192,-194,-195,-153,-153,-153,-153,-161,57,-153,-174,-245,-166,-153,57,-165,-278,-288,57,-153,-148,-162,-164,-153,-175,-153,-279,-280,-289,-290,-153,-146,-163,-147,-281,-155,]),'SEMI':([9,10,19,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,60,61,62,73,74,75,77,86,88,91,93,94,95,96,97,98,99,100,101,102,103,104,105,106,109,114,117,118,119,120,121,122,123,124,128,130,131,132,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,161,162,163,164,165,166,171,173,175,183,186,188,199,200,202,208,209,210,211,212,213,214,215,218,219,227,228,229,230,231,232,251,252,253,255,256,257,263,264,265,269,283,287,290,291,294,298,304,305,306,313,314,319,321,325,326,327,328,329,330,331,333,338,339,340,341,343,345,349,350,352,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,382,402,404,406,407,412,413,414,415,416,417,421,422,424,445,447,450,452,453,454,455,456,458,460,463,471,474,476,477,478,479,480,481,482,489,490,491,492,495,502,504,],[22,-116,-189,-190,-115,-153,-117,-153,-124,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-247,105,-116,-20,-153,-154,-188,-126,-119,-176,-248,-242,105,105,214,-249,-233,-234,-235,-236,-237,-238,-251,-4,219,105,229,230,231,-112,-99,-67,-97,-54,-69,-60,-95,-31,-21,-93,-27,-28,-29,-91,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-89,-87,-84,-79,-76,-73,-153,-114,-67,-4,-118,-193,-196,-125,-149,-150,-243,-250,214,-244,105,-246,-253,105,105,-252,105,339,-262,-263,-264,340,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-126,-153,-167,-168,-153,-191,-192,-194,-195,-127,-228,405,-174,-245,-239,-5,-7,105,-241,-113,-30,105,-261,-265,-100,-96,-33,-35,-36,-94,-92,-90,-8,-6,-88,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-166,-165,-148,448,-169,-153,-175,-240,105,-68,105,105,-32,-34,-59,-229,-146,-171,-153,-147,-254,-256,-257,105,-98,-43,-155,-230,-170,-153,-172,105,491,-259,105,-173,-255,-258,-260,-44,-45,-46,]),'LBRACE':([11,19,21,22,23,54,55,56,57,58,60,61,75,77,84,89,91,93,94,95,96,98,99,100,101,102,103,104,105,114,186,188,198,200,202,208,209,211,212,213,214,215,218,219,229,230,231,298,304,305,306,315,325,326,329,330,339,340,413,414,416,417,446,454,455,456,458,471,479,481,482,490,491,492,],[61,-189,-190,-115,61,-153,90,-151,-152,61,-247,61,-154,-188,61,201,207,-248,-242,61,61,-249,-233,-234,-235,-236,-237,-238,-251,61,-193,-196,315,316,320,-243,-250,-244,61,-246,-253,61,61,-252,-262,-263,-264,-191,-192,-194,-195,315,-245,-239,61,-241,-261,-265,-240,61,61,61,315,-254,-256,-257,61,-155,61,-259,61,-255,-258,-260,]),'PP_DEFINE_NAME':([12,14,],[63,67,]),'PP_DEFINE_MACRO_NAME':([12,],[64,]),'error':([12,22,60,61,93,94,95,96,98,99,100,101,102,103,104,105,114,208,209,211,212,213,214,215,218,219,227,229,230,231,325,326,329,330,338,339,340,413,414,416,417,454,455,456,458,479,481,482,490,491,492,],[65,-115,-247,97,-248,-242,210,210,-249,-233,-234,-235,-236,-237,-238,-251,210,-243,-250,-244,210,-246,-253,210,210,-252,210,-262,-263,-264,-245,-239,210,-241,210,-261,-265,-240,210,210,210,-254,-256,-257,210,210,-259,210,-255,-258,-260,]),'PRAGMA_PACK':([16,],[69,]),'STRING_LITERAL':([16,22,60,61,63,68,70,71,72,73,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,140,144,146,158,174,181,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,263,264,265,266,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,353,375,385,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,464,479,481,482,490,491,492,493,494,496,501,],[73,-115,-247,73,73,73,-296,-298,-299,-20,73,-248,-242,73,73,-249,-233,-234,-235,-236,-237,-238,-251,73,73,73,73,73,73,73,73,-61,-62,-63,-64,-65,-66,-21,73,-25,-22,73,-297,73,-243,-250,-244,73,-246,-253,73,73,-252,73,73,73,73,73,-262,-263,-264,73,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,73,73,73,73,73,73,73,73,-23,-24,-25,73,73,-26,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,-245,-239,73,-241,73,73,-261,-265,73,73,73,73,-240,73,73,73,73,73,73,73,73,73,73,-254,-256,-257,73,73,73,-259,73,-255,-258,-260,73,73,73,73,]),'EQUALS':([19,21,23,73,75,77,86,106,122,124,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,166,173,175,186,188,206,251,252,253,255,256,257,263,264,265,269,283,298,304,305,306,327,328,333,345,349,350,356,357,415,421,422,424,463,471,495,502,504,],[-189,-190,-153,-20,-154,-188,198,-4,234,-54,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-153,-67,-4,-193,-196,323,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,198,-191,-192,-194,-195,-5,-7,-30,-33,-35,-36,-8,-6,-68,-32,-34,-59,-43,-155,-44,-45,-46,]),'COMMA':([19,20,21,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,73,74,75,77,80,81,82,83,86,88,91,106,109,120,121,122,123,124,128,130,131,132,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,161,162,163,164,165,166,170,171,173,175,183,186,188,189,190,191,192,193,194,195,196,199,200,202,204,205,206,222,232,251,252,253,255,256,257,263,264,265,269,283,286,287,288,289,290,291,294,296,297,298,302,303,304,305,306,309,310,311,313,314,321,327,328,331,332,333,335,336,341,342,343,344,345,346,347,348,349,350,352,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,378,380,382,391,392,393,394,396,397,399,400,402,404,406,407,410,411,412,415,421,422,424,426,427,429,432,433,437,440,441,443,445,447,450,452,453,457,459,460,461,462,463,464,465,466,467,471,472,473,474,475,476,477,478,483,484,485,488,489,494,495,497,498,499,500,501,502,503,504,],[-189,-197,-190,-153,87,-153,-124,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-20,-153,-154,-188,-198,-199,-201,-202,-126,-119,-176,-4,220,-112,-99,-67,-97,-54,-69,-60,-95,-31,-21,-93,-27,-28,-29,-91,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-89,-87,-84,-79,-76,-73,-153,-214,-114,-67,-4,-118,-193,-196,307,308,-212,-207,-211,-200,-203,-204,-125,-149,-150,322,-179,-181,220,220,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-126,-215,-153,-216,-153,-167,-168,-153,386,-284,-191,390,-158,-192,-194,-195,-153,-210,-216,-127,-228,-174,-5,-7,-113,220,-30,220,220,-100,220,-96,220,-33,423,-39,-41,-35,-36,-94,-92,-90,-8,-6,-88,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-166,-153,-217,-224,-220,-165,443,-156,-159,-213,-208,-209,446,-231,-148,449,-169,-153,-180,-182,-175,-68,-32,-34,-59,-218,-222,-226,-225,-221,-285,469,470,-158,-229,-146,-171,-153,-147,220,220,-98,-40,-42,-43,-47,-223,-227,-219,-155,-157,423,-230,-232,-170,-153,-172,-50,496,-48,-160,-173,-47,-44,220,496,-49,-51,-47,-45,496,-46,]),'RPAREN':([19,20,21,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,73,74,75,77,79,80,81,82,83,88,91,105,120,121,122,123,124,128,130,131,132,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,161,162,163,164,165,170,173,175,176,178,182,183,186,187,188,189,190,191,192,193,194,195,196,200,202,214,219,222,223,248,251,252,253,255,256,257,263,264,265,269,286,287,288,289,290,291,292,294,296,297,298,300,301,302,303,304,305,306,309,310,311,312,321,327,328,331,332,333,335,336,341,343,345,346,347,348,349,350,351,352,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,376,378,379,380,382,391,392,393,394,395,396,397,402,412,415,419,421,422,424,425,426,427,429,430,431,432,433,437,440,441,442,443,447,453,457,459,460,461,462,463,464,465,466,467,471,472,473,483,484,485,486,487,488,494,495,497,498,499,500,501,502,503,504,],[-189,-197,-190,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-20,-153,-154,-188,188,-198,-199,-201,-202,-119,-176,-251,-112,-99,-67,-97,-54,-69,-60,-95,-31,-21,-93,-27,-28,-29,-91,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-89,-87,-84,-79,-76,-73,-214,-67,-4,295,298,299,-118,-193,305,-196,306,-205,-212,-207,-211,-200,-203,-204,-149,-150,-253,-252,333,334,345,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-215,-153,-216,-153,-167,-168,378,-153,385,-284,-191,388,389,-291,-158,-192,-194,-195,-153,-210,-216,378,-174,-5,-7,-113,414,-30,416,417,-100,-96,-33,422,-39,-41,-35,-36,424,-94,-92,-90,-8,-6,-88,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-166,-153,-217,429,-224,432,-220,-165,442,-156,-159,-213,-206,-208,-209,-148,-175,-68,458,-32,-34,-59,463,-218,-222,-226,466,467,-225,-221,-285,-292,-295,471,-158,-146,-147,480,482,-98,-40,-42,-43,-47,-223,-227,-219,-155,-157,488,-50,495,-48,-293,-294,-160,-47,-44,500,502,-49,-51,-47,-45,504,-46,]),'COLON':([19,21,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,73,75,77,91,106,108,120,121,122,123,124,128,130,131,132,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,161,162,163,164,165,171,173,175,186,188,200,202,217,251,252,253,255,256,257,263,264,265,269,287,290,291,294,298,304,305,306,319,321,327,328,331,333,341,342,343,345,349,350,352,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,382,402,407,412,415,421,422,424,425,447,449,453,460,463,464,471,483,484,485,494,495,498,499,500,502,504,],[-189,-190,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-20,-154,-188,-176,215,218,-112,-99,-67,-97,-54,-69,-60,-95,-31,-21,-93,-27,-28,-29,-91,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-89,-87,-84,-79,-76,-73,-114,-67,-4,-193,-196,-149,-150,329,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-153,-167,-168,-153,-191,-192,-194,-195,408,-174,-5,-7,-113,-30,-100,420,-96,-33,-35,-36,-94,-92,-90,-8,-6,-88,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-166,-165,-148,451,-175,-68,-32,-34,-59,464,-146,408,-147,-98,-43,-47,-155,-50,494,-48,-47,-44,501,-49,-51,-45,-46,]),'LBRACKET':([19,20,21,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,73,74,75,77,80,81,82,83,88,91,106,124,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,170,175,183,186,188,193,194,195,196,200,202,251,252,263,264,265,269,287,288,289,290,291,292,294,298,304,305,306,311,312,321,327,328,333,345,349,350,356,357,372,373,377,378,380,382,398,402,412,421,422,427,429,432,433,447,453,465,466,467,471,],[78,-197,-190,-153,-120,-121,-122,-123,-128,-129,-130,-131,-132,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,-186,-187,-20,-153,-154,78,-198,-199,-201,-202,-119,-176,-4,247,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,293,-4,-118,-193,-196,293,-200,-203,-204,-149,-150,-37,-38,-23,-24,-25,-26,-153,293,375,-167,-168,-153,-153,-191,-192,-194,-195,293,-153,-174,-5,-7,-30,-33,-35,-36,-8,-6,-166,375,293,-224,-220,-165,293,-148,-175,-32,-34,-222,-226,-225,-221,-146,-147,-223,-227,-219,-155,]),'PP_END_DEFINE':([20,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,63,65,67,73,75,80,81,82,83,91,123,124,128,130,131,132,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,161,162,163,164,165,168,169,170,171,173,175,194,195,196,200,202,251,252,253,255,256,257,263,264,265,269,286,287,288,289,290,291,294,295,321,327,328,333,343,345,349,350,352,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,378,380,382,384,385,402,412,415,421,422,424,426,427,429,432,433,436,447,453,460,463,465,466,467,471,495,502,504,],[-197,-133,-134,-135,-136,-137,-138,-139,-140,-141,-142,-143,-144,-145,-183,-184,-185,167,177,179,-20,-154,-198,-199,-201,-202,-176,-97,-54,-69,-60,-95,-31,-21,-93,-27,-28,-29,-91,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-89,-87,-84,-79,-76,-73,284,285,-214,-114,-67,-4,-200,-203,-204,-149,-150,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-215,-153,-216,-153,-167,-168,-153,383,-174,-5,-7,-30,-96,-33,-35,-36,-94,-92,-90,-8,-6,-88,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-166,-153,-217,-224,-220,-165,434,435,-148,-175,-68,-32,-34,-59,-218,-222,-226,-225,-221,468,-146,-147,-98,-43,-223,-227,-219,-155,-44,-45,-46,]),'RBRACE':([22,60,61,73,93,94,95,96,97,98,99,100,101,102,103,104,105,121,122,123,124,128,130,131,132,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,161,162,163,164,165,171,173,175,203,204,205,206,208,209,211,212,213,214,219,229,230,231,251,252,253,255,256,257,263,264,265,269,314,317,318,322,324,325,326,327,328,330,333,339,340,341,343,345,349,350,352,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,399,400,401,403,405,409,410,411,413,415,421,422,424,445,446,448,454,455,456,460,463,474,475,481,490,491,492,495,502,504,],[-115,-247,94,-20,-248,-242,208,211,213,-249,-233,-234,-235,-236,-237,-238,-251,-99,-67,-97,-54,-69,-60,-95,-31,-21,-93,-27,-28,-29,-91,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-89,-87,-84,-79,-76,-73,-114,-67,-4,321,-177,-179,-181,-243,-250,-244,325,-246,-253,-252,-262,-263,-264,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-228,402,-161,-178,412,-245,-239,-5,-7,-241,-30,-261,-265,-100,-96,-33,-35,-36,-94,-92,-90,-8,-6,-88,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,445,-231,447,-162,-164,453,-180,-182,-240,-68,-32,-34,-59,-229,474,-163,-254,-256,-257,-98,-43,-230,-232,-259,-255,-258,-260,-44,-45,-46,]),'CASE':([22,60,61,93,94,95,96,98,99,100,101,102,103,104,105,114,208,209,211,212,213,214,215,218,219,229,230,231,325,326,329,330,339,340,413,414,416,417,454,455,456,458,479,481,482,490,491,492,],[-115,-247,107,-248,-242,107,107,-249,-233,-234,-235,-236,-237,-238,-251,107,-243,-250,-244,107,-246,-253,107,107,-252,-262,-263,-264,-245,-239,107,-241,-261,-265,-240,107,107,107,-254,-256,-257,107,107,-259,107,-255,-258,-260,]),'DEFAULT':([22,60,61,93,94,95,96,98,99,100,101,102,103,104,105,114,208,209,211,212,213,214,215,218,219,229,230,231,325,326,329,330,339,340,413,414,416,417,454,455,456,458,479,481,482,490,491,492,],[-115,-247,108,-248,-242,108,108,-249,-233,-234,-235,-236,-237,-238,-251,108,-243,-250,-244,108,-246,-253,108,108,-252,-262,-263,-264,-245,-239,108,-241,-261,-265,-240,108,108,108,-254,-256,-257,108,108,-259,108,-255,-258,-260,]),'IF':([22,60,61,93,94,95,96,98,99,100,101,102,103,104,105,114,208,209,211,212,213,214,215,218,219,229,230,231,325,326,329,330,339,340,413,414,416,417,454,455,456,458,479,481,482,490,491,492,],[-115,-247,110,-248,-242,110,110,-249,-233,-234,-235,-236,-237,-238,-251,110,-243,-250,-244,110,-246,-253,110,110,-252,-262,-263,-264,-245,-239,110,-241,-261,-265,-240,110,110,110,-254,-256,-257,110,110,-259,110,-255,-258,-260,]),'SWITCH':([22,60,61,93,94,95,96,98,99,100,101,102,103,104,105,114,208,209,211,212,213,214,215,218,219,229,230,231,325,326,329,330,339,340,413,414,416,417,454,455,456,458,479,481,482,490,491,492,],[-115,-247,112,-248,-242,112,112,-249,-233,-234,-235,-236,-237,-238,-251,112,-243,-250,-244,112,-246,-253,112,112,-252,-262,-263,-264,-245,-239,112,-241,-261,-265,-240,112,112,112,-254,-256,-257,112,112,-259,112,-255,-258,-260,]),'WHILE':([22,60,61,93,94,95,96,98,99,100,101,102,103,104,105,114,208,209,211,212,213,214,215,218,219,226,229,230,231,325,326,329,330,339,340,413,414,416,417,454,455,456,458,479,481,482,490,491,492,],[-115,-247,113,-248,-242,113,113,-249,-233,-234,-235,-236,-237,-238,-251,113,-243,-250,-244,113,-246,-253,113,113,-252,337,-262,-263,-264,-245,-239,113,-241,-261,-265,-240,113,113,113,-254,-256,-257,113,113,-259,113,-255,-258,-260,]),'DO':([22,60,61,93,94,95,96,98,99,100,101,102,103,104,105,114,208,209,211,212,213,214,215,218,219,229,230,231,325,326,329,330,339,340,413,414,416,417,454,455,456,458,479,481,482,490,491,492,],[-115,-247,114,-248,-242,114,114,-249,-233,-234,-235,-236,-237,-238,-251,114,-243,-250,-244,114,-246,-253,114,114,-252,-262,-263,-264,-245,-239,114,-241,-261,-265,-240,114,114,114,-254,-256,-257,114,114,-259,114,-255,-258,-260,]),'FOR':([22,60,61,93,94,95,96,98,99,100,101,102,103,104,105,114,208,209,211,212,213,214,215,218,219,229,230,231,325,326,329,330,339,340,413,414,416,417,454,455,456,458,479,481,482,490,491,492,],[-115,-247,115,-248,-242,115,115,-249,-233,-234,-235,-236,-237,-238,-251,115,-243,-250,-244,115,-246,-253,115,115,-252,-262,-263,-264,-245,-239,115,-241,-261,-265,-240,115,115,115,-254,-256,-257,115,115,-259,115,-255,-258,-260,]),'GOTO':([22,60,61,93,94,95,96,98,99,100,101,102,103,104,105,114,208,209,211,212,213,214,215,218,219,229,230,231,325,326,329,330,339,340,413,414,416,417,454,455,456,458,479,481,482,490,491,492,],[-115,-247,116,-248,-242,116,116,-249,-233,-234,-235,-236,-237,-238,-251,116,-243,-250,-244,116,-246,-253,116,116,-252,-262,-263,-264,-245,-239,116,-241,-261,-265,-240,116,116,116,-254,-256,-257,116,116,-259,116,-255,-258,-260,]),'CONTINUE':([22,60,61,93,94,95,96,98,99,100,101,102,103,104,105,114,208,209,211,212,213,214,215,218,219,229,230,231,325,326,329,330,339,340,413,414,416,417,454,455,456,458,479,481,482,490,491,492,],[-115,-247,117,-248,-242,117,117,-249,-233,-234,-235,-236,-237,-238,-251,117,-243,-250,-244,117,-246,-253,117,117,-252,-262,-263,-264,-245,-239,117,-241,-261,-265,-240,117,117,117,-254,-256,-257,117,117,-259,117,-255,-258,-260,]),'BREAK':([22,60,61,93,94,95,96,98,99,100,101,102,103,104,105,114,208,209,211,212,213,214,215,218,219,229,230,231,325,326,329,330,339,340,413,414,416,417,454,455,456,458,479,481,482,490,491,492,],[-115,-247,118,-248,-242,118,118,-249,-233,-234,-235,-236,-237,-238,-251,118,-243,-250,-244,118,-246,-253,118,118,-252,-262,-263,-264,-245,-239,118,-241,-261,-265,-240,118,118,118,-254,-256,-257,118,118,-259,118,-255,-258,-260,]),'RETURN':([22,60,61,93,94,95,96,98,99,100,101,102,103,104,105,114,208,209,211,212,213,214,215,218,219,229,230,231,325,326,329,330,339,340,413,414,416,417,454,455,456,458,479,481,482,490,491,492,],[-115,-247,119,-248,-242,119,119,-249,-233,-234,-235,-236,-237,-238,-251,119,-243,-250,-244,119,-246,-253,119,119,-252,-262,-263,-264,-245,-239,119,-241,-261,-265,-240,119,119,119,-254,-256,-257,119,119,-259,119,-255,-258,-260,]),'INC_OP':([22,60,61,63,73,78,93,94,95,96,98,99,100,101,102,103,104,105,106,107,111,114,119,124,125,126,127,129,132,133,134,135,136,137,138,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,174,175,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,251,252,254,258,259,262,263,264,265,266,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,327,328,329,330,333,334,338,339,340,345,349,350,356,357,375,385,408,413,414,416,417,418,419,420,421,422,423,444,446,451,454,455,456,458,479,481,482,490,491,492,493,],[-115,-247,125,125,-20,125,-248,-242,125,125,-249,-233,-234,-235,-236,-237,-238,-251,-4,125,125,125,125,251,125,125,125,125,-31,-61,-62,-63,-64,-65,-66,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,125,-4,125,-243,-250,-244,125,-246,-253,125,125,-252,125,125,125,125,125,-262,-263,-264,125,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,125,125,125,125,-37,-38,125,125,125,125,-23,-24,-25,125,125,-26,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,-245,-239,-5,-7,125,-241,-30,125,125,-261,-265,-33,-35,-36,-8,-6,125,125,125,-240,125,125,125,125,125,125,-32,-34,125,125,125,125,-254,-256,-257,125,125,-259,125,-255,-258,-260,125,]),'DEC_OP':([22,60,61,63,73,78,93,94,95,96,98,99,100,101,102,103,104,105,106,107,111,114,119,124,125,126,127,129,132,133,134,135,136,137,138,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,174,175,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,251,252,254,258,259,262,263,264,265,266,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,327,328,329,330,333,334,338,339,340,345,349,350,356,357,375,385,408,413,414,416,417,418,419,420,421,422,423,444,446,451,454,455,456,458,479,481,482,490,491,492,493,],[-115,-247,126,126,-20,126,-248,-242,126,126,-249,-233,-234,-235,-236,-237,-238,-251,-4,126,126,126,126,252,126,126,126,126,-31,-61,-62,-63,-64,-65,-66,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,126,-4,126,-243,-250,-244,126,-246,-253,126,126,-252,126,126,126,126,126,-262,-263,-264,126,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,126,126,126,126,-37,-38,126,126,126,126,-23,-24,-25,126,126,-26,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,-245,-239,-5,-7,126,-241,-30,126,126,-261,-265,-33,-35,-36,-8,-6,126,126,126,-240,126,126,126,126,126,126,-32,-34,126,126,126,126,-254,-256,-257,126,126,-259,126,-255,-258,-260,126,]),'SIZEOF':([22,60,61,63,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,174,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,479,481,482,490,491,492,493,],[-115,-247,129,129,129,-248,-242,129,129,-249,-233,-234,-235,-236,-237,-238,-251,129,129,129,129,129,129,129,129,-61,-62,-63,-64,-65,-66,129,129,-243,-250,-244,129,-246,-253,129,129,-252,129,129,129,129,129,-262,-263,-264,129,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,-245,-239,129,-241,129,129,-261,-265,129,129,129,-240,129,129,129,129,129,129,129,129,129,129,-254,-256,-257,129,129,-259,129,-255,-258,-260,129,]),'AND':([22,60,61,63,73,78,93,94,95,96,98,99,100,101,102,103,104,105,106,107,111,114,119,122,124,125,126,127,128,129,130,132,133,134,135,136,137,138,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,159,161,162,163,164,165,173,174,175,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,251,252,253,254,255,256,257,258,259,262,263,264,265,266,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,327,328,329,330,333,334,338,339,340,345,349,350,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,375,385,408,413,414,415,416,417,418,419,420,421,422,423,424,444,446,451,454,455,456,458,463,479,481,482,490,491,492,493,495,502,504,],[-115,-247,133,133,-20,133,-248,-242,133,133,-249,-233,-234,-235,-236,-237,-238,-251,-4,133,133,133,133,-67,-54,133,133,133,-69,133,-60,-31,-61,-62,-63,-64,-65,-66,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,268,-87,-84,-79,-76,-73,-67,133,-4,133,-243,-250,-244,133,-246,-253,133,133,-252,133,133,133,133,133,-262,-263,-264,133,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,133,133,133,133,-37,-38,-55,133,-56,-57,-58,133,133,133,-23,-24,-25,133,133,-26,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,-245,-239,-5,-7,133,-241,-30,133,133,-261,-265,-33,-35,-36,268,-8,-6,-88,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,133,133,133,-240,133,-68,133,133,133,133,133,-32,-34,133,-59,133,133,133,-254,-256,-257,133,-43,133,-259,133,-255,-258,-260,133,-44,-45,-46,]),'PLUS':([22,60,61,63,73,78,93,94,95,96,98,99,100,101,102,103,104,105,106,107,111,114,119,122,124,125,126,127,128,129,130,132,133,134,135,136,137,138,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,164,165,173,174,175,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,251,252,253,254,255,256,257,258,259,262,263,264,265,266,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,327,328,329,330,333,334,338,339,340,345,349,350,356,357,365,366,367,368,369,370,371,375,385,408,413,414,415,416,417,418,419,420,421,422,423,424,444,446,451,454,455,456,458,463,479,481,482,490,491,492,493,495,502,504,],[-115,-247,135,135,-20,135,-248,-242,135,135,-249,-233,-234,-235,-236,-237,-238,-251,-4,135,135,135,135,-67,-54,135,135,135,-69,135,-60,-31,-61,-62,-63,-64,-65,-66,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,278,-73,-67,135,-4,135,-243,-250,-244,135,-246,-253,135,135,-252,135,135,135,135,135,-262,-263,-264,135,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,135,135,135,135,-37,-38,-55,135,-56,-57,-58,135,135,135,-23,-24,-25,135,135,-26,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,-245,-239,-5,-7,135,-241,-30,135,135,-261,-265,-33,-35,-36,-8,-6,278,278,-74,-75,-70,-71,-72,135,135,135,-240,135,-68,135,135,135,135,135,-32,-34,135,-59,135,135,135,-254,-256,-257,135,-43,135,-259,135,-255,-258,-260,135,-44,-45,-46,]),'MINUS':([22,60,61,63,73,78,93,94,95,96,98,99,100,101,102,103,104,105,106,107,111,114,119,122,124,125,126,127,128,129,130,132,133,134,135,136,137,138,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,164,165,173,174,175,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,251,252,253,254,255,256,257,258,259,262,263,264,265,266,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,327,328,329,330,333,334,338,339,340,345,349,350,356,357,365,366,367,368,369,370,371,375,385,408,413,414,415,416,417,418,419,420,421,422,423,424,444,446,451,454,455,456,458,463,479,481,482,490,491,492,493,495,502,504,],[-115,-247,136,136,-20,136,-248,-242,136,136,-249,-233,-234,-235,-236,-237,-238,-251,-4,136,136,136,136,-67,-54,136,136,136,-69,136,-60,-31,-61,-62,-63,-64,-65,-66,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,279,-73,-67,136,-4,136,-243,-250,-244,136,-246,-253,136,136,-252,136,136,136,136,136,-262,-263,-264,136,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,136,136,136,136,-37,-38,-55,136,-56,-57,-58,136,136,136,-23,-24,-25,136,136,-26,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,-245,-239,-5,-7,136,-241,-30,136,136,-261,-265,-33,-35,-36,-8,-6,279,279,-74,-75,-70,-71,-72,136,136,136,-240,136,-68,136,136,136,136,136,-32,-34,136,-59,136,136,136,-254,-256,-257,136,-43,136,-259,136,-255,-258,-260,136,-44,-45,-46,]),'NOT':([22,60,61,63,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,174,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,479,481,482,490,491,492,493,],[-115,-247,137,137,137,-248,-242,137,137,-249,-233,-234,-235,-236,-237,-238,-251,137,137,137,137,137,137,137,137,-61,-62,-63,-64,-65,-66,137,137,-243,-250,-244,137,-246,-253,137,137,-252,137,137,137,137,137,-262,-263,-264,137,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,-245,-239,137,-241,137,137,-261,-265,137,137,137,-240,137,137,137,137,137,137,137,137,137,137,-254,-256,-257,137,137,-259,137,-255,-258,-260,137,]),'LNOT':([22,60,61,63,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,174,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,479,481,482,490,491,492,493,],[-115,-247,138,138,138,-248,-242,138,138,-249,-233,-234,-235,-236,-237,-238,-251,138,138,138,138,138,138,138,138,-61,-62,-63,-64,-65,-66,138,138,-243,-250,-244,138,-246,-253,138,138,-252,138,138,138,138,138,-262,-263,-264,138,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,-245,-239,138,-241,138,138,-261,-265,138,138,138,-240,138,138,138,138,138,138,138,138,138,138,-254,-256,-257,138,138,-259,138,-255,-258,-260,138,]),'__ASM__':([22,60,61,63,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,174,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,479,481,482,490,491,492,493,],[-115,-247,139,139,139,-248,-242,139,139,-249,-233,-234,-235,-236,-237,-238,-251,139,139,139,139,139,139,139,139,-61,-62,-63,-64,-65,-66,139,139,-243,-250,-244,139,-246,-253,139,139,-252,139,139,139,139,139,-262,-263,-264,139,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,-245,-239,139,-241,139,139,-261,-265,139,139,139,-240,139,139,139,139,139,139,139,139,139,139,-254,-256,-257,139,139,-259,139,-255,-258,-260,139,]),'PP_MACRO_PARAM':([22,60,61,63,73,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,140,144,146,158,160,174,176,198,208,209,211,212,213,214,215,216,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,386,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,479,481,482,490,491,492,493,],[-115,-247,146,146,-20,146,-248,-242,146,146,-249,-233,-234,-235,-236,-237,-238,-251,146,146,146,146,146,146,146,146,-61,-62,-63,-64,-65,-66,-21,265,-25,-22,269,146,297,146,-243,-250,-244,146,-246,-253,146,328,146,-252,146,146,146,146,146,-262,-263,-264,146,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,146,146,146,146,146,146,146,146,-23,-24,-25,146,356,146,-26,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,-245,-239,146,-241,146,146,-261,-265,146,146,437,146,-240,146,146,146,146,146,146,146,146,146,146,-254,-256,-257,146,146,-259,146,-255,-258,-260,146,]),'I_CONST_HEX':([22,60,61,63,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,174,182,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,390,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,469,479,481,482,490,491,492,493,],[-115,-247,147,147,147,-248,-242,147,147,-249,-233,-234,-235,-236,-237,-238,-251,147,147,147,147,147,147,147,147,-61,-62,-63,-64,-65,-66,147,147,147,-243,-250,-244,147,-246,-253,147,147,-252,147,147,147,147,147,-262,-263,-264,147,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,-245,-239,147,-241,147,147,-261,-265,147,147,147,147,-240,147,147,147,147,147,147,147,147,147,147,-254,-256,-257,147,147,147,-259,147,-255,-258,-260,147,]),'I_CONST_DEC':([22,60,61,63,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,174,182,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,390,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,469,479,481,482,490,491,492,493,],[-115,-247,148,148,148,-248,-242,148,148,-249,-233,-234,-235,-236,-237,-238,-251,148,148,148,148,148,148,148,148,-61,-62,-63,-64,-65,-66,148,148,148,-243,-250,-244,148,-246,-253,148,148,-252,148,148,148,148,148,-262,-263,-264,148,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,-245,-239,148,-241,148,148,-261,-265,148,148,148,148,-240,148,148,148,148,148,148,148,148,148,148,-254,-256,-257,148,148,148,-259,148,-255,-258,-260,148,]),'I_CONST_OCT':([22,60,61,63,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,174,182,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,390,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,469,479,481,482,490,491,492,493,],[-115,-247,149,149,149,-248,-242,149,149,-249,-233,-234,-235,-236,-237,-238,-251,149,149,149,149,149,149,149,149,-61,-62,-63,-64,-65,-66,149,149,149,-243,-250,-244,149,-246,-253,149,149,-252,149,149,149,149,149,-262,-263,-264,149,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,-245,-239,149,-241,149,149,-261,-265,149,149,149,149,-240,149,149,149,149,149,149,149,149,149,149,-254,-256,-257,149,149,149,-259,149,-255,-258,-260,149,]),'I_CONST_BIN':([22,60,61,63,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,174,182,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,390,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,469,479,481,482,490,491,492,493,],[-115,-247,150,150,150,-248,-242,150,150,-249,-233,-234,-235,-236,-237,-238,-251,150,150,150,150,150,150,150,150,-61,-62,-63,-64,-65,-66,150,150,150,-243,-250,-244,150,-246,-253,150,150,-252,150,150,150,150,150,-262,-263,-264,150,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,-245,-239,150,-241,150,150,-261,-265,150,150,150,150,-240,150,150,150,150,150,150,150,150,150,150,-254,-256,-257,150,150,150,-259,150,-255,-258,-260,150,]),'F_CONST_1':([22,60,61,63,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,174,182,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,390,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,469,479,481,482,490,491,492,493,],[-115,-247,151,151,151,-248,-242,151,151,-249,-233,-234,-235,-236,-237,-238,-251,151,151,151,151,151,151,151,151,-61,-62,-63,-64,-65,-66,151,151,151,-243,-250,-244,151,-246,-253,151,151,-252,151,151,151,151,151,-262,-263,-264,151,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,-245,-239,151,-241,151,151,-261,-265,151,151,151,151,-240,151,151,151,151,151,151,151,151,151,151,-254,-256,-257,151,151,151,-259,151,-255,-258,-260,151,]),'F_CONST_2':([22,60,61,63,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,174,182,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,390,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,469,479,481,482,490,491,492,493,],[-115,-247,152,152,152,-248,-242,152,152,-249,-233,-234,-235,-236,-237,-238,-251,152,152,152,152,152,152,152,152,-61,-62,-63,-64,-65,-66,152,152,152,-243,-250,-244,152,-246,-253,152,152,-252,152,152,152,152,152,-262,-263,-264,152,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,-245,-239,152,-241,152,152,-261,-265,152,152,152,152,-240,152,152,152,152,152,152,152,152,152,152,-254,-256,-257,152,152,152,-259,152,-255,-258,-260,152,]),'F_CONST_3':([22,60,61,63,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,174,182,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,390,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,469,479,481,482,490,491,492,493,],[-115,-247,153,153,153,-248,-242,153,153,-249,-233,-234,-235,-236,-237,-238,-251,153,153,153,153,153,153,153,153,-61,-62,-63,-64,-65,-66,153,153,153,-243,-250,-244,153,-246,-253,153,153,-252,153,153,153,153,153,-262,-263,-264,153,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,-245,-239,153,-241,153,153,-261,-265,153,153,153,153,-240,153,153,153,153,153,153,153,153,153,153,-254,-256,-257,153,153,153,-259,153,-255,-258,-260,153,]),'F_CONST_4':([22,60,61,63,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,174,182,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,390,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,469,479,481,482,490,491,492,493,],[-115,-247,154,154,154,-248,-242,154,154,-249,-233,-234,-235,-236,-237,-238,-251,154,154,154,154,154,154,154,154,-61,-62,-63,-64,-65,-66,154,154,154,-243,-250,-244,154,-246,-253,154,154,-252,154,154,154,154,154,-262,-263,-264,154,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,-245,-239,154,-241,154,154,-261,-265,154,154,154,154,-240,154,154,154,154,154,154,154,154,154,154,-254,-256,-257,154,154,154,-259,154,-255,-258,-260,154,]),'F_CONST_5':([22,60,61,63,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,174,182,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,390,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,469,479,481,482,490,491,492,493,],[-115,-247,155,155,155,-248,-242,155,155,-249,-233,-234,-235,-236,-237,-238,-251,155,155,155,155,155,155,155,155,-61,-62,-63,-64,-65,-66,155,155,155,-243,-250,-244,155,-246,-253,155,155,-252,155,155,155,155,155,-262,-263,-264,155,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,-245,-239,155,-241,155,155,-261,-265,155,155,155,155,-240,155,155,155,155,155,155,155,155,155,155,-254,-256,-257,155,155,155,-259,155,-255,-258,-260,155,]),'F_CONST_6':([22,60,61,63,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,174,182,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,390,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,469,479,481,482,490,491,492,493,],[-115,-247,156,156,156,-248,-242,156,156,-249,-233,-234,-235,-236,-237,-238,-251,156,156,156,156,156,156,156,156,-61,-62,-63,-64,-65,-66,156,156,156,-243,-250,-244,156,-246,-253,156,156,-252,156,156,156,156,156,-262,-263,-264,156,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,-245,-239,156,-241,156,156,-261,-265,156,156,156,156,-240,156,156,156,156,156,156,156,156,156,156,-254,-256,-257,156,156,156,-259,156,-255,-258,-260,156,]),'CHARACTER_CONSTANT':([22,60,61,63,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,174,182,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,390,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,469,479,481,482,490,491,492,493,],[-115,-247,157,157,157,-248,-242,157,157,-249,-233,-234,-235,-236,-237,-238,-251,157,157,157,157,157,157,157,157,-61,-62,-63,-64,-65,-66,157,157,157,-243,-250,-244,157,-246,-253,157,157,-252,157,157,157,157,157,-262,-263,-264,157,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,-245,-239,157,-241,157,157,-261,-265,157,157,157,157,-240,157,157,157,157,157,157,157,157,157,157,-254,-256,-257,157,157,157,-259,157,-255,-258,-260,157,]),'PP_STRINGIFY':([22,60,61,63,73,78,93,94,95,96,98,99,100,101,102,103,104,105,107,111,114,119,125,126,127,129,133,134,135,136,137,138,140,144,146,158,174,198,208,209,211,212,213,214,215,218,219,220,221,224,225,227,229,230,231,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,254,258,259,262,263,264,265,266,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,325,326,329,330,334,338,339,340,375,385,408,413,414,416,417,418,419,420,423,444,446,451,454,455,456,458,479,481,482,490,491,492,493,],[-115,-247,160,160,-20,160,-248,-242,160,160,-249,-233,-234,-235,-236,-237,-238,-251,160,160,160,160,160,160,160,160,-61,-62,-63,-64,-65,-66,-21,160,-25,-22,160,160,-243,-250,-244,160,-246,-253,160,160,-252,160,160,160,160,160,-262,-263,-264,160,-101,-102,-103,-104,-105,-106,-107,-108,-109,-110,-111,160,160,160,160,160,160,160,160,-23,-24,-25,160,160,-26,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,-245,-239,160,-241,160,160,-261,-265,160,160,160,-240,160,160,160,160,160,160,160,160,160,160,-254,-256,-257,160,160,-259,160,-255,-258,-260,160,]),'PRAGMA_END':([68,70,71,72,73,181,299,388,389,],[180,-296,-298,-299,-20,-297,387,438,439,]),'PERIOD':([73,106,124,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,175,251,252,263,264,265,269,327,328,333,345,349,350,356,357,421,422,],[-20,-4,249,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-4,-37,-38,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,-32,-34,]),'PTR_OP':([73,106,124,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,175,251,252,263,264,265,269,327,328,333,345,349,350,356,357,421,422,],[-20,-4,250,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-4,-37,-38,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,-32,-34,]),'MUL_ASSIGN':([73,106,122,124,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,415,421,422,424,463,495,502,504,],[-20,-4,235,-54,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,-68,-32,-34,-59,-43,-44,-45,-46,]),'DIV_ASSIGN':([73,106,122,124,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,415,421,422,424,463,495,502,504,],[-20,-4,236,-54,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,-68,-32,-34,-59,-43,-44,-45,-46,]),'MOD_ASSIGN':([73,106,122,124,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,415,421,422,424,463,495,502,504,],[-20,-4,237,-54,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,-68,-32,-34,-59,-43,-44,-45,-46,]),'ADD_ASSIGN':([73,106,122,124,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,415,421,422,424,463,495,502,504,],[-20,-4,238,-54,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,-68,-32,-34,-59,-43,-44,-45,-46,]),'SUB_ASSIGN':([73,106,122,124,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,415,421,422,424,463,495,502,504,],[-20,-4,239,-54,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,-68,-32,-34,-59,-43,-44,-45,-46,]),'LEFT_ASSIGN':([73,106,122,124,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,415,421,422,424,463,495,502,504,],[-20,-4,240,-54,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,-68,-32,-34,-59,-43,-44,-45,-46,]),'RIGHT_ASSIGN':([73,106,122,124,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,415,421,422,424,463,495,502,504,],[-20,-4,241,-54,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,-68,-32,-34,-59,-43,-44,-45,-46,]),'AND_ASSIGN':([73,106,122,124,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,415,421,422,424,463,495,502,504,],[-20,-4,242,-54,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,-68,-32,-34,-59,-43,-44,-45,-46,]),'XOR_ASSIGN':([73,106,122,124,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,415,421,422,424,463,495,502,504,],[-20,-4,243,-54,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,-68,-32,-34,-59,-43,-44,-45,-46,]),'OR_ASSIGN':([73,106,122,124,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,415,421,422,424,463,495,502,504,],[-20,-4,244,-54,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,-68,-32,-34,-59,-43,-44,-45,-46,]),'DIVIDE':([73,106,122,124,128,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,165,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,367,368,369,370,371,415,421,422,424,463,495,502,504,],[-20,-4,-67,-54,-69,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,281,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,281,281,-70,-71,-72,-68,-32,-34,-59,-43,-44,-45,-46,]),'MOD':([73,106,122,124,128,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,165,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,367,368,369,370,371,415,421,422,424,463,495,502,504,],[-20,-4,-67,-54,-69,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,282,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,282,282,-70,-71,-72,-68,-32,-34,-59,-43,-44,-45,-46,]),'LEFT_OP':([73,106,122,124,128,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,163,164,165,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,361,362,363,364,365,366,367,368,369,370,371,415,421,422,424,463,495,502,504,],[-20,-4,-67,-54,-69,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,276,-76,-73,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,276,276,276,276,-77,-78,-74,-75,-70,-71,-72,-68,-32,-34,-59,-43,-44,-45,-46,]),'RIGHT_OP':([73,106,122,124,128,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,163,164,165,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,361,362,363,364,365,366,367,368,369,370,371,415,421,422,424,463,495,502,504,],[-20,-4,-67,-54,-69,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,277,-76,-73,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,277,277,277,277,-77,-78,-74,-75,-70,-71,-72,-68,-32,-34,-59,-43,-44,-45,-46,]),'LT':([73,106,122,124,128,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,162,163,164,165,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,359,360,361,362,363,364,365,366,367,368,369,370,371,415,421,422,424,463,495,502,504,],[-20,-4,-67,-54,-69,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,272,-79,-76,-73,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,272,272,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-68,-32,-34,-59,-43,-44,-45,-46,]),'GT':([73,106,122,124,128,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,162,163,164,165,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,359,360,361,362,363,364,365,366,367,368,369,370,371,415,421,422,424,463,495,502,504,],[-20,-4,-67,-54,-69,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,273,-79,-76,-73,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,273,273,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-68,-32,-34,-59,-43,-44,-45,-46,]),'LE_OP':([73,106,122,124,128,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,162,163,164,165,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,359,360,361,362,363,364,365,366,367,368,369,370,371,415,421,422,424,463,495,502,504,],[-20,-4,-67,-54,-69,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,274,-79,-76,-73,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,274,274,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-68,-32,-34,-59,-43,-44,-45,-46,]),'GE_OP':([73,106,122,124,128,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,162,163,164,165,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,359,360,361,362,363,364,365,366,367,368,369,370,371,415,421,422,424,463,495,502,504,],[-20,-4,-67,-54,-69,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,275,-79,-76,-73,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,275,275,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-68,-32,-34,-59,-43,-44,-45,-46,]),'EQ_OP':([73,106,122,124,128,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,161,162,163,164,165,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,415,421,422,424,463,495,502,504,],[-20,-4,-67,-54,-69,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,270,-84,-79,-76,-73,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,270,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-68,-32,-34,-59,-43,-44,-45,-46,]),'NE_OP':([73,106,122,124,128,130,132,140,142,143,144,146,147,148,149,150,151,152,153,154,155,156,157,158,161,162,163,164,165,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,415,421,422,424,463,495,502,504,],[-20,-4,-67,-54,-69,-60,-31,-21,-27,-28,-29,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,271,-84,-79,-76,-73,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,-8,-6,271,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-68,-32,-34,-59,-43,-44,-45,-46,]),'XOR':([73,106,122,124,128,130,132,140,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,161,162,163,164,165,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,415,421,422,424,463,495,502,504,],[-20,-4,-67,-54,-69,-60,-31,-21,-27,-28,-29,266,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-89,-87,-84,-79,-76,-73,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,266,-90,-8,-6,-88,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-68,-32,-34,-59,-43,-44,-45,-46,]),'OR':([73,106,122,124,128,130,132,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,161,162,163,164,165,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,345,349,350,352,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,415,421,422,424,463,495,502,504,],[-20,-4,-67,-54,-69,-60,-31,-21,262,-27,-28,-29,-91,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-89,-87,-84,-79,-76,-73,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-33,-35,-36,262,-92,-90,-8,-6,-88,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-68,-32,-34,-59,-43,-44,-45,-46,]),'AND_OP':([73,106,122,124,128,130,131,132,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,161,162,163,164,165,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,343,345,349,350,352,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,415,421,422,424,463,495,502,504,],[-20,-4,-67,-54,-69,-60,259,-31,-21,-93,-27,-28,-29,-91,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-89,-87,-84,-79,-76,-73,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,259,-33,-35,-36,-94,-92,-90,-8,-6,-88,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-68,-32,-34,-59,-43,-44,-45,-46,]),'CONDOP':([73,106,122,123,124,128,130,131,132,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,161,162,163,164,165,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,343,345,349,350,352,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,415,421,422,424,463,495,502,504,],[-20,-4,-67,245,-54,-69,-60,-95,-31,-21,-93,-27,-28,-29,-91,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-89,-87,-84,-79,-76,-73,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-96,-33,-35,-36,-94,-92,-90,-8,-6,-88,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-68,-32,-34,-59,-43,-44,-45,-46,]),'OR_OP':([73,106,122,123,124,128,130,131,132,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,161,162,163,164,165,173,175,251,252,253,255,256,257,263,264,265,269,327,328,333,343,345,349,350,352,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,415,421,422,424,463,495,502,504,],[-20,-4,-67,246,-54,-69,-60,-95,-31,-21,-93,-27,-28,-29,-91,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-89,-87,-84,-79,-76,-73,-67,-4,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,-5,-7,-30,-96,-33,-35,-36,-94,-92,-90,-8,-6,-88,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,-68,-32,-34,-59,-43,-44,-45,-46,]),'RBRACKET':([73,78,120,121,122,123,124,128,130,131,132,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,161,162,163,164,165,171,173,175,185,251,252,253,255,256,257,263,264,265,269,293,327,328,331,333,341,343,344,345,349,350,352,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,375,381,415,421,422,424,428,460,463,495,502,504,],[-20,186,-112,-99,-67,-97,-54,-69,-60,-95,-31,-21,-93,-27,-28,-29,-91,-25,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-22,-89,-87,-84,-79,-76,-73,-114,-67,-4,304,-37,-38,-55,-56,-57,-58,-23,-24,-25,-26,380,-5,-7,-113,-30,-100,-96,421,-33,-35,-36,-94,-92,-90,-8,-6,-88,-85,-86,-80,-81,-82,-83,-77,-78,-74,-75,-70,-71,-72,427,433,-68,-32,-34,-59,465,-98,-43,-44,-45,-46,]),'ELSE':([94,99,100,101,102,103,104,105,208,211,213,214,219,229,230,231,325,326,330,339,340,413,454,455,456,481,490,491,492,],[-242,-233,-234,-235,-236,-237,-238,-251,-243,-244,-246,-253,-252,-262,-263,-264,-245,-239,-241,-261,-265,-240,479,-256,-257,-259,-255,-258,-260,]),'PP_IDENTIFIER_PASTE':([106,146,175,328,356,],[216,267,216,267,267,]),'ELLIPSIS':([308,],[395,]),} _lr_action = {} for _k, _v in _lr_action_items.items(): for _x,_y in zip(_v[0],_v[1]): if not _x in _lr_action: _lr_action[_x] = {} _lr_action[_x][_k] = _y del _lr_action_items _lr_goto_items = {'translation_unit':([0,],[1,]),'external_declaration':([1,],[2,]),'directive':([1,],[3,]),'declaration':([1,11,23,58,61,84,96,],[4,60,60,93,60,93,93,]),'function_definition':([1,],[5,]),'define':([1,],[6,]),'undefine':([1,],[7,]),'pragma':([1,],[8,]),'declaration_impl':([1,11,23,58,61,84,96,],[9,9,9,9,9,9,9,]),'declaration_specifier_list':([1,11,23,58,61,79,84,96,292,308,312,376,],[10,62,62,62,62,193,62,62,193,193,193,193,]),'declarator':([1,10,62,66,87,193,319,398,449,],[11,23,166,178,166,309,407,178,407,]),'pragma_pack':([1,],[15,]),'gcc_attributes':([1,11,13,23,25,54,58,61,63,74,79,84,96,111,166,174,201,248,258,287,289,292,294,308,309,312,316,317,320,373,376,401,407,409,423,444,452,477,],[17,17,66,86,88,89,17,17,172,183,17,17,17,172,283,172,172,172,172,372,374,377,382,17,397,398,172,172,172,426,17,172,450,172,172,172,478,489,]),'pointer':([1,10,20,62,66,80,87,170,193,319,377,398,449,],[18,18,81,18,18,194,18,288,311,18,288,311,18,]),'direct_declarator':([1,10,18,62,66,87,193,311,319,398,449,],[19,19,77,19,19,19,19,77,19,19,19,]),'init_declarator_list':([10,62,],[24,24,]),'declaration_specifier':([10,17,62,86,193,377,398,],[25,74,25,74,25,74,74,]),'init_declarator':([10,62,87,],[26,26,199,]),'storage_class_specifier':([10,17,62,86,193,377,398,],[27,27,27,27,27,27,27,]),'type_specifier':([10,17,62,86,170,172,193,319,377,398,],[28,28,28,28,290,290,28,290,28,28,]),'type_qualifier':([10,17,20,62,80,86,170,172,193,319,377,398,],[29,29,82,29,195,29,291,291,29,291,29,29,]),'function_specifier':([10,17,62,86,193,377,398,],[30,30,30,30,30,30,30,]),'struct_or_union_specifier':([10,17,62,86,170,172,193,319,377,398,],[46,46,46,46,46,46,46,46,46,46,]),'enum_specifier':([10,17,62,86,170,172,193,319,377,398,],[47,47,47,47,47,47,47,47,47,47,]),'struct_or_union':([10,17,62,86,170,172,193,319,377,398,],[54,54,54,54,54,54,54,54,54,54,]),'declaration_list':([11,23,61,],[58,84,96,]),'compound_statement':([11,23,58,61,84,95,96,114,212,215,218,329,414,416,417,458,479,482,],[59,85,92,100,197,100,100,100,100,100,100,100,100,100,100,100,100,100,]),'pragma_directive_list':([16,],[68,]),'pragma_directive':([16,68,],[70,181,]),'string_literal':([16,61,63,68,78,95,96,107,111,114,119,125,126,127,129,144,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,329,334,338,353,375,385,408,414,416,417,418,419,420,423,444,446,451,458,464,479,482,493,494,496,501,],[72,140,140,72,140,140,140,140,140,140,140,140,140,140,140,263,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,425,140,140,140,140,140,140,140,140,140,140,140,140,140,140,483,140,140,140,483,483,483,]),'gcc_attribute':([17,20,66,80,86,88,89,172,183,283,372,374,377,382,397,398,426,450,478,489,],[75,83,75,196,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,]),'type_qualifier_list':([20,],[80,]),'statement_list':([61,96,],[95,212,]),'statement':([61,95,96,114,212,215,218,329,414,416,417,458,479,482,],[98,209,98,226,209,326,330,413,454,455,456,481,490,492,]),'labeled_statement':([61,95,96,114,212,215,218,329,414,416,417,458,479,482,],[99,99,99,99,99,99,99,99,99,99,99,99,99,99,]),'expression_statement':([61,95,96,114,212,215,218,227,329,338,414,416,417,458,479,482,],[101,101,101,101,101,101,101,338,101,419,101,101,101,101,101,101,]),'selection_statement':([61,95,96,114,212,215,218,329,414,416,417,458,479,482,],[102,102,102,102,102,102,102,102,102,102,102,102,102,102,]),'iteration_statement':([61,95,96,114,212,215,218,329,414,416,417,458,479,482,],[103,103,103,103,103,103,103,103,103,103,103,103,103,103,]),'jump_statement':([61,95,96,114,212,215,218,329,414,416,417,458,479,482,],[104,104,104,104,104,104,104,104,104,104,104,104,104,104,]),'expression':([61,95,96,111,114,119,174,212,215,218,221,224,225,227,245,247,254,258,329,338,414,416,417,418,419,458,479,482,493,],[109,109,109,222,109,232,222,109,109,109,332,335,336,109,342,344,222,222,109,109,109,109,109,457,459,109,109,109,497,]),'assignment_expression':([61,95,96,111,114,119,174,198,212,215,218,220,221,224,225,227,233,245,247,248,254,258,315,329,338,414,416,417,418,419,423,444,446,458,479,482,493,],[120,120,120,120,120,120,120,314,120,120,120,331,120,120,120,120,341,120,120,347,120,120,314,120,120,120,120,120,120,120,461,347,314,120,120,120,120,]),'conditional_expression':([61,63,78,95,96,107,111,114,119,174,198,212,215,218,220,221,224,225,227,233,245,247,248,254,258,293,295,315,323,329,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[121,171,171,121,121,171,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,171,171,121,171,121,121,171,171,171,121,121,121,121,121,460,121,121,121,171,121,121,121,121,]),'unary_expression':([61,63,78,95,96,107,111,114,119,125,126,127,129,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,329,334,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[122,173,173,122,122,173,122,122,122,253,255,173,257,122,122,122,122,122,122,122,122,122,122,122,122,173,122,122,122,122,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,122,173,122,173,122,173,173,173,122,122,122,122,122,173,122,122,122,173,122,122,122,122,]),'logical_or_expression':([61,63,78,95,96,107,111,114,119,174,198,212,215,218,220,221,224,225,227,233,245,247,248,254,258,293,295,315,323,329,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,]),'postfix_expression':([61,63,78,95,96,107,111,114,119,125,126,127,129,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,329,334,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,]),'unary_operator':([61,63,78,95,96,107,111,114,119,125,126,127,129,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,329,334,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,]),'cast_expression':([61,63,78,95,96,107,111,114,119,127,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,329,334,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[128,128,128,128,128,128,128,128,128,256,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,369,370,371,128,128,128,128,128,415,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,]),'asm_expression':([61,63,78,95,96,107,111,114,119,125,126,127,129,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,329,334,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,]),'logical_and_expression':([61,63,78,95,96,107,111,114,119,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,293,295,315,323,329,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,343,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,]),'primary_expression':([61,63,78,95,96,107,111,114,119,125,126,127,129,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,329,334,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,]),'inclusive_or_expression':([61,63,78,95,96,107,111,114,119,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,293,295,315,323,329,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,352,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,]),'identifier':([61,63,78,95,96,107,111,114,119,125,126,127,129,174,198,212,215,216,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,267,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,329,334,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,327,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,357,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,]),'constant':([61,63,78,95,96,107,111,114,119,125,126,127,129,174,182,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,329,334,338,375,385,390,408,414,416,417,418,419,420,423,444,446,451,458,469,479,482,493,],[143,143,143,143,143,143,143,143,143,143,143,143,143,143,300,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,441,143,143,143,143,143,143,143,143,143,143,143,143,486,143,143,143,]),'multi_string_literal':([61,63,78,95,96,107,111,114,119,125,126,127,129,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,329,334,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,]),'exclusive_or_expression':([61,63,78,95,96,107,111,114,119,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,293,295,315,323,329,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,354,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,]),'macro_param':([61,63,78,95,96,107,111,114,119,125,126,127,129,144,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,280,281,282,293,295,315,323,329,334,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[158,158,158,158,158,158,158,158,158,158,158,158,158,264,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,]),'and_expression':([61,63,78,95,96,107,111,114,119,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,293,295,315,323,329,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,355,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,]),'equality_expression':([61,63,78,95,96,107,111,114,119,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,268,293,295,315,323,329,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,358,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,]),'relational_expression':([61,63,78,95,96,107,111,114,119,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,268,270,271,293,295,315,323,329,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,359,360,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,]),'shift_expression':([61,63,78,95,96,107,111,114,119,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,293,295,315,323,329,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,361,362,363,364,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,]),'additive_expression':([61,63,78,95,96,107,111,114,119,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,293,295,315,323,329,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,365,366,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,]),'multiplicative_expression':([61,63,78,95,96,107,111,114,119,174,198,212,215,218,220,221,224,225,227,233,245,246,247,248,254,258,259,262,266,268,270,271,272,273,274,275,276,277,278,279,293,295,315,323,329,338,375,385,408,414,416,417,418,419,420,423,444,446,451,458,479,482,493,],[165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,367,368,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,]),'type_name':([63,111,174,248,258,423,444,],[168,223,223,348,351,462,348,]),'constant_expression':([63,78,107,293,295,323,375,385,408,451,],[169,185,217,381,384,411,428,436,452,477,]),'specifier_qualifier_list':([63,111,174,201,248,258,316,317,320,401,409,423,444,],[170,170,170,319,170,170,319,319,319,319,319,170,170,]),'parameter_type_list':([79,292,312,376,],[187,379,379,430,]),'identifier_list':([79,],[189,]),'parameter_list':([79,292,312,376,],[190,190,190,190,]),'parameter_declaration':([79,292,308,312,376,],[192,192,396,192,192,]),'enumerator_list':([90,207,],[203,324,]),'enumerator_list_iso':([90,207,],[204,204,]),'enumerator':([90,207,322,],[205,205,410,]),'assignment_operator':([122,],[233,]),'volatile_opt':([139,],[260,]),'abstract_declarator':([170,193,377,398,],[286,310,431,431,]),'specifier_qualifier':([170,172,319,],[287,294,287,]),'direct_abstract_declarator':([170,193,288,311,377,398,],[289,289,373,373,289,289,]),'macro_parameter_list':([176,],[296,]),'pragma_pack_stack_args':([182,],[301,]),'initializer':([198,315,446,],[313,400,475,]),'member_declaration_list':([201,316,320,],[317,401,409,]),'member_declaration':([201,316,317,320,401,409,],[318,318,403,318,403,403,]),'argument_expression_list':([248,444,],[346,473,]),'gcc_attrib_list':([303,],[391,]),'gcc_attrib':([303,443,],[392,472,]),'initializer_list':([315,],[399,]),'member_declarator_list':([319,],[404,]),'member_declarator':([319,449,],[406,476,]),'str_opt_expr_pair_list':([464,494,501,],[484,498,503,]),'str_opt_expr_pair':([464,494,496,501,],[485,485,499,485,]),} _lr_goto = {} for _k, _v in _lr_goto_items.items(): for _x, _y in zip(_v[0], _v[1]): if not _x in _lr_goto: _lr_goto[_x] = {} _lr_goto[_x][_k] = _y del _lr_goto_items _lr_productions = [ ("S' -> translation_unit","S'",1,None,None,None), ('translation_unit -> ','translation_unit',0,'p_translation_unit','cgrammar.py',124), ('translation_unit -> translation_unit external_declaration','translation_unit',2,'p_translation_unit','cgrammar.py',125), ('translation_unit -> translation_unit directive','translation_unit',2,'p_translation_unit','cgrammar.py',126), ('identifier -> IDENTIFIER','identifier',1,'p_identifier','cgrammar.py',135), ('identifier -> IDENTIFIER PP_IDENTIFIER_PASTE identifier','identifier',3,'p_identifier','cgrammar.py',136), ('identifier -> PP_MACRO_PARAM PP_IDENTIFIER_PASTE identifier','identifier',3,'p_identifier','cgrammar.py',137), ('identifier -> IDENTIFIER PP_IDENTIFIER_PASTE PP_MACRO_PARAM','identifier',3,'p_identifier','cgrammar.py',138), ('identifier -> PP_MACRO_PARAM PP_IDENTIFIER_PASTE PP_MACRO_PARAM','identifier',3,'p_identifier','cgrammar.py',139), ('constant -> I_CONST_HEX','constant',1,'p_constant_integer','cgrammar.py',157), ('constant -> I_CONST_DEC','constant',1,'p_constant_integer','cgrammar.py',158), ('constant -> I_CONST_OCT','constant',1,'p_constant_integer','cgrammar.py',159), ('constant -> I_CONST_BIN','constant',1,'p_constant_integer','cgrammar.py',160), ('constant -> F_CONST_1','constant',1,'p_constant_float','cgrammar.py',173), ('constant -> F_CONST_2','constant',1,'p_constant_float','cgrammar.py',174), ('constant -> F_CONST_3','constant',1,'p_constant_float','cgrammar.py',175), ('constant -> F_CONST_4','constant',1,'p_constant_float','cgrammar.py',176), ('constant -> F_CONST_5','constant',1,'p_constant_float','cgrammar.py',177), ('constant -> F_CONST_6','constant',1,'p_constant_float','cgrammar.py',178), ('constant -> CHARACTER_CONSTANT','constant',1,'p_constant_character','cgrammar.py',184), ('string_literal -> STRING_LITERAL','string_literal',1,'p_string_literal','cgrammar.py',192), ('multi_string_literal -> string_literal','multi_string_literal',1,'p_multi_string_literal','cgrammar.py',198), ('multi_string_literal -> macro_param','multi_string_literal',1,'p_multi_string_literal','cgrammar.py',199), ('multi_string_literal -> multi_string_literal string_literal','multi_string_literal',2,'p_multi_string_literal','cgrammar.py',200), ('multi_string_literal -> multi_string_literal macro_param','multi_string_literal',2,'p_multi_string_literal','cgrammar.py',201), ('macro_param -> PP_MACRO_PARAM','macro_param',1,'p_macro_param','cgrammar.py',212), ('macro_param -> PP_STRINGIFY PP_MACRO_PARAM','macro_param',2,'p_macro_param','cgrammar.py',213), ('primary_expression -> identifier','primary_expression',1,'p_primary_expression','cgrammar.py',222), ('primary_expression -> constant','primary_expression',1,'p_primary_expression','cgrammar.py',223), ('primary_expression -> multi_string_literal','primary_expression',1,'p_primary_expression','cgrammar.py',224), ('primary_expression -> LPAREN expression RPAREN','primary_expression',3,'p_primary_expression','cgrammar.py',225), ('postfix_expression -> primary_expression','postfix_expression',1,'p_postfix_expression','cgrammar.py',234), ('postfix_expression -> postfix_expression LBRACKET expression RBRACKET','postfix_expression',4,'p_postfix_expression','cgrammar.py',235), ('postfix_expression -> postfix_expression LPAREN RPAREN','postfix_expression',3,'p_postfix_expression','cgrammar.py',236), ('postfix_expression -> postfix_expression LPAREN argument_expression_list RPAREN','postfix_expression',4,'p_postfix_expression','cgrammar.py',237), ('postfix_expression -> postfix_expression PERIOD IDENTIFIER','postfix_expression',3,'p_postfix_expression','cgrammar.py',238), ('postfix_expression -> postfix_expression PTR_OP IDENTIFIER','postfix_expression',3,'p_postfix_expression','cgrammar.py',239), ('postfix_expression -> postfix_expression INC_OP','postfix_expression',2,'p_postfix_expression','cgrammar.py',240), ('postfix_expression -> postfix_expression DEC_OP','postfix_expression',2,'p_postfix_expression','cgrammar.py',241), ('argument_expression_list -> assignment_expression','argument_expression_list',1,'p_argument_expression_list','cgrammar.py',280), ('argument_expression_list -> argument_expression_list COMMA assignment_expression','argument_expression_list',3,'p_argument_expression_list','cgrammar.py',281), ('argument_expression_list -> type_name','argument_expression_list',1,'p_argument_expression_list','cgrammar.py',282), ('argument_expression_list -> argument_expression_list COMMA type_name','argument_expression_list',3,'p_argument_expression_list','cgrammar.py',283), ('asm_expression -> __ASM__ volatile_opt LPAREN string_literal RPAREN','asm_expression',5,'p_asm_expression','cgrammar.py',293), ('asm_expression -> __ASM__ volatile_opt LPAREN string_literal COLON str_opt_expr_pair_list RPAREN','asm_expression',7,'p_asm_expression','cgrammar.py',294), ('asm_expression -> __ASM__ volatile_opt LPAREN string_literal COLON str_opt_expr_pair_list COLON str_opt_expr_pair_list RPAREN','asm_expression',9,'p_asm_expression','cgrammar.py',295), ('asm_expression -> __ASM__ volatile_opt LPAREN string_literal COLON str_opt_expr_pair_list COLON str_opt_expr_pair_list COLON str_opt_expr_pair_list RPAREN','asm_expression',11,'p_asm_expression','cgrammar.py',296), ('str_opt_expr_pair_list -> ','str_opt_expr_pair_list',0,'p_str_opt_expr_pair_list','cgrammar.py',309), ('str_opt_expr_pair_list -> str_opt_expr_pair','str_opt_expr_pair_list',1,'p_str_opt_expr_pair_list','cgrammar.py',310), ('str_opt_expr_pair_list -> str_opt_expr_pair_list COMMA str_opt_expr_pair','str_opt_expr_pair_list',3,'p_str_opt_expr_pair_list','cgrammar.py',311), ('str_opt_expr_pair -> string_literal','str_opt_expr_pair',1,'p_str_opt_expr_pair','cgrammar.py',316), ('str_opt_expr_pair -> string_literal LPAREN expression RPAREN','str_opt_expr_pair',4,'p_str_opt_expr_pair','cgrammar.py',317), ('volatile_opt -> ','volatile_opt',0,'p_volatile_opt','cgrammar.py',322), ('volatile_opt -> VOLATILE','volatile_opt',1,'p_volatile_opt','cgrammar.py',323), ('unary_expression -> postfix_expression','unary_expression',1,'p_unary_expression','cgrammar.py',340), ('unary_expression -> INC_OP unary_expression','unary_expression',2,'p_unary_expression','cgrammar.py',341), ('unary_expression -> DEC_OP unary_expression','unary_expression',2,'p_unary_expression','cgrammar.py',342), ('unary_expression -> unary_operator cast_expression','unary_expression',2,'p_unary_expression','cgrammar.py',343), ('unary_expression -> SIZEOF unary_expression','unary_expression',2,'p_unary_expression','cgrammar.py',344), ('unary_expression -> SIZEOF LPAREN type_name RPAREN','unary_expression',4,'p_unary_expression','cgrammar.py',345), ('unary_expression -> asm_expression','unary_expression',1,'p_unary_expression','cgrammar.py',346), ('unary_operator -> AND','unary_operator',1,'p_unary_operator','cgrammar.py',363), ('unary_operator -> TIMES','unary_operator',1,'p_unary_operator','cgrammar.py',364), ('unary_operator -> PLUS','unary_operator',1,'p_unary_operator','cgrammar.py',365), ('unary_operator -> MINUS','unary_operator',1,'p_unary_operator','cgrammar.py',366), ('unary_operator -> NOT','unary_operator',1,'p_unary_operator','cgrammar.py',367), ('unary_operator -> LNOT','unary_operator',1,'p_unary_operator','cgrammar.py',368), ('cast_expression -> unary_expression','cast_expression',1,'p_cast_expression','cgrammar.py',374), ('cast_expression -> LPAREN type_name RPAREN cast_expression','cast_expression',4,'p_cast_expression','cgrammar.py',375), ('multiplicative_expression -> cast_expression','multiplicative_expression',1,'p_multiplicative_expression','cgrammar.py',391), ('multiplicative_expression -> multiplicative_expression TIMES cast_expression','multiplicative_expression',3,'p_multiplicative_expression','cgrammar.py',392), ('multiplicative_expression -> multiplicative_expression DIVIDE cast_expression','multiplicative_expression',3,'p_multiplicative_expression','cgrammar.py',393), ('multiplicative_expression -> multiplicative_expression MOD cast_expression','multiplicative_expression',3,'p_multiplicative_expression','cgrammar.py',394), ('additive_expression -> multiplicative_expression','additive_expression',1,'p_additive_expression','cgrammar.py',410), ('additive_expression -> additive_expression PLUS multiplicative_expression','additive_expression',3,'p_additive_expression','cgrammar.py',411), ('additive_expression -> additive_expression MINUS multiplicative_expression','additive_expression',3,'p_additive_expression','cgrammar.py',412), ('shift_expression -> additive_expression','shift_expression',1,'p_shift_expression','cgrammar.py',428), ('shift_expression -> shift_expression LEFT_OP additive_expression','shift_expression',3,'p_shift_expression','cgrammar.py',429), ('shift_expression -> shift_expression RIGHT_OP additive_expression','shift_expression',3,'p_shift_expression','cgrammar.py',430), ('relational_expression -> shift_expression','relational_expression',1,'p_relational_expression','cgrammar.py',448), ('relational_expression -> relational_expression LT shift_expression','relational_expression',3,'p_relational_expression','cgrammar.py',449), ('relational_expression -> relational_expression GT shift_expression','relational_expression',3,'p_relational_expression','cgrammar.py',450), ('relational_expression -> relational_expression LE_OP shift_expression','relational_expression',3,'p_relational_expression','cgrammar.py',451), ('relational_expression -> relational_expression GE_OP shift_expression','relational_expression',3,'p_relational_expression','cgrammar.py',452), ('equality_expression -> relational_expression','equality_expression',1,'p_equality_expression','cgrammar.py',468), ('equality_expression -> equality_expression EQ_OP relational_expression','equality_expression',3,'p_equality_expression','cgrammar.py',469), ('equality_expression -> equality_expression NE_OP relational_expression','equality_expression',3,'p_equality_expression','cgrammar.py',470), ('and_expression -> equality_expression','and_expression',1,'p_and_expression','cgrammar.py',480), ('and_expression -> and_expression AND equality_expression','and_expression',3,'p_and_expression','cgrammar.py',481), ('exclusive_or_expression -> and_expression','exclusive_or_expression',1,'p_exclusive_or_expression','cgrammar.py',492), ('exclusive_or_expression -> exclusive_or_expression XOR and_expression','exclusive_or_expression',3,'p_exclusive_or_expression','cgrammar.py',493), ('inclusive_or_expression -> exclusive_or_expression','inclusive_or_expression',1,'p_inclusive_or_expression','cgrammar.py',504), ('inclusive_or_expression -> inclusive_or_expression OR exclusive_or_expression','inclusive_or_expression',3,'p_inclusive_or_expression','cgrammar.py',505), ('logical_and_expression -> inclusive_or_expression','logical_and_expression',1,'p_logical_and_expression','cgrammar.py',516), ('logical_and_expression -> logical_and_expression AND_OP inclusive_or_expression','logical_and_expression',3,'p_logical_and_expression','cgrammar.py',517), ('logical_or_expression -> logical_and_expression','logical_or_expression',1,'p_logical_or_expression','cgrammar.py',528), ('logical_or_expression -> logical_or_expression OR_OP logical_and_expression','logical_or_expression',3,'p_logical_or_expression','cgrammar.py',529), ('conditional_expression -> logical_or_expression','conditional_expression',1,'p_conditional_expression','cgrammar.py',540), ('conditional_expression -> logical_or_expression CONDOP expression COLON conditional_expression','conditional_expression',5,'p_conditional_expression','cgrammar.py',541), ('assignment_expression -> conditional_expression','assignment_expression',1,'p_assignment_expression','cgrammar.py',564), ('assignment_expression -> unary_expression assignment_operator assignment_expression','assignment_expression',3,'p_assignment_expression','cgrammar.py',565), ('assignment_operator -> EQUALS','assignment_operator',1,'p_assignment_operator','cgrammar.py',580), ('assignment_operator -> MUL_ASSIGN','assignment_operator',1,'p_assignment_operator','cgrammar.py',581), ('assignment_operator -> DIV_ASSIGN','assignment_operator',1,'p_assignment_operator','cgrammar.py',582), ('assignment_operator -> MOD_ASSIGN','assignment_operator',1,'p_assignment_operator','cgrammar.py',583), ('assignment_operator -> ADD_ASSIGN','assignment_operator',1,'p_assignment_operator','cgrammar.py',584), ('assignment_operator -> SUB_ASSIGN','assignment_operator',1,'p_assignment_operator','cgrammar.py',585), ('assignment_operator -> LEFT_ASSIGN','assignment_operator',1,'p_assignment_operator','cgrammar.py',586), ('assignment_operator -> RIGHT_ASSIGN','assignment_operator',1,'p_assignment_operator','cgrammar.py',587), ('assignment_operator -> AND_ASSIGN','assignment_operator',1,'p_assignment_operator','cgrammar.py',588), ('assignment_operator -> XOR_ASSIGN','assignment_operator',1,'p_assignment_operator','cgrammar.py',589), ('assignment_operator -> OR_ASSIGN','assignment_operator',1,'p_assignment_operator','cgrammar.py',590), ('expression -> assignment_expression','expression',1,'p_expression','cgrammar.py',596), ('expression -> expression COMMA assignment_expression','expression',3,'p_expression','cgrammar.py',597), ('constant_expression -> conditional_expression','constant_expression',1,'p_constant_expression','cgrammar.py',604), ('declaration -> declaration_impl SEMI','declaration',2,'p_declaration','cgrammar.py',610), ('declaration_impl -> declaration_specifier_list','declaration_impl',1,'p_declaration_impl','cgrammar.py',618), ('declaration_impl -> declaration_specifier_list init_declarator_list','declaration_impl',2,'p_declaration_impl','cgrammar.py',619), ('declaration_specifier_list -> gcc_attributes declaration_specifier gcc_attributes','declaration_specifier_list',3,'p_declaration_specifier_list','cgrammar.py',638), ('declaration_specifier_list -> declaration_specifier_list declaration_specifier gcc_attributes','declaration_specifier_list',3,'p_declaration_specifier_list','cgrammar.py',639), ('declaration_specifier -> storage_class_specifier','declaration_specifier',1,'p_declaration_specifier','cgrammar.py',652), ('declaration_specifier -> type_specifier','declaration_specifier',1,'p_declaration_specifier','cgrammar.py',653), ('declaration_specifier -> type_qualifier','declaration_specifier',1,'p_declaration_specifier','cgrammar.py',654), ('declaration_specifier -> function_specifier','declaration_specifier',1,'p_declaration_specifier','cgrammar.py',655), ('init_declarator_list -> init_declarator','init_declarator_list',1,'p_init_declarator_list','cgrammar.py',661), ('init_declarator_list -> init_declarator_list COMMA init_declarator','init_declarator_list',3,'p_init_declarator_list','cgrammar.py',662), ('init_declarator -> declarator gcc_attributes','init_declarator',2,'p_init_declarator','cgrammar.py',671), ('init_declarator -> declarator gcc_attributes EQUALS initializer','init_declarator',4,'p_init_declarator','cgrammar.py',672), ('storage_class_specifier -> TYPEDEF','storage_class_specifier',1,'p_storage_class_specifier','cgrammar.py',683), ('storage_class_specifier -> EXTERN','storage_class_specifier',1,'p_storage_class_specifier','cgrammar.py',684), ('storage_class_specifier -> STATIC','storage_class_specifier',1,'p_storage_class_specifier','cgrammar.py',685), ('storage_class_specifier -> AUTO','storage_class_specifier',1,'p_storage_class_specifier','cgrammar.py',686), ('storage_class_specifier -> REGISTER','storage_class_specifier',1,'p_storage_class_specifier','cgrammar.py',687), ('type_specifier -> VOID','type_specifier',1,'p_type_specifier','cgrammar.py',693), ('type_specifier -> _BOOL','type_specifier',1,'p_type_specifier','cgrammar.py',694), ('type_specifier -> CHAR','type_specifier',1,'p_type_specifier','cgrammar.py',695), ('type_specifier -> SHORT','type_specifier',1,'p_type_specifier','cgrammar.py',696), ('type_specifier -> INT','type_specifier',1,'p_type_specifier','cgrammar.py',697), ('type_specifier -> LONG','type_specifier',1,'p_type_specifier','cgrammar.py',698), ('type_specifier -> FLOAT','type_specifier',1,'p_type_specifier','cgrammar.py',699), ('type_specifier -> DOUBLE','type_specifier',1,'p_type_specifier','cgrammar.py',700), ('type_specifier -> SIGNED','type_specifier',1,'p_type_specifier','cgrammar.py',701), ('type_specifier -> UNSIGNED','type_specifier',1,'p_type_specifier','cgrammar.py',702), ('type_specifier -> struct_or_union_specifier','type_specifier',1,'p_type_specifier','cgrammar.py',703), ('type_specifier -> enum_specifier','type_specifier',1,'p_type_specifier','cgrammar.py',704), ('type_specifier -> TYPE_NAME','type_specifier',1,'p_type_specifier','cgrammar.py',705), ('struct_or_union_specifier -> struct_or_union gcc_attributes IDENTIFIER LBRACE member_declaration_list RBRACE','struct_or_union_specifier',6,'p_struct_or_union_specifier','cgrammar.py',714), ('struct_or_union_specifier -> struct_or_union gcc_attributes TYPE_NAME LBRACE member_declaration_list RBRACE','struct_or_union_specifier',6,'p_struct_or_union_specifier','cgrammar.py',715), ('struct_or_union_specifier -> struct_or_union gcc_attributes LBRACE member_declaration_list RBRACE','struct_or_union_specifier',5,'p_struct_or_union_specifier','cgrammar.py',716), ('struct_or_union_specifier -> struct_or_union gcc_attributes IDENTIFIER','struct_or_union_specifier',3,'p_struct_or_union_specifier','cgrammar.py',717), ('struct_or_union_specifier -> struct_or_union gcc_attributes TYPE_NAME','struct_or_union_specifier',3,'p_struct_or_union_specifier','cgrammar.py',718), ('struct_or_union -> STRUCT','struct_or_union',1,'p_struct_or_union','cgrammar.py',743), ('struct_or_union -> UNION','struct_or_union',1,'p_struct_or_union','cgrammar.py',744), ('gcc_attributes -> ','gcc_attributes',0,'p_gcc_attributes','cgrammar.py',750), ('gcc_attributes -> gcc_attributes gcc_attribute','gcc_attributes',2,'p_gcc_attributes','cgrammar.py',751), ('gcc_attribute -> __ATTRIBUTE__ LPAREN LPAREN gcc_attrib_list RPAREN RPAREN','gcc_attribute',6,'p_gcc_attribute','cgrammar.py',762), ('gcc_attrib_list -> gcc_attrib','gcc_attrib_list',1,'p_gcc_attrib_list','cgrammar.py',769), ('gcc_attrib_list -> gcc_attrib_list COMMA gcc_attrib','gcc_attrib_list',3,'p_gcc_attrib_list','cgrammar.py',770), ('gcc_attrib -> ','gcc_attrib',0,'p_gcc_attrib','cgrammar.py',779), ('gcc_attrib -> IDENTIFIER','gcc_attrib',1,'p_gcc_attrib','cgrammar.py',780), ('gcc_attrib -> IDENTIFIER LPAREN argument_expression_list RPAREN','gcc_attrib',4,'p_gcc_attrib','cgrammar.py',781), ('member_declaration_list -> member_declaration','member_declaration_list',1,'p_member_declaration_list','cgrammar.py',794), ('member_declaration_list -> member_declaration_list member_declaration','member_declaration_list',2,'p_member_declaration_list','cgrammar.py',795), ('member_declaration -> specifier_qualifier_list member_declarator_list SEMI','member_declaration',3,'p_member_declaration','cgrammar.py',804), ('member_declaration -> specifier_qualifier_list SEMI','member_declaration',2,'p_member_declaration','cgrammar.py',805), ('specifier_qualifier_list -> gcc_attributes specifier_qualifier gcc_attributes','specifier_qualifier_list',3,'p_specifier_qualifier_list','cgrammar.py',826), ('specifier_qualifier_list -> specifier_qualifier_list specifier_qualifier gcc_attributes','specifier_qualifier_list',3,'p_specifier_qualifier_list','cgrammar.py',827), ('specifier_qualifier -> type_specifier','specifier_qualifier',1,'p_specifier_qualifier','cgrammar.py',836), ('specifier_qualifier -> type_qualifier','specifier_qualifier',1,'p_specifier_qualifier','cgrammar.py',837), ('member_declarator_list -> member_declarator','member_declarator_list',1,'p_member_declarator_list','cgrammar.py',843), ('member_declarator_list -> member_declarator_list COMMA member_declarator','member_declarator_list',3,'p_member_declarator_list','cgrammar.py',844), ('member_declarator -> declarator gcc_attributes','member_declarator',2,'p_member_declarator','cgrammar.py',853), ('member_declarator -> COLON constant_expression gcc_attributes','member_declarator',3,'p_member_declarator','cgrammar.py',854), ('member_declarator -> declarator COLON constant_expression gcc_attributes','member_declarator',4,'p_member_declarator','cgrammar.py',855), ('enum_specifier -> ENUM LBRACE enumerator_list RBRACE','enum_specifier',4,'p_enum_specifier','cgrammar.py',870), ('enum_specifier -> ENUM IDENTIFIER LBRACE enumerator_list RBRACE','enum_specifier',5,'p_enum_specifier','cgrammar.py',871), ('enum_specifier -> ENUM IDENTIFIER','enum_specifier',2,'p_enum_specifier','cgrammar.py',872), ('enumerator_list -> enumerator_list_iso','enumerator_list',1,'p_enumerator_list','cgrammar.py',886), ('enumerator_list -> enumerator_list_iso COMMA','enumerator_list',2,'p_enumerator_list','cgrammar.py',887), ('enumerator_list_iso -> enumerator','enumerator_list_iso',1,'p_enumerator_list_iso','cgrammar.py',895), ('enumerator_list_iso -> enumerator_list_iso COMMA enumerator','enumerator_list_iso',3,'p_enumerator_list_iso','cgrammar.py',896), ('enumerator -> IDENTIFIER','enumerator',1,'p_enumerator','cgrammar.py',905), ('enumerator -> IDENTIFIER EQUALS constant_expression','enumerator',3,'p_enumerator','cgrammar.py',906), ('type_qualifier -> CONST','type_qualifier',1,'p_type_qualifier','cgrammar.py',915), ('type_qualifier -> VOLATILE','type_qualifier',1,'p_type_qualifier','cgrammar.py',916), ('type_qualifier -> RESTRICT','type_qualifier',1,'p_type_qualifier','cgrammar.py',917), ('function_specifier -> INLINE','function_specifier',1,'p_function_specifier','cgrammar.py',923), ('function_specifier -> _NORETURN','function_specifier',1,'p_function_specifier','cgrammar.py',924), ('declarator -> pointer direct_declarator','declarator',2,'p_declarator','cgrammar.py',929), ('declarator -> direct_declarator','declarator',1,'p_declarator','cgrammar.py',930), ('direct_declarator -> IDENTIFIER','direct_declarator',1,'p_direct_declarator','cgrammar.py',944), ('direct_declarator -> LPAREN gcc_attributes declarator RPAREN','direct_declarator',4,'p_direct_declarator','cgrammar.py',945), ('direct_declarator -> direct_declarator LBRACKET constant_expression RBRACKET','direct_declarator',4,'p_direct_declarator','cgrammar.py',946), ('direct_declarator -> direct_declarator LBRACKET RBRACKET','direct_declarator',3,'p_direct_declarator','cgrammar.py',947), ('direct_declarator -> direct_declarator LPAREN parameter_type_list RPAREN','direct_declarator',4,'p_direct_declarator','cgrammar.py',948), ('direct_declarator -> direct_declarator LPAREN identifier_list RPAREN','direct_declarator',4,'p_direct_declarator','cgrammar.py',949), ('direct_declarator -> direct_declarator LPAREN RPAREN','direct_declarator',3,'p_direct_declarator','cgrammar.py',950), ('pointer -> TIMES','pointer',1,'p_pointer','cgrammar.py',980), ('pointer -> TIMES type_qualifier_list','pointer',2,'p_pointer','cgrammar.py',981), ('pointer -> TIMES pointer','pointer',2,'p_pointer','cgrammar.py',982), ('pointer -> TIMES type_qualifier_list pointer','pointer',3,'p_pointer','cgrammar.py',983), ('type_qualifier_list -> type_qualifier','type_qualifier_list',1,'p_type_qualifier_list','cgrammar.py',1005), ('type_qualifier_list -> gcc_attribute','type_qualifier_list',1,'p_type_qualifier_list','cgrammar.py',1006), ('type_qualifier_list -> type_qualifier_list type_qualifier','type_qualifier_list',2,'p_type_qualifier_list','cgrammar.py',1007), ('type_qualifier_list -> type_qualifier_list gcc_attribute','type_qualifier_list',2,'p_type_qualifier_list','cgrammar.py',1008), ('parameter_type_list -> parameter_list','parameter_type_list',1,'p_parameter_type_list','cgrammar.py',1017), ('parameter_type_list -> parameter_list COMMA ELLIPSIS','parameter_type_list',3,'p_parameter_type_list','cgrammar.py',1018), ('parameter_list -> parameter_declaration','parameter_list',1,'p_parameter_list','cgrammar.py',1027), ('parameter_list -> parameter_list COMMA parameter_declaration','parameter_list',3,'p_parameter_list','cgrammar.py',1028), ('parameter_declaration -> declaration_specifier_list declarator gcc_attributes','parameter_declaration',3,'p_parameter_declaration','cgrammar.py',1037), ('parameter_declaration -> declaration_specifier_list abstract_declarator','parameter_declaration',2,'p_parameter_declaration','cgrammar.py',1038), ('parameter_declaration -> declaration_specifier_list','parameter_declaration',1,'p_parameter_declaration','cgrammar.py',1039), ('identifier_list -> IDENTIFIER','identifier_list',1,'p_identifier_list','cgrammar.py',1055), ('identifier_list -> identifier_list COMMA IDENTIFIER','identifier_list',3,'p_identifier_list','cgrammar.py',1056), ('type_name -> specifier_qualifier_list','type_name',1,'p_type_name','cgrammar.py',1069), ('type_name -> specifier_qualifier_list abstract_declarator','type_name',2,'p_type_name','cgrammar.py',1070), ('abstract_declarator -> pointer','abstract_declarator',1,'p_abstract_declarator','cgrammar.py',1086), ('abstract_declarator -> direct_abstract_declarator gcc_attributes','abstract_declarator',2,'p_abstract_declarator','cgrammar.py',1087), ('abstract_declarator -> pointer direct_abstract_declarator gcc_attributes','abstract_declarator',3,'p_abstract_declarator','cgrammar.py',1088), ('direct_abstract_declarator -> LPAREN gcc_attributes abstract_declarator RPAREN','direct_abstract_declarator',4,'p_direct_abstract_declarator','cgrammar.py',1115), ('direct_abstract_declarator -> LBRACKET RBRACKET','direct_abstract_declarator',2,'p_direct_abstract_declarator','cgrammar.py',1116), ('direct_abstract_declarator -> LBRACKET constant_expression RBRACKET','direct_abstract_declarator',3,'p_direct_abstract_declarator','cgrammar.py',1117), ('direct_abstract_declarator -> direct_abstract_declarator LBRACKET RBRACKET','direct_abstract_declarator',3,'p_direct_abstract_declarator','cgrammar.py',1118), ('direct_abstract_declarator -> direct_abstract_declarator LBRACKET constant_expression RBRACKET','direct_abstract_declarator',4,'p_direct_abstract_declarator','cgrammar.py',1119), ('direct_abstract_declarator -> LPAREN RPAREN','direct_abstract_declarator',2,'p_direct_abstract_declarator','cgrammar.py',1120), ('direct_abstract_declarator -> LPAREN parameter_type_list RPAREN','direct_abstract_declarator',3,'p_direct_abstract_declarator','cgrammar.py',1121), ('direct_abstract_declarator -> direct_abstract_declarator LPAREN RPAREN','direct_abstract_declarator',3,'p_direct_abstract_declarator','cgrammar.py',1122), ('direct_abstract_declarator -> direct_abstract_declarator LPAREN parameter_type_list RPAREN','direct_abstract_declarator',4,'p_direct_abstract_declarator','cgrammar.py',1123), ('initializer -> assignment_expression','initializer',1,'p_initializer','cgrammar.py',1162), ('initializer -> LBRACE initializer_list RBRACE','initializer',3,'p_initializer','cgrammar.py',1163), ('initializer -> LBRACE initializer_list COMMA RBRACE','initializer',4,'p_initializer','cgrammar.py',1164), ('initializer_list -> initializer','initializer_list',1,'p_initializer_list','cgrammar.py',1169), ('initializer_list -> initializer_list COMMA initializer','initializer_list',3,'p_initializer_list','cgrammar.py',1170), ('statement -> labeled_statement','statement',1,'p_statement','cgrammar.py',1175), ('statement -> compound_statement','statement',1,'p_statement','cgrammar.py',1176), ('statement -> expression_statement','statement',1,'p_statement','cgrammar.py',1177), ('statement -> selection_statement','statement',1,'p_statement','cgrammar.py',1178), ('statement -> iteration_statement','statement',1,'p_statement','cgrammar.py',1179), ('statement -> jump_statement','statement',1,'p_statement','cgrammar.py',1180), ('labeled_statement -> IDENTIFIER COLON statement','labeled_statement',3,'p_labeled_statement','cgrammar.py',1185), ('labeled_statement -> CASE constant_expression COLON statement','labeled_statement',4,'p_labeled_statement','cgrammar.py',1186), ('labeled_statement -> DEFAULT COLON statement','labeled_statement',3,'p_labeled_statement','cgrammar.py',1187), ('compound_statement -> LBRACE RBRACE','compound_statement',2,'p_compound_statement','cgrammar.py',1192), ('compound_statement -> LBRACE statement_list RBRACE','compound_statement',3,'p_compound_statement','cgrammar.py',1193), ('compound_statement -> LBRACE declaration_list RBRACE','compound_statement',3,'p_compound_statement','cgrammar.py',1194), ('compound_statement -> LBRACE declaration_list statement_list RBRACE','compound_statement',4,'p_compound_statement','cgrammar.py',1195), ('compound_statement -> LBRACE error RBRACE','compound_statement',3,'p_compound_statement_error','cgrammar.py',1200), ('declaration_list -> declaration','declaration_list',1,'p_declaration_list','cgrammar.py',1206), ('declaration_list -> declaration_list declaration','declaration_list',2,'p_declaration_list','cgrammar.py',1207), ('statement_list -> statement','statement_list',1,'p_statement_list','cgrammar.py',1212), ('statement_list -> statement_list statement','statement_list',2,'p_statement_list','cgrammar.py',1213), ('expression_statement -> SEMI','expression_statement',1,'p_expression_statement','cgrammar.py',1218), ('expression_statement -> expression SEMI','expression_statement',2,'p_expression_statement','cgrammar.py',1219), ('expression_statement -> error SEMI','expression_statement',2,'p_expression_statement_error','cgrammar.py',1224), ('selection_statement -> IF LPAREN expression RPAREN statement','selection_statement',5,'p_selection_statement','cgrammar.py',1230), ('selection_statement -> IF LPAREN expression RPAREN statement ELSE statement','selection_statement',7,'p_selection_statement','cgrammar.py',1231), ('selection_statement -> SWITCH LPAREN expression RPAREN statement','selection_statement',5,'p_selection_statement','cgrammar.py',1232), ('iteration_statement -> WHILE LPAREN expression RPAREN statement','iteration_statement',5,'p_iteration_statement','cgrammar.py',1237), ('iteration_statement -> DO statement WHILE LPAREN expression RPAREN SEMI','iteration_statement',7,'p_iteration_statement','cgrammar.py',1238), ('iteration_statement -> FOR LPAREN expression_statement expression_statement RPAREN statement','iteration_statement',6,'p_iteration_statement','cgrammar.py',1239), ('iteration_statement -> FOR LPAREN expression_statement expression_statement expression RPAREN statement','iteration_statement',7,'p_iteration_statement','cgrammar.py',1240), ('jump_statement -> GOTO IDENTIFIER SEMI','jump_statement',3,'p_jump_statement','cgrammar.py',1245), ('jump_statement -> CONTINUE SEMI','jump_statement',2,'p_jump_statement','cgrammar.py',1246), ('jump_statement -> BREAK SEMI','jump_statement',2,'p_jump_statement','cgrammar.py',1247), ('jump_statement -> RETURN SEMI','jump_statement',2,'p_jump_statement','cgrammar.py',1248), ('jump_statement -> RETURN expression SEMI','jump_statement',3,'p_jump_statement','cgrammar.py',1249), ('external_declaration -> declaration','external_declaration',1,'p_external_declaration','cgrammar.py',1254), ('external_declaration -> function_definition','external_declaration',1,'p_external_declaration','cgrammar.py',1255), ('function_definition -> declaration_specifier_list declarator declaration_list compound_statement','function_definition',4,'p_function_definition','cgrammar.py',1261), ('function_definition -> declaration_specifier_list declarator compound_statement','function_definition',3,'p_function_definition','cgrammar.py',1262), ('function_definition -> declarator declaration_list compound_statement','function_definition',3,'p_function_definition','cgrammar.py',1263), ('function_definition -> declarator compound_statement','function_definition',2,'p_function_definition','cgrammar.py',1264), ('directive -> define','directive',1,'p_directive','cgrammar.py',1270), ('directive -> undefine','directive',1,'p_directive','cgrammar.py',1271), ('directive -> pragma','directive',1,'p_directive','cgrammar.py',1272), ('define -> PP_DEFINE PP_DEFINE_NAME PP_END_DEFINE','define',3,'p_define','cgrammar.py',1277), ('define -> PP_DEFINE PP_DEFINE_NAME type_name PP_END_DEFINE','define',4,'p_define','cgrammar.py',1278), ('define -> PP_DEFINE PP_DEFINE_NAME constant_expression PP_END_DEFINE','define',4,'p_define','cgrammar.py',1279), ('define -> PP_DEFINE PP_DEFINE_MACRO_NAME LPAREN RPAREN PP_END_DEFINE','define',5,'p_define','cgrammar.py',1280), ('define -> PP_DEFINE PP_DEFINE_MACRO_NAME LPAREN RPAREN constant_expression PP_END_DEFINE','define',6,'p_define','cgrammar.py',1281), ('define -> PP_DEFINE PP_DEFINE_MACRO_NAME LPAREN macro_parameter_list RPAREN PP_END_DEFINE','define',6,'p_define','cgrammar.py',1282), ('define -> PP_DEFINE PP_DEFINE_MACRO_NAME LPAREN macro_parameter_list RPAREN constant_expression PP_END_DEFINE','define',7,'p_define','cgrammar.py',1283), ('define -> PP_DEFINE error PP_END_DEFINE','define',3,'p_define_error','cgrammar.py',1314), ('undefine -> PP_UNDEFINE PP_DEFINE_NAME PP_END_DEFINE','undefine',3,'p_undefine','cgrammar.py',1342), ('macro_parameter_list -> PP_MACRO_PARAM','macro_parameter_list',1,'p_macro_parameter_list','cgrammar.py',1353), ('macro_parameter_list -> macro_parameter_list COMMA PP_MACRO_PARAM','macro_parameter_list',3,'p_macro_parameter_list','cgrammar.py',1354), ('pragma -> pragma_pack','pragma',1,'p_pragma','cgrammar.py',1378), ('pragma -> PRAGMA pragma_directive_list PRAGMA_END','pragma',3,'p_pragma','cgrammar.py',1379), ('pragma_pack -> PRAGMA PRAGMA_PACK LPAREN RPAREN PRAGMA_END','pragma_pack',5,'p_pragma_pack','cgrammar.py',1384), ('pragma_pack -> PRAGMA PRAGMA_PACK LPAREN constant RPAREN PRAGMA_END','pragma_pack',6,'p_pragma_pack','cgrammar.py',1385), ('pragma_pack -> PRAGMA PRAGMA_PACK LPAREN pragma_pack_stack_args RPAREN PRAGMA_END','pragma_pack',6,'p_pragma_pack','cgrammar.py',1386), ('pragma_pack_stack_args -> IDENTIFIER','pragma_pack_stack_args',1,'p_pragma_pack_stack_args','cgrammar.py',1410), ('pragma_pack_stack_args -> IDENTIFIER COMMA IDENTIFIER','pragma_pack_stack_args',3,'p_pragma_pack_stack_args','cgrammar.py',1411), ('pragma_pack_stack_args -> IDENTIFIER COMMA IDENTIFIER COMMA constant','pragma_pack_stack_args',5,'p_pragma_pack_stack_args','cgrammar.py',1412), ('pragma_pack_stack_args -> IDENTIFIER COMMA constant COMMA IDENTIFIER','pragma_pack_stack_args',5,'p_pragma_pack_stack_args','cgrammar.py',1413), ('pragma_pack_stack_args -> IDENTIFIER COMMA constant','pragma_pack_stack_args',3,'p_pragma_pack_stack_args','cgrammar.py',1414), ('pragma_directive_list -> pragma_directive','pragma_directive_list',1,'p_pragma_directive_list','cgrammar.py',1434), ('pragma_directive_list -> pragma_directive_list pragma_directive','pragma_directive_list',2,'p_pragma_directive_list','cgrammar.py',1435), ('pragma_directive -> IDENTIFIER','pragma_directive',1,'p_pragma_directive','cgrammar.py',1444), ('pragma_directive -> string_literal','pragma_directive',1,'p_pragma_directive','cgrammar.py',1445), ] ================================================ FILE: ctypesgen/parser/pplexer.py ================================================ """Preprocess a C source file using gcc and convert the result into a token stream Reference is C99 with additions from C11 and C2x: * http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1124.pdf * http://www.quut.com/c/ANSI-C-grammar-l-2011.html * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2731.pdf """ __docformat__ = "restructuredtext" from ctypesgen.parser.lex import TOKEN from ctypesgen.parser import cgrammar tokens = cgrammar.tokens keywords = cgrammar.keywords states = [("DEFINE", "exclusive"), ("PRAGMA", "exclusive")] _B_ = r"[0-1]" _O_ = r"[0-7]" _D_ = r"[0-9]" _NZ_ = r"[1-9]" _L_ = r"[a-zA-Z_]" _A_ = r"[a-zA-Z_0-9]" _H_ = r"[a-fA-F0-9]" _HP_ = r"0[xX]" _BP_ = r"0[bB]" _E_ = r"([Ee][+-]?" + _D_ + r"+)" _P_ = r"([Pp][+-]?" + _D_ + r"+)" _FS_ = r"(f|F|l|L)" _IS_ = r"(((u|U)(ll|LL|l|L)?)|((ll|LL|l|L)(u|U)?))" _CP_ = r"(u|U|L)" _SP_ = r"(u8|u|U|L)" _ES_ = r"(\\([\'\"\?\\abfnrtv]|[0-7]{1,3}|x[a-fA-F0-9]+))" _WS_ = r"[ \t\v\n\f]" I_CONST_HEX = r"(?P" + _HP_ + _H_ + r"+" + r")" + _IS_ + r"?" I_CONST_DEC = r"(?P" + _NZ_ + _D_ + r"*" + r")" + _IS_ + r"?" I_CONST_OCT = r"0" + r"(?P" + _O_ + r"*)" + _IS_ + r"?" I_CONST_BIN = r"(?P" + _BP_ + _B_ + "*" + r")" + _IS_ + "?" F_CONST_1 = r"(?P" + _D_ + r"+" + r")(?P" + _E_ + r")" + _FS_ + r"?" F_CONST_2 = r"(?P" + _D_ + r"*\." + _D_ + r"+" + r")(?P" + _E_ + r"?)" + _FS_ + r"?" F_CONST_3 = r"(?P" + _D_ + r"+\." + r")(?P" + _E_ + r"?)" + _FS_ + r"?" F_CONST_4 = r"(?P" + _HP_ + _H_ + r"+" + _P_ + r")" + _FS_ + r"?" F_CONST_5 = r"(?P" + _HP_ + _H_ + r"*\." + _H_ + r"+" + _P_ + r")" + _FS_ + r"?" F_CONST_6 = r"(?P" + _HP_ + _H_ + r"+\." + _P_ + r")" + _FS_ + r"?" CHARACTER_CONSTANT = _SP_ + r"?'(?P\\.|[^\\'])+'" IDENTIFIER = _L_ + _A_ + r"*" escape_sequence_start_in_string = r"""(\\[0-9a-zA-Z._~!=&\^\-\\?'"])""" string_char = r"""([^"\\\n]|""" + escape_sequence_start_in_string + ")" STRING_LITERAL = '"' + string_char + '*"' # Process line-number directives from the preprocessor # See https://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html DIRECTIVE = r'\#\s+(?P\d+)\s+"(?P[^"]+)"[ \d]*\n' # -------------------------------------------------------------------------- # Token value types # -------------------------------------------------------------------------- # Numbers represented as int and float types. # For all other tokens, type is just str representation. class StringLiteral(str): def __new__(cls, value): assert value[0] == '"' and value[-1] == '"' # Unescaping probably not perfect but close enough. value = value[1:-1] # .decode('string_escape') return str.__new__(cls, value) # -------------------------------------------------------------------------- # Token declarations # -------------------------------------------------------------------------- # Assignment operators t_ANY_EQUALS = r"=" t_ANY_RIGHT_ASSIGN = r">>=" t_ANY_LEFT_ASSIGN = r"<<=" t_ANY_ADD_ASSIGN = r"\+=" t_ANY_SUB_ASSIGN = r"-=" t_ANY_MUL_ASSIGN = r"\*=" t_ANY_DIV_ASSIGN = r"/=" t_ANY_MOD_ASSIGN = r"%=" t_ANY_AND_ASSIGN = r"&=" t_ANY_XOR_ASSIGN = r"\^=" t_ANY_OR_ASSIGN = r"\|=" # Operators t_ANY_PLUS = r"\+" t_ANY_MINUS = r"-" t_ANY_TIMES = r"\*" t_ANY_DIVIDE = r"/" t_ANY_MOD = r"%" t_ANY_AND = r"&" t_ANY_OR = r"\|" t_ANY_NOT = r"~" t_ANY_XOR = r"\^" t_ANY_RIGHT_OP = r">>" t_ANY_LEFT_OP = r"<<" t_ANY_INC_OP = r"\+\+" t_ANY_DEC_OP = r"--" t_ANY_PTR_OP = r"->" t_ANY_AND_OP = r"&&" t_ANY_OR_OP = r"\|\|" t_ANY_LE_OP = r"<=" t_ANY_GE_OP = r">=" t_ANY_EQ_OP = r"==" t_ANY_NE_OP = r"!=" t_ANY_LNOT = r"!" t_ANY_LT = r"<" t_ANY_GT = r">" t_ANY_CONDOP = r"\?" # Delimiters t_ANY_PERIOD = r"\." t_ANY_LPAREN = r"\(" t_ANY_RPAREN = r"\)" t_ANY_ELLIPSIS = r"\.\.\." t_ANY_LBRACKET = r"\[" t_ANY_RBRACKET = r"\]" t_ANY_LBRACE = r"\{" t_ANY_RBRACE = r"\}" t_ANY_COMMA = r"," t_ANY_SEMI = r";" t_ANY_COLON = r":" @TOKEN(DIRECTIVE) def t_ANY_directive(t): m = t.lexer.lexmatch t.lexer.filename = m.group("filename") t.lexer.lineno = int(m.group("lineno")) return None @TOKEN(F_CONST_1) def t_ANY_f_const_1(t): t.type = "F_CONST_1" m = t.lexer.lexmatch sig = m.group("sig") exp = m.group("exp") t.value = sig + exp return t @TOKEN(F_CONST_2) def t_ANY_f_const_2(t): t.type = "F_CONST_2" m = t.lexer.lexmatch sig = m.group("sig") exp = m.group("exp") t.value = sig + exp return t @TOKEN(F_CONST_3) def t_ANY_f_const_3(t): t.type = "F_CONST_3" m = t.lexer.lexmatch sig = m.group("sig") exp = m.group("exp") t.value = sig + exp return t @TOKEN(F_CONST_4) def t_ANY_f_const_4(t): t.type = "F_CONST_4" m = t.lexer.lexmatch t.value = 'float.fromhex("' + m.group("hex") + '")' return t @TOKEN(F_CONST_5) def t_ANY_f_const_5(t): t.type = "F_CONST_5" m = t.lexer.lexmatch t.value = 'float.fromhex("' + m.group("hex") + '")' return t @TOKEN(F_CONST_6) def t_ANY_f_const_6(t): t.type = "F_CONST_6" m = t.lexer.lexmatch t.value = 'float.fromhex("' + m.group("hex") + '")' return t @TOKEN(I_CONST_BIN) def t_ANY_i_const_bin(t): t.type = "I_CONST_BIN" m = t.lexer.lexmatch t.value = m.group("p1") return t @TOKEN(I_CONST_HEX) def t_ANY_i_const_hex(t): t.type = "I_CONST_HEX" m = t.lexer.lexmatch t.value = m.group("p1") return t @TOKEN(I_CONST_DEC) def t_ANY_i_const_dec(t): t.type = "I_CONST_DEC" m = t.lexer.lexmatch t.value = m.group("p1") return t @TOKEN(I_CONST_OCT) def t_ANY_i_const_oct(t): t.type = "I_CONST_OCT" m = t.lexer.lexmatch p1 = m.group("p1") if not p1: t.value = "0" else: t.value = "0o" + m.group("p1") return t @TOKEN(CHARACTER_CONSTANT) def t_ANY_character_constant(t): t.type = "CHARACTER_CONSTANT" m = t.lexer.lexmatch p1 = m.group("p1") t.value = p1 return t @TOKEN(STRING_LITERAL) def t_ANY_string_literal(t): t.type = "STRING_LITERAL" t.value = StringLiteral(t.value) return t @TOKEN(IDENTIFIER) def t_INITIAL_identifier(t): t.type = "IDENTIFIER" return t @TOKEN(IDENTIFIER) def t_DEFINE_identifier(t): if t.lexer.next_is_define_name: # This identifier is the name of a macro # We need to look ahead and see if this macro takes parameters or not. if ( t.lexpos + len(t.value) < t.lexer.lexlen and t.lexer.lexdata[t.lexpos + len(t.value)] == "(" ): t.type = "PP_DEFINE_MACRO_NAME" # Look ahead and read macro parameter list lexdata = t.lexer.lexdata pos = t.lexpos + len(t.value) + 1 while lexdata[pos] not in "\n)": pos += 1 params = lexdata[t.lexpos + len(t.value) + 1 : pos] paramlist = [x.strip() for x in params.split(",") if x.strip()] t.lexer.macro_params = paramlist else: t.type = "PP_DEFINE_NAME" t.lexer.next_is_define_name = False elif t.value in t.lexer.macro_params: t.type = "PP_MACRO_PARAM" else: t.type = "IDENTIFIER" return t @TOKEN(r"\n") def t_INITIAL_newline(t): t.lexer.lineno += 1 return None @TOKEN(r"\#undef") def t_INITIAL_pp_undefine(t): t.type = "PP_UNDEFINE" t.lexer.begin("DEFINE") t.lexer.next_is_define_name = True t.lexer.macro_params = set() return t @TOKEN(r"\#define") def t_INITIAL_pp_define(t): t.type = "PP_DEFINE" t.lexer.begin("DEFINE") t.lexer.next_is_define_name = True t.lexer.macro_params = set() return t @TOKEN(r"\#pragma") def t_INITIAL_pragma(t): t.type = "PRAGMA" t.lexer.begin("PRAGMA") return t @TOKEN(r"pack") def t_PRAGMA_pack(t): t.type = "PRAGMA_PACK" return t @TOKEN(r"\n") def t_PRAGMA_newline(t): t.type = "PRAGMA_END" t.lexer.begin("INITIAL") t.lexer.lineno += 1 return t @TOKEN(IDENTIFIER) def t_PRAGMA_identifier(t): t.type = "IDENTIFIER" return t def t_PRAGMA_error(t): t.type = "OTHER" t.value = t.value[0:30] t.lexer.lexpos += 1 # Skip it if it's an error in a #pragma return t @TOKEN(r"\n") def t_DEFINE_newline(t): t.type = "PP_END_DEFINE" t.lexer.begin("INITIAL") t.lexer.lineno += 1 del t.lexer.macro_params # Damage control in case the token immediately after the #define failed # to handle this t.lexer.next_is_define_name = False return t @TOKEN(r"(\#\#)|(\#)") def t_DEFINE_pp_param_op(t): if t.value == "#": t.type = "PP_STRINGIFY" else: t.type = "PP_IDENTIFIER_PASTE" return t def t_INITIAL_error(t): t.type = "OTHER" return t def t_DEFINE_error(t): t.type = "OTHER" t.value = t.value[0] t.lexer.lexpos += 1 # Skip it if it's an error in a #define return t t_ANY_ignore = " \t\v\f\r" ================================================ FILE: ctypesgen/parser/preprocessor.py ================================================ """Preprocess a C source file using gcc and convert the result into a token stream Reference is C99: * http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1124.pdf """ __docformat__ = "restructuredtext" import os import re import sys import subprocess from ctypesgen.parser import pplexer, lex from ctypesgen.parser.lex import LexError IS_WINDOWS = sys.platform.startswith("win") IS_MAC = sys.platform.startswith("darwin") # -------------------------------------------------------------------------- # Lexers # -------------------------------------------------------------------------- class PreprocessorLexer(lex.Lexer): def __init__(self): lex.Lexer.__init__(self) self.filename = "" self.in_define = False def input(self, data, filename=None): if filename: self.filename = filename self.lasttoken = None lex.Lexer.input(self, data) def token(self): result = lex.Lexer.token(self) if result: self.lasttoken = result.type result.filename = self.filename else: self.lasttoken = None return result # -------------------------------------------------------------------------- # Grammars # -------------------------------------------------------------------------- class PreprocessorParser(object): def __init__(self, options, cparser): self.defines = [ "__extension__=", "__const=const", "__asm__(x)=", "__asm(x)=", "CTYPESGEN=1", ] # On macOS, explicitly add these defines to keep from getting syntax # errors in the macOS standard headers. if IS_MAC: self.defines += [ "_Nullable=", "_Nonnull=", ] self.matches = [] self.output = [] optimize = options.optimize_lexer if hasattr(options, "optimize_lexer") else False self.lexer = lex.lex( cls=PreprocessorLexer, optimize=optimize, lextab="lextab", outputdir=os.path.dirname(__file__), module=pplexer, ) self.options = options self.cparser = cparser # An instance of CParser def parse(self, filename): """Parse a file and save its output""" cmd = self.options.cpp # Legacy behaviour is to implicitly undefine '__GNUC__' # Continue doing this, unless user explicitly requested to allow it. if self.options.allow_gnu_c: # New behaviour. No implicit override. # (currently NOT enabled by default yet) pass else: # Legacy behaviour. Add an implicit override. # (currently the default) cmd += " -U __GNUC__" cmd += " -dD" for undefine in self.options.cpp_undefines: cmd += " -U%s" % undefine # This fixes Issue #6 where OS X 10.6+ adds a C extension that breaks # the parser. Blocks shouldn't be needed for ctypesgen support anyway. if IS_MAC: cmd += " -U __BLOCKS__" for path in self.options.include_search_paths: cmd += ' -I"%s"' % path for define in self.defines + self.options.cpp_defines: cmd += ' "-D%s"' % define cmd += ' "' + filename + '"' self.cparser.handle_status(cmd) pp = subprocess.Popen( cmd, shell=True, universal_newlines=False, # binary stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) ppout_data, pperr_data = pp.communicate() try: ppout = ppout_data.decode("utf-8") except UnicodeError: if IS_MAC: ppout = ppout_data.decode("utf-8", errors="replace") else: raise UnicodeError pperr = pperr_data.decode("utf-8") if IS_WINDOWS: ppout = ppout.replace("\r\n", "\n") pperr = pperr.replace("\r\n", "\n") for line in pperr.split("\n"): if line: self.cparser.handle_pp_error(line) # We separate lines to two groups: directives and c-source. Note that # #pragma directives actually belong to the source category for this. # This is necessary because some source files intermix preprocessor # directives with source--this is not tolerated by ctypesgen's single # grammar. # We put all the source lines first, then all the #define lines. source_lines = [] define_lines = [] first_token_reg = re.compile(r"^#\s*([^ ]+)($|\s)") for line in ppout.split("\n"): line += "\n" search = first_token_reg.match(line) hash_token = search.group(1) if search else None if (not hash_token) or hash_token == "pragma": source_lines.append(line) define_lines.append("\n") elif hash_token.isdigit(): # Line number information has to go with both groups source_lines.append(line) define_lines.append(line) else: # hash_token in ("define", "undef"): source_lines.append("\n") define_lines.append(line) text = "".join(source_lines + define_lines) if self.options.save_preprocessed_headers: self.cparser.handle_status( "Saving preprocessed headers to %s." % self.options.save_preprocessed_headers ) try: with open(self.options.save_preprocessed_headers, "w") as f: f.write(text) except IOError: self.cparser.handle_error("Couldn't save headers.") self.lexer.input(text) self.output = [] try: while True: token = self.lexer.token() if token is not None: self.output.append(token) else: break except LexError as e: self.cparser.handle_error("{}; {}".format(e, e.text.partition("\n")[0]), filename, 0) ================================================ FILE: ctypesgen/parser/yacc.py ================================================ # ----------------------------------------------------------------------------- # ply: yacc.py # # Copyright (C) 2001-2018 # David M. Beazley (Dabeaz LLC) # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the David Beazley or Dabeaz LLC may be used to # endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # # This implements an LR parser that is constructed from grammar rules defined # as Python functions. The grammar is specified by supplying the BNF inside # Python documentation strings. The inspiration for this technique was borrowed # from John Aycock's Spark parsing system. PLY might be viewed as cross between # Spark and the GNU bison utility. # # The current implementation is only somewhat object-oriented. The # LR parser itself is defined in terms of an object (which allows multiple # parsers to co-exist). However, most of the variables used during table # construction are defined in terms of global variables. Users shouldn't # notice unless they are trying to define multiple parsers at the same # time using threads (in which case they should have their head examined). # # This implementation supports both SLR and LALR(1) parsing. LALR(1) # support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu), # using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles, # Techniques, and Tools" (The Dragon Book). LALR(1) has since been replaced # by the more efficient DeRemer and Pennello algorithm. # # :::::::: WARNING ::::::: # # Construction of LR parsing tables is fairly complicated and expensive. # To make this module run fast, a *LOT* of work has been put into # optimization---often at the expensive of readability and what might # consider to be good Python "coding style." Modify the code at your # own risk! # ---------------------------------------------------------------------------- import re import types import sys import os.path import inspect import warnings __version__ = '3.11' __tabversion__ = '3.10' #----------------------------------------------------------------------------- # === User configurable parameters === # # Change these to modify the default behavior of yacc (if you wish) #----------------------------------------------------------------------------- yaccdebug = True # Debugging mode. If set, yacc generates a # a 'parser.out' file in the current directory debug_file = 'parser.out' # Default name of the debugging file tab_module = 'parsetab' # Default name of the table module default_lr = 'LALR' # Default LR table generation method error_count = 3 # Number of symbols that must be shifted to leave recovery mode yaccdevel = False # Set to True if developing yacc. This turns off optimized # implementations of certain functions. resultlimit = 40 # Size limit of results when running in debug mode. pickle_protocol = 0 # Protocol to use when writing pickle files # String type-checking compatibility if sys.version_info[0] < 3: string_types = basestring else: string_types = str MAXINT = sys.maxsize # This object is a stand-in for a logging object created by the # logging module. PLY will use this by default to create things # such as the parser.out file. If a user wants more detailed # information, they can create their own logging object and pass # it into PLY. class PlyLogger(object): def __init__(self, f): self.f = f def debug(self, msg, *args, **kwargs): self.f.write((msg % args) + '\n') info = debug def warning(self, msg, *args, **kwargs): self.f.write('WARNING: ' + (msg % args) + '\n') def error(self, msg, *args, **kwargs): self.f.write('ERROR: ' + (msg % args) + '\n') critical = debug # Null logger is used when no output is generated. Does nothing. class NullLogger(object): def __getattribute__(self, name): return self def __call__(self, *args, **kwargs): return self # Exception raised for yacc-related errors class YaccError(Exception): pass # Format the result message that the parser produces when running in debug mode. def format_result(r): repr_str = repr(r) if '\n' in repr_str: repr_str = repr(repr_str) if len(repr_str) > resultlimit: repr_str = repr_str[:resultlimit] + ' ...' result = '<%s @ 0x%x> (%s)' % (type(r).__name__, id(r), repr_str) return result # Format stack entries when the parser is running in debug mode def format_stack_entry(r): repr_str = repr(r) if '\n' in repr_str: repr_str = repr(repr_str) if len(repr_str) < 16: return repr_str else: return '<%s @ 0x%x>' % (type(r).__name__, id(r)) # Panic mode error recovery support. This feature is being reworked--much of the # code here is to offer a deprecation/backwards compatible transition _errok = None _token = None _restart = None _warnmsg = '''PLY: Don't use global functions errok(), token(), and restart() in p_error(). Instead, invoke the methods on the associated parser instance: def p_error(p): ... # Use parser.errok(), parser.token(), parser.restart() ... parser = yacc.yacc() ''' def errok(): warnings.warn(_warnmsg) return _errok() def restart(): warnings.warn(_warnmsg) return _restart() def token(): warnings.warn(_warnmsg) return _token() # Utility function to call the p_error() function with some deprecation hacks def call_errorfunc(errorfunc, token, parser): global _errok, _token, _restart _errok = parser.errok _token = parser.token _restart = parser.restart r = errorfunc(token) try: del _errok, _token, _restart except NameError: pass return r #----------------------------------------------------------------------------- # === LR Parsing Engine === # # The following classes are used for the LR parser itself. These are not # used during table construction and are independent of the actual LR # table generation algorithm #----------------------------------------------------------------------------- # This class is used to hold non-terminal grammar symbols during parsing. # It normally has the following attributes set: # .type = Grammar symbol type # .value = Symbol value # .lineno = Starting line number # .endlineno = Ending line number (optional, set automatically) # .lexpos = Starting lex position # .endlexpos = Ending lex position (optional, set automatically) class YaccSymbol: def __str__(self): return self.type def __repr__(self): return str(self) # This class is a wrapper around the objects actually passed to each # grammar rule. Index lookup and assignment actually assign the # .value attribute of the underlying YaccSymbol object. # The lineno() method returns the line number of a given # item (or 0 if not defined). The linespan() method returns # a tuple of (startline,endline) representing the range of lines # for a symbol. The lexspan() method returns a tuple (lexpos,endlexpos) # representing the range of positional information for a symbol. class YaccProduction: def __init__(self, s, stack=None): self.slice = s self.stack = stack self.lexer = None self.parser = None def __getitem__(self, n): if isinstance(n, slice): return [s.value for s in self.slice[n]] elif n >= 0: return self.slice[n].value else: return self.stack[n].value def __setitem__(self, n, v): self.slice[n].value = v def __getslice__(self, i, j): return [s.value for s in self.slice[i:j]] def __len__(self): return len(self.slice) def lineno(self, n): return getattr(self.slice[n], 'lineno', 0) def set_lineno(self, n, lineno): self.slice[n].lineno = lineno def linespan(self, n): startline = getattr(self.slice[n], 'lineno', 0) endline = getattr(self.slice[n], 'endlineno', startline) return startline, endline def lexpos(self, n): return getattr(self.slice[n], 'lexpos', 0) def set_lexpos(self, n, lexpos): self.slice[n].lexpos = lexpos def lexspan(self, n): startpos = getattr(self.slice[n], 'lexpos', 0) endpos = getattr(self.slice[n], 'endlexpos', startpos) return startpos, endpos def error(self): raise SyntaxError # ----------------------------------------------------------------------------- # == LRParser == # # The LR Parsing engine. # ----------------------------------------------------------------------------- class LRParser: def __init__(self, lrtab, errorf): self.productions = lrtab.lr_productions self.action = lrtab.lr_action self.goto = lrtab.lr_goto self.errorfunc = errorf self.set_defaulted_states() self.errorok = True def errok(self): self.errorok = True def restart(self): del self.statestack[:] del self.symstack[:] sym = YaccSymbol() sym.type = '$end' self.symstack.append(sym) self.statestack.append(0) # Defaulted state support. # This method identifies parser states where there is only one possible reduction action. # For such states, the parser can make a choose to make a rule reduction without consuming # the next look-ahead token. This delayed invocation of the tokenizer can be useful in # certain kinds of advanced parsing situations where the lexer and parser interact with # each other or change states (i.e., manipulation of scope, lexer states, etc.). # # See: http://www.gnu.org/software/bison/manual/html_node/Default-Reductions.html#Default-Reductions def set_defaulted_states(self): self.defaulted_states = {} for state, actions in self.action.items(): rules = list(actions.values()) if len(rules) == 1 and rules[0] < 0: self.defaulted_states[state] = rules[0] def disable_defaulted_states(self): self.defaulted_states = {} def parse(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): if debug or yaccdevel: if isinstance(debug, int): debug = PlyLogger(sys.stderr) return self.parsedebug(input, lexer, debug, tracking, tokenfunc) elif tracking: return self.parseopt(input, lexer, debug, tracking, tokenfunc) else: return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc) # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # parsedebug(). # # This is the debugging enabled version of parse(). All changes made to the # parsing engine should be made here. Optimized versions of this function # are automatically created by the ply/ygen.py script. This script cuts out # sections enclosed in markers such as this: # # #--! DEBUG # statements # #--! DEBUG # # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! def parsedebug(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): #--! parsedebug-start lookahead = None # Current lookahead symbol lookaheadstack = [] # Stack of lookahead symbols actions = self.action # Local reference to action table (to avoid lookup on self.) goto = self.goto # Local reference to goto table (to avoid lookup on self.) prod = self.productions # Local reference to production list (to avoid lookup on self.) defaulted_states = self.defaulted_states # Local reference to defaulted states pslice = YaccProduction(None) # Production object passed to grammar rules errorcount = 0 # Used during error recovery #--! DEBUG debug.info('PLY: PARSE DEBUG START') #--! DEBUG # If no lexer was given, we will try to use the lex module if not lexer: from . import lex lexer = lex.lexer # Set up the lexer and parser objects on pslice pslice.lexer = lexer pslice.parser = self # If input was supplied, pass to lexer if input is not None: lexer.input(input) if tokenfunc is None: # Tokenize function get_token = lexer.token else: get_token = tokenfunc # Set the parser() token method (sometimes used in error recovery) self.token = get_token # Set up the state and symbol stacks statestack = [] # Stack of parsing states self.statestack = statestack symstack = [] # Stack of grammar symbols self.symstack = symstack pslice.stack = symstack # Put in the production errtoken = None # Err token # The start state is assumed to be (0,$end) statestack.append(0) sym = YaccSymbol() sym.type = '$end' symstack.append(sym) state = 0 while True: # Get the next symbol on the input. If a lookahead symbol # is already set, we just use that. Otherwise, we'll pull # the next token off of the lookaheadstack or from the lexer #--! DEBUG debug.debug('') debug.debug('State : %s', state) #--! DEBUG if state not in defaulted_states: if not lookahead: if not lookaheadstack: lookahead = get_token() # Get the next token else: lookahead = lookaheadstack.pop() if not lookahead: lookahead = YaccSymbol() lookahead.type = '$end' # Check the action table ltype = lookahead.type t = actions[state].get(ltype) else: t = defaulted_states[state] #--! DEBUG debug.debug('Defaulted state %s: Reduce using %d', state, -t) #--! DEBUG #--! DEBUG debug.debug('Stack : %s', ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) #--! DEBUG if t is not None: if t > 0: # shift a symbol on the stack statestack.append(t) state = t #--! DEBUG debug.debug('Action : Shift and goto state %s', t) #--! DEBUG symstack.append(lookahead) lookahead = None # Decrease error count on successful shift if errorcount: errorcount -= 1 continue if t < 0: # reduce a symbol on the stack, emit a production p = prod[-t] pname = p.name plen = p.len # Get production function sym = YaccSymbol() sym.type = pname # Production name sym.value = None #--! DEBUG if plen: debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str, '['+','.join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+']', goto[statestack[-1-plen]][pname]) else: debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str, [], goto[statestack[-1]][pname]) #--! DEBUG if plen: targ = symstack[-plen-1:] targ[0] = sym #--! TRACKING if tracking: t1 = targ[1] sym.lineno = t1.lineno sym.lexpos = t1.lexpos sym.filename = getattr(t1, 'filename', "") # t1 = targ[-1] sym.endlineno = getattr(t1, 'endlineno', t1.lineno) sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos) #--! TRACKING # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # The code enclosed in this section is duplicated # below as a performance optimization. Make sure # changes get made in both locations. pslice.slice = targ try: # Call the grammar rule with our special slice object del symstack[-plen:] self.state = state p.callable(pslice) del statestack[-plen:] #--! DEBUG debug.info('Result : %s', format_result(pslice[0])) #--! DEBUG symstack.append(sym) state = goto[statestack[-1]][pname] statestack.append(state) except SyntaxError: # If an error was set. Enter error recovery state lookaheadstack.append(lookahead) # Save the current lookahead token symstack.extend(targ[1:-1]) # Put the production slice back on the stack statestack.pop() # Pop back one state (before the reduce) state = statestack[-1] sym.type = 'error' sym.value = 'error' lookahead = sym errorcount = error_count self.errorok = False continue # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! else: #--! TRACKING if tracking: sym.lineno = lexer.lineno sym.lexpos = lexer.lexpos sym.filename = getattr(lexer, 'filename', "") # #--! TRACKING targ = [sym] # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # The code enclosed in this section is duplicated # above as a performance optimization. Make sure # changes get made in both locations. pslice.slice = targ try: # Call the grammar rule with our special slice object self.state = state p.callable(pslice) #--! DEBUG debug.info('Result : %s', format_result(pslice[0])) #--! DEBUG symstack.append(sym) state = goto[statestack[-1]][pname] statestack.append(state) except SyntaxError: # If an error was set. Enter error recovery state lookaheadstack.append(lookahead) # Save the current lookahead token statestack.pop() # Pop back one state (before the reduce) state = statestack[-1] sym.type = 'error' sym.value = 'error' lookahead = sym errorcount = error_count self.errorok = False continue # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if t == 0: n = symstack[-1] result = getattr(n, 'value', None) #--! DEBUG debug.info('Done : Returning %s', format_result(result)) debug.info('PLY: PARSE DEBUG END') #--! DEBUG return result if t is None: #--! DEBUG debug.error('Error : %s', ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) #--! DEBUG # We have some kind of parsing error here. To handle # this, we are going to push the current token onto # the tokenstack and replace it with an 'error' token. # If there are any synchronization rules, they may # catch it. # # In addition to pushing the error token, we call call # the user defined p_error() function if this is the # first syntax error. This function is only called if # errorcount == 0. if errorcount == 0 or self.errorok: errorcount = error_count self.errorok = False errtoken = lookahead if errtoken.type == '$end': errtoken = None # End of file! if self.errorfunc: if errtoken and not hasattr(errtoken, 'lexer'): errtoken.lexer = lexer self.state = state tok = call_errorfunc(self.errorfunc, errtoken, self) if self.errorok: # User must have done some kind of panic # mode recovery on their own. The # returned token is the next lookahead lookahead = tok errtoken = None continue else: if errtoken: if hasattr(errtoken, 'lineno'): lineno = lookahead.lineno else: lineno = 0 if lineno: sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type)) else: sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type) else: sys.stderr.write('yacc: Parse error in input. EOF\n') return else: errorcount = error_count # case 1: the statestack only has 1 entry on it. If we're in this state, the # entire parse has been rolled back and we're completely hosed. The token is # discarded and we just keep going. if len(statestack) <= 1 and lookahead.type != '$end': lookahead = None errtoken = None state = 0 # Nuke the pushback stack del lookaheadstack[:] continue # case 2: the statestack has a couple of entries on it, but we're # at the end of the file. nuke the top entry and generate an error token # Start nuking entries on the stack if lookahead.type == '$end': # Whoa. We're really hosed here. Bail out return if lookahead.type != 'error': sym = symstack[-1] if sym.type == 'error': # Hmmm. Error is on top of stack, we'll just nuke input # symbol and continue #--! TRACKING if tracking: sym.endlineno = getattr(lookahead, 'lineno', sym.lineno) sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos) #--! TRACKING lookahead = None continue # Create the error symbol for the first time and make it the new lookahead symbol t = YaccSymbol() t.type = 'error' if hasattr(lookahead, 'lineno'): t.lineno = t.endlineno = lookahead.lineno if hasattr(lookahead, 'lexpos'): t.lexpos = t.endlexpos = lookahead.lexpos t.value = lookahead lookaheadstack.append(lookahead) lookahead = t else: sym = symstack.pop() #--! TRACKING if tracking: lookahead.lineno = sym.lineno lookahead.lexpos = sym.lexpos #--! TRACKING statestack.pop() state = statestack[-1] continue # Call an error function here raise RuntimeError('yacc: internal parser error!!!\n') #--! parsedebug-end # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # parseopt(). # # Optimized version of parse() method. DO NOT EDIT THIS CODE DIRECTLY! # This code is automatically generated by the ply/ygen.py script. Make # changes to the parsedebug() method instead. # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! def parseopt(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): #--! parseopt-start lookahead = None # Current lookahead symbol lookaheadstack = [] # Stack of lookahead symbols actions = self.action # Local reference to action table (to avoid lookup on self.) goto = self.goto # Local reference to goto table (to avoid lookup on self.) prod = self.productions # Local reference to production list (to avoid lookup on self.) defaulted_states = self.defaulted_states # Local reference to defaulted states pslice = YaccProduction(None) # Production object passed to grammar rules errorcount = 0 # Used during error recovery # If no lexer was given, we will try to use the lex module if not lexer: from . import lex lexer = lex.lexer # Set up the lexer and parser objects on pslice pslice.lexer = lexer pslice.parser = self # If input was supplied, pass to lexer if input is not None: lexer.input(input) if tokenfunc is None: # Tokenize function get_token = lexer.token else: get_token = tokenfunc # Set the parser() token method (sometimes used in error recovery) self.token = get_token # Set up the state and symbol stacks statestack = [] # Stack of parsing states self.statestack = statestack symstack = [] # Stack of grammar symbols self.symstack = symstack pslice.stack = symstack # Put in the production errtoken = None # Err token # The start state is assumed to be (0,$end) statestack.append(0) sym = YaccSymbol() sym.type = '$end' symstack.append(sym) state = 0 while True: # Get the next symbol on the input. If a lookahead symbol # is already set, we just use that. Otherwise, we'll pull # the next token off of the lookaheadstack or from the lexer if state not in defaulted_states: if not lookahead: if not lookaheadstack: lookahead = get_token() # Get the next token else: lookahead = lookaheadstack.pop() if not lookahead: lookahead = YaccSymbol() lookahead.type = '$end' # Check the action table ltype = lookahead.type t = actions[state].get(ltype) else: t = defaulted_states[state] if t is not None: if t > 0: # shift a symbol on the stack statestack.append(t) state = t symstack.append(lookahead) lookahead = None # Decrease error count on successful shift if errorcount: errorcount -= 1 continue if t < 0: # reduce a symbol on the stack, emit a production p = prod[-t] pname = p.name plen = p.len # Get production function sym = YaccSymbol() sym.type = pname # Production name sym.value = None if plen: targ = symstack[-plen-1:] targ[0] = sym #--! TRACKING if tracking: t1 = targ[1] sym.lineno = t1.lineno sym.lexpos = t1.lexpos sym.filename = getattr(t1, 'filename', "") # t1 = targ[-1] sym.endlineno = getattr(t1, 'endlineno', t1.lineno) sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos) #--! TRACKING # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # The code enclosed in this section is duplicated # below as a performance optimization. Make sure # changes get made in both locations. pslice.slice = targ try: # Call the grammar rule with our special slice object del symstack[-plen:] self.state = state p.callable(pslice) del statestack[-plen:] symstack.append(sym) state = goto[statestack[-1]][pname] statestack.append(state) except SyntaxError: # If an error was set. Enter error recovery state lookaheadstack.append(lookahead) # Save the current lookahead token symstack.extend(targ[1:-1]) # Put the production slice back on the stack statestack.pop() # Pop back one state (before the reduce) state = statestack[-1] sym.type = 'error' sym.value = 'error' lookahead = sym errorcount = error_count self.errorok = False continue # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! else: #--! TRACKING if tracking: sym.lineno = lexer.lineno sym.lexpos = lexer.lexpos sym.filename = getattr(lexer, 'filename', "") # #--! TRACKING targ = [sym] # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # The code enclosed in this section is duplicated # above as a performance optimization. Make sure # changes get made in both locations. pslice.slice = targ try: # Call the grammar rule with our special slice object self.state = state p.callable(pslice) symstack.append(sym) state = goto[statestack[-1]][pname] statestack.append(state) except SyntaxError: # If an error was set. Enter error recovery state lookaheadstack.append(lookahead) # Save the current lookahead token statestack.pop() # Pop back one state (before the reduce) state = statestack[-1] sym.type = 'error' sym.value = 'error' lookahead = sym errorcount = error_count self.errorok = False continue # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if t == 0: n = symstack[-1] result = getattr(n, 'value', None) return result if t is None: # We have some kind of parsing error here. To handle # this, we are going to push the current token onto # the tokenstack and replace it with an 'error' token. # If there are any synchronization rules, they may # catch it. # # In addition to pushing the error token, we call call # the user defined p_error() function if this is the # first syntax error. This function is only called if # errorcount == 0. if errorcount == 0 or self.errorok: errorcount = error_count self.errorok = False errtoken = lookahead if errtoken.type == '$end': errtoken = None # End of file! if self.errorfunc: if errtoken and not hasattr(errtoken, 'lexer'): errtoken.lexer = lexer self.state = state tok = call_errorfunc(self.errorfunc, errtoken, self) if self.errorok: # User must have done some kind of panic # mode recovery on their own. The # returned token is the next lookahead lookahead = tok errtoken = None continue else: if errtoken: if hasattr(errtoken, 'lineno'): lineno = lookahead.lineno else: lineno = 0 if lineno: sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type)) else: sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type) else: sys.stderr.write('yacc: Parse error in input. EOF\n') return else: errorcount = error_count # case 1: the statestack only has 1 entry on it. If we're in this state, the # entire parse has been rolled back and we're completely hosed. The token is # discarded and we just keep going. if len(statestack) <= 1 and lookahead.type != '$end': lookahead = None errtoken = None state = 0 # Nuke the pushback stack del lookaheadstack[:] continue # case 2: the statestack has a couple of entries on it, but we're # at the end of the file. nuke the top entry and generate an error token # Start nuking entries on the stack if lookahead.type == '$end': # Whoa. We're really hosed here. Bail out return if lookahead.type != 'error': sym = symstack[-1] if sym.type == 'error': # Hmmm. Error is on top of stack, we'll just nuke input # symbol and continue #--! TRACKING if tracking: sym.endlineno = getattr(lookahead, 'lineno', sym.lineno) sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos) #--! TRACKING lookahead = None continue # Create the error symbol for the first time and make it the new lookahead symbol t = YaccSymbol() t.type = 'error' if hasattr(lookahead, 'lineno'): t.lineno = t.endlineno = lookahead.lineno if hasattr(lookahead, 'lexpos'): t.lexpos = t.endlexpos = lookahead.lexpos t.value = lookahead lookaheadstack.append(lookahead) lookahead = t else: sym = symstack.pop() #--! TRACKING if tracking: lookahead.lineno = sym.lineno lookahead.lexpos = sym.lexpos #--! TRACKING statestack.pop() state = statestack[-1] continue # Call an error function here raise RuntimeError('yacc: internal parser error!!!\n') #--! parseopt-end # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # parseopt_notrack(). # # Optimized version of parseopt() with line number tracking removed. # DO NOT EDIT THIS CODE DIRECTLY. This code is automatically generated # by the ply/ygen.py script. Make changes to the parsedebug() method instead. # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! def parseopt_notrack(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): #--! parseopt-notrack-start lookahead = None # Current lookahead symbol lookaheadstack = [] # Stack of lookahead symbols actions = self.action # Local reference to action table (to avoid lookup on self.) goto = self.goto # Local reference to goto table (to avoid lookup on self.) prod = self.productions # Local reference to production list (to avoid lookup on self.) defaulted_states = self.defaulted_states # Local reference to defaulted states pslice = YaccProduction(None) # Production object passed to grammar rules errorcount = 0 # Used during error recovery # If no lexer was given, we will try to use the lex module if not lexer: from . import lex lexer = lex.lexer # Set up the lexer and parser objects on pslice pslice.lexer = lexer pslice.parser = self # If input was supplied, pass to lexer if input is not None: lexer.input(input) if tokenfunc is None: # Tokenize function get_token = lexer.token else: get_token = tokenfunc # Set the parser() token method (sometimes used in error recovery) self.token = get_token # Set up the state and symbol stacks statestack = [] # Stack of parsing states self.statestack = statestack symstack = [] # Stack of grammar symbols self.symstack = symstack pslice.stack = symstack # Put in the production errtoken = None # Err token # The start state is assumed to be (0,$end) statestack.append(0) sym = YaccSymbol() sym.type = '$end' symstack.append(sym) state = 0 while True: # Get the next symbol on the input. If a lookahead symbol # is already set, we just use that. Otherwise, we'll pull # the next token off of the lookaheadstack or from the lexer if state not in defaulted_states: if not lookahead: if not lookaheadstack: lookahead = get_token() # Get the next token else: lookahead = lookaheadstack.pop() if not lookahead: lookahead = YaccSymbol() lookahead.type = '$end' # Check the action table ltype = lookahead.type t = actions[state].get(ltype) else: t = defaulted_states[state] if t is not None: if t > 0: # shift a symbol on the stack statestack.append(t) state = t symstack.append(lookahead) lookahead = None # Decrease error count on successful shift if errorcount: errorcount -= 1 continue if t < 0: # reduce a symbol on the stack, emit a production p = prod[-t] pname = p.name plen = p.len # Get production function sym = YaccSymbol() sym.type = pname # Production name sym.value = None if plen: targ = symstack[-plen-1:] targ[0] = sym # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # The code enclosed in this section is duplicated # below as a performance optimization. Make sure # changes get made in both locations. pslice.slice = targ try: # Call the grammar rule with our special slice object del symstack[-plen:] self.state = state p.callable(pslice) del statestack[-plen:] symstack.append(sym) state = goto[statestack[-1]][pname] statestack.append(state) except SyntaxError: # If an error was set. Enter error recovery state lookaheadstack.append(lookahead) # Save the current lookahead token symstack.extend(targ[1:-1]) # Put the production slice back on the stack statestack.pop() # Pop back one state (before the reduce) state = statestack[-1] sym.type = 'error' sym.value = 'error' lookahead = sym errorcount = error_count self.errorok = False continue # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! else: targ = [sym] # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # The code enclosed in this section is duplicated # above as a performance optimization. Make sure # changes get made in both locations. pslice.slice = targ try: # Call the grammar rule with our special slice object self.state = state p.callable(pslice) symstack.append(sym) state = goto[statestack[-1]][pname] statestack.append(state) except SyntaxError: # If an error was set. Enter error recovery state lookaheadstack.append(lookahead) # Save the current lookahead token statestack.pop() # Pop back one state (before the reduce) state = statestack[-1] sym.type = 'error' sym.value = 'error' lookahead = sym errorcount = error_count self.errorok = False continue # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if t == 0: n = symstack[-1] result = getattr(n, 'value', None) return result if t is None: # We have some kind of parsing error here. To handle # this, we are going to push the current token onto # the tokenstack and replace it with an 'error' token. # If there are any synchronization rules, they may # catch it. # # In addition to pushing the error token, we call call # the user defined p_error() function if this is the # first syntax error. This function is only called if # errorcount == 0. if errorcount == 0 or self.errorok: errorcount = error_count self.errorok = False errtoken = lookahead if errtoken.type == '$end': errtoken = None # End of file! if self.errorfunc: if errtoken and not hasattr(errtoken, 'lexer'): errtoken.lexer = lexer self.state = state tok = call_errorfunc(self.errorfunc, errtoken, self) if self.errorok: # User must have done some kind of panic # mode recovery on their own. The # returned token is the next lookahead lookahead = tok errtoken = None continue else: if errtoken: if hasattr(errtoken, 'lineno'): lineno = lookahead.lineno else: lineno = 0 if lineno: sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type)) else: sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type) else: sys.stderr.write('yacc: Parse error in input. EOF\n') return else: errorcount = error_count # case 1: the statestack only has 1 entry on it. If we're in this state, the # entire parse has been rolled back and we're completely hosed. The token is # discarded and we just keep going. if len(statestack) <= 1 and lookahead.type != '$end': lookahead = None errtoken = None state = 0 # Nuke the pushback stack del lookaheadstack[:] continue # case 2: the statestack has a couple of entries on it, but we're # at the end of the file. nuke the top entry and generate an error token # Start nuking entries on the stack if lookahead.type == '$end': # Whoa. We're really hosed here. Bail out return if lookahead.type != 'error': sym = symstack[-1] if sym.type == 'error': # Hmmm. Error is on top of stack, we'll just nuke input # symbol and continue lookahead = None continue # Create the error symbol for the first time and make it the new lookahead symbol t = YaccSymbol() t.type = 'error' if hasattr(lookahead, 'lineno'): t.lineno = t.endlineno = lookahead.lineno if hasattr(lookahead, 'lexpos'): t.lexpos = t.endlexpos = lookahead.lexpos t.value = lookahead lookaheadstack.append(lookahead) lookahead = t else: sym = symstack.pop() statestack.pop() state = statestack[-1] continue # Call an error function here raise RuntimeError('yacc: internal parser error!!!\n') #--! parseopt-notrack-end # ----------------------------------------------------------------------------- # === Grammar Representation === # # The following functions, classes, and variables are used to represent and # manipulate the rules that make up a grammar. # ----------------------------------------------------------------------------- # regex matching identifiers _is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$') # ----------------------------------------------------------------------------- # class Production: # # This class stores the raw information about a single production or grammar rule. # A grammar rule refers to a specification such as this: # # expr : expr PLUS term # # Here are the basic attributes defined on all productions # # name - Name of the production. For example 'expr' # prod - A list of symbols on the right side ['expr','PLUS','term'] # prec - Production precedence level # number - Production number. # func - Function that executes on reduce # file - File where production function is defined # lineno - Line number where production function is defined # # The following attributes are defined or optional. # # len - Length of the production (number of symbols on right hand side) # usyms - Set of unique symbols found in the production # ----------------------------------------------------------------------------- class Production(object): reduced = 0 def __init__(self, number, name, prod, precedence=('right', 0), func=None, file='', line=0): self.name = name self.prod = tuple(prod) self.number = number self.func = func self.callable = None self.file = file self.line = line self.prec = precedence # Internal settings used during table construction self.len = len(self.prod) # Length of the production # Create a list of unique production symbols used in the production self.usyms = [] for s in self.prod: if s not in self.usyms: self.usyms.append(s) # List of all LR items for the production self.lr_items = [] self.lr_next = None # Create a string representation if self.prod: self.str = '%s -> %s' % (self.name, ' '.join(self.prod)) else: self.str = '%s -> ' % self.name def __str__(self): return self.str def __repr__(self): return 'Production(' + str(self) + ')' def __len__(self): return len(self.prod) def __nonzero__(self): return 1 def __getitem__(self, index): return self.prod[index] # Return the nth lr_item from the production (or None if at the end) def lr_item(self, n): if n > len(self.prod): return None p = LRItem(self, n) # Precompute the list of productions immediately following. try: p.lr_after = self.Prodnames[p.prod[n+1]] except (IndexError, KeyError): p.lr_after = [] try: p.lr_before = p.prod[n-1] except IndexError: p.lr_before = None return p # Bind the production function name to a callable def bind(self, pdict): if self.func: self.callable = pdict[self.func] # This class serves as a minimal standin for Production objects when # reading table data from files. It only contains information # actually used by the LR parsing engine, plus some additional # debugging information. class MiniProduction(object): def __init__(self, str, name, len, func, file, line): self.name = name self.len = len self.func = func self.callable = None self.file = file self.line = line self.str = str def __str__(self): return self.str def __repr__(self): return 'MiniProduction(%s)' % self.str # Bind the production function name to a callable def bind(self, pdict): if self.func: self.callable = pdict[self.func] # ----------------------------------------------------------------------------- # class LRItem # # This class represents a specific stage of parsing a production rule. For # example: # # expr : expr . PLUS term # # In the above, the "." represents the current location of the parse. Here # basic attributes: # # name - Name of the production. For example 'expr' # prod - A list of symbols on the right side ['expr','.', 'PLUS','term'] # number - Production number. # # lr_next Next LR item. Example, if we are ' expr -> expr . PLUS term' # then lr_next refers to 'expr -> expr PLUS . term' # lr_index - LR item index (location of the ".") in the prod list. # lookaheads - LALR lookahead symbols for this item # len - Length of the production (number of symbols on right hand side) # lr_after - List of all productions that immediately follow # lr_before - Grammar symbol immediately before # ----------------------------------------------------------------------------- class LRItem(object): def __init__(self, p, n): self.name = p.name self.prod = list(p.prod) self.number = p.number self.lr_index = n self.lookaheads = {} self.prod.insert(n, '.') self.prod = tuple(self.prod) self.len = len(self.prod) self.usyms = p.usyms def __str__(self): if self.prod: s = '%s -> %s' % (self.name, ' '.join(self.prod)) else: s = '%s -> ' % self.name return s def __repr__(self): return 'LRItem(' + str(self) + ')' # ----------------------------------------------------------------------------- # rightmost_terminal() # # Return the rightmost terminal from a list of symbols. Used in add_production() # ----------------------------------------------------------------------------- def rightmost_terminal(symbols, terminals): i = len(symbols) - 1 while i >= 0: if symbols[i] in terminals: return symbols[i] i -= 1 return None # ----------------------------------------------------------------------------- # === GRAMMAR CLASS === # # The following class represents the contents of the specified grammar along # with various computed properties such as first sets, follow sets, LR items, etc. # This data is used for critical parts of the table generation process later. # ----------------------------------------------------------------------------- class GrammarError(YaccError): pass class Grammar(object): def __init__(self, terminals): self.Productions = [None] # A list of all of the productions. The first # entry is always reserved for the purpose of # building an augmented grammar self.Prodnames = {} # A dictionary mapping the names of nonterminals to a list of all # productions of that nonterminal. self.Prodmap = {} # A dictionary that is only used to detect duplicate # productions. self.Terminals = {} # A dictionary mapping the names of terminal symbols to a # list of the rules where they are used. for term in terminals: self.Terminals[term] = [] self.Terminals['error'] = [] self.Nonterminals = {} # A dictionary mapping names of nonterminals to a list # of rule numbers where they are used. self.First = {} # A dictionary of precomputed FIRST(x) symbols self.Follow = {} # A dictionary of precomputed FOLLOW(x) symbols self.Precedence = {} # Precedence rules for each terminal. Contains tuples of the # form ('right',level) or ('nonassoc', level) or ('left',level) self.UsedPrecedence = set() # Precedence rules that were actually used by the grammar. # This is only used to provide error checking and to generate # a warning about unused precedence rules. self.Start = None # Starting symbol for the grammar def __len__(self): return len(self.Productions) def __getitem__(self, index): return self.Productions[index] # ----------------------------------------------------------------------------- # set_precedence() # # Sets the precedence for a given terminal. assoc is the associativity such as # 'left','right', or 'nonassoc'. level is a numeric level. # # ----------------------------------------------------------------------------- def set_precedence(self, term, assoc, level): assert self.Productions == [None], 'Must call set_precedence() before add_production()' if term in self.Precedence: raise GrammarError('Precedence already specified for terminal %r' % term) if assoc not in ['left', 'right', 'nonassoc']: raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'") self.Precedence[term] = (assoc, level) # ----------------------------------------------------------------------------- # add_production() # # Given an action function, this function assembles a production rule and # computes its precedence level. # # The production rule is supplied as a list of symbols. For example, # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and # symbols ['expr','PLUS','term']. # # Precedence is determined by the precedence of the right-most non-terminal # or the precedence of a terminal specified by %prec. # # A variety of error checks are performed to make sure production symbols # are valid and that %prec is used correctly. # ----------------------------------------------------------------------------- def add_production(self, prodname, syms, func=None, file='', line=0): if prodname in self.Terminals: raise GrammarError('%s:%d: Illegal rule name %r. Already defined as a token' % (file, line, prodname)) if prodname == 'error': raise GrammarError('%s:%d: Illegal rule name %r. error is a reserved word' % (file, line, prodname)) if not _is_identifier.match(prodname): raise GrammarError('%s:%d: Illegal rule name %r' % (file, line, prodname)) # Look for literal tokens for n, s in enumerate(syms): if s[0] in "'\"": try: c = eval(s) if (len(c) > 1): raise GrammarError('%s:%d: Literal token %s in rule %r may only be a single character' % (file, line, s, prodname)) if c not in self.Terminals: self.Terminals[c] = [] syms[n] = c continue except SyntaxError: pass if not _is_identifier.match(s) and s != '%prec': raise GrammarError('%s:%d: Illegal name %r in rule %r' % (file, line, s, prodname)) # Determine the precedence level if '%prec' in syms: if syms[-1] == '%prec': raise GrammarError('%s:%d: Syntax error. Nothing follows %%prec' % (file, line)) if syms[-2] != '%prec': raise GrammarError('%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule' % (file, line)) precname = syms[-1] prodprec = self.Precedence.get(precname) if not prodprec: raise GrammarError('%s:%d: Nothing known about the precedence of %r' % (file, line, precname)) else: self.UsedPrecedence.add(precname) del syms[-2:] # Drop %prec from the rule else: # If no %prec, precedence is determined by the rightmost terminal symbol precname = rightmost_terminal(syms, self.Terminals) prodprec = self.Precedence.get(precname, ('right', 0)) # See if the rule is already in the rulemap map = '%s -> %s' % (prodname, syms) if map in self.Prodmap: m = self.Prodmap[map] raise GrammarError('%s:%d: Duplicate rule %s. ' % (file, line, m) + 'Previous definition at %s:%d' % (m.file, m.line)) # From this point on, everything is valid. Create a new Production instance pnumber = len(self.Productions) if prodname not in self.Nonterminals: self.Nonterminals[prodname] = [] # Add the production number to Terminals and Nonterminals for t in syms: if t in self.Terminals: self.Terminals[t].append(pnumber) else: if t not in self.Nonterminals: self.Nonterminals[t] = [] self.Nonterminals[t].append(pnumber) # Create a production and add it to the list of productions p = Production(pnumber, prodname, syms, prodprec, func, file, line) self.Productions.append(p) self.Prodmap[map] = p # Add to the global productions list try: self.Prodnames[prodname].append(p) except KeyError: self.Prodnames[prodname] = [p] # ----------------------------------------------------------------------------- # set_start() # # Sets the starting symbol and creates the augmented grammar. Production # rule 0 is S' -> start where start is the start symbol. # ----------------------------------------------------------------------------- def set_start(self, start=None): if not start: start = self.Productions[1].name if start not in self.Nonterminals: raise GrammarError('start symbol %s undefined' % start) self.Productions[0] = Production(0, "S'", [start]) self.Nonterminals[start].append(0) self.Start = start # ----------------------------------------------------------------------------- # find_unreachable() # # Find all of the nonterminal symbols that can't be reached from the starting # symbol. Returns a list of nonterminals that can't be reached. # ----------------------------------------------------------------------------- def find_unreachable(self): # Mark all symbols that are reachable from a symbol s def mark_reachable_from(s): if s in reachable: return reachable.add(s) for p in self.Prodnames.get(s, []): for r in p.prod: mark_reachable_from(r) reachable = set() mark_reachable_from(self.Productions[0].prod[0]) return [s for s in self.Nonterminals if s not in reachable] # ----------------------------------------------------------------------------- # infinite_cycles() # # This function looks at the various parsing rules and tries to detect # infinite recursion cycles (grammar rules where there is no possible way # to derive a string of only terminals). # ----------------------------------------------------------------------------- def infinite_cycles(self): terminates = {} # Terminals: for t in self.Terminals: terminates[t] = True terminates['$end'] = True # Nonterminals: # Initialize to false: for n in self.Nonterminals: terminates[n] = False # Then propagate termination until no change: while True: some_change = False for (n, pl) in self.Prodnames.items(): # Nonterminal n terminates iff any of its productions terminates. for p in pl: # Production p terminates iff all of its rhs symbols terminate. for s in p.prod: if not terminates[s]: # The symbol s does not terminate, # so production p does not terminate. p_terminates = False break else: # didn't break from the loop, # so every symbol s terminates # so production p terminates. p_terminates = True if p_terminates: # symbol n terminates! if not terminates[n]: terminates[n] = True some_change = True # Don't need to consider any more productions for this n. break if not some_change: break infinite = [] for (s, term) in terminates.items(): if not term: if s not in self.Prodnames and s not in self.Terminals and s != 'error': # s is used-but-not-defined, and we've already warned of that, # so it would be overkill to say that it's also non-terminating. pass else: infinite.append(s) return infinite # ----------------------------------------------------------------------------- # undefined_symbols() # # Find all symbols that were used the grammar, but not defined as tokens or # grammar rules. Returns a list of tuples (sym, prod) where sym in the symbol # and prod is the production where the symbol was used. # ----------------------------------------------------------------------------- def undefined_symbols(self): result = [] for p in self.Productions: if not p: continue for s in p.prod: if s not in self.Prodnames and s not in self.Terminals and s != 'error': result.append((s, p)) return result # ----------------------------------------------------------------------------- # unused_terminals() # # Find all terminals that were defined, but not used by the grammar. Returns # a list of all symbols. # ----------------------------------------------------------------------------- def unused_terminals(self): unused_tok = [] for s, v in self.Terminals.items(): if s != 'error' and not v: unused_tok.append(s) return unused_tok # ------------------------------------------------------------------------------ # unused_rules() # # Find all grammar rules that were defined, but not used (maybe not reachable) # Returns a list of productions. # ------------------------------------------------------------------------------ def unused_rules(self): unused_prod = [] for s, v in self.Nonterminals.items(): if not v: p = self.Prodnames[s][0] unused_prod.append(p) return unused_prod # ----------------------------------------------------------------------------- # unused_precedence() # # Returns a list of tuples (term,precedence) corresponding to precedence # rules that were never used by the grammar. term is the name of the terminal # on which precedence was applied and precedence is a string such as 'left' or # 'right' corresponding to the type of precedence. # ----------------------------------------------------------------------------- def unused_precedence(self): unused = [] for termname in self.Precedence: if not (termname in self.Terminals or termname in self.UsedPrecedence): unused.append((termname, self.Precedence[termname][0])) return unused # ------------------------------------------------------------------------- # _first() # # Compute the value of FIRST1(beta) where beta is a tuple of symbols. # # During execution of compute_first1, the result may be incomplete. # Afterward (e.g., when called from compute_follow()), it will be complete. # ------------------------------------------------------------------------- def _first(self, beta): # We are computing First(x1,x2,x3,...,xn) result = [] for x in beta: x_produces_empty = False # Add all the non- symbols of First[x] to the result. for f in self.First[x]: if f == '': x_produces_empty = True else: if f not in result: result.append(f) if x_produces_empty: # We have to consider the next x in beta, # i.e. stay in the loop. pass else: # We don't have to consider any further symbols in beta. break else: # There was no 'break' from the loop, # so x_produces_empty was true for all x in beta, # so beta produces empty as well. result.append('') return result # ------------------------------------------------------------------------- # compute_first() # # Compute the value of FIRST1(X) for all symbols # ------------------------------------------------------------------------- def compute_first(self): if self.First: return self.First # Terminals: for t in self.Terminals: self.First[t] = [t] self.First['$end'] = ['$end'] # Nonterminals: # Initialize to the empty set: for n in self.Nonterminals: self.First[n] = [] # Then propagate symbols until no change: while True: some_change = False for n in self.Nonterminals: for p in self.Prodnames[n]: for f in self._first(p.prod): if f not in self.First[n]: self.First[n].append(f) some_change = True if not some_change: break return self.First # --------------------------------------------------------------------- # compute_follow() # # Computes all of the follow sets for every non-terminal symbol. The # follow set is the set of all symbols that might follow a given # non-terminal. See the Dragon book, 2nd Ed. p. 189. # --------------------------------------------------------------------- def compute_follow(self, start=None): # If already computed, return the result if self.Follow: return self.Follow # If first sets not computed yet, do that first. if not self.First: self.compute_first() # Add '$end' to the follow list of the start symbol for k in self.Nonterminals: self.Follow[k] = [] if not start: start = self.Productions[1].name self.Follow[start] = ['$end'] while True: didadd = False for p in self.Productions[1:]: # Here is the production set for i, B in enumerate(p.prod): if B in self.Nonterminals: # Okay. We got a non-terminal in a production fst = self._first(p.prod[i+1:]) hasempty = False for f in fst: if f != '' and f not in self.Follow[B]: self.Follow[B].append(f) didadd = True if f == '': hasempty = True if hasempty or i == (len(p.prod)-1): # Add elements of follow(a) to follow(b) for f in self.Follow[p.name]: if f not in self.Follow[B]: self.Follow[B].append(f) didadd = True if not didadd: break return self.Follow # ----------------------------------------------------------------------------- # build_lritems() # # This function walks the list of productions and builds a complete set of the # LR items. The LR items are stored in two ways: First, they are uniquely # numbered and placed in the list _lritems. Second, a linked list of LR items # is built for each production. For example: # # E -> E PLUS E # # Creates the list # # [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ] # ----------------------------------------------------------------------------- def build_lritems(self): for p in self.Productions: lastlri = p i = 0 lr_items = [] while True: if i > len(p): lri = None else: lri = LRItem(p, i) # Precompute the list of productions immediately following try: lri.lr_after = self.Prodnames[lri.prod[i+1]] except (IndexError, KeyError): lri.lr_after = [] try: lri.lr_before = lri.prod[i-1] except IndexError: lri.lr_before = None lastlri.lr_next = lri if not lri: break lr_items.append(lri) lastlri = lri i += 1 p.lr_items = lr_items # ----------------------------------------------------------------------------- # == Class LRTable == # # This basic class represents a basic table of LR parsing information. # Methods for generating the tables are not defined here. They are defined # in the derived class LRGeneratedTable. # ----------------------------------------------------------------------------- class VersionError(YaccError): pass class LRTable(object): def __init__(self): self.lr_action = None self.lr_goto = None self.lr_productions = None self.lr_method = None def read_table(self, module): if isinstance(module, types.ModuleType): parsetab = module else: exec('import %s' % module) parsetab = sys.modules[module] if parsetab._tabversion != __tabversion__: raise VersionError('yacc table file version is out of date') self.lr_action = parsetab._lr_action self.lr_goto = parsetab._lr_goto self.lr_productions = [] for p in parsetab._lr_productions: self.lr_productions.append(MiniProduction(*p)) self.lr_method = parsetab._lr_method return parsetab._lr_signature def read_pickle(self, filename): try: import cPickle as pickle except ImportError: import pickle if not os.path.exists(filename): raise ImportError in_f = open(filename, 'rb') tabversion = pickle.load(in_f) if tabversion != __tabversion__: raise VersionError('yacc table file version is out of date') self.lr_method = pickle.load(in_f) signature = pickle.load(in_f) self.lr_action = pickle.load(in_f) self.lr_goto = pickle.load(in_f) productions = pickle.load(in_f) self.lr_productions = [] for p in productions: self.lr_productions.append(MiniProduction(*p)) in_f.close() return signature # Bind all production function names to callable objects in pdict def bind_callables(self, pdict): for p in self.lr_productions: p.bind(pdict) # ----------------------------------------------------------------------------- # === LR Generator === # # The following classes and functions are used to generate LR parsing tables on # a grammar. # ----------------------------------------------------------------------------- # ----------------------------------------------------------------------------- # digraph() # traverse() # # The following two functions are used to compute set valued functions # of the form: # # F(x) = F'(x) U U{F(y) | x R y} # # This is used to compute the values of Read() sets as well as FOLLOW sets # in LALR(1) generation. # # Inputs: X - An input set # R - A relation # FP - Set-valued function # ------------------------------------------------------------------------------ def digraph(X, R, FP): N = {} for x in X: N[x] = 0 stack = [] F = {} for x in X: if N[x] == 0: traverse(x, N, stack, F, X, R, FP) return F def traverse(x, N, stack, F, X, R, FP): stack.append(x) d = len(stack) N[x] = d F[x] = FP(x) # F(X) <- F'(x) rel = R(x) # Get y's related to x for y in rel: if N[y] == 0: traverse(y, N, stack, F, X, R, FP) N[x] = min(N[x], N[y]) for a in F.get(y, []): if a not in F[x]: F[x].append(a) if N[x] == d: N[stack[-1]] = MAXINT F[stack[-1]] = F[x] element = stack.pop() while element != x: N[stack[-1]] = MAXINT F[stack[-1]] = F[x] element = stack.pop() class LALRError(YaccError): pass # ----------------------------------------------------------------------------- # == LRGeneratedTable == # # This class implements the LR table generation algorithm. There are no # public methods except for write() # ----------------------------------------------------------------------------- class LRGeneratedTable(LRTable): def __init__(self, grammar, method='LALR', log=None): if method not in ['SLR', 'LALR']: raise LALRError('Unsupported method %s' % method) self.grammar = grammar self.lr_method = method # Set up the logger if not log: log = NullLogger() self.log = log # Internal attributes self.lr_action = {} # Action table self.lr_goto = {} # Goto table self.lr_productions = grammar.Productions # Copy of grammar Production array self.lr_goto_cache = {} # Cache of computed gotos self.lr0_cidhash = {} # Cache of closures self._add_count = 0 # Internal counter used to detect cycles # Diagonistic information filled in by the table generator self.sr_conflict = 0 self.rr_conflict = 0 self.conflicts = [] # List of conflicts self.sr_conflicts = [] self.rr_conflicts = [] # Build the tables self.grammar.build_lritems() self.grammar.compute_first() self.grammar.compute_follow() self.lr_parse_table() # Compute the LR(0) closure operation on I, where I is a set of LR(0) items. def lr0_closure(self, I): self._add_count += 1 # Add everything in I to J J = I[:] didadd = True while didadd: didadd = False for j in J: for x in j.lr_after: if getattr(x, 'lr0_added', 0) == self._add_count: continue # Add B --> .G to J J.append(x.lr_next) x.lr0_added = self._add_count didadd = True return J # Compute the LR(0) goto function goto(I,X) where I is a set # of LR(0) items and X is a grammar symbol. This function is written # in a way that guarantees uniqueness of the generated goto sets # (i.e. the same goto set will never be returned as two different Python # objects). With uniqueness, we can later do fast set comparisons using # id(obj) instead of element-wise comparison. def lr0_goto(self, I, x): # First we look for a previously cached entry g = self.lr_goto_cache.get((id(I), x)) if g: return g # Now we generate the goto set in a way that guarantees uniqueness # of the result s = self.lr_goto_cache.get(x) if not s: s = {} self.lr_goto_cache[x] = s gs = [] for p in I: n = p.lr_next if n and n.lr_before == x: s1 = s.get(id(n)) if not s1: s1 = {} s[id(n)] = s1 gs.append(n) s = s1 g = s.get('$end') if not g: if gs: g = self.lr0_closure(gs) s['$end'] = g else: s['$end'] = gs self.lr_goto_cache[(id(I), x)] = g return g # Compute the LR(0) sets of item function def lr0_items(self): C = [self.lr0_closure([self.grammar.Productions[0].lr_next])] i = 0 for I in C: self.lr0_cidhash[id(I)] = i i += 1 # Loop over the items in C and each grammar symbols i = 0 while i < len(C): I = C[i] i += 1 # Collect all of the symbols that could possibly be in the goto(I,X) sets asyms = {} for ii in I: for s in ii.usyms: asyms[s] = None for x in asyms: g = self.lr0_goto(I, x) if not g or id(g) in self.lr0_cidhash: continue self.lr0_cidhash[id(g)] = len(C) C.append(g) return C # ----------------------------------------------------------------------------- # ==== LALR(1) Parsing ==== # # LALR(1) parsing is almost exactly the same as SLR except that instead of # relying upon Follow() sets when performing reductions, a more selective # lookahead set that incorporates the state of the LR(0) machine is utilized. # Thus, we mainly just have to focus on calculating the lookahead sets. # # The method used here is due to DeRemer and Pennelo (1982). # # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1) # Lookahead Sets", ACM Transactions on Programming Languages and Systems, # Vol. 4, No. 4, Oct. 1982, pp. 615-649 # # Further details can also be found in: # # J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing", # McGraw-Hill Book Company, (1985). # # ----------------------------------------------------------------------------- # ----------------------------------------------------------------------------- # compute_nullable_nonterminals() # # Creates a dictionary containing all of the non-terminals that might produce # an empty production. # ----------------------------------------------------------------------------- def compute_nullable_nonterminals(self): nullable = set() num_nullable = 0 while True: for p in self.grammar.Productions[1:]: if p.len == 0: nullable.add(p.name) continue for t in p.prod: if t not in nullable: break else: nullable.add(p.name) if len(nullable) == num_nullable: break num_nullable = len(nullable) return nullable # ----------------------------------------------------------------------------- # find_nonterminal_trans(C) # # Given a set of LR(0) items, this functions finds all of the non-terminal # transitions. These are transitions in which a dot appears immediately before # a non-terminal. Returns a list of tuples of the form (state,N) where state # is the state number and N is the nonterminal symbol. # # The input C is the set of LR(0) items. # ----------------------------------------------------------------------------- def find_nonterminal_transitions(self, C): trans = [] for stateno, state in enumerate(C): for p in state: if p.lr_index < p.len - 1: t = (stateno, p.prod[p.lr_index+1]) if t[1] in self.grammar.Nonterminals: if t not in trans: trans.append(t) return trans # ----------------------------------------------------------------------------- # dr_relation() # # Computes the DR(p,A) relationships for non-terminal transitions. The input # is a tuple (state,N) where state is a number and N is a nonterminal symbol. # # Returns a list of terminals. # ----------------------------------------------------------------------------- def dr_relation(self, C, trans, nullable): state, N = trans terms = [] g = self.lr0_goto(C[state], N) for p in g: if p.lr_index < p.len - 1: a = p.prod[p.lr_index+1] if a in self.grammar.Terminals: if a not in terms: terms.append(a) # This extra bit is to handle the start state if state == 0 and N == self.grammar.Productions[0].prod[0]: terms.append('$end') return terms # ----------------------------------------------------------------------------- # reads_relation() # # Computes the READS() relation (p,A) READS (t,C). # ----------------------------------------------------------------------------- def reads_relation(self, C, trans, empty): # Look for empty transitions rel = [] state, N = trans g = self.lr0_goto(C[state], N) j = self.lr0_cidhash.get(id(g), -1) for p in g: if p.lr_index < p.len - 1: a = p.prod[p.lr_index + 1] if a in empty: rel.append((j, a)) return rel # ----------------------------------------------------------------------------- # compute_lookback_includes() # # Determines the lookback and includes relations # # LOOKBACK: # # This relation is determined by running the LR(0) state machine forward. # For example, starting with a production "N : . A B C", we run it forward # to obtain "N : A B C ." We then build a relationship between this final # state and the starting state. These relationships are stored in a dictionary # lookdict. # # INCLUDES: # # Computes the INCLUDE() relation (p,A) INCLUDES (p',B). # # This relation is used to determine non-terminal transitions that occur # inside of other non-terminal transition states. (p,A) INCLUDES (p', B) # if the following holds: # # B -> LAT, where T -> epsilon and p' -L-> p # # L is essentially a prefix (which may be empty), T is a suffix that must be # able to derive an empty string. State p' must lead to state p with the string L. # # ----------------------------------------------------------------------------- def compute_lookback_includes(self, C, trans, nullable): lookdict = {} # Dictionary of lookback relations includedict = {} # Dictionary of include relations # Make a dictionary of non-terminal transitions dtrans = {} for t in trans: dtrans[t] = 1 # Loop over all transitions and compute lookbacks and includes for state, N in trans: lookb = [] includes = [] for p in C[state]: if p.name != N: continue # Okay, we have a name match. We now follow the production all the way # through the state machine until we get the . on the right hand side lr_index = p.lr_index j = state while lr_index < p.len - 1: lr_index = lr_index + 1 t = p.prod[lr_index] # Check to see if this symbol and state are a non-terminal transition if (j, t) in dtrans: # Yes. Okay, there is some chance that this is an includes relation # the only way to know for certain is whether the rest of the # production derives empty li = lr_index + 1 while li < p.len: if p.prod[li] in self.grammar.Terminals: break # No forget it if p.prod[li] not in nullable: break li = li + 1 else: # Appears to be a relation between (j,t) and (state,N) includes.append((j, t)) g = self.lr0_goto(C[j], t) # Go to next set j = self.lr0_cidhash.get(id(g), -1) # Go to next state # When we get here, j is the final state, now we have to locate the production for r in C[j]: if r.name != p.name: continue if r.len != p.len: continue i = 0 # This look is comparing a production ". A B C" with "A B C ." while i < r.lr_index: if r.prod[i] != p.prod[i+1]: break i = i + 1 else: lookb.append((j, r)) for i in includes: if i not in includedict: includedict[i] = [] includedict[i].append((state, N)) lookdict[(state, N)] = lookb return lookdict, includedict # ----------------------------------------------------------------------------- # compute_read_sets() # # Given a set of LR(0) items, this function computes the read sets. # # Inputs: C = Set of LR(0) items # ntrans = Set of nonterminal transitions # nullable = Set of empty transitions # # Returns a set containing the read sets # ----------------------------------------------------------------------------- def compute_read_sets(self, C, ntrans, nullable): FP = lambda x: self.dr_relation(C, x, nullable) R = lambda x: self.reads_relation(C, x, nullable) F = digraph(ntrans, R, FP) return F # ----------------------------------------------------------------------------- # compute_follow_sets() # # Given a set of LR(0) items, a set of non-terminal transitions, a readset, # and an include set, this function computes the follow sets # # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)} # # Inputs: # ntrans = Set of nonterminal transitions # readsets = Readset (previously computed) # inclsets = Include sets (previously computed) # # Returns a set containing the follow sets # ----------------------------------------------------------------------------- def compute_follow_sets(self, ntrans, readsets, inclsets): FP = lambda x: readsets[x] R = lambda x: inclsets.get(x, []) F = digraph(ntrans, R, FP) return F # ----------------------------------------------------------------------------- # add_lookaheads() # # Attaches the lookahead symbols to grammar rules. # # Inputs: lookbacks - Set of lookback relations # followset - Computed follow set # # This function directly attaches the lookaheads to productions contained # in the lookbacks set # ----------------------------------------------------------------------------- def add_lookaheads(self, lookbacks, followset): for trans, lb in lookbacks.items(): # Loop over productions in lookback for state, p in lb: if state not in p.lookaheads: p.lookaheads[state] = [] f = followset.get(trans, []) for a in f: if a not in p.lookaheads[state]: p.lookaheads[state].append(a) # ----------------------------------------------------------------------------- # add_lalr_lookaheads() # # This function does all of the work of adding lookahead information for use # with LALR parsing # ----------------------------------------------------------------------------- def add_lalr_lookaheads(self, C): # Determine all of the nullable nonterminals nullable = self.compute_nullable_nonterminals() # Find all non-terminal transitions trans = self.find_nonterminal_transitions(C) # Compute read sets readsets = self.compute_read_sets(C, trans, nullable) # Compute lookback/includes relations lookd, included = self.compute_lookback_includes(C, trans, nullable) # Compute LALR FOLLOW sets followsets = self.compute_follow_sets(trans, readsets, included) # Add all of the lookaheads self.add_lookaheads(lookd, followsets) # ----------------------------------------------------------------------------- # lr_parse_table() # # This function constructs the parse tables for SLR or LALR # ----------------------------------------------------------------------------- def lr_parse_table(self): Productions = self.grammar.Productions Precedence = self.grammar.Precedence goto = self.lr_goto # Goto array action = self.lr_action # Action array log = self.log # Logger for output actionp = {} # Action production array (temporary) log.info('Parsing method: %s', self.lr_method) # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items # This determines the number of states C = self.lr0_items() if self.lr_method == 'LALR': self.add_lalr_lookaheads(C) # Build the parser table, state by state st = 0 for I in C: # Loop over each production in I actlist = [] # List of actions st_action = {} st_actionp = {} st_goto = {} log.info('') log.info('state %d', st) log.info('') for p in I: log.info(' (%d) %s', p.number, p) log.info('') for p in I: if p.len == p.lr_index + 1: if p.name == "S'": # Start symbol. Accept! st_action['$end'] = 0 st_actionp['$end'] = p else: # We are at the end of a production. Reduce! if self.lr_method == 'LALR': laheads = p.lookaheads[st] else: laheads = self.grammar.Follow[p.name] for a in laheads: actlist.append((a, p, 'reduce using rule %d (%s)' % (p.number, p))) r = st_action.get(a) if r is not None: # Whoa. Have a shift/reduce or reduce/reduce conflict if r > 0: # Need to decide on shift or reduce here # By default we favor shifting. Need to add # some precedence rules here. # Shift precedence comes from the token sprec, slevel = Precedence.get(a, ('right', 0)) # Reduce precedence comes from rule being reduced (p) rprec, rlevel = Productions[p.number].prec if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')): # We really need to reduce here. st_action[a] = -p.number st_actionp[a] = p if not slevel and not rlevel: log.info(' ! shift/reduce conflict for %s resolved as reduce', a) self.sr_conflicts.append((st, a, 'reduce')) Productions[p.number].reduced += 1 elif (slevel == rlevel) and (rprec == 'nonassoc'): st_action[a] = None else: # Hmmm. Guess we'll keep the shift if not rlevel: log.info(' ! shift/reduce conflict for %s resolved as shift', a) self.sr_conflicts.append((st, a, 'shift')) elif r < 0: # Reduce/reduce conflict. In this case, we favor the rule # that was defined first in the grammar file oldp = Productions[-r] pp = Productions[p.number] if oldp.line > pp.line: st_action[a] = -p.number st_actionp[a] = p chosenp, rejectp = pp, oldp Productions[p.number].reduced += 1 Productions[oldp.number].reduced -= 1 else: chosenp, rejectp = oldp, pp self.rr_conflicts.append((st, chosenp, rejectp)) log.info(' ! reduce/reduce conflict for %s resolved using rule %d (%s)', a, st_actionp[a].number, st_actionp[a]) else: raise LALRError('Unknown conflict in state %d' % st) else: st_action[a] = -p.number st_actionp[a] = p Productions[p.number].reduced += 1 else: i = p.lr_index a = p.prod[i+1] # Get symbol right after the "." if a in self.grammar.Terminals: g = self.lr0_goto(I, a) j = self.lr0_cidhash.get(id(g), -1) if j >= 0: # We are in a shift state actlist.append((a, p, 'shift and go to state %d' % j)) r = st_action.get(a) if r is not None: # Whoa have a shift/reduce or shift/shift conflict if r > 0: if r != j: raise LALRError('Shift/shift conflict in state %d' % st) elif r < 0: # Do a precedence check. # - if precedence of reduce rule is higher, we reduce. # - if precedence of reduce is same and left assoc, we reduce. # - otherwise we shift # Shift precedence comes from the token sprec, slevel = Precedence.get(a, ('right', 0)) # Reduce precedence comes from the rule that could have been reduced rprec, rlevel = Productions[st_actionp[a].number].prec if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')): # We decide to shift here... highest precedence to shift Productions[st_actionp[a].number].reduced -= 1 st_action[a] = j st_actionp[a] = p if not rlevel: log.info(' ! shift/reduce conflict for %s resolved as shift', a) self.sr_conflicts.append((st, a, 'shift')) elif (slevel == rlevel) and (rprec == 'nonassoc'): st_action[a] = None else: # Hmmm. Guess we'll keep the reduce if not slevel and not rlevel: log.info(' ! shift/reduce conflict for %s resolved as reduce', a) self.sr_conflicts.append((st, a, 'reduce')) else: raise LALRError('Unknown conflict in state %d' % st) else: st_action[a] = j st_actionp[a] = p # Print the actions associated with each terminal _actprint = {} for a, p, m in actlist: if a in st_action: if p is st_actionp[a]: log.info(' %-15s %s', a, m) _actprint[(a, m)] = 1 log.info('') # Print the actions that were not used. (debugging) not_used = 0 for a, p, m in actlist: if a in st_action: if p is not st_actionp[a]: if not (a, m) in _actprint: log.debug(' ! %-15s [ %s ]', a, m) not_used = 1 _actprint[(a, m)] = 1 if not_used: log.debug('') # Construct the goto table for this state nkeys = {} for ii in I: for s in ii.usyms: if s in self.grammar.Nonterminals: nkeys[s] = None for n in nkeys: g = self.lr0_goto(I, n) j = self.lr0_cidhash.get(id(g), -1) if j >= 0: st_goto[n] = j log.info(' %-30s shift and go to state %d', n, j) action[st] = st_action actionp[st] = st_actionp goto[st] = st_goto st += 1 # ----------------------------------------------------------------------------- # write() # # This function writes the LR parsing tables to a file # ----------------------------------------------------------------------------- def write_table(self, tabmodule, outputdir='', signature=''): if isinstance(tabmodule, types.ModuleType): raise IOError("Won't overwrite existing tabmodule") basemodulename = tabmodule.split('.')[-1] filename = os.path.join(outputdir, basemodulename) + '.py' try: f = open(filename, 'w') f.write(''' # %s # This file is automatically generated. Do not edit. # pylint: disable=W,C,R _tabversion = %r _lr_method = %r _lr_signature = %r ''' % (os.path.basename(filename), __tabversion__, self.lr_method, signature)) # Change smaller to 0 to go back to original tables smaller = 1 # Factor out names to try and make smaller if smaller: items = {} for s, nd in self.lr_action.items(): for name, v in nd.items(): i = items.get(name) if not i: i = ([], []) items[name] = i i[0].append(s) i[1].append(v) f.write('\n_lr_action_items = {') for k, v in items.items(): f.write('%r:([' % k) for i in v[0]: f.write('%r,' % i) f.write('],[') for i in v[1]: f.write('%r,' % i) f.write(']),') f.write('}\n') f.write(''' _lr_action = {} for _k, _v in _lr_action_items.items(): for _x,_y in zip(_v[0],_v[1]): if not _x in _lr_action: _lr_action[_x] = {} _lr_action[_x][_k] = _y del _lr_action_items ''') else: f.write('\n_lr_action = { ') for k, v in self.lr_action.items(): f.write('(%r,%r):%r,' % (k[0], k[1], v)) f.write('}\n') if smaller: # Factor out names to try and make smaller items = {} for s, nd in self.lr_goto.items(): for name, v in nd.items(): i = items.get(name) if not i: i = ([], []) items[name] = i i[0].append(s) i[1].append(v) f.write('\n_lr_goto_items = {') for k, v in items.items(): f.write('%r:([' % k) for i in v[0]: f.write('%r,' % i) f.write('],[') for i in v[1]: f.write('%r,' % i) f.write(']),') f.write('}\n') f.write(''' _lr_goto = {} for _k, _v in _lr_goto_items.items(): for _x, _y in zip(_v[0], _v[1]): if not _x in _lr_goto: _lr_goto[_x] = {} _lr_goto[_x][_k] = _y del _lr_goto_items ''') else: f.write('\n_lr_goto = { ') for k, v in self.lr_goto.items(): f.write('(%r,%r):%r,' % (k[0], k[1], v)) f.write('}\n') # Write production table f.write('_lr_productions = [\n') for p in self.lr_productions: if p.func: f.write(' (%r,%r,%d,%r,%r,%d),\n' % (p.str, p.name, p.len, p.func, os.path.basename(p.file), p.line)) else: f.write(' (%r,%r,%d,None,None,None),\n' % (str(p), p.name, p.len)) f.write(']\n') f.close() except IOError as e: raise # ----------------------------------------------------------------------------- # pickle_table() # # This function pickles the LR parsing tables to a supplied file object # ----------------------------------------------------------------------------- def pickle_table(self, filename, signature=''): try: import cPickle as pickle except ImportError: import pickle with open(filename, 'wb') as outf: pickle.dump(__tabversion__, outf, pickle_protocol) pickle.dump(self.lr_method, outf, pickle_protocol) pickle.dump(signature, outf, pickle_protocol) pickle.dump(self.lr_action, outf, pickle_protocol) pickle.dump(self.lr_goto, outf, pickle_protocol) outp = [] for p in self.lr_productions: if p.func: outp.append((p.str, p.name, p.len, p.func, os.path.basename(p.file), p.line)) else: outp.append((str(p), p.name, p.len, None, None, None)) pickle.dump(outp, outf, pickle_protocol) # ----------------------------------------------------------------------------- # === INTROSPECTION === # # The following functions and classes are used to implement the PLY # introspection features followed by the yacc() function itself. # ----------------------------------------------------------------------------- # ----------------------------------------------------------------------------- # get_caller_module_dict() # # This function returns a dictionary containing all of the symbols defined within # a caller further down the call stack. This is used to get the environment # associated with the yacc() call if none was provided. # ----------------------------------------------------------------------------- def get_caller_module_dict(levels): f = sys._getframe(levels) ldict = f.f_globals.copy() if f.f_globals != f.f_locals: ldict.update(f.f_locals) return ldict # ----------------------------------------------------------------------------- # parse_grammar() # # This takes a raw grammar rule string and parses it into production data # ----------------------------------------------------------------------------- def parse_grammar(doc, file, line): grammar = [] # Split the doc string into lines pstrings = doc.splitlines() lastp = None dline = line for ps in pstrings: dline += 1 p = ps.split() if not p: continue try: if p[0] == '|': # This is a continuation of a previous rule if not lastp: raise SyntaxError("%s:%d: Misplaced '|'" % (file, dline)) prodname = lastp syms = p[1:] else: prodname = p[0] lastp = prodname syms = p[2:] assign = p[1] if assign != ':' and assign != '::=': raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file, dline)) grammar.append((file, dline, prodname, syms)) except SyntaxError: raise except Exception: raise SyntaxError('%s:%d: Syntax error in rule %r' % (file, dline, ps.strip())) return grammar # ----------------------------------------------------------------------------- # ParserReflect() # # This class represents information extracted for building a parser including # start symbol, error function, tokens, precedence list, action functions, # etc. # ----------------------------------------------------------------------------- class ParserReflect(object): def __init__(self, pdict, log=None): self.pdict = pdict self.start = None self.error_func = None self.tokens = None self.modules = set() self.grammar = [] self.error = False if log is None: self.log = PlyLogger(sys.stderr) else: self.log = log # Get all of the basic information def get_all(self): self.get_start() self.get_error_func() self.get_tokens() self.get_precedence() self.get_pfunctions() # Validate all of the information def validate_all(self): self.validate_start() self.validate_error_func() self.validate_tokens() self.validate_precedence() self.validate_pfunctions() self.validate_modules() return self.error # Compute a signature over the grammar def signature(self): parts = [] try: if self.start: parts.append(self.start) if self.prec: parts.append(''.join([''.join(p) for p in self.prec])) if self.tokens: parts.append(' '.join(self.tokens)) for f in self.pfuncs: if f[3]: parts.append(f[3]) except (TypeError, ValueError): pass return ''.join(parts) # ----------------------------------------------------------------------------- # validate_modules() # # This method checks to see if there are duplicated p_rulename() functions # in the parser module file. Without this function, it is really easy for # users to make mistakes by cutting and pasting code fragments (and it's a real # bugger to try and figure out why the resulting parser doesn't work). Therefore, # we just do a little regular expression pattern matching of def statements # to try and detect duplicates. # ----------------------------------------------------------------------------- def validate_modules(self): # Match def p_funcname( fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(') for module in self.modules: try: lines, linen = inspect.getsourcelines(module) except IOError: continue counthash = {} for linen, line in enumerate(lines): linen += 1 m = fre.match(line) if m: name = m.group(1) prev = counthash.get(name) if not prev: counthash[name] = linen else: filename = inspect.getsourcefile(module) self.log.warning('%s:%d: Function %s redefined. Previously defined on line %d', filename, linen, name, prev) # Get the start symbol def get_start(self): self.start = self.pdict.get('start') # Validate the start symbol def validate_start(self): if self.start is not None: if not isinstance(self.start, string_types): self.log.error("'start' must be a string") # Look for error handler def get_error_func(self): self.error_func = self.pdict.get('p_error') # Validate the error function def validate_error_func(self): if self.error_func: if isinstance(self.error_func, types.FunctionType): ismethod = 0 elif isinstance(self.error_func, types.MethodType): ismethod = 1 else: self.log.error("'p_error' defined, but is not a function or method") self.error = True return eline = self.error_func.__code__.co_firstlineno efile = self.error_func.__code__.co_filename module = inspect.getmodule(self.error_func) self.modules.add(module) argcount = self.error_func.__code__.co_argcount - ismethod if argcount != 1: self.log.error('%s:%d: p_error() requires 1 argument', efile, eline) self.error = True # Get the tokens map def get_tokens(self): tokens = self.pdict.get('tokens') if not tokens: self.log.error('No token list is defined') self.error = True return if not isinstance(tokens, (list, tuple)): self.log.error('tokens must be a list or tuple') self.error = True return if not tokens: self.log.error('tokens is empty') self.error = True return self.tokens = sorted(tokens) # Validate the tokens def validate_tokens(self): # Validate the tokens. if 'error' in self.tokens: self.log.error("Illegal token name 'error'. Is a reserved word") self.error = True return terminals = set() for n in self.tokens: if n in terminals: self.log.warning('Token %r multiply defined', n) terminals.add(n) # Get the precedence map (if any) def get_precedence(self): self.prec = self.pdict.get('precedence') # Validate and parse the precedence map def validate_precedence(self): preclist = [] if self.prec: if not isinstance(self.prec, (list, tuple)): self.log.error('precedence must be a list or tuple') self.error = True return for level, p in enumerate(self.prec): if not isinstance(p, (list, tuple)): self.log.error('Bad precedence table') self.error = True return if len(p) < 2: self.log.error('Malformed precedence entry %s. Must be (assoc, term, ..., term)', p) self.error = True return assoc = p[0] if not isinstance(assoc, string_types): self.log.error('precedence associativity must be a string') self.error = True return for term in p[1:]: if not isinstance(term, string_types): self.log.error('precedence items must be strings') self.error = True return preclist.append((term, assoc, level+1)) self.preclist = preclist # Get all p_functions from the grammar def get_pfunctions(self): p_functions = [] for name, item in self.pdict.items(): if not name.startswith('p_') or name == 'p_error': continue if isinstance(item, (types.FunctionType, types.MethodType)): line = getattr(item, 'co_firstlineno', item.__code__.co_firstlineno) module = inspect.getmodule(item) p_functions.append((line, module, name, item.__doc__)) # Sort all of the actions by line number; make sure to stringify # modules to make them sortable, since `line` may not uniquely sort all # p functions p_functions.sort(key=lambda p_function: ( p_function[0], str(p_function[1]), p_function[2], p_function[3])) self.pfuncs = p_functions # Validate all of the p_functions def validate_pfunctions(self): grammar = [] # Check for non-empty symbols if len(self.pfuncs) == 0: self.log.error('no rules of the form p_rulename are defined') self.error = True return for line, module, name, doc in self.pfuncs: file = inspect.getsourcefile(module) func = self.pdict[name] if isinstance(func, types.MethodType): reqargs = 2 else: reqargs = 1 if func.__code__.co_argcount > reqargs: self.log.error('%s:%d: Rule %r has too many arguments', file, line, func.__name__) self.error = True elif func.__code__.co_argcount < reqargs: self.log.error('%s:%d: Rule %r requires an argument', file, line, func.__name__) self.error = True elif not func.__doc__: self.log.warning('%s:%d: No documentation string specified in function %r (ignored)', file, line, func.__name__) else: try: parsed_g = parse_grammar(doc, file, line) for g in parsed_g: grammar.append((name, g)) except SyntaxError as e: self.log.error(str(e)) self.error = True # Looks like a valid grammar rule # Mark the file in which defined. self.modules.add(module) # Secondary validation step that looks for p_ definitions that are not functions # or functions that look like they might be grammar rules. for n, v in self.pdict.items(): if n.startswith('p_') and isinstance(v, (types.FunctionType, types.MethodType)): continue if n.startswith('t_'): continue if n.startswith('p_') and n != 'p_error': self.log.warning('%r not defined as a function', n) if ((isinstance(v, types.FunctionType) and v.__code__.co_argcount == 1) or (isinstance(v, types.MethodType) and v.__func__.__code__.co_argcount == 2)): if v.__doc__: try: doc = v.__doc__.split(' ') if doc[1] == ':': self.log.warning('%s:%d: Possible grammar rule %r defined without p_ prefix', v.__code__.co_filename, v.__code__.co_firstlineno, n) except IndexError: pass self.grammar = grammar # ----------------------------------------------------------------------------- # yacc(module) # # Build a parser # ----------------------------------------------------------------------------- def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None, check_recursion=True, optimize=False, write_tables=True, debugfile=debug_file, outputdir=None, debuglog=None, errorlog=None, picklefile=None): if tabmodule is None: tabmodule = tab_module # Reference to the parsing method of the last built parser global parse # If pickling is enabled, table files are not created if picklefile: write_tables = 0 if errorlog is None: errorlog = PlyLogger(sys.stderr) # Get the module dictionary used for the parser if module: _items = [(k, getattr(module, k)) for k in dir(module)] pdict = dict(_items) # If no __file__ or __package__ attributes are available, try to obtain them # from the __module__ instead if '__file__' not in pdict: pdict['__file__'] = sys.modules[pdict['__module__']].__file__ if '__package__' not in pdict and '__module__' in pdict: if hasattr(sys.modules[pdict['__module__']], '__package__'): pdict['__package__'] = sys.modules[pdict['__module__']].__package__ else: pdict = get_caller_module_dict(2) if outputdir is None: # If no output directory is set, the location of the output files # is determined according to the following rules: # - If tabmodule specifies a package, files go into that package directory # - Otherwise, files go in the same directory as the specifying module if isinstance(tabmodule, types.ModuleType): srcfile = tabmodule.__file__ else: if '.' not in tabmodule: srcfile = pdict['__file__'] else: parts = tabmodule.split('.') pkgname = '.'.join(parts[:-1]) exec('import %s' % pkgname) srcfile = getattr(sys.modules[pkgname], '__file__', '') outputdir = os.path.dirname(srcfile) # Determine if the module is package of a package or not. # If so, fix the tabmodule setting so that tables load correctly pkg = pdict.get('__package__') if pkg and isinstance(tabmodule, str): if '.' not in tabmodule: tabmodule = pkg + '.' + tabmodule # Set start symbol if it's specified directly using an argument if start is not None: pdict['start'] = start # Collect parser information from the dictionary pinfo = ParserReflect(pdict, log=errorlog) pinfo.get_all() if pinfo.error: raise YaccError('Unable to build parser') # Check signature against table files (if any) signature = pinfo.signature() # Read the tables try: lr = LRTable() if picklefile: read_signature = lr.read_pickle(picklefile) else: read_signature = lr.read_table(tabmodule) if optimize or (read_signature == signature): try: lr.bind_callables(pinfo.pdict) parser = LRParser(lr, pinfo.error_func) parse = parser.parse return parser except Exception as e: errorlog.warning('There was a problem loading the table file: %r', e) except VersionError as e: errorlog.warning(str(e)) except ImportError: pass if debuglog is None: if debug: try: debuglog = PlyLogger(open(os.path.join(outputdir, debugfile), 'w')) except IOError as e: errorlog.warning("Couldn't open %r. %s" % (debugfile, e)) debuglog = NullLogger() else: debuglog = NullLogger() debuglog.info('Created by PLY version %s (http://www.dabeaz.com/ply)', __version__) errors = False # Validate the parser information if pinfo.validate_all(): raise YaccError('Unable to build parser') if not pinfo.error_func: errorlog.warning('no p_error() function is defined') # Create a grammar object grammar = Grammar(pinfo.tokens) # Set precedence level for terminals for term, assoc, level in pinfo.preclist: try: grammar.set_precedence(term, assoc, level) except GrammarError as e: errorlog.warning('%s', e) # Add productions to the grammar for funcname, gram in pinfo.grammar: file, line, prodname, syms = gram try: grammar.add_production(prodname, syms, funcname, file, line) except GrammarError as e: errorlog.error('%s', e) errors = True # Set the grammar start symbols try: if start is None: grammar.set_start(pinfo.start) else: grammar.set_start(start) except GrammarError as e: errorlog.error(str(e)) errors = True if errors: raise YaccError('Unable to build parser') # Verify the grammar structure undefined_symbols = grammar.undefined_symbols() for sym, prod in undefined_symbols: errorlog.error('%s:%d: Symbol %r used, but not defined as a token or a rule', prod.file, prod.line, sym) errors = True unused_terminals = grammar.unused_terminals() if unused_terminals: debuglog.info('') debuglog.info('Unused terminals:') debuglog.info('') for term in unused_terminals: errorlog.warning('Token %r defined, but not used', term) debuglog.info(' %s', term) # Print out all productions to the debug log if debug: debuglog.info('') debuglog.info('Grammar') debuglog.info('') for n, p in enumerate(grammar.Productions): debuglog.info('Rule %-5d %s', n, p) # Find unused non-terminals unused_rules = grammar.unused_rules() for prod in unused_rules: errorlog.warning('%s:%d: Rule %r defined, but not used', prod.file, prod.line, prod.name) if len(unused_terminals) == 1: errorlog.warning('There is 1 unused token') if len(unused_terminals) > 1: errorlog.warning('There are %d unused tokens', len(unused_terminals)) if len(unused_rules) == 1: errorlog.warning('There is 1 unused rule') if len(unused_rules) > 1: errorlog.warning('There are %d unused rules', len(unused_rules)) if debug: debuglog.info('') debuglog.info('Terminals, with rules where they appear') debuglog.info('') terms = list(grammar.Terminals) terms.sort() for term in terms: debuglog.info('%-20s : %s', term, ' '.join([str(s) for s in grammar.Terminals[term]])) debuglog.info('') debuglog.info('Nonterminals, with rules where they appear') debuglog.info('') nonterms = list(grammar.Nonterminals) nonterms.sort() for nonterm in nonterms: debuglog.info('%-20s : %s', nonterm, ' '.join([str(s) for s in grammar.Nonterminals[nonterm]])) debuglog.info('') if check_recursion: unreachable = grammar.find_unreachable() for u in unreachable: errorlog.warning('Symbol %r is unreachable', u) infinite = grammar.infinite_cycles() for inf in infinite: errorlog.error('Infinite recursion detected for symbol %r', inf) errors = True unused_prec = grammar.unused_precedence() for term, assoc in unused_prec: errorlog.error('Precedence rule %r defined for unknown symbol %r', assoc, term) errors = True if errors: raise YaccError('Unable to build parser') # Run the LRGeneratedTable on the grammar if debug: errorlog.debug('Generating %s tables', method) lr = LRGeneratedTable(grammar, method, debuglog) if debug: num_sr = len(lr.sr_conflicts) # Report shift/reduce and reduce/reduce conflicts if num_sr == 1: errorlog.warning('1 shift/reduce conflict') elif num_sr > 1: errorlog.warning('%d shift/reduce conflicts', num_sr) num_rr = len(lr.rr_conflicts) if num_rr == 1: errorlog.warning('1 reduce/reduce conflict') elif num_rr > 1: errorlog.warning('%d reduce/reduce conflicts', num_rr) # Write out conflicts to the output file if debug and (lr.sr_conflicts or lr.rr_conflicts): debuglog.warning('') debuglog.warning('Conflicts:') debuglog.warning('') for state, tok, resolution in lr.sr_conflicts: debuglog.warning('shift/reduce conflict for %s in state %d resolved as %s', tok, state, resolution) already_reported = set() for state, rule, rejected in lr.rr_conflicts: if (state, id(rule), id(rejected)) in already_reported: continue debuglog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule) debuglog.warning('rejected rule (%s) in state %d', rejected, state) errorlog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule) errorlog.warning('rejected rule (%s) in state %d', rejected, state) already_reported.add((state, id(rule), id(rejected))) warned_never = [] for state, rule, rejected in lr.rr_conflicts: if not rejected.reduced and (rejected not in warned_never): debuglog.warning('Rule (%s) is never reduced', rejected) errorlog.warning('Rule (%s) is never reduced', rejected) warned_never.append(rejected) # Write the table file if requested if write_tables: try: lr.write_table(tabmodule, outputdir, signature) if tabmodule in sys.modules: del sys.modules[tabmodule] except IOError as e: errorlog.warning("Couldn't create %r. %s" % (tabmodule, e)) # Write a pickled version of the tables if picklefile: try: lr.pickle_table(picklefile, signature) except IOError as e: errorlog.warning("Couldn't create %r. %s" % (picklefile, e)) # Build the parser lr.bind_callables(pinfo.pdict) parser = LRParser(lr, pinfo.error_func) parse = parser.parse return parser ================================================ FILE: ctypesgen/printer_json/__init__.py ================================================ """ This module is the backend to ctypesgen; it contains classes to produce the final .py output files. """ from .printer import WrapperPrinter __all__ = ["WrapperPrinter"] ================================================ FILE: ctypesgen/printer_json/printer.py ================================================ import os import sys import json from ctypesgen.ctypedescs import CtypesBitfield from ctypesgen.messages import status_message # From: # http://stackoverflow.com/questions/1036409/recursively-convert-python-object-graph-to-dictionary def todict(obj, classkey="Klass"): if isinstance(obj, dict): for k in obj.keys(): obj[k] = todict(obj[k], classkey) return obj elif isinstance(obj, str) or isinstance(obj, bytes): # must handle strings before __iter__ test, since they now have # __iter__ in Python3 return obj elif hasattr(obj, "__iter__"): return [todict(v, classkey) for v in obj] elif hasattr(obj, "__dict__"): data = dict( [ (key, todict(value, classkey)) for key, value in obj.__dict__.items() if not callable(value) and not key.startswith("_") ] ) if classkey is not None and hasattr(obj, "__class__"): data[classkey] = obj.__class__.__name__ return data else: return obj class WrapperPrinter: def __init__(self, outpath, options, data): status_message("Writing to %s." % (outpath or "stdout")) self.file = open(outpath, "w") if outpath else sys.stdout self.options = options if self.options.strip_build_path and self.options.strip_build_path[-1] != os.path.sep: self.options.strip_build_path += os.path.sep self.print_group(self.options.libraries, "libraries", self.print_library) method_table = { "function": self.print_function, "macro": self.print_macro, "struct": self.print_struct, "struct-body": self.print_struct_members, "typedef": self.print_typedef, "variable": self.print_variable, "enum": self.print_enum, "constant": self.print_constant, "undef": self.print_undef, } res = [] for kind, desc in data.output_order: if desc.included: item = method_table[kind](desc) if item: res.append(item) self.file.write(json.dumps(res, sort_keys=True, indent=4)) self.file.write("\n") def __del__(self): self.file.close() def print_group(self, list, name, function): if list: return [function(obj) for obj in list] def print_library(self, library): return {"load_library": library} def print_constant(self, constant): return {"type": "constant", "name": constant.name, "value": constant.value.py_string(False)} def print_undef(self, undef): return {"type": "undef", "value": undef.macro.py_string(False)} def print_typedef(self, typedef): return {"type": "typedef", "name": typedef.name, "ctype": todict(typedef.ctype)} def print_struct(self, struct): res = {"type": struct.variety, "name": struct.tag, "attrib": struct.attrib} if not struct.opaque: res["fields"] = [] for name, ctype in struct.members: field = {"name": name, "ctype": todict(ctype)} if isinstance(ctype, CtypesBitfield): field["bitfield"] = ctype.bitfield.py_string(False) res["fields"].append(field) return res def print_struct_members(self, struct): pass def print_enum(self, enum): res = {"type": "enum", "name": enum.tag} if not enum.opaque: res["fields"] = [] for name, ctype in enum.members: field = {"name": name, "ctype": todict(ctype)} res["fields"].append(field) return res def print_function(self, function): res = { "type": "function", "name": function.c_name(), "variadic": function.variadic, "args": todict(function.argtypes), "return": todict(function.restype), "attrib": function.attrib, } if function.source_library: res["source"] = function.source_library return res def print_variable(self, variable): res = {"type": "variable", "ctype": todict(variable.ctype), "name": variable.c_name()} if variable.source_library: res["source"] = variable.source_library return res def print_macro(self, macro): if macro.params: return { "type": "macro_function", "name": macro.name, "args": macro.params, "body": macro.expr.py_string(True), } else: # The macro translator makes heroic efforts but it occasionally fails. # Beware the contents of the value! return {"type": "macro", "name": macro.name, "value": macro.expr.py_string(True)} ================================================ FILE: ctypesgen/printer_python/__init__.py ================================================ """ This module is the backend to ctypesgen; it contains classes to produce the final .py output files. """ from .printer import WrapperPrinter __all__ = ["WrapperPrinter"] ================================================ FILE: ctypesgen/printer_python/defaultheader.py ================================================ r"""Wrapper for %(name)s Generated with: %(argv)s Do not modify this file. """ __docformat__ = "restructuredtext" ================================================ FILE: ctypesgen/printer_python/preamble.py ================================================ import ctypes import sys from ctypes import * # noqa: F401, F403 _int_types = (ctypes.c_int16, ctypes.c_int32) if hasattr(ctypes, "c_int64"): # Some builds of ctypes apparently do not have ctypes.c_int64 # defined; it's a pretty good bet that these builds do not # have 64-bit pointers. _int_types += (ctypes.c_int64,) for t in _int_types: if ctypes.sizeof(t) == ctypes.sizeof(ctypes.c_size_t): c_ptrdiff_t = t del t del _int_types # ~POINTER~ class UserString: def __init__(self, seq): if isinstance(seq, bytes): self.data = seq elif isinstance(seq, UserString): self.data = seq.data[:] else: self.data = str(seq).encode() def __bytes__(self): return self.data def __str__(self): return self.data.decode() def __repr__(self): return repr(self.data) def __int__(self): return int(self.data.decode()) def __long__(self): return int(self.data.decode()) def __float__(self): return float(self.data.decode()) def __complex__(self): return complex(self.data.decode()) def __hash__(self): return hash(self.data) def __le__(self, string): if isinstance(string, UserString): return self.data <= string.data else: return self.data <= string def __lt__(self, string): if isinstance(string, UserString): return self.data < string.data else: return self.data < string def __ge__(self, string): if isinstance(string, UserString): return self.data >= string.data else: return self.data >= string def __gt__(self, string): if isinstance(string, UserString): return self.data > string.data else: return self.data > string def __eq__(self, string): if isinstance(string, UserString): return self.data == string.data else: return self.data == string def __ne__(self, string): if isinstance(string, UserString): return self.data != string.data else: return self.data != string def __contains__(self, char): return char in self.data def __len__(self): return len(self.data) def __getitem__(self, index): return self.__class__(self.data[index]) def __getslice__(self, start, end): start = max(start, 0) end = max(end, 0) return self.__class__(self.data[start:end]) def __add__(self, other): if isinstance(other, UserString): return self.__class__(self.data + other.data) elif isinstance(other, bytes): return self.__class__(self.data + other) else: return self.__class__(self.data + str(other).encode()) def __radd__(self, other): if isinstance(other, bytes): return self.__class__(other + self.data) else: return self.__class__(str(other).encode() + self.data) def __mul__(self, n): return self.__class__(self.data * n) __rmul__ = __mul__ def __mod__(self, args): return self.__class__(self.data % args) # the following methods are defined in alphabetical order: def capitalize(self): return self.__class__(self.data.capitalize()) def center(self, width, *args): return self.__class__(self.data.center(width, *args)) def count(self, sub, start=0, end=sys.maxsize): return self.data.count(sub, start, end) def decode(self, encoding=None, errors=None): # XXX improve this? if encoding: if errors: return self.__class__(self.data.decode(encoding, errors)) else: return self.__class__(self.data.decode(encoding)) else: return self.__class__(self.data.decode()) def encode(self, encoding=None, errors=None): # XXX improve this? if encoding: if errors: return self.__class__(self.data.encode(encoding, errors)) else: return self.__class__(self.data.encode(encoding)) else: return self.__class__(self.data.encode()) def endswith(self, suffix, start=0, end=sys.maxsize): return self.data.endswith(suffix, start, end) def expandtabs(self, tabsize=8): return self.__class__(self.data.expandtabs(tabsize)) def find(self, sub, start=0, end=sys.maxsize): return self.data.find(sub, start, end) def index(self, sub, start=0, end=sys.maxsize): return self.data.index(sub, start, end) def isalpha(self): return self.data.isalpha() def isalnum(self): return self.data.isalnum() def isdecimal(self): return self.data.isdecimal() def isdigit(self): return self.data.isdigit() def islower(self): return self.data.islower() def isnumeric(self): return self.data.isnumeric() def isspace(self): return self.data.isspace() def istitle(self): return self.data.istitle() def isupper(self): return self.data.isupper() def join(self, seq): return self.data.join(seq) def ljust(self, width, *args): return self.__class__(self.data.ljust(width, *args)) def lower(self): return self.__class__(self.data.lower()) def lstrip(self, chars=None): return self.__class__(self.data.lstrip(chars)) def partition(self, sep): return self.data.partition(sep) def replace(self, old, new, maxsplit=-1): return self.__class__(self.data.replace(old, new, maxsplit)) def rfind(self, sub, start=0, end=sys.maxsize): return self.data.rfind(sub, start, end) def rindex(self, sub, start=0, end=sys.maxsize): return self.data.rindex(sub, start, end) def rjust(self, width, *args): return self.__class__(self.data.rjust(width, *args)) def rpartition(self, sep): return self.data.rpartition(sep) def rstrip(self, chars=None): return self.__class__(self.data.rstrip(chars)) def split(self, sep=None, maxsplit=-1): return self.data.split(sep, maxsplit) def rsplit(self, sep=None, maxsplit=-1): return self.data.rsplit(sep, maxsplit) def splitlines(self, keepends=0): return self.data.splitlines(keepends) def startswith(self, prefix, start=0, end=sys.maxsize): return self.data.startswith(prefix, start, end) def strip(self, chars=None): return self.__class__(self.data.strip(chars)) def swapcase(self): return self.__class__(self.data.swapcase()) def title(self): return self.__class__(self.data.title()) def translate(self, *args): return self.__class__(self.data.translate(*args)) def upper(self): return self.__class__(self.data.upper()) def zfill(self, width): return self.__class__(self.data.zfill(width)) class MutableString(UserString): """mutable string objects Python strings are immutable objects. This has the advantage, that strings may be used as dictionary keys. If this property isn't needed and you insist on changing string values in place instead, you may cheat and use MutableString. But the purpose of this class is an educational one: to prevent people from inventing their own mutable string class derived from UserString and than forget thereby to remove (override) the __hash__ method inherited from UserString. This would lead to errors that would be very hard to track down. A faster and better solution is to rewrite your program using lists.""" def __init__(self, string=""): self.data = string def __hash__(self): raise TypeError("unhashable type (it is mutable)") def __setitem__(self, index, sub): if index < 0: index += len(self.data) if index < 0 or index >= len(self.data): raise IndexError self.data = self.data[:index] + sub + self.data[index + 1 :] def __delitem__(self, index): if index < 0: index += len(self.data) if index < 0 or index >= len(self.data): raise IndexError self.data = self.data[:index] + self.data[index + 1 :] def __setslice__(self, start, end, sub): start = max(start, 0) end = max(end, 0) if isinstance(sub, UserString): self.data = self.data[:start] + sub.data + self.data[end:] elif isinstance(sub, bytes): self.data = self.data[:start] + sub + self.data[end:] else: self.data = self.data[:start] + str(sub).encode() + self.data[end:] def __delslice__(self, start, end): start = max(start, 0) end = max(end, 0) self.data = self.data[:start] + self.data[end:] def immutable(self): return UserString(self.data) def __iadd__(self, other): if isinstance(other, UserString): self.data += other.data elif isinstance(other, bytes): self.data += other else: self.data += str(other).encode() return self def __imul__(self, n): self.data *= n return self class String(MutableString, ctypes.Union): _fields_ = [("raw", ctypes.POINTER(ctypes.c_char)), ("data", ctypes.c_char_p)] def __init__(self, obj=b""): if isinstance(obj, (bytes, UserString)): self.data = bytes(obj) else: self.raw = obj def __len__(self): return self.data and len(self.data) or 0 def from_param(cls, obj): # Convert None or 0 if obj is None or obj == 0: return cls(ctypes.POINTER(ctypes.c_char)()) # Convert from String elif isinstance(obj, String): return obj # Convert from bytes elif isinstance(obj, bytes): return cls(obj) # Convert from str elif isinstance(obj, str): return cls(obj.encode()) # Convert from c_char_p elif isinstance(obj, ctypes.c_char_p): return obj # Convert from POINTER(ctypes.c_char) elif isinstance(obj, ctypes.POINTER(ctypes.c_char)): return obj # Convert from raw pointer elif isinstance(obj, int): return cls(ctypes.cast(obj, ctypes.POINTER(ctypes.c_char))) # Convert from ctypes.c_char array elif isinstance(obj, ctypes.c_char * len(obj)): return obj # Convert from object else: return String.from_param(obj._as_parameter_) from_param = classmethod(from_param) def ReturnString(obj, func=None, arguments=None): return String.from_param(obj) # As of ctypes 1.0, ctypes does not support custom error-checking # functions on callbacks, nor does it support custom datatypes on # callbacks, so we must ensure that all callbacks return # primitive datatypes. # # Non-primitive return values wrapped with UNCHECKED won't be # typechecked, and will be converted to ctypes.c_void_p. def UNCHECKED(type): if hasattr(type, "_type_") and isinstance(type._type_, str) and type._type_ != "P": return type else: return ctypes.c_void_p # ctypes doesn't have direct support for variadic functions, so we have to write # our own wrapper class class _variadic_function(object): def __init__(self, func, restype, argtypes, errcheck): self.func = func self.func.restype = restype self.argtypes = argtypes if errcheck: self.func.errcheck = errcheck def _as_parameter_(self): # So we can pass this variadic function as a function pointer return self.func def __call__(self, *args): fixed_args = [] i = 0 for argtype in self.argtypes: # Typecheck what we can fixed_args.append(argtype.from_param(args[i])) i += 1 return self.func(*fixed_args + list(args[i:])) def ord_if_char(value): """ Simple helper used for casts to simple builtin types: if the argument is a string type, it will be converted to it's ordinal value. This function will raise an exception if the argument is string with more than one characters. """ return ord(value) if (isinstance(value, bytes) or isinstance(value, str)) else value ================================================ FILE: ctypesgen/printer_python/printer.py ================================================ import os import os.path import sys import time import shutil from ctypesgen.ctypedescs import CtypesBitfield, CtypesStruct from ctypesgen.expressions import ExpressionNode from ctypesgen.messages import error_message, status_message THIS_DIR = os.path.dirname(__file__) CTYPESGEN_DIR = os.path.join(THIS_DIR, os.path.pardir) PREAMBLE_PATH = os.path.join(THIS_DIR, "preamble.py") DEFAULTHEADER_PATH = os.path.join(THIS_DIR, "defaultheader.py") LIBRARYLOADER_PATH = os.path.join(CTYPESGEN_DIR, "libraryloader.py") class WrapperPrinter: def __init__(self, outpath, options, data): status_message("Writing to %s." % (outpath or "stdout")) self.file = open(outpath, "w") if outpath else sys.stdout self.options = options if self.options.strip_build_path and self.options.strip_build_path[-1] != os.path.sep: self.options.strip_build_path += os.path.sep if not self.options.embed_preamble and outpath: self._copy_preamble_loader_files(outpath) self.print_header() self.file.write("\n") self.print_preamble() self.file.write("\n") self.print_loader() self.file.write("\n") self.print_group(self.options.libraries, "libraries", self.print_library) self.print_group(self.options.modules, "modules", self.print_module) method_table = { "function": self.print_function, "macro": self.print_macro, "struct": self.print_struct, "struct-body": self.print_struct_members, "typedef": self.print_typedef, "variable": self.print_variable, "enum": self.print_enum, "constant": self.print_constant, "undef": self.print_undef, } for kind, desc in data.output_order: if desc.included: method_table[kind](desc) self.file.write("\n") self.print_group(self.options.inserted_files, "inserted files", self.insert_file) self.strip_prefixes() def __del__(self): self.file.close() def print_group(self, list, name, function): if list: self.file.write("# Begin %s\n" % name) for obj in list: function(obj) self.file.write("\n") self.file.write("# %d %s\n" % (len(list), name)) self.file.write("# End %s\n" % name) else: self.file.write("# No %s\n" % name) self.file.write("\n") def srcinfo(self, src): if src is None: self.file.write("\n") else: filename, lineno = src if filename in ("", ""): self.file.write("# %s\n" % filename) else: if self.options.strip_build_path and filename.startswith( self.options.strip_build_path ): filename = filename[len(self.options.strip_build_path) :] self.file.write("# %s: %s\n" % (filename, lineno)) def template_subs(self): template_subs = { "date": time.ctime(), "argv": " ".join([x for x in sys.argv if not x.startswith("--strip-build-path")]), "name": os.path.basename(self.options.headers[0]), } for opt, value in self.options.__dict__.items(): if type(value) == str: template_subs[opt] = value elif isinstance(value, (list, tuple)): template_subs[opt] = (os.path.sep).join(value) else: template_subs[opt] = repr(value) return template_subs def print_header(self): template_file = None if self.options.header_template: path = self.options.header_template try: template_file = open(path, "r") except IOError: error_message( 'Cannot load header template from file "%s" ' " - using default template." % path, cls="missing-file", ) if not template_file: template_file = open(DEFAULTHEADER_PATH, "r") template_subs = self.template_subs() self.file.write(template_file.read() % template_subs) template_file.close() def print_preamble(self): self.file.write("# Begin preamble for Python\n\n") if self.options.embed_preamble: with open(PREAMBLE_PATH, "r") as preamble_file: preamble_file_content = preamble_file.read() filecontent = preamble_file_content.replace("# ~POINTER~", "") self.file.write(filecontent) else: self.file.write("from .ctypes_preamble import *\n") self.file.write("from .ctypes_preamble import _variadic_function\n") self.file.write("\n# End preamble\n") def _copy_preamble_loader_files(self, path): if os.path.isfile(path): abspath = os.path.abspath(path) dst = os.path.dirname(abspath) else: error_message( "Cannot copy preamble and loader files", cls="missing-file", ) return c_preamblefile = f"{dst}/ctypes_preamble.py" if os.path.isfile(c_preamblefile): return pointer = """def POINTER(obj): p = ctypes.POINTER(obj) # Convert None to a real NULL pointer to work around bugs # in how ctypes handles None on 64-bit platforms if not isinstance(p.from_param, classmethod): def from_param(cls, x): if x is None: return cls() else: return x p.from_param = classmethod(from_param) return p """ with open(PREAMBLE_PATH) as preamble_file: preamble_file_content = preamble_file.read() filecontent = preamble_file_content.replace("# ~POINTER~", pointer) with open(c_preamblefile, "w") as f: f.write(filecontent) shutil.copy(LIBRARYLOADER_PATH, f"{dst}") os.rename(f"{dst}/libraryloader.py", f"{dst}/ctypes_loader.py") def print_loader(self): self.file.write("_libs = {}\n") self.file.write("_libdirs = %s\n\n" % self.options.compile_libdirs) self.file.write("# Begin loader\n\n") if self.options.embed_preamble: with open(LIBRARYLOADER_PATH, "r") as loader_file: self.file.write(loader_file.read()) else: self.file.write("from .ctypes_loader import *\n") self.file.write("\n# End loader\n\n") self.file.write( "add_library_search_dirs([%s])" % ", ".join([repr(d) for d in self.options.runtime_libdirs]) ) self.file.write("\n") def print_library(self, library): self.file.write('_libs["%s"] = load_library("%s")\n' % (library, library)) def print_module(self, module): self.file.write("from %s import *\n" % module) def print_constant(self, constant): self.file.write("%s = %s" % (constant.name, constant.value.py_string(False))) self.srcinfo(constant.src) def print_undef(self, undef): self.srcinfo(undef.src) self.file.write( "# #undef {macro}\n" "try:\n" " del {macro}\n" "except NameError:\n" " pass\n".format(macro=undef.macro.py_string(False)) ) def print_typedef(self, typedef): self.file.write("%s = %s" % (typedef.name, typedef.ctype.py_string())) self.srcinfo(typedef.src) def print_struct(self, struct): self.srcinfo(struct.src) base = {"union": "Union", "struct": "Structure"}[struct.variety] self.file.write("class %s_%s(%s):\n" " pass\n" % (struct.variety, struct.tag, base)) def print_struct_members(self, struct): if struct.opaque: return # is this supposed to be packed? if struct.attrib.get("packed", False): aligned = struct.attrib.get("aligned", [1]) assert len(aligned) == 1, "cgrammar gave more than one arg for aligned attribute" aligned = aligned[0] if isinstance(aligned, ExpressionNode): # TODO: for non-constant expression nodes, this will fail: aligned = aligned.evaluate(None) self.file.write("{}_{}._pack_ = {}\n".format(struct.variety, struct.tag, aligned)) # handle unnamed fields. unnamed_fields = [] names = set([x[0] for x in struct.members]) anon_prefix = "unnamed_" n = 1 for mi in range(len(struct.members)): mem = list(struct.members[mi]) if mem[0] is None: while True: name = "%s%i" % (anon_prefix, n) n += 1 if name not in names: break mem[0] = name names.add(name) if type(mem[1]) is CtypesStruct: unnamed_fields.append(name) struct.members[mi] = mem self.file.write("%s_%s.__slots__ = [\n" % (struct.variety, struct.tag)) for name, ctype in struct.members: self.file.write(" '%s',\n" % name) self.file.write("]\n") if len(unnamed_fields) > 0: self.file.write("%s_%s._anonymous_ = [\n" % (struct.variety, struct.tag)) for name in unnamed_fields: self.file.write(" '%s',\n" % name) self.file.write("]\n") self.file.write("%s_%s._fields_ = [\n" % (struct.variety, struct.tag)) for name, ctype in struct.members: if isinstance(ctype, CtypesBitfield): self.file.write( " ('%s', %s, %s),\n" % (name, ctype.py_string(), ctype.bitfield.py_string(False)) ) else: self.file.write(" ('%s', %s),\n" % (name, ctype.py_string())) self.file.write("]\n") def print_enum(self, enum): self.file.write("enum_%s = c_int" % enum.tag) self.srcinfo(enum.src) # Values of enumerator are output as constants. def print_function(self, function): if function.variadic: self.print_variadic_function(function) else: self.print_fixed_function(function) def print_fixed_function(self, function): self.srcinfo(function.src) CC = "stdcall" if function.attrib.get("stdcall", False) else "cdecl" # If we know what library the function lives in, or we have only a single library, # look there. Otherwise, check all the libraries. use_single_lib = function.source_library or len(self.options.libraries) == 1 if use_single_lib: lib = function.source_library if function.source_library else self.options.libraries[0] self.file.write( 'if _libs["{L}"].has("{CN}", "{CC}"):\n' ' {PN} = _libs["{L}"].get("{CN}", "{CC}")\n'.format( L=lib, CN=function.c_name(), PN=function.py_name(), CC=CC ) ) else: self.file.write( "for _lib in _libs.values():\n" ' if not _lib.has("{CN}", "{CC}"):\n' " continue\n" ' {PN} = _lib.get("{CN}", "{CC}")\n'.format( CN=function.c_name(), PN=function.py_name(), CC=CC ) ) # Argument types self.file.write( " %s.argtypes = [%s]\n" % (function.py_name(), ", ".join([a.py_string() for a in function.argtypes])) ) # Return value if function.restype.py_string() == "String": self.file.write( " if sizeof(c_int) == sizeof(c_void_p):\n" " {PN}.restype = ReturnString\n" " else:\n" " {PN}.restype = {RT}\n" " {PN}.errcheck = ReturnString\n".format( PN=function.py_name(), RT=function.restype.py_string() ) ) else: self.file.write( " %s.restype = %s\n" % (function.py_name(), function.restype.py_string()) ) if function.errcheck: self.file.write( " %s.errcheck = %s\n" % (function.py_name(), function.errcheck.py_string()) ) if not use_single_lib: self.file.write(" break\n") def print_variadic_function(self, function): CC = "stdcall" if function.attrib.get("stdcall", False) else "cdecl" self.srcinfo(function.src) if function.source_library: self.file.write( 'if _libs["{L}"].has("{CN}", "{CC}"):\n' ' _func = _libs["{L}"].get("{CN}", "{CC}")\n' " _restype = {RT}\n" " _errcheck = {E}\n" " _argtypes = [{t0}]\n" " {PN} = _variadic_function(_func,_restype,_argtypes,_errcheck)\n".format( L=function.source_library, CN=function.c_name(), RT=function.restype.py_string(), E=function.errcheck.py_string(), t0=", ".join([a.py_string() for a in function.argtypes]), PN=function.py_name(), CC=CC, ) ) else: self.file.write( "for _lib in _libs.values():\n" ' if _lib.has("{CN}", "{CC}"):\n' ' _func = _lib.get("{CN}", "{CC}")\n' " _restype = {RT}\n" " _errcheck = {E}\n" " _argtypes = [{t0}]\n" " {PN} = _variadic_function(_func,_restype,_argtypes,_errcheck)\n".format( CN=function.c_name(), RT=function.restype.py_string(), E=function.errcheck.py_string(), t0=", ".join([a.py_string() for a in function.argtypes]), PN=function.py_name(), CC=CC, ) ) def print_variable(self, variable): self.srcinfo(variable.src) if variable.source_library: self.file.write( "try:\n" ' {PN} = ({PS}).in_dll(_libs["{L}"], "{CN}")\n' "except:\n" " pass\n".format( PN=variable.py_name(), PS=variable.ctype.py_string(), L=variable.source_library, CN=variable.c_name(), ) ) else: self.file.write( "for _lib in _libs.values():\n" " try:\n" ' {PN} = ({PS}).in_dll(_lib, "{CN}")\n' " break\n" " except:\n" " pass\n".format( PN=variable.py_name(), PS=variable.ctype.py_string(), CN=variable.c_name() ) ) def print_macro(self, macro): # important: must check precisely against None because params may be # an empty list for a func macro if macro.params is None: self.print_simple_macro(macro) else: self.print_func_macro(macro) def print_simple_macro(self, macro): # The macro translator makes heroic efforts but it occasionally fails. # We want to contain the failures as much as possible. # Hence the try statement. self.srcinfo(macro.src) self.file.write( "try:\n" " {MN} = {ME}\n" "except:\n" " pass\n".format(MN=macro.name, ME=macro.expr.py_string(True)) ) def print_func_macro(self, macro): self.srcinfo(macro.src) self.file.write( "def {MN}({MP}):\n" " return {ME}\n".format( MN=macro.name, MP=", ".join(macro.params), ME=macro.expr.py_string(True) ) ) def strip_prefixes(self): if not self.options.strip_prefixes: self.file.write("# No prefix-stripping\n\n") return self.file.write( "# Begin prefix-stripping\n" "\n" "# Strip prefixes from all symbols following regular expression:\n" "# {expr}\n" "\n" "import re as __re_module\n" "\n" "__strip_expr = __re_module.compile('{expr}')\n" "for __k, __v in globals().copy().items():\n" " __m = __strip_expr.match(__k)\n" " if __m:\n" " globals()[__k[__m.end():]] = __v\n" " # remove symbol with prefix(?)\n" " # globals().pop(__k)\n" "del __re_module, __k, __v, __m, __strip_expr\n" "\n" "# End prefix-stripping\n" "\n".format(expr="({})".format("|".join(self.options.strip_prefixes))) ) def insert_file(self, filename): try: inserted_file = open(filename, "r") except IOError: error_message('Cannot open file "%s". Skipped it.' % filename, cls="missing-file") self.file.write( '# Begin "{filename}"\n' "\n{file}\n" '# End "{filename}"\n'.format(filename=filename, file=inserted_file.read()) ) inserted_file.close() ================================================ FILE: ctypesgen/processor/__init__.py ================================================ """ This module contains functions to operate on the DeclarationCollection produced by the parser module and prepare it for output. A convenience_function, process(), calls everything else. """ __all__ = ["process"] from .pipeline import process ================================================ FILE: ctypesgen/processor/dependencies.py ================================================ """ The dependencies module determines which descriptions depend on which other descriptions. """ from ctypesgen.descriptions import MacroDescription, UndefDescription from ctypesgen.ctypedescs import visit_type_and_collect_info def find_dependencies(data, opts): """Visit each description in `data` and figure out which other descriptions it depends on, putting the results in desc.requirements. Also find errors in ctypedecls or expressions attached to the description and transfer them to the description.""" struct_names = {} enum_names = {} typedef_names = {} ident_names = {} # Start the lookup tables with names from imported modules for name in opts.other_known_names: typedef_names[name] = None ident_names[name] = None if name.startswith("struct_") or name.startswith("enum_"): variety = name.split("_")[0] tag = "_".join(name.split("_")[1:]) struct_names[(variety, tag)] = None if name.startswith("enum_"): enum_names[name] = None def depend(desc, nametable, name): """Try to add `name` as a requirement for `desc`, looking `name` up in `nametable`. Returns True if found.""" if name in nametable: requirement = nametable[name] if requirement: desc.add_requirements([requirement]) return True else: return False def co_depend(desc, nametable, name): """ Try to add `name` as a requirement for `desc`, looking `name` up in `nametable`. Also try to add desc as a requirement for `name`. Returns Description of `name` if found. """ requirement = nametable.get(name, None) if requirement is None: return desc.add_requirements([requirement]) requirement.add_requirements([desc]) return requirement def find_dependencies_for(desc, kind): """Find all the descriptions that `desc` depends on and add them as dependencies for `desc`. Also collect error messages regarding `desc` and convert unlocateable descriptions into error messages.""" if kind == "constant": roots = [desc.value] elif kind == "struct": roots = [] elif kind == "struct-body": roots = [desc.ctype] elif kind == "enum": roots = [] elif kind == "typedef": roots = [desc.ctype] elif kind == "function": roots = desc.argtypes + [desc.restype] elif kind == "variable": roots = [desc.ctype] elif kind == "macro": if desc.expr: roots = [desc.expr] else: roots = [] elif kind == "undef": roots = [desc.macro] cstructs, cenums, ctypedefs, errors, identifiers = [], [], [], [], [] for root in roots: s, e, t, errs, i = visit_type_and_collect_info(root) cstructs.extend(s) cenums.extend(e) ctypedefs.extend(t) errors.extend(errs) identifiers.extend(i) unresolvables = [] for cstruct in cstructs: if kind == "struct" and desc.variety == cstruct.variety and desc.tag == cstruct.tag: continue if not depend(desc, struct_names, (cstruct.variety, cstruct.tag)): unresolvables.append('%s "%s"' % (cstruct.variety, cstruct.tag)) for cenum in cenums: if kind == "enum" and desc.tag == cenum.tag: continue if not depend(desc, enum_names, cenum.tag): unresolvables.append('enum "%s"' % cenum.tag) for ctypedef in ctypedefs: if not depend(desc, typedef_names, ctypedef): unresolvables.append('typedef "%s"' % ctypedef) for ident in identifiers: if isinstance(desc, MacroDescription) and desc.params and ident in desc.params: continue elif opts.include_undefs and isinstance(desc, UndefDescription): macro_desc = None if ident == desc.macro.name: macro_desc = co_depend(desc, ident_names, ident) if macro_desc is None or not isinstance(macro_desc, MacroDescription): unresolvables.append('identifier "%s"' % ident) elif not depend(desc, ident_names, ident): unresolvables.append('identifier "%s"' % ident) for u in unresolvables: errors.append(("%s depends on an unknown %s." % (desc.casual_name(), u), None)) for err, cls in errors: err += " %s will not be output" % desc.casual_name() desc.error(err, cls=cls) def add_to_lookup_table(desc, kind): """Add `desc` to the lookup table so that other descriptions that use it can find it.""" if kind == "struct": if (desc.variety, desc.tag) not in struct_names: struct_names[(desc.variety, desc.tag)] = desc if kind == "enum": if desc.tag not in enum_names: enum_names[desc.tag] = desc if kind == "typedef": if desc.name not in typedef_names: typedef_names[desc.name] = desc if kind in ("function", "constant", "variable", "macro"): if desc.name not in ident_names: ident_names[desc.name] = desc # Macros are handled differently from everything else because macros can # call other macros that are referenced after them in the input file, but # no other type of description can look ahead like that. for kind, desc in data.output_order: add_to_lookup_table(desc, kind) if kind != "macro": find_dependencies_for(desc, kind) for kind, desc in data.output_order: if kind == "macro": find_dependencies_for(desc, kind) ================================================ FILE: ctypesgen/processor/operations.py ================================================ """ The operations module contains various functions to process the DescriptionCollection and prepare it for output. ctypesgen.processor.pipeline calls the operations module. """ import re import os import keyword from ctypesgen import libraryloader from ctypesgen.descriptions import ( EnumDescription, StructDescription, TypedefDescription, ) from ctypesgen.messages import warning_message, status_message # Processor functions def automatically_typedef_structs(data, options): """automatically_typedef_structs() aliases "struct_" to "" for every struct and union.""" # XXX Check if it has already been aliased in the C code. for struct in data.structs: if not struct.ctype.anonymous: # Don't alias anonymous structs typedef = TypedefDescription(struct.tag, struct.ctype, src=struct.src) typedef.add_requirements(set([struct])) data.typedefs.append(typedef) data.all.insert(data.all.index(struct) + 1, typedef) data.output_order.append(("typedef", typedef)) def remove_NULL(data, options): """remove_NULL() removes any NULL definitions from the C headers because ctypesgen supplies its own NULL definition.""" for macro in data.macros: if macro.name == "NULL": macro.include_rule = "never" def remove_descriptions_in_system_headers(data, opts): """remove_descriptions_in_system_headers() removes descriptions if they came from files outside of the header files specified from the command line.""" known_headers = [os.path.basename(x) for x in opts.headers] for description in data.all: if description.src is not None: if description.src[0] == "": description.include_rule = "if_needed" elif description.src[0] == "": if not opts.builtin_symbols: description.include_rule = "if_needed" elif os.path.basename(description.src[0]) not in known_headers: if not opts.all_headers: # If something else requires this, include it even though # it is in a system header file. description.include_rule = "if_needed" def remove_macros(data, opts): """remove_macros() removes macros if --no-macros is set.""" if not opts.include_macros: for macro in data.macros: macro.include_rule = "never" def filter_by_regexes_exclude(data, opts): """filter_by_regexes_exclude() uses regular expressions specified by options dictionary to filter symbols.""" if opts.exclude_symbols: expr = re.compile("({})".format("|".join(opts.exclude_symbols))) for object in data.all: if expr.match(object.py_name()): object.include_rule = "never" def filter_by_regexes_include(data, opts): """filter_by_regexes_include() uses regular expressions specified by options dictionary to re-include symbols previously rejected by other operations.""" if opts.include_symbols: expr = re.compile("({})".format("|".join(opts.include_symbols))) for object in data.all: if object.include_rule != "never": if expr.match(object.py_name()): object.include_rule = "yes" def fix_conflicting_names(data, opts): """If any descriptions from the C code would overwrite Python builtins or other important names, fix_conflicting_names() adds underscores to resolve the name conflict.""" # This is the order of priority for names descriptions = ( data.functions + data.variables + data.structs + data.typedefs + data.enums + data.constants + data.macros ) # This dictionary maps names to a string representing where the name # came from. important_names = {} preamble_names = set() preamble_names = preamble_names.union( [ "DarwinLibraryLoader", "LibraryLoader", "LinuxLibraryLoader", "WindowsLibraryLoader", "_WindowsLibrary", "add_library_search_dirs", "_environ_path", "ctypes", "load_library", "loader", "os", "re", "sys", ] ) preamble_names = preamble_names.union( [ "ArgumentError", "CFUNCTYPE", "POINTER", "ReturnString", "String", "Structure", "UNCHECKED", "Union", "UserString", "_variadic_function", "addressof", "c_buffer", "c_byte", "c_char", "c_char_p", "c_double", "c_float", "c_int", "c_int16", "c_int32", "c_int64", "c_int8", "c_long", "c_longlong", "c_ptrdiff_t", "c_short", "c_size_t", "c_ubyte", "c_uint", "c_uint16", "c_uint32", "c_uint64", "c_uint8", "c_ulong", "c_ulonglong", "c_ushort", "c_void", "c_void_p", "c_voidp", "c_wchar", "c_wchar_p", "cast", "ctypes", "os", "pointer", "sizeof", ] ) for name in preamble_names: important_names[name] = "a name needed by ctypes or ctypesgen" for name in dir(__builtins__): important_names[name] = "a Python builtin" for name in opts.other_known_names: important_names[name] = "a name from an included Python module" for name in keyword.kwlist: important_names[name] = "a Python keyword" for description in descriptions: if description.py_name() in important_names: conflict_name = important_names[description.py_name()] original_name = description.casual_name() while description.py_name() in important_names: if isinstance(description, (StructDescription, EnumDescription)): description.tag += "_" else: description.name = "_" + description.name if not description.dependents: description.warning( "%s has been renamed to %s due to a name " "conflict with %s." % (original_name, description.casual_name(), conflict_name), cls="rename", ) else: description.warning( "%s has been renamed to %s due to a name " "conflict with %s. Other objects depend on %s - those " "objects will be skipped." % (original_name, description.casual_name(), conflict_name, original_name), cls="rename", ) for dependent in description.dependents: dependent.include_rule = "never" if description.include_rule == "yes": important_names[description.py_name()] = description.casual_name() # Names of struct members don't conflict with much, but they can conflict # with Python keywords. for struct in data.structs: if not struct.opaque: for i, (name, type) in enumerate(struct.members): if name in keyword.kwlist: struct.members[i] = ("_" + name, type) struct.warning( 'Member "%s" of %s has been renamed to ' '"%s" because it has the same name as a Python ' "keyword." % (name, struct.casual_name(), "_" + name), cls="rename", ) # Macro arguments may be have names that conflict with Python keywords. # In a perfect world, this would simply rename the parameter instead # of throwing an error message. for macro in data.macros: if macro.params: for param in macro.params: if param in keyword.kwlist: macro.error( 'One of the parameters to %s, "%s" has the ' "same name as a Python keyword. %s will be skipped." % (macro.casual_name(), param, macro.casual_name()), cls="name-conflict", ) def find_source_libraries(data, opts): """find_source_libraries() determines which library contains each function and variable.""" all_symbols = data.functions + data.variables for symbol in all_symbols: symbol.source_library = None libraryloader.add_library_search_dirs(opts.compile_libdirs) for library_name in opts.libraries: if opts.no_load_library: status_message("Bypass load_library %s" % library_name) continue try: library = libraryloader.load_library(library_name) except ImportError: warning_message( 'Could not load library "%s". Okay, I\'ll ' "try to load it at runtime instead. " % (library_name), cls="missing-library", ) continue for symbol in all_symbols: if symbol.source_library is None: if hasattr(library, symbol.c_name()): symbol.source_library = library_name ================================================ FILE: ctypesgen/processor/pipeline.py ================================================ """ A brief explanation of the processing steps: 1. The dependencies module builds a dependency graph for the descriptions. 2. Operation functions are called to perform various operations on the descriptions. The operation functions are found in operations.py. 3. If an operation function decides to exclude a description from the output, it sets 'description.include_rule' to "never"; if an operation function decides not to include a description by default, but to allow if required, it sets 'description.include_rule' to "if_needed". 4. If an operation function encounters an error that makes a description unfit for output, it appends a string error message to 'description.errors'. 'description.warnings' is a list of warning messages that will be displayed but will not prevent the description from being output. 5. Based on 'description.include_rule', calculate_final_inclusion() decides which descriptions to include in the output. It sets 'description.included' to True or False. 6. For each description, print_errors_encountered() checks if there are error messages in 'description.errors'. If so, print_errors_encountered() prints the error messages, but only if 'description.included' is True - it doesn't bother the user with error messages regarding descriptions that would not be in the output anyway. It also prints 'description.warnings'. 7. calculate_final_inclusion() is called again to recalculate based on the errors that print_errors_encountered() has flagged. """ from ctypesgen.descriptions import MacroDescription from ctypesgen.messages import ( error_message, status_message, warning_message, ) from ctypesgen.processor.dependencies import find_dependencies from ctypesgen.processor.operations import ( automatically_typedef_structs, filter_by_regexes_exclude, filter_by_regexes_include, find_source_libraries, fix_conflicting_names, remove_descriptions_in_system_headers, remove_macros, remove_NULL, ) def process(data, options): status_message("Processing description list.") find_dependencies(data, options) automatically_typedef_structs(data, options) remove_NULL(data, options) remove_descriptions_in_system_headers(data, options) filter_by_regexes_exclude(data, options) filter_by_regexes_include(data, options) remove_macros(data, options) if options.output_language.startswith("py"): # this function is python specific fix_conflicting_names(data, options) find_source_libraries(data, options) calculate_final_inclusion(data, options) print_errors_encountered(data, options) calculate_final_inclusion(data, options) def calculate_final_inclusion(data, opts): """Calculates which descriptions will be included in the output library. An object with include_rule="never" is never included. An object with include_rule="yes" is included if its requirements can be included. An object with include_rule="if_needed" is included if an object to be included requires it and if its requirements can be included. """ def can_include_desc(desc): if desc.can_include is None: if desc.include_rule == "no": desc.can_include = False elif desc.include_rule == "yes" or desc.include_rule == "if_needed": desc.can_include = True for req in desc.requirements: if not can_include_desc(req): desc.can_include = False return desc.can_include def do_include_desc(desc): if desc.included: return # We've already been here desc.included = True for req in desc.requirements: do_include_desc(req) for desc in data.all: desc.can_include = None # None means "Not Yet Decided" desc.included = False for desc in data.all: if desc.include_rule == "yes": if can_include_desc(desc): do_include_desc(desc) def print_errors_encountered(data, opts): # See descriptions.py for an explanation of the error-handling mechanism for desc in data.all: # If description would not have been included, dont bother user by # printing warnings. if desc.included or opts.show_all_errors: if opts.show_long_errors or len(desc.errors) + len(desc.warnings) <= 2: for error, cls in desc.errors: # Macro errors will always be displayed as warnings. if isinstance(desc, MacroDescription): if opts.show_macro_warnings: warning_message(error, cls) else: error_message(error, cls) for warning, cls in desc.warnings: warning_message(warning, cls) else: if desc.errors: error1, cls1 = desc.errors[0] error_message(error1, cls1) numerrs = len(desc.errors) - 1 numwarns = len(desc.warnings) if numwarns: error_message( "%d more errors and %d more warnings " "for %s" % (numerrs, numwarns, desc.casual_name()) ) else: error_message("%d more errors for %s " % (numerrs, desc.casual_name())) else: warning1, cls1 = desc.warnings[0] warning_message(warning1, cls1) warning_message( "%d more errors for %s" % (len(desc.warnings) - 1, desc.casual_name()) ) if desc.errors: # process() will recalculate to take this into account desc.include_rule = "never" ================================================ FILE: ctypesgen/version.py ================================================ #!/usr/bin/env python3 from subprocess import Popen, PIPE import os from os import path THIS_DIR = path.dirname(__file__) VERSION_FILE = path.join(THIS_DIR, "VERSION") DEFAULT_PREFIX = "ctypesgen" __all__ = ["VERSION", "version_tuple", "version", "compatible"] def version_tuple(v): try: vs = v.split("-") t = tuple(int(i) for i in vs[1].split(".")) if len(vs) > 2: t += (int(vs[2]),) return t except Exception: return (-1, -1, -1, v) def read_file_version(): f = open(VERSION_FILE) v = f.readline() f.close() return v.strip() def version(): try: args = {"cwd": THIS_DIR} devnull = open(os.devnull, "w") p = Popen(["git", "describe"], stdout=PIPE, stderr=devnull, **args) out, err = p.communicate() if p.returncode: raise RuntimeError("no version defined?") git_tag = out.strip().decode() return f"{DEFAULT_PREFIX}-{git_tag}" except Exception: # failover is to try VERSION_FILE instead try: return f"{DEFAULT_PREFIX}-{read_file_version()}" except Exception: return f"{DEFAULT_PREFIX}-0.0.0" def version_number(): return version().partition("-")[-1] def compatible(v0, v1): v0 = version_tuple(v0) v1 = version_tuple(v1) return v0[:2] == v1[:2] def write_version_file(v=None): if v is None: v = version() f = open(VERSION_FILE, "w") f.write(v) f.close() VERSION = version() VERSION_NUMBER = version_number() if __name__ == "__main__": import argparse p = argparse.ArgumentParser() p.add_argument("--save", action="store_true", help=f"Store version to {VERSION_FILE}") p.add_argument( "--read-file-version", action="store_true", help=f"Read the version stored in {VERSION_FILE}", ) args = p.parse_args() v = version() if args.save: write_version_file(v) if args.read_file_version: v = read_file_version() print(v) ================================================ FILE: debian/.gitignore ================================================ changelog copyright ctypesgen/ *.log *.substvars files debhelper-build-stamp *.postinst.debhelper *.prerm.debhelper *.swp ctypesgen.1 ================================================ FILE: debian/compat ================================================ 9 ================================================ FILE: debian/control ================================================ Source: ctypesgen Section: universe/python Priority: optional Build-Depends: debhelper (>= 9), dh-python, python3-all (>=3.2), python3-setuptools, help2man #python-all (>=2.6.6-3~) Maintainer: Spencer E. Olson Homepage: https://github.com/davidjamesca/ctypesgen Vcs-Git: https://github.com/davidjamesca/ctypesgen.git Standards-Version: 3.9.2 #X-Python-Version: >= 2.7 #XS-Python-Version: >= 2.7 X-Python3-Version: >= 3.2 XS-Python3-Version: >= 3.2 Package: ctypesgen Architecture: all Depends: ${python3:Depends}, ${misc:Depends} Description: Python ctypes wrapper generator ctypesgen reads parses c header files and creates a wrapper for libraries base on what it finds. Preprocessor macros are handled in a manner consistent with typical c code. Preprocessor macro functions are translated into Python functions that are then made available to the user of the newly-generated Python wrapper library. . ctypesgen can also output JSON, which can be used with Mork, which generates bindings for Lua, using the alien module (which binds libffi to Lua). ================================================ FILE: debian/ctypesgen.docs ================================================ demo/ ================================================ FILE: debian/ctypesgen.manpages ================================================ debian/ctypesgen.1 ================================================ FILE: debian/mk_changelog ================================================ #!/usr/bin/env python3 # vim: ts=2:sw=2:et:tw=80:nowrap from subprocess import Popen, PIPE import re, io from datetime import datetime class GitVersion(object): PACKAGE_FMT = '(?P(-[^0-9]|[^-])*)-(?P[0-9].*)' PACKAGE_NAME= 'ctypesgen' # basic `git describe` command (add --tags to include un-annotated tags) GIT_DESCRIBE = 'git describe'.split() def __init__(self, tag=None): self.date = None if tag is None: p = Popen(self.GIT_DESCRIBE, stdout=PIPE) self.current_git_version = p.communicate()[0].decode().strip() p = Popen(self.GIT_DESCRIBE + ['--abbrev=0'], stdout=PIPE) self.current_git_tag = p.communicate()[0].decode().strip() # let's set the date for this version to the last commit p = Popen(['git','log','-1','--format=%cD',self.current_git_version], stdout=PIPE) self.date = p.communicate()[0].decode().strip() else: self.current_git_version = self.current_git_tag = tag p = Popen(self.GIT_DESCRIBE + [self.current_git_version+'~1', '--abbrev=0'], stdout=PIPE, stderr=PIPE) self.last_git_version = p.communicate()[0].decode().strip() p = Popen(['git','show',self.current_git_tag], stdout=PIPE) tag_commit = p.communicate()[0].decode() if 'Tagger:' in tag_commit: # this is an annotated tag m = re.search('\nTagger:\s*(?P[^\n]*)\nDate:\s*(?P[^\n]*)\n', tag_commit) else: # this is a non-annotated tag m = re.search('\nAuthor:\s*(?P[^\n]*)\nDate:\s*(?P[^\n]*)\n', tag_commit) self.maintainer = m.groupdict()['name'] if not self.date: date = datetime.strptime(m.groupdict()['date'], '%a %b %d %H:%M:%S %Y %z') self.date = date.strftime('%a, %d %b %Y %H:%M:%S %z') def match_version(self): try: D = re.match(self.PACKAGE_FMT, self.current_git_version).groupdict() D.setdefault('name', self.PACKAGE_NAME) return D except: return dict(name='unknown', version='0.0.0') @property def version_number(self): m = self.match_version() if '-g' in m['version']: return m['version'].rpartition('-g')[0] else: return m['version'] @property def package_name(self): return self.match_version()['name'] @property def changes(self): if self.last_git_version: p = Popen(['git', 'log', self.last_git_version + '..' + self.current_git_version, '--format=* %s%n%b'], stdout=PIPE) psed = Popen(['sed','-e','s/^/ /', '-e', 's/\\s*\\*/ */'], stdin=p.stdout, stdout=PIPE) return psed.communicate()[0].decode() else: return ' * Initial version\n' @property def chlog_first_line(self): return '{} ({}) unstable; urgency=low' \ .format(self.package_name, self.version_number) @property def chlog_last_line(self): return ' -- {} {}\n'.format(self.maintainer, self.date) def __call__(self): return '{}\n{}{}\n' \ .format(self.chlog_first_line, self.changes, self.chlog_last_line) class GitVersionCollection(object): def __init__(self): p = Popen(['git', 'tag'], stdout=PIPE) self.tags = p.communicate()[0].decode().splitlines() self.tags.reverse() self.has_untagged_changes = False p = Popen(GitVersion.GIT_DESCRIBE, stdout=PIPE) current = p.communicate()[0].decode().strip() if current not in self.tags: self.has_untagged_changes = True def __call__(self): out = io.StringIO() if self.has_untagged_changes: out.write(GitVersion()()) for tag in self.tags: out.write(GitVersion(tag)()) return out.getvalue() def main(): c = GitVersionCollection() print(c()) if __name__=='__main__': main() ================================================ FILE: debian/mk_manpage ================================================ #!/usr/bin/env python3 """ This script generates a manual page. """ from os import path, system, unlink, mkdir, rmdir import debian.deb822 THIS_DIR = path.dirname(__file__) SECTIONS = """\ [DESCRIPTION] {description} [AVAILABILITY] {homepage} [COPYRIGHT] {LICENSE} """ def mkman(): D = dict() with open(path.join(THIS_DIR, 'control')) as f: Source, P = debian.deb822.Packages.iter_paragraphs(f) desc_paragraphs = P['Description'].splitlines() D['homepage'] = Source['Homepage'] D['longname'] = desc_paragraphs[0] desc = [] for di in desc_paragraphs[1:]: dis = di.strip() desc.append('' if dis == '.' else dis) D['description'] = '\n'.join(desc) with open(path.join(THIS_DIR, path.pardir, 'LICENSE')) as f: D['LICENSE'] = f.read() with open(path.join(THIS_DIR, 'ctypesgen.1.input'), 'w') as f: f.write(SECTIONS.format(**D)) try: mkdir(path.join(THIS_DIR,'tmp.cmdir')) except FileExistsError: pass system('ln -s {} {}'.format( path.abspath(path.join(THIS_DIR,path.pardir, 'run.py')), path.join(THIS_DIR, 'tmp.cmdir', 'ctypesgen'))) system('help2man -n "{}" -s 1 -N -i {} -o {} {}'.format( D['longname'], path.join(THIS_DIR, 'ctypesgen.1.input'), path.join(THIS_DIR, 'ctypesgen.1'), path.join(THIS_DIR, 'tmp.cmdir', 'ctypesgen'))) # cleanup unlink(path.join(THIS_DIR, 'tmp.cmdir', 'ctypesgen')) unlink(path.join(THIS_DIR, 'ctypesgen.1.input')) rmdir(path.join(THIS_DIR, 'tmp.cmdir')) if __name__ == '__main__': mkman() ================================================ FILE: debian/rules ================================================ #!/usr/bin/make -f # debian/rules # -*- makefile -*- export DH_VERBOSE=1 DH_VERBOSE = 1 # see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/default.mk # see FEATURE AREAS in dpkg-buildflags(1) #export DEB_BUILD_MAINT_OPTIONS = hardening=+all # see ENVIRONMENT in dpkg-buildflags(1) # package maintainers to append CFLAGS #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic # package maintainers to append LDFLAGS #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed # main packaging script based on dh7 syntax %: dh $@ --with python3 --buildsystem=pybuild build binary : $(CURDIR)/debian/changelog \ $(CURDIR)/debian/copyright \ $(CURDIR)/debian/ctypesgen.1 override_dh_compress: dh_compress -X.c -X.py override_dh_installdocs: dh_installdocs $(RM) $(CURDIR)/debian/ctypesgen/usr/share/doc/ctypesgen/demo/.gitignore $(CURDIR)/debian/changelog : $(CURDIR)/debian/mk_changelog $(CURDIR)/debian/mk_changelog > $(CURDIR)/debian/changelog $(CURDIR)/debian/copyright : $(CURDIR)/LICENSE cp $(CURDIR)/LICENSE $(CURDIR)/debian/copyright $(CURDIR)/debian/ctypesgen.1 : $(CURDIR)/debian/mk_manpage \ $(CURDIR)/debian/control \ $(CURDIR)/LICENSE $(CURDIR)/debian/mk_manpage ================================================ FILE: demo/.gitignore ================================================ demoapp demolib.o demolib.so ================================================ FILE: demo/README.md ================================================ Small Demonstration of Ctypesgen ================================ This little demonstration was originally written by developer clach04 (when this was still residing on code.google.com). This example shows how bindings for a very simple c-library and associated header can be quickly generated using Ctypesgen and accessed by a Python program. Most of the instructions are included in the top of the various files, but a summary is given here. Steps: ---------- 1. Compile the shared c-library `gcc -fPIC -shared -o demolib.so demolib.c` 2. (Re)Generate the bindings (or you can just try the bindings that were already generated and saved in this directory) `../run.py -o pydemolib.py -l demolib.so demolib.h` 3. Run the app that uses these newly generated bindings `./demoapp.py` The results of this execution should give ``` a 1 b 2 result 3 ``` 4. You can also try executing the same code completely from a c-program - Compile test code: `gcc -o demoapp demoapp.c demolib.c demolib.h` - Execute: `./demoapp` - Observe the same results as before: ``` a 1 b 2 result 3 ``` ================================================ FILE: demo/demoapp.c ================================================ /* ** Trivial ctypesgen demo library consumer ** from http://code.google.com/p/ctypesgen ** ** This demoapp it self is not useful, it is a sanity check for the library. ** ** Build static: cc -o demoapp demoapp.c demolib.c demolib.h ** */ #include #include #include "demolib.h" int main(int argc, char **argv) { int a = 1; int b = 2; int result = 0; result = trivial_add(a, b); printf("a %d\n", a); printf("b %d\n", b); printf("result %d\n", result); } ================================================ FILE: demo/demoapp.py ================================================ #!/usr/bin/env python3 """ Trivial ctypesgen demo library consumer from http://code.google.com/p/ctypesgen NOTE demolib.py needs to be generated via: ../run.py -o pydemolib.py -l demolib demolib.h ../run.py -o pydemolib.py -l demolib.so demolib.h """ import sys import pydemolib # generated from demolib.h by ctypesgen def do_demo(): a = 1 b = 2 result = pydemolib.trivial_add(a, b) print("a", a) print("b", b) print("result", result) def main(argv=None): if argv is None: argv = sys.argv do_demo() return 0 if __name__ == "__main__": sys.exit(main()) ================================================ FILE: demo/demolib.c ================================================ /* ** Trivial ctypesgen demo library ** from http://code.google.com/p/ctypesgen Dumb manual build with: gcc -fPIC -c demolib.c gcc -shared -o demolib.so demolib.o gcc -fPIC -shared -o demolib.so demolib.c */ #include "demolib.h" int trivial_add(int a, int b) { return a + b; } ================================================ FILE: demo/demolib.h ================================================ /* ** Trivial ctypesgen demo library ** from http://code.google.com/p/ctypesgen */ int trivial_add(int a, int b); ================================================ FILE: demo/pydemolib.py ================================================ r"""Wrapper for demolib.h Generated with: ../run.py -o pydemolib.py -l demolib.so demolib.h Do not modify this file. """ __docformat__ = "restructuredtext" # Begin preamble for Python import ctypes import sys from ctypes import * # noqa: F401, F403 _int_types = (ctypes.c_int16, ctypes.c_int32) if hasattr(ctypes, "c_int64"): # Some builds of ctypes apparently do not have ctypes.c_int64 # defined; it's a pretty good bet that these builds do not # have 64-bit pointers. _int_types += (ctypes.c_int64,) for t in _int_types: if ctypes.sizeof(t) == ctypes.sizeof(ctypes.c_size_t): c_ptrdiff_t = t del t del _int_types class UserString: def __init__(self, seq): if isinstance(seq, bytes): self.data = seq elif isinstance(seq, UserString): self.data = seq.data[:] else: self.data = str(seq).encode() def __bytes__(self): return self.data def __str__(self): return self.data.decode() def __repr__(self): return repr(self.data) def __int__(self): return int(self.data.decode()) def __long__(self): return int(self.data.decode()) def __float__(self): return float(self.data.decode()) def __complex__(self): return complex(self.data.decode()) def __hash__(self): return hash(self.data) def __le__(self, string): if isinstance(string, UserString): return self.data <= string.data else: return self.data <= string def __lt__(self, string): if isinstance(string, UserString): return self.data < string.data else: return self.data < string def __ge__(self, string): if isinstance(string, UserString): return self.data >= string.data else: return self.data >= string def __gt__(self, string): if isinstance(string, UserString): return self.data > string.data else: return self.data > string def __eq__(self, string): if isinstance(string, UserString): return self.data == string.data else: return self.data == string def __ne__(self, string): if isinstance(string, UserString): return self.data != string.data else: return self.data != string def __contains__(self, char): return char in self.data def __len__(self): return len(self.data) def __getitem__(self, index): return self.__class__(self.data[index]) def __getslice__(self, start, end): start = max(start, 0) end = max(end, 0) return self.__class__(self.data[start:end]) def __add__(self, other): if isinstance(other, UserString): return self.__class__(self.data + other.data) elif isinstance(other, bytes): return self.__class__(self.data + other) else: return self.__class__(self.data + str(other).encode()) def __radd__(self, other): if isinstance(other, bytes): return self.__class__(other + self.data) else: return self.__class__(str(other).encode() + self.data) def __mul__(self, n): return self.__class__(self.data * n) __rmul__ = __mul__ def __mod__(self, args): return self.__class__(self.data % args) # the following methods are defined in alphabetical order: def capitalize(self): return self.__class__(self.data.capitalize()) def center(self, width, *args): return self.__class__(self.data.center(width, *args)) def count(self, sub, start=0, end=sys.maxsize): return self.data.count(sub, start, end) def decode(self, encoding=None, errors=None): # XXX improve this? if encoding: if errors: return self.__class__(self.data.decode(encoding, errors)) else: return self.__class__(self.data.decode(encoding)) else: return self.__class__(self.data.decode()) def encode(self, encoding=None, errors=None): # XXX improve this? if encoding: if errors: return self.__class__(self.data.encode(encoding, errors)) else: return self.__class__(self.data.encode(encoding)) else: return self.__class__(self.data.encode()) def endswith(self, suffix, start=0, end=sys.maxsize): return self.data.endswith(suffix, start, end) def expandtabs(self, tabsize=8): return self.__class__(self.data.expandtabs(tabsize)) def find(self, sub, start=0, end=sys.maxsize): return self.data.find(sub, start, end) def index(self, sub, start=0, end=sys.maxsize): return self.data.index(sub, start, end) def isalpha(self): return self.data.isalpha() def isalnum(self): return self.data.isalnum() def isdecimal(self): return self.data.isdecimal() def isdigit(self): return self.data.isdigit() def islower(self): return self.data.islower() def isnumeric(self): return self.data.isnumeric() def isspace(self): return self.data.isspace() def istitle(self): return self.data.istitle() def isupper(self): return self.data.isupper() def join(self, seq): return self.data.join(seq) def ljust(self, width, *args): return self.__class__(self.data.ljust(width, *args)) def lower(self): return self.__class__(self.data.lower()) def lstrip(self, chars=None): return self.__class__(self.data.lstrip(chars)) def partition(self, sep): return self.data.partition(sep) def replace(self, old, new, maxsplit=-1): return self.__class__(self.data.replace(old, new, maxsplit)) def rfind(self, sub, start=0, end=sys.maxsize): return self.data.rfind(sub, start, end) def rindex(self, sub, start=0, end=sys.maxsize): return self.data.rindex(sub, start, end) def rjust(self, width, *args): return self.__class__(self.data.rjust(width, *args)) def rpartition(self, sep): return self.data.rpartition(sep) def rstrip(self, chars=None): return self.__class__(self.data.rstrip(chars)) def split(self, sep=None, maxsplit=-1): return self.data.split(sep, maxsplit) def rsplit(self, sep=None, maxsplit=-1): return self.data.rsplit(sep, maxsplit) def splitlines(self, keepends=0): return self.data.splitlines(keepends) def startswith(self, prefix, start=0, end=sys.maxsize): return self.data.startswith(prefix, start, end) def strip(self, chars=None): return self.__class__(self.data.strip(chars)) def swapcase(self): return self.__class__(self.data.swapcase()) def title(self): return self.__class__(self.data.title()) def translate(self, *args): return self.__class__(self.data.translate(*args)) def upper(self): return self.__class__(self.data.upper()) def zfill(self, width): return self.__class__(self.data.zfill(width)) class MutableString(UserString): """mutable string objects Python strings are immutable objects. This has the advantage, that strings may be used as dictionary keys. If this property isn't needed and you insist on changing string values in place instead, you may cheat and use MutableString. But the purpose of this class is an educational one: to prevent people from inventing their own mutable string class derived from UserString and than forget thereby to remove (override) the __hash__ method inherited from UserString. This would lead to errors that would be very hard to track down. A faster and better solution is to rewrite your program using lists.""" def __init__(self, string=""): self.data = string def __hash__(self): raise TypeError("unhashable type (it is mutable)") def __setitem__(self, index, sub): if index < 0: index += len(self.data) if index < 0 or index >= len(self.data): raise IndexError self.data = self.data[:index] + sub + self.data[index + 1 :] def __delitem__(self, index): if index < 0: index += len(self.data) if index < 0 or index >= len(self.data): raise IndexError self.data = self.data[:index] + self.data[index + 1 :] def __setslice__(self, start, end, sub): start = max(start, 0) end = max(end, 0) if isinstance(sub, UserString): self.data = self.data[:start] + sub.data + self.data[end:] elif isinstance(sub, bytes): self.data = self.data[:start] + sub + self.data[end:] else: self.data = self.data[:start] + str(sub).encode() + self.data[end:] def __delslice__(self, start, end): start = max(start, 0) end = max(end, 0) self.data = self.data[:start] + self.data[end:] def immutable(self): return UserString(self.data) def __iadd__(self, other): if isinstance(other, UserString): self.data += other.data elif isinstance(other, bytes): self.data += other else: self.data += str(other).encode() return self def __imul__(self, n): self.data *= n return self class String(MutableString, ctypes.Union): _fields_ = [("raw", ctypes.POINTER(ctypes.c_char)), ("data", ctypes.c_char_p)] def __init__(self, obj=b""): if isinstance(obj, (bytes, UserString)): self.data = bytes(obj) else: self.raw = obj def __len__(self): return self.data and len(self.data) or 0 def from_param(cls, obj): # Convert None or 0 if obj is None or obj == 0: return cls(ctypes.POINTER(ctypes.c_char)()) # Convert from String elif isinstance(obj, String): return obj # Convert from bytes elif isinstance(obj, bytes): return cls(obj) # Convert from str elif isinstance(obj, str): return cls(obj.encode()) # Convert from c_char_p elif isinstance(obj, ctypes.c_char_p): return obj # Convert from POINTER(ctypes.c_char) elif isinstance(obj, ctypes.POINTER(ctypes.c_char)): return obj # Convert from raw pointer elif isinstance(obj, int): return cls(ctypes.cast(obj, ctypes.POINTER(ctypes.c_char))) # Convert from ctypes.c_char array elif isinstance(obj, ctypes.c_char * len(obj)): return obj # Convert from object else: return String.from_param(obj._as_parameter_) from_param = classmethod(from_param) def ReturnString(obj, func=None, arguments=None): return String.from_param(obj) # As of ctypes 1.0, ctypes does not support custom error-checking # functions on callbacks, nor does it support custom datatypes on # callbacks, so we must ensure that all callbacks return # primitive datatypes. # # Non-primitive return values wrapped with UNCHECKED won't be # typechecked, and will be converted to ctypes.c_void_p. def UNCHECKED(type): if hasattr(type, "_type_") and isinstance(type._type_, str) and type._type_ != "P": return type else: return ctypes.c_void_p # ctypes doesn't have direct support for variadic functions, so we have to write # our own wrapper class class _variadic_function(object): def __init__(self, func, restype, argtypes, errcheck): self.func = func self.func.restype = restype self.argtypes = argtypes if errcheck: self.func.errcheck = errcheck def _as_parameter_(self): # So we can pass this variadic function as a function pointer return self.func def __call__(self, *args): fixed_args = [] i = 0 for argtype in self.argtypes: # Typecheck what we can fixed_args.append(argtype.from_param(args[i])) i += 1 return self.func(*fixed_args + list(args[i:])) def ord_if_char(value): """ Simple helper used for casts to simple builtin types: if the argument is a string type, it will be converted to it's ordinal value. This function will raise an exception if the argument is string with more than one characters. """ return ord(value) if (isinstance(value, bytes) or isinstance(value, str)) else value # End preamble _libs = {} _libdirs = [] # Begin loader """ Load libraries - appropriately for all our supported platforms """ # ---------------------------------------------------------------------------- # Copyright (c) 2008 David James # Copyright (c) 2006-2008 Alex Holkner # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # * Neither the name of pyglet nor the names of its # contributors may be used to endorse or promote products # derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- import ctypes import ctypes.util import glob import os.path import platform import re import sys def _environ_path(name): """Split an environment variable into a path-like list elements""" if name in os.environ: return os.environ[name].split(":") return [] class LibraryLoader: """ A base class For loading of libraries ;-) Subclasses load libraries for specific platforms. """ # library names formatted specifically for platforms name_formats = ["%s"] class Lookup: """Looking up calling conventions for a platform""" mode = ctypes.DEFAULT_MODE def __init__(self, path): super(LibraryLoader.Lookup, self).__init__() self.access = dict(cdecl=ctypes.CDLL(path, self.mode)) def get(self, name, calling_convention="cdecl"): """Return the given name according to the selected calling convention""" if calling_convention not in self.access: raise LookupError( "Unknown calling convention '{}' for function '{}'".format( calling_convention, name ) ) return getattr(self.access[calling_convention], name) def has(self, name, calling_convention="cdecl"): """Return True if this given calling convention finds the given 'name'""" if calling_convention not in self.access: return False return hasattr(self.access[calling_convention], name) def __getattr__(self, name): return getattr(self.access["cdecl"], name) def __init__(self): self.other_dirs = [] def __call__(self, libname): """Given the name of a library, load it.""" paths = self.getpaths(libname) for path in paths: # noinspection PyBroadException try: return self.Lookup(path) except Exception: # pylint: disable=broad-except pass raise ImportError("Could not load %s." % libname) def getpaths(self, libname): """Return a list of paths where the library might be found.""" if os.path.isabs(libname): yield libname else: # search through a prioritized series of locations for the library # we first search any specific directories identified by user for dir_i in self.other_dirs: for fmt in self.name_formats: # dir_i should be absolute already yield os.path.join(dir_i, fmt % libname) # check if this code is even stored in a physical file try: this_file = __file__ except NameError: this_file = None # then we search the directory where the generated python interface is stored if this_file is not None: for fmt in self.name_formats: yield os.path.abspath(os.path.join(os.path.dirname(__file__), fmt % libname)) # now, use the ctypes tools to try to find the library for fmt in self.name_formats: path = ctypes.util.find_library(fmt % libname) if path: yield path # then we search all paths identified as platform-specific lib paths for path in self.getplatformpaths(libname): yield path # Finally, we'll try the users current working directory for fmt in self.name_formats: yield os.path.abspath(os.path.join(os.path.curdir, fmt % libname)) def getplatformpaths(self, _libname): # pylint: disable=no-self-use """Return all the library paths available in this platform""" return [] # Darwin (Mac OS X) class DarwinLibraryLoader(LibraryLoader): """Library loader for MacOS""" name_formats = [ "lib%s.dylib", "lib%s.so", "lib%s.bundle", "%s.dylib", "%s.so", "%s.bundle", "%s", ] class Lookup(LibraryLoader.Lookup): """ Looking up library files for this platform (Darwin aka MacOS) """ # Darwin requires dlopen to be called with mode RTLD_GLOBAL instead # of the default RTLD_LOCAL. Without this, you end up with # libraries not being loadable, resulting in "Symbol not found" # errors mode = ctypes.RTLD_GLOBAL def getplatformpaths(self, libname): if os.path.pathsep in libname: names = [libname] else: names = [fmt % libname for fmt in self.name_formats] for directory in self.getdirs(libname): for name in names: yield os.path.join(directory, name) @staticmethod def getdirs(libname): """Implements the dylib search as specified in Apple documentation: http://developer.apple.com/documentation/DeveloperTools/Conceptual/ DynamicLibraries/Articles/DynamicLibraryUsageGuidelines.html Before commencing the standard search, the method first checks the bundle's ``Frameworks`` directory if the application is running within a bundle (OS X .app). """ dyld_fallback_library_path = _environ_path("DYLD_FALLBACK_LIBRARY_PATH") if not dyld_fallback_library_path: dyld_fallback_library_path = [ os.path.expanduser("~/lib"), "/usr/local/lib", "/usr/lib", ] dirs = [] if "/" in libname: dirs.extend(_environ_path("DYLD_LIBRARY_PATH")) else: dirs.extend(_environ_path("LD_LIBRARY_PATH")) dirs.extend(_environ_path("DYLD_LIBRARY_PATH")) dirs.extend(_environ_path("LD_RUN_PATH")) if hasattr(sys, "frozen") and getattr(sys, "frozen") == "macosx_app": dirs.append(os.path.join(os.environ["RESOURCEPATH"], "..", "Frameworks")) dirs.extend(dyld_fallback_library_path) return dirs # Posix class PosixLibraryLoader(LibraryLoader): """Library loader for POSIX-like systems (including Linux)""" _ld_so_cache = None _include = re.compile(r"^\s*include\s+(?P.*)") name_formats = ["lib%s.so", "%s.so", "%s"] class _Directories(dict): """Deal with directories""" def __init__(self): dict.__init__(self) self.order = 0 def add(self, directory): """Add a directory to our current set of directories""" if len(directory) > 1: directory = directory.rstrip(os.path.sep) # only adds and updates order if exists and not already in set if not os.path.exists(directory): return order = self.setdefault(directory, self.order) if order == self.order: self.order += 1 def extend(self, directories): """Add a list of directories to our set""" for a_dir in directories: self.add(a_dir) def ordered(self): """Sort the list of directories""" return (i[0] for i in sorted(self.items(), key=lambda d: d[1])) def _get_ld_so_conf_dirs(self, conf, dirs): """ Recursive function to help parse all ld.so.conf files, including proper handling of the `include` directive. """ try: with open(conf) as fileobj: for dirname in fileobj: dirname = dirname.strip() if not dirname: continue match = self._include.match(dirname) if not match: dirs.add(dirname) else: for dir2 in glob.glob(match.group("pattern")): self._get_ld_so_conf_dirs(dir2, dirs) except IOError: pass def _create_ld_so_cache(self): # Recreate search path followed by ld.so. This is going to be # slow to build, and incorrect (ld.so uses ld.so.cache, which may # not be up-to-date). Used only as fallback for distros without # /sbin/ldconfig. # # We assume the DT_RPATH and DT_RUNPATH binary sections are omitted. directories = self._Directories() for name in ( "LD_LIBRARY_PATH", "SHLIB_PATH", # HP-UX "LIBPATH", # OS/2, AIX "LIBRARY_PATH", # BE/OS ): if name in os.environ: directories.extend(os.environ[name].split(os.pathsep)) self._get_ld_so_conf_dirs("/etc/ld.so.conf", directories) bitage = platform.architecture()[0] unix_lib_dirs_list = [] if bitage.startswith("64"): # prefer 64 bit if that is our arch unix_lib_dirs_list += ["/lib64", "/usr/lib64"] # must include standard libs, since those paths are also used by 64 bit # installs unix_lib_dirs_list += ["/lib", "/usr/lib"] if sys.platform.startswith("linux"): # Try and support multiarch work in Ubuntu # https://wiki.ubuntu.com/MultiarchSpec if bitage.startswith("32"): # Assume Intel/AMD x86 compat unix_lib_dirs_list += ["/lib/i386-linux-gnu", "/usr/lib/i386-linux-gnu"] elif bitage.startswith("64"): # Assume Intel/AMD x86 compatible unix_lib_dirs_list += [ "/lib/x86_64-linux-gnu", "/usr/lib/x86_64-linux-gnu", ] else: # guess... unix_lib_dirs_list += glob.glob("/lib/*linux-gnu") directories.extend(unix_lib_dirs_list) cache = {} lib_re = re.compile(r"lib(.*)\.s[ol]") # ext_re = re.compile(r"\.s[ol]$") for our_dir in directories.ordered(): try: for path in glob.glob("%s/*.s[ol]*" % our_dir): file = os.path.basename(path) # Index by filename cache_i = cache.setdefault(file, set()) cache_i.add(path) # Index by library name match = lib_re.match(file) if match: library = match.group(1) cache_i = cache.setdefault(library, set()) cache_i.add(path) except OSError: pass self._ld_so_cache = cache def getplatformpaths(self, libname): if self._ld_so_cache is None: self._create_ld_so_cache() result = self._ld_so_cache.get(libname, set()) for i in result: # we iterate through all found paths for library, since we may have # actually found multiple architectures or other library types that # may not load yield i # Windows class WindowsLibraryLoader(LibraryLoader): """Library loader for Microsoft Windows""" name_formats = ["%s.dll", "lib%s.dll", "%slib.dll", "%s"] class Lookup(LibraryLoader.Lookup): """Lookup class for Windows libraries...""" def __init__(self, path): super(WindowsLibraryLoader.Lookup, self).__init__(path) self.access["stdcall"] = ctypes.windll.LoadLibrary(path) # Platform switching # If your value of sys.platform does not appear in this dict, please contact # the Ctypesgen maintainers. loaderclass = { "darwin": DarwinLibraryLoader, "cygwin": WindowsLibraryLoader, "win32": WindowsLibraryLoader, "msys": WindowsLibraryLoader, } load_library = loaderclass.get(sys.platform, PosixLibraryLoader)() def add_library_search_dirs(other_dirs): """ Add libraries to search paths. If library paths are relative, convert them to absolute with respect to this file's directory """ for path in other_dirs: if not os.path.isabs(path): path = os.path.abspath(path) load_library.other_dirs.append(path) del loaderclass # End loader add_library_search_dirs([]) # Begin libraries _libs["demolib.so"] = load_library("demolib.so") # 1 libraries # End libraries # No modules # /home/olsonse/src/ctypesgen/demo/demolib.h: 6 if _libs["demolib.so"].has("trivial_add", "cdecl"): trivial_add = _libs["demolib.so"].get("trivial_add", "cdecl") trivial_add.argtypes = [c_int, c_int] trivial_add.restype = c_int # No inserted files # No prefix-stripping ================================================ FILE: docs/publishing.md ================================================ # How to Publish a New Release ## Versioning Versioning within ctypesgen follows these general rules: * Versions are all defined with specific reference to a commit that is relative to a Git tag. * Versions numbers include enough information to find the exact commit that represents the version release. * All tags should follow the format of: x.y.z * x : Major revision with major differences of capabilities as compared to other major revisions. The definition of "major capabilities" is a somewhat subjective concept, dependent on the developers. * y : Minor revision with incompatible differences of interfaces as compared to earlier revisions. Interfaces that are considered to impact the minor revision number are external interfaces such as the command line or perhaps python version support. * z : Micro revision indicating a general acceptance of multiple patches since last tag. This number may be used to help mark minor development milestones. By using the Git command `git describe`, a unique identifier of the full version string can be shown as: * x.y.z[-n-g*sha1*] where [-n-g*sha1*] shows up *automatically* if changes have been made since the last tag * n : Indicates the number of commits since the last tag * g*sha1*: Indicates the abreviated SHA1 hash of the latest commit Thus, the version *1.0.0-2* means that the last tag before that version was *1.0.0* and the version *1.0.0-2* is exactly 2 commits after the tag *1.0.0*. To re-baseline the [-n-g*sha1*] portion showing up in `git describe` (i.e. remove it until another commit is added), we simply add another tag following the *x.y.z* format. :exclamation: The version set for release should comply with [PEP 440](https://peps.python.org/pep-0440/). ## Last changes on repo ```bash # Update content and set version of latest changes in CHANGELOG.md vim CHANGELOG.md ... git commit -a -S version="2.2.5" commit=$(git rev-parse HEAD) tag_message="ctypesgen v${version}" git tag -a -m "$tag_message" $version $commit git push -u --tags ``` ## Publish on GitHub - Go to . - Choose the newly created tag and fill in title (preferable in the format of 'ctypesgen v.X.Y.Z') and description (if appropriate, use the content for this version listed in CHANGELOG.md). - Publish. ## Post-release After the release, a new headline `### Unreleased` should be added at the top of the file `CHANGELOG.md`. ================================================ FILE: pyproject.toml ================================================ [build-system] requires = ["setuptools>=64", "setuptools_scm>=7.1"] build-backend = "setuptools.build_meta" [project] name = "ctypesgen" description = "Python wrapper generator for ctypes" license = { text = "BSD-2-Clause" } classifiers = [ "License :: OSI Approved :: BSD License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3 :: Only", "Development Status :: 4 - Beta", "Operating System :: OS Independent", "Intended Audience :: Developers", "Topic :: Software Development :: Code Generators", "Topic :: Software Development :: Pre-processors", "Topic :: Software Development :: Build Tools", "Environment :: Console", ] dynamic = ["readme", "version"] requires-python = ">=3.7" [project.urls] Homepage = "https://github.com/ctypesgen/ctypesgen" Repository = "https://github.com/ctypesgen/ctypesgen.git" Issues = "https://github.com/ctypesgen/ctypesgen/issues" Changelog = "https://github.com/ctypesgen/ctypesgen/blob/master/CHANGELOG.md" [project.scripts] ctypesgen = "ctypesgen.__main__:main" [tool.setuptools] package-dir = {} include-package-data = true [tool.setuptools.packages.find] exclude = ["tests*"] namespaces = false [tool.setuptools.dynamic.readme] file = ["README.md", "LICENSE", "CHANGELOG.md"] content-type = "text/markdown" [tool.setuptools_scm] write_to = "ctypesgen/VERSION" write_to_template = "{version}" [tool.black] line-length = 100 exclude = '.*tab.py|ctypesgen/parser/cgrammar.py|ctypesgen/parser/lex.py|ctypesgen/parser/yacc.py' target-version = ["py37", "py38", "py39"] ================================================ FILE: run.py ================================================ #!/usr/bin/env python3 import sys import os # ensure that we can load the ctypesgen library THIS_DIR = os.path.dirname(__file__) sys.path.insert(0, THIS_DIR) from ctypesgen.__main__ import main # noqa: E402 if __name__ == "__main__": main() ================================================ FILE: setup.py ================================================ #!/usr/bin/env python3 from setuptools import setup if __name__ == "__main__": setup() ================================================ FILE: tests/.gitignore ================================================ temp.h temp.py common/ ================================================ FILE: tests/__init__.py ================================================ ================================================ FILE: tests/ctypesgentest.py ================================================ """ctypesgentest is a simple module for testing ctypesgen on various C constructs. It consists of a single function, test(). test() takes a string that represents a C header file, along with some keyword arguments representing options. It processes the header using ctypesgen and returns a tuple containing the resulting module object and the output that ctypesgen produced. """ import os import sys from io import StringIO import glob import json from contextlib import contextmanager import types import subprocess from shutil import rmtree from ctypesgen import options, messages, parser, processor from ctypesgen import printer_python, printer_json, VERSION module_factory = types.ModuleType # set redirect_stdout to False if using console based debugger like pdb redirect_stdout = True @contextmanager def redirect(stdout=sys.stdout): backup = sys.stdout sys.stdout = stdout try: yield stdout finally: sys.stdout = backup def generate(header, **more_options): assert isinstance(header, str) with open("temp.h", "wb") as f: f.write(header.encode('utf-8')) test_options = options.get_default_options() test_options.headers = ["temp.h"] for opt, val in more_options.items(): setattr(test_options, opt, val) if redirect_stdout: # Redirect output sys.stdout = StringIO() # Step 1: Parse descriptions = parser.parse(test_options.headers, test_options) # Step 2: Process processor.process(descriptions, test_options) # Step 3: Print if test_options.output_language.startswith("py"): def module_from_code(name, python_code): module = module_factory(name) exec(python_code, module.__dict__) return module # we have to redirect stdout, as WrapperPrinter is only able to write # to files or stdout with redirect(stdout=StringIO()) as printer_output: # do not discard WrapperPrinter object, as the target file gets # closed on printer deletion _ = printer_python.WrapperPrinter(None, test_options, descriptions) generated_python_code = printer_output.getvalue() module = module_from_code("temp", generated_python_code) retval = module elif test_options.output_language == "json": with redirect(stdout=StringIO()) as printer_output: # do not discard WrapperPrinter object, as the target file gets # closed on printer deletion _ = printer_json.WrapperPrinter(None, test_options, descriptions) JSON = json.loads(printer_output.getvalue()) retval = JSON else: raise RuntimeError("No such output language `" + test_options.output_language + "'") if redirect_stdout: # Un-redirect output output = sys.stdout.getvalue() sys.stdout.close() sys.stdout = sys.__stdout__ else: output = "" return retval, output def cleanup(filepattern="temp.*"): fnames = glob.glob(filepattern) for fname in fnames: os.unlink(fname) def set_logging_level(log_level): messages.log.setLevel(log_level) def ctypesgen_version(): return VERSION def sort_anon_fn(anon_tag): return int(anon_tag.split("_")[1]) class JsonHelper: """ Utility class preparing generated JSON result for testing. JSON stores the path to some source items. These need to be genericized in order for tests to succeed on all machines/user accounts. This is also the case for "anon_" tags, which are "reset" for each test to start from "anon_1". """ def __init__(self): self.anons = list() def prepare(self, json): """Prepares generated JSON result for testing""" self._search_anon_tags(json) unique_list = list(set(self.anons)) unique_sorted_list = sorted(unique_list, key=sort_anon_fn) mapped_tags = dict() counter = 1 for i in unique_sorted_list: mapped_tags[i] = "anon_{0}".format(counter) counter += 1 for (old_tag, new_tag) in mapped_tags.items(): self._replace_anon_tag(json, old_tag, new_tag) def _replace_anon_tag(self, json, tag, new_tag): """Replaces source paths and resets anon_ tags to increment from 1""" if isinstance(json, list): for item in json: self._replace_anon_tag(item, tag, new_tag) return if isinstance(json, dict): for key, value in json.items(): if key == "name" and isinstance(value, str): if value == tag: json[key] = new_tag elif key == "tag" and isinstance(value, str): if value == tag: json[key] = new_tag elif key == "src" and isinstance(value, list): if value and "temp.h" in value[0]: value[0] = "/some-path/temp.h" else: self._replace_anon_tag(value, tag, new_tag) def _search_anon_tags(self, json): """Search for anon_ tags""" if isinstance(json, list): for item in json: self._search_anon_tags(item) return if isinstance(json, dict): for key, value in json.items(): if key == "name" and isinstance(value, str): if value.startswith("anon_"): self.anons.append(value) else: self._search_anon_tags(value) # # Functions facilitating tests of use of cross inclusion # COMMON_DIR = os.path.join(os.path.dirname(__file__), "common") def generate_common(): common_lib = "libcommon.dll" if sys.platform == "win32" else "libcommon.so" _create_common_files() _compile_common(common_lib) for file_name in ["a", "b"]: _generate_common(file_name, common_lib) for file_name in ["a", "b"]: _generate_common(file_name, common_lib, False) def cleanup_common(): # Attention: currently not working on MS Windows. # cleanup_common() tries to delete "common.dll" while it is still loaded # by ctypes. See unittest for further details. rmtree(COMMON_DIR) def _compile_common(common_lib): subprocess.run(["gcc", "-c", f"{COMMON_DIR}/a.c", "-o", f"{COMMON_DIR}/a.o"]) subprocess.run(["gcc", "-c", f"{COMMON_DIR}/b.c", "-o", f"{COMMON_DIR}/b.o"]) subprocess.run( [ "gcc", "-shared", "-o", f"{COMMON_DIR}/{common_lib}", f"{COMMON_DIR}/a.o", f"{COMMON_DIR}/b.o", ] ) def _generate_common(file_name, common_lib, embed_preamble=True): test_options = options.get_default_options() test_options.headers = [f"{COMMON_DIR}/{file_name}.h"] test_options.include_search_paths = [COMMON_DIR] test_options.libraries = [common_lib] test_options.compile_libdirs = [COMMON_DIR] test_options.embed_preamble = embed_preamble if embed_preamble: output = f"{COMMON_DIR}/{file_name}.py" else: output = f"{COMMON_DIR}/{file_name}2.py" descriptions = parser.parse(test_options.headers, test_options) processor.process(descriptions, test_options) printer_python.WrapperPrinter(output, test_options, descriptions) def _create_common_files(): a_h = """#include "common.h" void foo(struct mystruct *m); """ a_c = """#include "a.h" void foo(struct mystruct *m) { } """ b_h = """#include "common.h" void bar(struct mystruct *m); """ b_c = """#include "b.h" void bar(struct mystruct *m) { } """ common_h = """struct mystruct { int a; }; """ try: os.mkdir(COMMON_DIR) except FileExistsError: rmtree(COMMON_DIR) os.mkdir(COMMON_DIR) names = {"a.h": a_h, "a.c": a_c, "b.h": b_h, "b.c": b_c, "common.h": common_h} for (name, source) in names.items(): with open(f"{COMMON_DIR}/{name}", "w") as f: f.write(source) ================================================ FILE: tests/testsuite.py ================================================ #!/usr/bin/env python3 """Simple test suite using unittest. By clach04 (Chris Clark). Calling: python3 -m unittest tests.testsuite Calling a specific test only: python3 -m unittest tests.testsuite.[TestCase class].[test name] e.g.: python3 -m unittest tests.testsuite.StdBoolTest.test_stdbool_type or pytest -v --showlocals tests/testsuite.py pytest -v --showlocals tests/testsuite.py::StdBoolTest::test_stdbool_type Could use any unitest compatible test runner (nose, etc.) Aims to test for regressions. Where possible use stdlib to avoid the need to compile C code. """ import sys import os import ctypes import json as JSON import math import unittest import logging from subprocess import Popen, PIPE from tests.ctypesgentest import ( cleanup, cleanup_common, ctypesgen_version, generate, generate_common, JsonHelper, set_logging_level, ) TEST_DIR = os.path.abspath(os.path.dirname(__file__)) def compare_json(test_instance, json, json_ans, verbose=False): json_helper = JsonHelper() json_helper.prepare(json) print_excess = False try: test_instance.assertEqual(len(json), len(json_ans)) except Exception: if verbose: print( "JSONs do not have same length: ", len(json), "generated vs", len(json_ans), "stored", ) print_excess = True else: raise # first fix paths that exist inside JSON to avoid user-specific paths: for i, ith_json_ans in zip(json, json_ans): try: test_instance.assertEqual(i, ith_json_ans) except Exception: if verbose: print("\nFailed JSON for: ", i["name"]) print("GENERATED:\n", i, "\nANS:\n", ith_json_ans) print("FAILED FOR================", JSON.dumps(i, indent=4)) print("GENERATED =============", JSON.dumps(ith_json_ans, indent=4)) raise if print_excess: if len(json) > len(json_ans): j, jlen, jlabel = json, len(json_ans), "generated" else: j, jlen, jlabel = json_ans, len(json), "stored" import pprint print("Excess JSON content from", jlabel, "content:") pprint.pprint(j[jlen:]) def compute_packed(modulo, fields): packs = [ ( modulo * int(ctypes.sizeof(f) / modulo) + modulo * (1 if (ctypes.sizeof(f) % modulo) else 0) ) for f in fields ] return sum(packs) class StdlibTest(unittest.TestCase): @classmethod def setUpClass(cls): header_str = "#include \n" if sys.platform == "win32": # pick something from %windir%\system32\msvc*dll that include stdlib libraries = ["msvcrt.dll"] libraries = ["msvcrt"] elif sys.platform.startswith("linux"): libraries = ["libc.so.6"] else: libraries = ["libc"] cls.module, _ = generate(header_str, libraries=libraries, all_headers=True) @classmethod def tearDownClass(cls): del cls.module cleanup() def test_getenv_returns_string(self): """Issue 8 - Regression for crash with 64 bit and bad strings on 32 bit. See http://code.google.com/p/ctypesgen/issues/detail?id=8 Test that we get a valid (non-NULL, non-empty) string back """ module = StdlibTest.module if sys.platform == "win32": # Check a variable that is already set env_var_name = ( "USERNAME" # this is always set (as is windir, ProgramFiles, USERPROFILE, etc.) ) expect_result = os.environ[env_var_name] self.assertTrue(expect_result, "this should not be None or empty") # reason for using an existing OS variable is that unless the # MSVCRT dll imported is the exact same one that Python was # built with you can't share structures, see # http://msdn.microsoft.com/en-us/library/ms235460.aspx # "Potential Errors Passing CRT Objects Across DLL Boundaries" else: env_var_name = "HELLO" os.environ[env_var_name] = "WORLD" # This doesn't work under win32 expect_result = "WORLD" result = str(module.getenv(env_var_name)) self.assertEqual(expect_result, result) def test_getenv_returns_null(self): """Related to issue 8. Test getenv of unset variable.""" module = StdlibTest.module env_var_name = "NOT SET" expect_result = None try: # ensure variable is not set, ignoring not set errors del os.environ[env_var_name] except KeyError: pass result = module.getenv(env_var_name) self.assertEqual(expect_result, result) # This test is currently not working on MS Windows. The reason is the call of # cleanup_common(), which tries to delete "common.dll" while it is still loaded # by ctypes. # "common.dll" gets loaded in multiple places: first, every call of # ctypesgen.processor.process() in generate_common() loads the dll to check # availability of the symbols mentioned in the header file. # In addition, each test imports the module generated by ctypesgen, which again # means the dll is loaded to memory. # On Linux/macOS this is no problem, as .so libaries can be overwritten/deleted # on file system while still loaded to memory. @unittest.skipIf( sys.platform == "win32", "Currently not working on Windows. See code comment for details." ) class CommonHeaderTest(unittest.TestCase): @classmethod def setUpClass(cls): generate_common() @classmethod def tearDownClass(cls): cleanup_common() @unittest.expectedFailure def test_two_import_with_embedded_preamble(self): from .common import a from .common import b m = b.struct_mystruct() b.bar(ctypes.byref(m)) a.foo(ctypes.byref(m)) def test_one_import(self): from .common import b m = b.struct_mystruct() b.bar(ctypes.byref(m)) def test_two_import(self): from .common import a2 from .common import b2 m = b2.struct_mystruct() b2.bar(ctypes.byref(m)) a2.foo(ctypes.byref(m)) class StdBoolTest(unittest.TestCase): "Test correct parsing and generation of bool type" @classmethod def setUpClass(cls): header_str = """ #include struct foo { bool is_bar; int a; }; """ cls.module, _ = generate(header_str) # , all_headers=True) @classmethod def tearDownClass(cls): del cls.module cleanup() def test_stdbool_type(self): """Test is bool is correctly parsed""" module = StdBoolTest.module struct_foo = module.struct_foo self.assertEqual(struct_foo._fields_, [("is_bar", ctypes.c_bool), ("a", ctypes.c_int)]) class IntTypesTest(unittest.TestCase): "Test correct parsing and generation of different integer types" @classmethod def setUpClass(cls): header_str = """ struct int_types { short t_short; short int t_short_int; unsigned short t_ushort; unsigned short int t_ushort_int; int t_int; long t_long; long int t_long_int; long long t_long_long; long long int t_long_long_int; unsigned long long int t_u_long_long_int; long int unsigned long t_long_int_u_long; }; """ cls.module, _ = generate(header_str) @classmethod def tearDownClass(cls): del cls.module cleanup() def test_int_types(self): """Test if different integer types are correctly parsed""" module = IntTypesTest.module struct_int_types = module.struct_int_types self.assertEqual( struct_int_types._fields_, [ ("t_short", ctypes.c_short), ("t_short_int", ctypes.c_short), ("t_ushort", ctypes.c_ushort), ("t_ushort_int", ctypes.c_ushort), ("t_int", ctypes.c_int), ("t_long", ctypes.c_long), ("t_long_int", ctypes.c_long), ("t_long_long", ctypes.c_longlong), ("t_long_long_int", ctypes.c_longlong), ("t_u_long_long_int", ctypes.c_ulonglong), ("t_long_int_u_long", ctypes.c_ulonglong), ], ) class SimpleMacrosTest(unittest.TestCase): """Based on simple_macros.py""" @classmethod def setUpClass(cls): header_str = """ #define A 1 #define B(x,y) x+y #define C(a,b,c) a?b:c #define funny(x) "funny" #x #define multipler_macro(x,y) x*y #define minus_macro(x,y) x-y #define divide_macro(x,y) x/y #define mod_macro(x,y) x%y #define subcall_macro_simple(x) (A) #define subcall_macro_simple_plus(x) (A) + (x) #define subcall_macro_minus(x,y) minus_macro(x,y) #define subcall_macro_minus_plus(x,y,z) (minus_macro(x,y)) + (z) """ cls.module, _ = generate(header_str) cls.json, _ = generate(header_str, output_language="json") def _json(self, name): for i in SimpleMacrosTest.json: if i["name"] == name: return i raise KeyError("Could not find JSON entry") @classmethod def tearDownClass(cls): del cls.module, cls.json cleanup() def test_macro_constant_int(self): """Tests from simple_macros.py""" module, json = SimpleMacrosTest.module, self._json self.assertEqual(module.A, 1) self.assertEqual(json("A"), {"name": "A", "type": "macro", "value": "1"}) def test_macro_addition_json(self): json = self._json self.assertEqual( json("B"), {"args": ["x", "y"], "body": "(x + y)", "name": "B", "type": "macro_function"}, ) def test_macro_addition(self): """Tests from simple_macros.py""" module = SimpleMacrosTest.module self.assertEqual(module.B(2, 2), 4) def test_macro_ternary_json(self): """Tests from simple_macros.py""" json = self._json self.assertEqual( json("C"), { "args": ["a", "b", "c"], "body": "a and b or c", "name": "C", "type": "macro_function", }, ) def test_macro_ternary_true(self): """Tests from simple_macros.py""" module = SimpleMacrosTest.module self.assertEqual(module.C(True, 1, 2), 1) def test_macro_ternary_false(self): """Tests from simple_macros.py""" module = SimpleMacrosTest.module self.assertEqual(module.C(False, 1, 2), 2) def test_macro_ternary_true_complex(self): """Test ?: with true, using values that can not be confused between True and 1""" module = SimpleMacrosTest.module self.assertEqual(module.C(True, 99, 100), 99) def test_macro_ternary_false_complex(self): """Test ?: with false, using values that can not be confused between True and 1""" module = SimpleMacrosTest.module self.assertEqual(module.C(False, 99, 100), 100) def test_macro_string_compose(self): """Tests from simple_macros.py""" module = SimpleMacrosTest.module self.assertEqual(module.funny("bunny"), "funnybunny") def test_macro_string_compose_json(self): """Tests from simple_macros.py""" json = self._json self.assertEqual( json("funny"), {"args": ["x"], "body": "('funny' + x)", "name": "funny", "type": "macro_function"}, ) def test_macro_math_multipler(self): module = SimpleMacrosTest.module x, y = 2, 5 self.assertEqual(module.multipler_macro(x, y), x * y) def test_macro_math_multiplier_json(self): json = self._json self.assertEqual( json("multipler_macro"), { "args": ["x", "y"], "body": "(x * y)", "name": "multipler_macro", "type": "macro_function", }, ) def test_macro_math_minus(self): module = SimpleMacrosTest.module x, y = 2, 5 self.assertEqual(module.minus_macro(x, y), x - y) def test_macro_math_minus_json(self): json = self._json self.assertEqual( json("minus_macro"), { "args": ["x", "y"], "body": "(x - y)", "name": "minus_macro", "type": "macro_function", }, ) def test_macro_math_divide(self): module = SimpleMacrosTest.module x, y = 2, 5 self.assertEqual(module.divide_macro(x, y), x / y) def test_macro_math_divide_json(self): json = self._json self.assertEqual( json("divide_macro"), { "args": ["x", "y"], "body": "(x / y)", "name": "divide_macro", "type": "macro_function", }, ) def test_macro_math_mod(self): module = SimpleMacrosTest.module x, y = 2, 5 self.assertEqual(module.mod_macro(x, y), x % y) def test_macro_math_mod_json(self): json = self._json self.assertEqual( json("mod_macro"), {"args": ["x", "y"], "body": "(x % y)", "name": "mod_macro", "type": "macro_function"}, ) def test_macro_subcall_simple(self): """Test use of a constant valued macro within a macro""" module = SimpleMacrosTest.module self.assertEqual(module.subcall_macro_simple(2), 1) def test_macro_subcall_simple_json(self): json = self._json self.assertEqual( json("subcall_macro_simple"), {"args": ["x"], "body": "A", "name": "subcall_macro_simple", "type": "macro_function"}, ) def test_macro_subcall_simple_plus(self): """Test math with constant valued macro within a macro""" module = SimpleMacrosTest.module self.assertEqual(module.subcall_macro_simple_plus(2), 1 + 2) def test_macro_subcall_simple_plus_json(self): json = self._json self.assertEqual( json("subcall_macro_simple_plus"), { "args": ["x"], "body": "(A + x)", "name": "subcall_macro_simple_plus", "type": "macro_function", }, ) def test_macro_subcall_minus(self): """Test use of macro function within a macro""" module = SimpleMacrosTest.module x, y = 2, 5 self.assertEqual(module.subcall_macro_minus(x, y), x - y) def test_macro_subcall_minus_json(self): json = self._json self.assertEqual( json("subcall_macro_minus"), { "args": ["x", "y"], "body": "(minus_macro (x, y))", "name": "subcall_macro_minus", "type": "macro_function", }, ) def test_macro_subcall_minus_plus(self): """Test math with a macro function within a macro""" module = SimpleMacrosTest.module x, y, z = 2, 5, 1 self.assertEqual(module.subcall_macro_minus_plus(x, y, z), (x - y) + z) def test_macro_subcall_minus_plus_json(self): json = self._json self.assertEqual( json("subcall_macro_minus_plus"), { "args": ["x", "y", "z"], "body": "((minus_macro (x, y)) + z)", "name": "subcall_macro_minus_plus", "type": "macro_function", }, ) class StructuresTest(unittest.TestCase): """Based on structures.py""" @classmethod def setUpClass(cls): """ NOTE: Very possibly, if you change this header string, you need to change the line numbers in the JSON output test result below (in test_struct_json). """ header_str = """ struct foo { int a; char b; int c; int d : 15; int : 17; }; struct __attribute__((packed)) packed_foo { int a; char b; int c; int d : 15; int : 17; }; typedef struct { int a; char b; int c; int d : 15; int : 17; } foo_t; typedef struct __attribute__((packed)) { int a; char b; int c; int d : 15; int : 17; } packed_foo_t; #pragma pack(push, 4) typedef struct { int a; char b; int c; int d : 15; int : 17; } pragma_packed_foo_t; #pragma pack(pop) #pragma pack(push, thing1, 2) #pragma pack(push, thing2, 4) #pragma pack(pop) #pragma pack(push, thing3, 8) #pragma pack(push, thing4, 16) #pragma pack(pop, thing3) struct pragma_packed_foo2 { int a; char b; int c; int d : 15; int : 17; }; #pragma pack(pop, thing1) struct foo3 { int a; char b; int c; int d : 15; int : 17; }; typedef int Int; typedef struct { int Int; } id_struct_t; typedef struct { int a; char b; } BAR0, *PBAR0; """ cls.module, _ = generate(header_str) cls.json, _ = generate(header_str, output_language="json") @classmethod def tearDownClass(cls): del StructuresTest.module cleanup() def test_struct_json(self): json_ans = [ { "attrib": {}, "fields": [ { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "a", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, "name": "b", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "c", }, { "bitfield": "15", "ctype": { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 15, }, "errors": [], }, "name": "d", }, { "bitfield": "17", "ctype": { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 17, }, "errors": [], }, "name": None, }, ], "name": "foo", "type": "struct", }, { "attrib": {"packed": True}, "fields": [ { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "a", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, "name": "b", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "c", }, { "bitfield": "15", "ctype": { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 15, }, "errors": [], }, "name": "d", }, { "bitfield": "17", "ctype": { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 17, }, "errors": [], }, "name": None, }, ], "name": "packed_foo", "type": "struct", }, { "attrib": {}, "fields": [ { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "a", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, "name": "b", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "c", }, { "bitfield": "15", "ctype": { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 15, }, "errors": [], }, "name": "d", }, { "bitfield": "17", "ctype": { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 17, }, "errors": [], }, "name": None, }, ], "name": "anon_1", "type": "struct", }, { "ctype": { "Klass": "CtypesStruct", "anonymous": True, "errors": [], "members": [ [ "a", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "b", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, ], [ "c", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "d", { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 15, }, "errors": [], }, ], [ None, { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 17, }, "errors": [], }, ], ], "opaque": False, "attrib": {}, "src": ["/some-path/temp.h", 21], "tag": "anon_1", "variety": "struct", }, "name": "foo_t", "type": "typedef", }, { "attrib": {"packed": True}, "fields": [ { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "a", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, "name": "b", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "c", }, { "bitfield": "15", "ctype": { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 15, }, "errors": [], }, "name": "d", }, { "bitfield": "17", "ctype": { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 17, }, "errors": [], }, "name": None, }, ], "name": "anon_2", "type": "struct", }, { "ctype": { "Klass": "CtypesStruct", "anonymous": True, "errors": [], "members": [ [ "a", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "b", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, ], [ "c", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "d", { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 15, }, "errors": [], }, ], [ None, { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 17, }, "errors": [], }, ], ], "opaque": False, "attrib": {"packed": True}, "src": ["/some-path/temp.h", 30], "tag": "anon_2", "variety": "struct", }, "name": "packed_foo_t", "type": "typedef", }, { "attrib": {"packed": True, "aligned": [4]}, "fields": [ { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "a", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, "name": "b", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "c", }, { "bitfield": "15", "ctype": { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 15, }, "errors": [], }, "name": "d", }, { "bitfield": "17", "ctype": { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 17, }, "errors": [], }, "name": None, }, ], "name": "anon_3", "type": "struct", }, { "ctype": { "Klass": "CtypesStruct", "anonymous": True, "errors": [], "members": [ [ "a", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "b", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, ], [ "c", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "d", { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 15, }, "errors": [], }, ], [ None, { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 17, }, "errors": [], }, ], ], "opaque": False, "attrib": {"packed": True, "aligned": [4]}, "src": ["/some-path/temp.h", 40], "tag": "anon_3", "variety": "struct", }, "name": "pragma_packed_foo_t", "type": "typedef", }, { "attrib": {"packed": True, "aligned": [2]}, "fields": [ { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "a", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, "name": "b", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "c", }, { "bitfield": "15", "ctype": { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 15, }, "errors": [], }, "name": "d", }, { "bitfield": "17", "ctype": { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 17, }, "errors": [], }, "name": None, }, ], "name": "pragma_packed_foo2", "type": "struct", }, { "attrib": {}, "fields": [ { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "a", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, "name": "b", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "c", }, { "bitfield": "15", "ctype": { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 15, }, "errors": [], }, "name": "d", }, { "bitfield": "17", "ctype": { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 17, }, "errors": [], }, "name": None, }, ], "name": "foo3", "type": "struct", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "Int", "type": "typedef", }, { "attrib": {}, "fields": [ { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "Int", } ], "name": "anon_4", "type": "struct", }, { "ctype": { "Klass": "CtypesStruct", "anonymous": True, "errors": [], "members": [ [ "Int", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ] ], "opaque": False, "attrib": {}, "src": ["/some-path/temp.h", 77], "tag": "anon_4", "variety": "struct", }, "name": "id_struct_t", "type": "typedef", }, { "attrib": {}, "fields": [ { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "name": "a", }, { "ctype": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, "name": "b", }, ], "name": "anon_5", "type": "struct", }, { "ctype": { "Klass": "CtypesStruct", "anonymous": True, "attrib": {}, "errors": [], "members": [ [ "a", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "b", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, ], ], "opaque": False, "src": ["/some-path/temp.h", 81], "tag": "anon_5", "variety": "struct", }, "name": "BAR0", "type": "typedef", }, { "ctype": { "Klass": "CtypesPointer", "destination": { "Klass": "CtypesStruct", "anonymous": True, "attrib": {}, "errors": [], "members": [ [ "a", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "b", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, ], ], "opaque": False, "src": ["/some-path/temp.h", 81], "tag": "anon_5", "variety": "struct", }, "errors": [], "qualifiers": [], }, "name": "PBAR0", "type": "typedef", }, { "ctype": { "Klass": "CtypesStruct", "anonymous": False, "errors": [], "members": [ [ "a", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "b", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, ], [ "c", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "d", { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 15, }, "errors": [], }, ], [ None, { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 17, }, "errors": [], }, ], ], "opaque": False, "attrib": {}, "src": ["/some-path/temp.h", 3], "tag": "foo", "variety": "struct", }, "name": "foo", "type": "typedef", }, { "ctype": { "Klass": "CtypesStruct", "anonymous": False, "errors": [], "members": [ [ "a", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "b", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, ], [ "c", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "d", { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 15, }, "errors": [], }, ], [ None, { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 17, }, "errors": [], }, ], ], "opaque": False, "attrib": {"packed": True}, "src": ["/some-path/temp.h", 12], "tag": "packed_foo", "variety": "struct", }, "name": "packed_foo", "type": "typedef", }, { "ctype": { "Klass": "CtypesStruct", "anonymous": False, "attrib": {"aligned": [2], "packed": True}, "errors": [], "members": [ [ "a", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "b", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, ], [ "c", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "d", { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 15, }, "errors": [], }, ], [ None, { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 17, }, "errors": [], }, ], ], "opaque": False, "src": ["/some-path/temp.h", 56], "tag": "pragma_packed_foo2", "variety": "struct", }, "name": "pragma_packed_foo2", "type": "typedef", }, { "ctype": { "Klass": "CtypesStruct", "anonymous": False, "attrib": {}, "errors": [], "members": [ [ "a", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "b", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "char", "signed": True, }, ], [ "c", { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, ], [ "d", { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 15, }, "errors": [], }, ], [ None, { "Klass": "CtypesBitfield", "base": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "bitfield": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 17, }, "errors": [], }, ], ], "opaque": False, "src": ["/some-path/temp.h", 66], "tag": "foo3", "variety": "struct", }, "name": "foo3", "type": "typedef", }, ] compare_json(self, StructuresTest.json, json_ans, True) def test_fields(self): """Test whether fields are built correctly.""" struct_foo = StructuresTest.module.struct_foo self.assertEqual( struct_foo._fields_, [ ("a", ctypes.c_int), ("b", ctypes.c_char), ("c", ctypes.c_int), ("d", ctypes.c_int, 15), ("unnamed_1", ctypes.c_int, 17), ], ) def test_pack(self): """Test whether gcc __attribute__((packed)) is interpreted correctly.""" module = StructuresTest.module unpacked_size = compute_packed(4, [ctypes.c_int] * 3 + [ctypes.c_char]) packed_size = compute_packed(1, [ctypes.c_int] * 3 + [ctypes.c_char]) struct_foo = module.struct_foo struct_packed_foo = module.struct_packed_foo foo_t = module.foo_t packed_foo_t = module.packed_foo_t self.assertEqual(getattr(struct_foo, "_pack_", 0), 0) self.assertEqual(getattr(struct_packed_foo, "_pack_", 0), 1) self.assertEqual(getattr(foo_t, "_pack_", 0), 0) self.assertEqual(getattr(packed_foo_t, "_pack_", -1), 1) self.assertEqual(ctypes.sizeof(struct_foo), unpacked_size) self.assertEqual(ctypes.sizeof(foo_t), unpacked_size) self.assertEqual(ctypes.sizeof(struct_packed_foo), packed_size) self.assertEqual(ctypes.sizeof(packed_foo_t), packed_size) def test_pragma_pack(self): """Test whether #pragma pack(...) is interpreted correctly.""" module = StructuresTest.module packed4_size = compute_packed(4, [ctypes.c_int] * 3 + [ctypes.c_char]) packed2_size = compute_packed(2, [ctypes.c_int] * 3 + [ctypes.c_char]) unpacked_size = compute_packed(4, [ctypes.c_int] * 3 + [ctypes.c_char]) pragma_packed_foo_t = module.pragma_packed_foo_t struct_pragma_packed_foo2 = module.struct_pragma_packed_foo2 struct_foo3 = module.struct_foo3 self.assertEqual(getattr(pragma_packed_foo_t, "_pack_", 0), 4) self.assertEqual(getattr(struct_pragma_packed_foo2, "_pack_", 0), 2) self.assertEqual(getattr(struct_foo3, "_pack_", 0), 0) self.assertEqual(ctypes.sizeof(pragma_packed_foo_t), packed4_size) self.assertEqual(ctypes.sizeof(struct_pragma_packed_foo2), packed2_size) self.assertEqual(ctypes.sizeof(struct_foo3), unpacked_size) def test_typedef_vs_field_id(self): """Test whether local field identifier names can override external typedef names. """ module = StructuresTest.module Int = module.Int id_struct_t = module.id_struct_t self.assertEqual(Int, ctypes.c_int) self.assertEqual(id_struct_t._fields_, [("Int", ctypes.c_int)]) def test_anonymous_tag_uniformity(self): """Test whether anonymous structs with multiple declarations all resolve to the same type. """ module = StructuresTest.module BAR0 = module.BAR0 PBAR0 = module.PBAR0 self.assertEqual(PBAR0._type_, BAR0) class MathTest(unittest.TestCase): """Based on math_functions.py""" @classmethod def setUpClass(cls): header_str = """ #include #define sin_plus_y(x,y) (sin(x) + (y)) """ if sys.platform == "win32": # pick something from %windir%\system32\msvc*dll that include stdlib libraries = ["msvcrt.dll"] libraries = ["msvcrt"] elif sys.platform.startswith("linux"): libraries = ["libm.so.6"] else: libraries = ["libc"] cls.module, _ = generate(header_str, libraries=libraries, all_headers=True) @classmethod def tearDownClass(cls): del cls.module cleanup() def test_sin(self): """Based on math_functions.py""" module = MathTest.module self.assertEqual(module.sin(2), math.sin(2)) def test_sqrt(self): """Based on math_functions.py""" module = MathTest.module self.assertEqual(module.sqrt(4), 2) def local_test(): module.sin("foobar") self.assertRaises(ctypes.ArgumentError, local_test) def test_bad_args_string_not_number(self): """Based on math_functions.py""" module = MathTest.module def local_test(): module.sin("foobar") self.assertRaises(ctypes.ArgumentError, local_test) def test_subcall_sin(self): """Test math with sin(x) in a macro""" module = MathTest.module self.assertEqual(module.sin_plus_y(2, 1), math.sin(2) + 1) class EnumTest(unittest.TestCase): @classmethod def setUpClass(cls): header_str = """ typedef enum { TEST_1 = 0, TEST_2 } test_status_t; """ cls.module, _ = generate(header_str) cls.json, _ = generate(header_str, output_language="json") @classmethod def tearDownClass(cls): del cls.module, cls.json cleanup() def test_enum(self): self.assertEqual(EnumTest.module.TEST_1, 0) self.assertEqual(EnumTest.module.TEST_2, 1) def test_enum_json(self): json_ans = [ { "fields": [ { "ctype": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 0, }, "name": "TEST_1", }, { "ctype": { "Klass": "BinaryExpressionNode", "can_be_ctype": [False, False], "errors": [], "format": "(%s + %s)", "left": { "Klass": "IdentifierExpressionNode", "errors": [], "name": "TEST_1", }, "name": "addition", "right": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 1, }, }, "name": "TEST_2", }, ], "name": "anon_1", "type": "enum", }, {"name": "TEST_1", "type": "constant", "value": "0"}, {"name": "TEST_2", "type": "constant", "value": "(TEST_1 + 1)"}, { "ctype": { "Klass": "CtypesEnum", "anonymous": True, "enumerators": [ [ "TEST_1", { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 0, }, ], [ "TEST_2", { "Klass": "BinaryExpressionNode", "can_be_ctype": [False, False], "errors": [], "format": "(%s + %s)", "left": { "Klass": "IdentifierExpressionNode", "errors": [], "name": "TEST_1", }, "name": "addition", "right": { "Klass": "ConstantExpressionNode", "errors": [], "is_literal": False, "value": 1, }, }, ], ], "errors": [], "opaque": False, "src": ["/some-path/temp.h", 2], "tag": "anon_1", }, "name": "test_status_t", "type": "typedef", }, ] compare_json(self, EnumTest.json, json_ans, True) class PrototypeTest(unittest.TestCase): @classmethod def setUpClass(cls): header_str = """ int bar2(int a); int bar(int); void foo(void); void foo2(void) __attribute__((stdcall)); void * __attribute__((stdcall)) foo3(void); void * __attribute__((stdcall)) * foo4(void); void foo5(void) __attribute__((__stdcall__)); """ cls.json, output = generate(header_str, output_language="json") @classmethod def tearDownClass(cls): del cls.json cleanup() def test_function_prototypes_json(self): json_ans = [ { "args": [ { "Klass": "CtypesSimple", "errors": [], "identifier": "a", "longs": 0, "name": "int", "signed": True, } ], "attrib": {}, "name": "bar2", "return": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "type": "function", "variadic": False, }, { "args": [ { "Klass": "CtypesSimple", "errors": [], "identifier": "", "longs": 0, "name": "int", "signed": True, } ], "attrib": {}, "name": "bar", "return": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "int", "signed": True, }, "type": "function", "variadic": False, }, { "args": [], "attrib": {}, "name": "foo", "return": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "void", "signed": True, }, "type": "function", "variadic": False, }, { "args": [], "attrib": {"stdcall": True}, "name": "foo2", "return": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "void", "signed": True, }, "type": "function", "variadic": False, }, { "args": [], "attrib": {"stdcall": True}, "name": "foo3", "return": { "Klass": "CtypesPointer", "destination": {"Klass": "CtypesSpecial", "errors": [], "name": "c_ubyte"}, "errors": [], "qualifiers": [], }, "type": "function", "variadic": False, }, { "args": [], "attrib": {"stdcall": True}, "name": "foo4", "return": { "Klass": "CtypesPointer", "destination": { "Klass": "CtypesPointer", "destination": { # this return type seems like it really ought to be # the same as for foo3 "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "void", "signed": True, }, "errors": [], "qualifiers": [], }, "errors": [], "qualifiers": [], }, "type": "function", "variadic": False, }, { "args": [], "attrib": {"stdcall": True}, "name": "foo5", "return": { "Klass": "CtypesSimple", "errors": [], "longs": 0, "name": "void", "signed": True, }, "type": "function", "variadic": False, }, ] compare_json(self, PrototypeTest.json, json_ans, True) class LongDoubleTest(unittest.TestCase): "Test correct parsing and generation of 'long double' type" @classmethod def setUpClass(cls): header_str = """ struct foo { long double is_bar; int a; }; """ cls.module, _ = generate(header_str) # , all_headers=True) @classmethod def tearDownClass(cls): del cls.module cleanup() def test_longdouble_type(self): """Test is long double is correctly parsed""" module = LongDoubleTest.module struct_foo = module.struct_foo self.assertEqual( struct_foo._fields_, [("is_bar", ctypes.c_longdouble), ("a", ctypes.c_int)] ) class MainTest(unittest.TestCase): script = os.path.join(TEST_DIR, os.pardir, "run.py") """Test primary entry point used for ctypesgen when called as executable: ctypesgen.main.main() This test does not directly execute the script that is autogenerated by setup.py, but does instead test the entry point as used by that script by executing `run.py`. `run.py` is a local work-alike (as compared to the setuptools-autogenerated script) that is only meant to be run in its *in* the root of source code tree. """ @staticmethod def _exec(args): p = Popen([sys.executable, MainTest.script] + args, stdout=PIPE, stderr=PIPE) o, e = p.communicate() print(o, e, p.returncode) return o, e, p.returncode def test_version(self): """Test version string returned by script interface""" o, e, c = self._exec(["--version"]) self.assertEqual(c, 0) self.assertEqual(o.decode().strip(), ctypesgen_version()) self.assertEqual(e.decode(), "") def test_help(self): """Test that script at least generates a help""" o, e, c = self._exec(["--help"]) self.assertEqual(c, 0) self.assertEqual( o.decode().splitlines()[0].startswith("usage: run.py"), True ) self.assertGreater(len(o), 3000) # its long, so it must be the generated help self.assertEqual(e.decode(), "") def test_invalid_option(self): """Test that script at least generates a help""" o, e, c = self._exec(["random_header.h", "--oh-what-a-goose-i-am"]) self.assertEqual(c, 2) self.assertEqual(o.decode(), "") self.assertEqual( e.decode().splitlines()[0].startswith("usage: run.py"), True ) self.assertIn("error: unrecognized arguments: --oh-what-a-goose-i-am", e.decode()) class UncheckedTest(unittest.TestCase): """Fixing a bug in 1.0.0 - basic type returns of function pointers get treated as pointers""" @classmethod def setUpClass(cls): header_str = """ typedef int (*some_type_of_answer)(void*); """ cls.module, cls.output = generate(header_str, all_headers=False) def test_unchecked_prototype(self): """Test is function type marked UNCHECKED (function pointer returning int) is handled correctly""" module = UncheckedTest.module A = module.some_type_of_answer() self.assertEqual(A.restype, ctypes.c_int) self.assertEqual(A.argtypes, (ctypes.c_void_p,)) @classmethod def tearDownClass(cls): del cls.module cleanup() class ConstantsTest(unittest.TestCase): "Test correct parsing and generation of NULL" @classmethod def setUpClass(cls): header_str = """ #define I_CONST_HEX 0xAFAFAFu #define I_CONST_DEC 15455u #define I_CONST_OCT 0433u #define I_CONST_BIN 0b0101L #define I_ZERO 0 #define I_ONE 1 #define I_ZERO_SUF 0L #define F_CONST_1 155e+0L #define F_CONST_2 35.2e+0f #define F_CONST_3 35.e+0f #define F_CONST_4 0xAFp012l #define F_CONST_5 0x1.FFFFFEp+127f #define F_CONST_6 0xAFAF.p35f struct foo { int a; char b; int c: 0b10; int d : 0xf; int : 17; }; #define CHAR_CONST u'🍌' """ cls.module, _ = generate(header_str) @classmethod def tearDownClass(cls): del ConstantsTest.module cleanup() def test_integer_constants(self): """Test if integer constants is correctly parsed""" self.assertEqual(ConstantsTest.module.I_CONST_HEX, 0xAFAFAF) self.assertEqual(ConstantsTest.module.I_CONST_DEC, int(15455)) self.assertEqual(ConstantsTest.module.I_CONST_OCT, 0o433) self.assertEqual(ConstantsTest.module.I_CONST_BIN, 0b0101) self.assertEqual(ConstantsTest.module.I_CONST_BIN, 5) self.assertEqual(ConstantsTest.module.I_ZERO, int(0)) self.assertEqual(ConstantsTest.module.I_ONE, int(1)) self.assertEqual(ConstantsTest.module.I_ZERO_SUF, int(0)) def test_floating_constants(self): """Test if floating constants is correctly parsed""" self.assertEqual(ConstantsTest.module.F_CONST_1, 155e0) self.assertEqual(ConstantsTest.module.F_CONST_2, 35.2e0) self.assertEqual(ConstantsTest.module.F_CONST_3, 35.0e0) self.assertEqual(ConstantsTest.module.F_CONST_4, float.fromhex("0xAFp012")) self.assertEqual(ConstantsTest.module.F_CONST_5, float.fromhex("0x1.fffffep+127")) self.assertEqual(ConstantsTest.module.F_CONST_6, float.fromhex("0xAFAF.p35")) def test_struct_fields(self): """Test whether fields are built correctly.""" struct_foo = ConstantsTest.module.struct_foo self.assertEqual( struct_foo._fields_, [ ("a", ctypes.c_int), ("b", ctypes.c_char), ("c", ctypes.c_int, 2), ("d", ctypes.c_int, 15), ("unnamed_1", ctypes.c_int, 17), ], ) def test_character_constants(self): """Test if integer constants is correctly parsed""" self.assertEqual(ConstantsTest.module.CHAR_CONST, "🍌") class NULLTest(unittest.TestCase): "Test correct parsing and generation of NULL" @classmethod def setUpClass(cls): header_str = "#define A_NULL_MACRO NULL" cls.module, _ = generate(header_str) # , all_headers=True) @classmethod def tearDownClass(cls): del NULLTest.module cleanup() def test_null_type(self): """Test if NULL is correctly parsed""" self.assertEqual(NULLTest.module.A_NULL_MACRO, None) @unittest.skipUnless(sys.platform == "darwin", "requires Mac") class MacromanEncodeTest(unittest.TestCase): """Test if source file with mac_roman encoding is correctly parsed. This test is skipped on non-mac platforms. """ @classmethod def setUpClass(cls): cls.mac_roman_file = "temp_mac.h" mac_header_str = b""" #define kICHelper "\xa9\\pHelper\xa5" """ with open(cls.mac_roman_file, "wb") as mac_file: mac_file.write(mac_header_str) header_str = """ #include "temp_mac.h" #define MYSTRING kICHelper """ cls.module, _ = generate(header_str) @classmethod def tearDownClass(cls): del cls.module os.remove(cls.mac_roman_file) cleanup() def test_macroman_encoding_source(self): module = MacromanEncodeTest.module expected = b"\xef\xbf\xbd\\pHelper\xef\xbf\xbd".decode("utf-8") self.assertEqual(module.MYSTRING, expected) def main(argv=None): if argv is None: argv = sys.argv set_logging_level(logging.CRITICAL) # do not log anything unittest.main() return 0 if __name__ == "__main__": sys.exit(main()) ================================================ FILE: todo.txt ================================================ 1. Convert defines from "errno.h" into imports from the Python errno module. 2. Search through code for "XXX" and see what can be done. ================================================ FILE: tox.ini ================================================ [tox] envlist = py37, py38, py39 skip_missing_interpreters = true [testenv] deps = pytest commands = pytest -v --showlocals tests/testsuite.py # pytest -v -v -k struct_json --showlocals ctypesgen/test/testsuite.py [testenv:black] deps = six black==23.3.0 basepython = python3.7 commands = black --check --diff setup.py run.py ctypesgen/ --exclude='.*tab.py|ctypesgen/parser/cgrammar.py|ctypesgen/parser/lex.py|ctypesgen/parser/yacc.py' [testenv:package] deps = build twine basepython = python3.7 commands = python -m build twine check dist/*.whl [testenv:upload] deps = build twine basepython = python3.7 commands = python -m build twine upload dist/*.whl