mirror of
https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git
synced 2025-08-14 00:25:46 +02:00
Initial commit
This commit is contained in:
93
.venv/Lib/site-packages/pycparser/__init__.py
Normal file
93
.venv/Lib/site-packages/pycparser/__init__.py
Normal file
@@ -0,0 +1,93 @@
|
||||
#-----------------------------------------------------------------
|
||||
# pycparser: __init__.py
|
||||
#
|
||||
# This package file exports some convenience functions for
|
||||
# interacting with pycparser
|
||||
#
|
||||
# Eli Bendersky [https://eli.thegreenplace.net/]
|
||||
# License: BSD
|
||||
#-----------------------------------------------------------------
|
||||
__all__ = ['c_lexer', 'c_parser', 'c_ast']
|
||||
__version__ = '2.22'
|
||||
|
||||
import io
|
||||
from subprocess import check_output
|
||||
from .c_parser import CParser
|
||||
|
||||
|
||||
def preprocess_file(filename, cpp_path='cpp', cpp_args=''):
|
||||
""" Preprocess a file using cpp.
|
||||
|
||||
filename:
|
||||
Name of the file you want to preprocess.
|
||||
|
||||
cpp_path:
|
||||
cpp_args:
|
||||
Refer to the documentation of parse_file for the meaning of these
|
||||
arguments.
|
||||
|
||||
When successful, returns the preprocessed file's contents.
|
||||
Errors from cpp will be printed out.
|
||||
"""
|
||||
path_list = [cpp_path]
|
||||
if isinstance(cpp_args, list):
|
||||
path_list += cpp_args
|
||||
elif cpp_args != '':
|
||||
path_list += [cpp_args]
|
||||
path_list += [filename]
|
||||
|
||||
try:
|
||||
# Note the use of universal_newlines to treat all newlines
|
||||
# as \n for Python's purpose
|
||||
text = check_output(path_list, universal_newlines=True)
|
||||
except OSError as e:
|
||||
raise RuntimeError("Unable to invoke 'cpp'. " +
|
||||
'Make sure its path was passed correctly\n' +
|
||||
('Original error: %s' % e))
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def parse_file(filename, use_cpp=False, cpp_path='cpp', cpp_args='',
|
||||
parser=None, encoding=None):
|
||||
""" Parse a C file using pycparser.
|
||||
|
||||
filename:
|
||||
Name of the file you want to parse.
|
||||
|
||||
use_cpp:
|
||||
Set to True if you want to execute the C pre-processor
|
||||
on the file prior to parsing it.
|
||||
|
||||
cpp_path:
|
||||
If use_cpp is True, this is the path to 'cpp' on your
|
||||
system. If no path is provided, it attempts to just
|
||||
execute 'cpp', so it must be in your PATH.
|
||||
|
||||
cpp_args:
|
||||
If use_cpp is True, set this to the command line arguments strings
|
||||
to cpp. Be careful with quotes - it's best to pass a raw string
|
||||
(r'') here. For example:
|
||||
r'-I../utils/fake_libc_include'
|
||||
If several arguments are required, pass a list of strings.
|
||||
|
||||
encoding:
|
||||
Encoding to use for the file to parse
|
||||
|
||||
parser:
|
||||
Optional parser object to be used instead of the default CParser
|
||||
|
||||
When successful, an AST is returned. ParseError can be
|
||||
thrown if the file doesn't parse successfully.
|
||||
|
||||
Errors from cpp will be printed out.
|
||||
"""
|
||||
if use_cpp:
|
||||
text = preprocess_file(filename, cpp_path, cpp_args)
|
||||
else:
|
||||
with io.open(filename, encoding=encoding) as f:
|
||||
text = f.read()
|
||||
|
||||
if parser is None:
|
||||
parser = CParser()
|
||||
return parser.parse(text, filename)
|
336
.venv/Lib/site-packages/pycparser/_ast_gen.py
Normal file
336
.venv/Lib/site-packages/pycparser/_ast_gen.py
Normal file
@@ -0,0 +1,336 @@
|
||||
#-----------------------------------------------------------------
|
||||
# _ast_gen.py
|
||||
#
|
||||
# Generates the AST Node classes from a specification given in
|
||||
# a configuration file
|
||||
#
|
||||
# The design of this module was inspired by astgen.py from the
|
||||
# Python 2.5 code-base.
|
||||
#
|
||||
# Eli Bendersky [https://eli.thegreenplace.net/]
|
||||
# License: BSD
|
||||
#-----------------------------------------------------------------
|
||||
from string import Template
|
||||
|
||||
|
||||
class ASTCodeGenerator(object):
|
||||
def __init__(self, cfg_filename='_c_ast.cfg'):
|
||||
""" Initialize the code generator from a configuration
|
||||
file.
|
||||
"""
|
||||
self.cfg_filename = cfg_filename
|
||||
self.node_cfg = [NodeCfg(name, contents)
|
||||
for (name, contents) in self.parse_cfgfile(cfg_filename)]
|
||||
|
||||
def generate(self, file=None):
|
||||
""" Generates the code into file, an open file buffer.
|
||||
"""
|
||||
src = Template(_PROLOGUE_COMMENT).substitute(
|
||||
cfg_filename=self.cfg_filename)
|
||||
|
||||
src += _PROLOGUE_CODE
|
||||
for node_cfg in self.node_cfg:
|
||||
src += node_cfg.generate_source() + '\n\n'
|
||||
|
||||
file.write(src)
|
||||
|
||||
def parse_cfgfile(self, filename):
|
||||
""" Parse the configuration file and yield pairs of
|
||||
(name, contents) for each node.
|
||||
"""
|
||||
with open(filename, "r") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
colon_i = line.find(':')
|
||||
lbracket_i = line.find('[')
|
||||
rbracket_i = line.find(']')
|
||||
if colon_i < 1 or lbracket_i <= colon_i or rbracket_i <= lbracket_i:
|
||||
raise RuntimeError("Invalid line in %s:\n%s\n" % (filename, line))
|
||||
|
||||
name = line[:colon_i]
|
||||
val = line[lbracket_i + 1:rbracket_i]
|
||||
vallist = [v.strip() for v in val.split(',')] if val else []
|
||||
yield name, vallist
|
||||
|
||||
|
||||
class NodeCfg(object):
|
||||
""" Node configuration.
|
||||
|
||||
name: node name
|
||||
contents: a list of contents - attributes and child nodes
|
||||
See comment at the top of the configuration file for details.
|
||||
"""
|
||||
|
||||
def __init__(self, name, contents):
|
||||
self.name = name
|
||||
self.all_entries = []
|
||||
self.attr = []
|
||||
self.child = []
|
||||
self.seq_child = []
|
||||
|
||||
for entry in contents:
|
||||
clean_entry = entry.rstrip('*')
|
||||
self.all_entries.append(clean_entry)
|
||||
|
||||
if entry.endswith('**'):
|
||||
self.seq_child.append(clean_entry)
|
||||
elif entry.endswith('*'):
|
||||
self.child.append(clean_entry)
|
||||
else:
|
||||
self.attr.append(entry)
|
||||
|
||||
def generate_source(self):
|
||||
src = self._gen_init()
|
||||
src += '\n' + self._gen_children()
|
||||
src += '\n' + self._gen_iter()
|
||||
src += '\n' + self._gen_attr_names()
|
||||
return src
|
||||
|
||||
def _gen_init(self):
|
||||
src = "class %s(Node):\n" % self.name
|
||||
|
||||
if self.all_entries:
|
||||
args = ', '.join(self.all_entries)
|
||||
slots = ', '.join("'{0}'".format(e) for e in self.all_entries)
|
||||
slots += ", 'coord', '__weakref__'"
|
||||
arglist = '(self, %s, coord=None)' % args
|
||||
else:
|
||||
slots = "'coord', '__weakref__'"
|
||||
arglist = '(self, coord=None)'
|
||||
|
||||
src += " __slots__ = (%s)\n" % slots
|
||||
src += " def __init__%s:\n" % arglist
|
||||
|
||||
for name in self.all_entries + ['coord']:
|
||||
src += " self.%s = %s\n" % (name, name)
|
||||
|
||||
return src
|
||||
|
||||
def _gen_children(self):
|
||||
src = ' def children(self):\n'
|
||||
|
||||
if self.all_entries:
|
||||
src += ' nodelist = []\n'
|
||||
|
||||
for child in self.child:
|
||||
src += (
|
||||
' if self.%(child)s is not None:' +
|
||||
' nodelist.append(("%(child)s", self.%(child)s))\n') % (
|
||||
dict(child=child))
|
||||
|
||||
for seq_child in self.seq_child:
|
||||
src += (
|
||||
' for i, child in enumerate(self.%(child)s or []):\n'
|
||||
' nodelist.append(("%(child)s[%%d]" %% i, child))\n') % (
|
||||
dict(child=seq_child))
|
||||
|
||||
src += ' return tuple(nodelist)\n'
|
||||
else:
|
||||
src += ' return ()\n'
|
||||
|
||||
return src
|
||||
|
||||
def _gen_iter(self):
|
||||
src = ' def __iter__(self):\n'
|
||||
|
||||
if self.all_entries:
|
||||
for child in self.child:
|
||||
src += (
|
||||
' if self.%(child)s is not None:\n' +
|
||||
' yield self.%(child)s\n') % (dict(child=child))
|
||||
|
||||
for seq_child in self.seq_child:
|
||||
src += (
|
||||
' for child in (self.%(child)s or []):\n'
|
||||
' yield child\n') % (dict(child=seq_child))
|
||||
|
||||
if not (self.child or self.seq_child):
|
||||
# Empty generator
|
||||
src += (
|
||||
' return\n' +
|
||||
' yield\n')
|
||||
else:
|
||||
# Empty generator
|
||||
src += (
|
||||
' return\n' +
|
||||
' yield\n')
|
||||
|
||||
return src
|
||||
|
||||
def _gen_attr_names(self):
|
||||
src = " attr_names = (" + ''.join("%r, " % nm for nm in self.attr) + ')'
|
||||
return src
|
||||
|
||||
|
||||
_PROLOGUE_COMMENT = \
|
||||
r'''#-----------------------------------------------------------------
|
||||
# ** ATTENTION **
|
||||
# This code was automatically generated from the file:
|
||||
# $cfg_filename
|
||||
#
|
||||
# Do not modify it directly. Modify the configuration file and
|
||||
# run the generator again.
|
||||
# ** ** *** ** **
|
||||
#
|
||||
# pycparser: c_ast.py
|
||||
#
|
||||
# AST Node classes.
|
||||
#
|
||||
# Eli Bendersky [https://eli.thegreenplace.net/]
|
||||
# License: BSD
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
'''
|
||||
|
||||
_PROLOGUE_CODE = r'''
|
||||
import sys
|
||||
|
||||
def _repr(obj):
|
||||
"""
|
||||
Get the representation of an object, with dedicated pprint-like format for lists.
|
||||
"""
|
||||
if isinstance(obj, list):
|
||||
return '[' + (',\n '.join((_repr(e).replace('\n', '\n ') for e in obj))) + '\n]'
|
||||
else:
|
||||
return repr(obj)
|
||||
|
||||
class Node(object):
|
||||
__slots__ = ()
|
||||
""" Abstract base class for AST nodes.
|
||||
"""
|
||||
def __repr__(self):
|
||||
""" Generates a python representation of the current node
|
||||
"""
|
||||
result = self.__class__.__name__ + '('
|
||||
|
||||
indent = ''
|
||||
separator = ''
|
||||
for name in self.__slots__[:-2]:
|
||||
result += separator
|
||||
result += indent
|
||||
result += name + '=' + (_repr(getattr(self, name)).replace('\n', '\n ' + (' ' * (len(name) + len(self.__class__.__name__)))))
|
||||
|
||||
separator = ','
|
||||
indent = '\n ' + (' ' * len(self.__class__.__name__))
|
||||
|
||||
result += indent + ')'
|
||||
|
||||
return result
|
||||
|
||||
def children(self):
|
||||
""" A sequence of all children that are Nodes
|
||||
"""
|
||||
pass
|
||||
|
||||
def show(self, buf=sys.stdout, offset=0, attrnames=False, nodenames=False, showcoord=False, _my_node_name=None):
|
||||
""" Pretty print the Node and all its attributes and
|
||||
children (recursively) to a buffer.
|
||||
|
||||
buf:
|
||||
Open IO buffer into which the Node is printed.
|
||||
|
||||
offset:
|
||||
Initial offset (amount of leading spaces)
|
||||
|
||||
attrnames:
|
||||
True if you want to see the attribute names in
|
||||
name=value pairs. False to only see the values.
|
||||
|
||||
nodenames:
|
||||
True if you want to see the actual node names
|
||||
within their parents.
|
||||
|
||||
showcoord:
|
||||
Do you want the coordinates of each Node to be
|
||||
displayed.
|
||||
"""
|
||||
lead = ' ' * offset
|
||||
if nodenames and _my_node_name is not None:
|
||||
buf.write(lead + self.__class__.__name__+ ' <' + _my_node_name + '>: ')
|
||||
else:
|
||||
buf.write(lead + self.__class__.__name__+ ': ')
|
||||
|
||||
if self.attr_names:
|
||||
if attrnames:
|
||||
nvlist = [(n, getattr(self,n)) for n in self.attr_names]
|
||||
attrstr = ', '.join('%s=%s' % nv for nv in nvlist)
|
||||
else:
|
||||
vlist = [getattr(self, n) for n in self.attr_names]
|
||||
attrstr = ', '.join('%s' % v for v in vlist)
|
||||
buf.write(attrstr)
|
||||
|
||||
if showcoord:
|
||||
buf.write(' (at %s)' % self.coord)
|
||||
buf.write('\n')
|
||||
|
||||
for (child_name, child) in self.children():
|
||||
child.show(
|
||||
buf,
|
||||
offset=offset + 2,
|
||||
attrnames=attrnames,
|
||||
nodenames=nodenames,
|
||||
showcoord=showcoord,
|
||||
_my_node_name=child_name)
|
||||
|
||||
|
||||
class NodeVisitor(object):
|
||||
""" A base NodeVisitor class for visiting c_ast nodes.
|
||||
Subclass it and define your own visit_XXX methods, where
|
||||
XXX is the class name you want to visit with these
|
||||
methods.
|
||||
|
||||
For example:
|
||||
|
||||
class ConstantVisitor(NodeVisitor):
|
||||
def __init__(self):
|
||||
self.values = []
|
||||
|
||||
def visit_Constant(self, node):
|
||||
self.values.append(node.value)
|
||||
|
||||
Creates a list of values of all the constant nodes
|
||||
encountered below the given node. To use it:
|
||||
|
||||
cv = ConstantVisitor()
|
||||
cv.visit(node)
|
||||
|
||||
Notes:
|
||||
|
||||
* generic_visit() will be called for AST nodes for which
|
||||
no visit_XXX method was defined.
|
||||
* The children of nodes for which a visit_XXX was
|
||||
defined will not be visited - if you need this, call
|
||||
generic_visit() on the node.
|
||||
You can use:
|
||||
NodeVisitor.generic_visit(self, node)
|
||||
* Modeled after Python's own AST visiting facilities
|
||||
(the ast module of Python 3.0)
|
||||
"""
|
||||
|
||||
_method_cache = None
|
||||
|
||||
def visit(self, node):
|
||||
""" Visit a node.
|
||||
"""
|
||||
|
||||
if self._method_cache is None:
|
||||
self._method_cache = {}
|
||||
|
||||
visitor = self._method_cache.get(node.__class__.__name__, None)
|
||||
if visitor is None:
|
||||
method = 'visit_' + node.__class__.__name__
|
||||
visitor = getattr(self, method, self.generic_visit)
|
||||
self._method_cache[node.__class__.__name__] = visitor
|
||||
|
||||
return visitor(node)
|
||||
|
||||
def generic_visit(self, node):
|
||||
""" Called if no explicit visitor function exists for a
|
||||
node. Implements preorder visiting of the node.
|
||||
"""
|
||||
for c in node:
|
||||
self.visit(c)
|
||||
|
||||
'''
|
40
.venv/Lib/site-packages/pycparser/_build_tables.py
Normal file
40
.venv/Lib/site-packages/pycparser/_build_tables.py
Normal file
@@ -0,0 +1,40 @@
|
||||
#-----------------------------------------------------------------
|
||||
# pycparser: _build_tables.py
|
||||
#
|
||||
# A dummy for generating the lexing/parsing tables and and
|
||||
# compiling them into .pyc for faster execution in optimized mode.
|
||||
# Also generates AST code from the configuration file.
|
||||
# Should be called from the pycparser directory.
|
||||
#
|
||||
# Eli Bendersky [https://eli.thegreenplace.net/]
|
||||
# License: BSD
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
# Insert '.' and '..' as first entries to the search path for modules.
|
||||
# Restricted environments like embeddable python do not include the
|
||||
# current working directory on startup.
|
||||
import importlib
|
||||
import sys
|
||||
sys.path[0:0] = ['.', '..']
|
||||
|
||||
# Generate c_ast.py
|
||||
from _ast_gen import ASTCodeGenerator
|
||||
ast_gen = ASTCodeGenerator('_c_ast.cfg')
|
||||
ast_gen.generate(open('c_ast.py', 'w'))
|
||||
|
||||
from pycparser import c_parser
|
||||
|
||||
# Generates the tables
|
||||
#
|
||||
c_parser.CParser(
|
||||
lex_optimize=True,
|
||||
yacc_debug=False,
|
||||
yacc_optimize=True)
|
||||
|
||||
# Load to compile into .pyc
|
||||
#
|
||||
importlib.invalidate_caches()
|
||||
|
||||
import lextab
|
||||
import yacctab
|
||||
import c_ast
|
195
.venv/Lib/site-packages/pycparser/_c_ast.cfg
Normal file
195
.venv/Lib/site-packages/pycparser/_c_ast.cfg
Normal file
@@ -0,0 +1,195 @@
|
||||
#-----------------------------------------------------------------
|
||||
# pycparser: _c_ast.cfg
|
||||
#
|
||||
# Defines the AST Node classes used in pycparser.
|
||||
#
|
||||
# Each entry is a Node sub-class name, listing the attributes
|
||||
# and child nodes of the class:
|
||||
# <name>* - a child node
|
||||
# <name>** - a sequence of child nodes
|
||||
# <name> - an attribute
|
||||
#
|
||||
# Eli Bendersky [https://eli.thegreenplace.net/]
|
||||
# License: BSD
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
# ArrayDecl is a nested declaration of an array with the given type.
|
||||
# dim: the dimension (for example, constant 42)
|
||||
# dim_quals: list of dimension qualifiers, to support C99's allowing 'const'
|
||||
# and 'static' within the array dimension in function declarations.
|
||||
ArrayDecl: [type*, dim*, dim_quals]
|
||||
|
||||
ArrayRef: [name*, subscript*]
|
||||
|
||||
# op: =, +=, /= etc.
|
||||
#
|
||||
Assignment: [op, lvalue*, rvalue*]
|
||||
|
||||
Alignas: [alignment*]
|
||||
|
||||
BinaryOp: [op, left*, right*]
|
||||
|
||||
Break: []
|
||||
|
||||
Case: [expr*, stmts**]
|
||||
|
||||
Cast: [to_type*, expr*]
|
||||
|
||||
# Compound statement in C99 is a list of block items (declarations or
|
||||
# statements).
|
||||
#
|
||||
Compound: [block_items**]
|
||||
|
||||
# Compound literal (anonymous aggregate) for C99.
|
||||
# (type-name) {initializer_list}
|
||||
# type: the typename
|
||||
# init: InitList for the initializer list
|
||||
#
|
||||
CompoundLiteral: [type*, init*]
|
||||
|
||||
# type: int, char, float, string, etc.
|
||||
#
|
||||
Constant: [type, value]
|
||||
|
||||
Continue: []
|
||||
|
||||
# name: the variable being declared
|
||||
# quals: list of qualifiers (const, volatile)
|
||||
# funcspec: list function specifiers (i.e. inline in C99)
|
||||
# storage: list of storage specifiers (extern, register, etc.)
|
||||
# type: declaration type (probably nested with all the modifiers)
|
||||
# init: initialization value, or None
|
||||
# bitsize: bit field size, or None
|
||||
#
|
||||
Decl: [name, quals, align, storage, funcspec, type*, init*, bitsize*]
|
||||
|
||||
DeclList: [decls**]
|
||||
|
||||
Default: [stmts**]
|
||||
|
||||
DoWhile: [cond*, stmt*]
|
||||
|
||||
# Represents the ellipsis (...) parameter in a function
|
||||
# declaration
|
||||
#
|
||||
EllipsisParam: []
|
||||
|
||||
# An empty statement (a semicolon ';' on its own)
|
||||
#
|
||||
EmptyStatement: []
|
||||
|
||||
# Enumeration type specifier
|
||||
# name: an optional ID
|
||||
# values: an EnumeratorList
|
||||
#
|
||||
Enum: [name, values*]
|
||||
|
||||
# A name/value pair for enumeration values
|
||||
#
|
||||
Enumerator: [name, value*]
|
||||
|
||||
# A list of enumerators
|
||||
#
|
||||
EnumeratorList: [enumerators**]
|
||||
|
||||
# A list of expressions separated by the comma operator.
|
||||
#
|
||||
ExprList: [exprs**]
|
||||
|
||||
# This is the top of the AST, representing a single C file (a
|
||||
# translation unit in K&R jargon). It contains a list of
|
||||
# "external-declaration"s, which is either declarations (Decl),
|
||||
# Typedef or function definitions (FuncDef).
|
||||
#
|
||||
FileAST: [ext**]
|
||||
|
||||
# for (init; cond; next) stmt
|
||||
#
|
||||
For: [init*, cond*, next*, stmt*]
|
||||
|
||||
# name: Id
|
||||
# args: ExprList
|
||||
#
|
||||
FuncCall: [name*, args*]
|
||||
|
||||
# type <decl>(args)
|
||||
#
|
||||
FuncDecl: [args*, type*]
|
||||
|
||||
# Function definition: a declarator for the function name and
|
||||
# a body, which is a compound statement.
|
||||
# There's an optional list of parameter declarations for old
|
||||
# K&R-style definitions
|
||||
#
|
||||
FuncDef: [decl*, param_decls**, body*]
|
||||
|
||||
Goto: [name]
|
||||
|
||||
ID: [name]
|
||||
|
||||
# Holder for types that are a simple identifier (e.g. the built
|
||||
# ins void, char etc. and typedef-defined types)
|
||||
#
|
||||
IdentifierType: [names]
|
||||
|
||||
If: [cond*, iftrue*, iffalse*]
|
||||
|
||||
# An initialization list used for compound literals.
|
||||
#
|
||||
InitList: [exprs**]
|
||||
|
||||
Label: [name, stmt*]
|
||||
|
||||
# A named initializer for C99.
|
||||
# The name of a NamedInitializer is a sequence of Nodes, because
|
||||
# names can be hierarchical and contain constant expressions.
|
||||
#
|
||||
NamedInitializer: [name**, expr*]
|
||||
|
||||
# a list of comma separated function parameter declarations
|
||||
#
|
||||
ParamList: [params**]
|
||||
|
||||
PtrDecl: [quals, type*]
|
||||
|
||||
Return: [expr*]
|
||||
|
||||
StaticAssert: [cond*, message*]
|
||||
|
||||
# name: struct tag name
|
||||
# decls: declaration of members
|
||||
#
|
||||
Struct: [name, decls**]
|
||||
|
||||
# type: . or ->
|
||||
# name.field or name->field
|
||||
#
|
||||
StructRef: [name*, type, field*]
|
||||
|
||||
Switch: [cond*, stmt*]
|
||||
|
||||
# cond ? iftrue : iffalse
|
||||
#
|
||||
TernaryOp: [cond*, iftrue*, iffalse*]
|
||||
|
||||
# A base type declaration
|
||||
#
|
||||
TypeDecl: [declname, quals, align, type*]
|
||||
|
||||
# A typedef declaration.
|
||||
# Very similar to Decl, but without some attributes
|
||||
#
|
||||
Typedef: [name, quals, storage, type*]
|
||||
|
||||
Typename: [name, quals, align, type*]
|
||||
|
||||
UnaryOp: [op, expr*]
|
||||
|
||||
# name: union tag name
|
||||
# decls: declaration of members
|
||||
#
|
||||
Union: [name, decls**]
|
||||
|
||||
While: [cond*, stmt*]
|
||||
|
||||
Pragma: [string]
|
164
.venv/Lib/site-packages/pycparser/ast_transforms.py
Normal file
164
.venv/Lib/site-packages/pycparser/ast_transforms.py
Normal file
@@ -0,0 +1,164 @@
|
||||
#------------------------------------------------------------------------------
|
||||
# pycparser: ast_transforms.py
|
||||
#
|
||||
# Some utilities used by the parser to create a friendlier AST.
|
||||
#
|
||||
# Eli Bendersky [https://eli.thegreenplace.net/]
|
||||
# License: BSD
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
from . import c_ast
|
||||
|
||||
|
||||
def fix_switch_cases(switch_node):
|
||||
""" The 'case' statements in a 'switch' come out of parsing with one
|
||||
child node, so subsequent statements are just tucked to the parent
|
||||
Compound. Additionally, consecutive (fall-through) case statements
|
||||
come out messy. This is a peculiarity of the C grammar. The following:
|
||||
|
||||
switch (myvar) {
|
||||
case 10:
|
||||
k = 10;
|
||||
p = k + 1;
|
||||
return 10;
|
||||
case 20:
|
||||
case 30:
|
||||
return 20;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Creates this tree (pseudo-dump):
|
||||
|
||||
Switch
|
||||
ID: myvar
|
||||
Compound:
|
||||
Case 10:
|
||||
k = 10
|
||||
p = k + 1
|
||||
return 10
|
||||
Case 20:
|
||||
Case 30:
|
||||
return 20
|
||||
Default:
|
||||
break
|
||||
|
||||
The goal of this transform is to fix this mess, turning it into the
|
||||
following:
|
||||
|
||||
Switch
|
||||
ID: myvar
|
||||
Compound:
|
||||
Case 10:
|
||||
k = 10
|
||||
p = k + 1
|
||||
return 10
|
||||
Case 20:
|
||||
Case 30:
|
||||
return 20
|
||||
Default:
|
||||
break
|
||||
|
||||
A fixed AST node is returned. The argument may be modified.
|
||||
"""
|
||||
assert isinstance(switch_node, c_ast.Switch)
|
||||
if not isinstance(switch_node.stmt, c_ast.Compound):
|
||||
return switch_node
|
||||
|
||||
# The new Compound child for the Switch, which will collect children in the
|
||||
# correct order
|
||||
new_compound = c_ast.Compound([], switch_node.stmt.coord)
|
||||
|
||||
# The last Case/Default node
|
||||
last_case = None
|
||||
|
||||
# Goes over the children of the Compound below the Switch, adding them
|
||||
# either directly below new_compound or below the last Case as appropriate
|
||||
# (for `switch(cond) {}`, block_items would have been None)
|
||||
for child in (switch_node.stmt.block_items or []):
|
||||
if isinstance(child, (c_ast.Case, c_ast.Default)):
|
||||
# If it's a Case/Default:
|
||||
# 1. Add it to the Compound and mark as "last case"
|
||||
# 2. If its immediate child is also a Case or Default, promote it
|
||||
# to a sibling.
|
||||
new_compound.block_items.append(child)
|
||||
_extract_nested_case(child, new_compound.block_items)
|
||||
last_case = new_compound.block_items[-1]
|
||||
else:
|
||||
# Other statements are added as children to the last case, if it
|
||||
# exists.
|
||||
if last_case is None:
|
||||
new_compound.block_items.append(child)
|
||||
else:
|
||||
last_case.stmts.append(child)
|
||||
|
||||
switch_node.stmt = new_compound
|
||||
return switch_node
|
||||
|
||||
|
||||
def _extract_nested_case(case_node, stmts_list):
|
||||
""" Recursively extract consecutive Case statements that are made nested
|
||||
by the parser and add them to the stmts_list.
|
||||
"""
|
||||
if isinstance(case_node.stmts[0], (c_ast.Case, c_ast.Default)):
|
||||
stmts_list.append(case_node.stmts.pop())
|
||||
_extract_nested_case(stmts_list[-1], stmts_list)
|
||||
|
||||
|
||||
def fix_atomic_specifiers(decl):
|
||||
""" Atomic specifiers like _Atomic(type) are unusually structured,
|
||||
conferring a qualifier upon the contained type.
|
||||
|
||||
This function fixes a decl with atomic specifiers to have a sane AST
|
||||
structure, by removing spurious Typename->TypeDecl pairs and attaching
|
||||
the _Atomic qualifier in the right place.
|
||||
"""
|
||||
# There can be multiple levels of _Atomic in a decl; fix them until a
|
||||
# fixed point is reached.
|
||||
while True:
|
||||
decl, found = _fix_atomic_specifiers_once(decl)
|
||||
if not found:
|
||||
break
|
||||
|
||||
# Make sure to add an _Atomic qual on the topmost decl if needed. Also
|
||||
# restore the declname on the innermost TypeDecl (it gets placed in the
|
||||
# wrong place during construction).
|
||||
typ = decl
|
||||
while not isinstance(typ, c_ast.TypeDecl):
|
||||
try:
|
||||
typ = typ.type
|
||||
except AttributeError:
|
||||
return decl
|
||||
if '_Atomic' in typ.quals and '_Atomic' not in decl.quals:
|
||||
decl.quals.append('_Atomic')
|
||||
if typ.declname is None:
|
||||
typ.declname = decl.name
|
||||
|
||||
return decl
|
||||
|
||||
|
||||
def _fix_atomic_specifiers_once(decl):
|
||||
""" Performs one 'fix' round of atomic specifiers.
|
||||
Returns (modified_decl, found) where found is True iff a fix was made.
|
||||
"""
|
||||
parent = decl
|
||||
grandparent = None
|
||||
node = decl.type
|
||||
while node is not None:
|
||||
if isinstance(node, c_ast.Typename) and '_Atomic' in node.quals:
|
||||
break
|
||||
try:
|
||||
grandparent = parent
|
||||
parent = node
|
||||
node = node.type
|
||||
except AttributeError:
|
||||
# If we've reached a node without a `type` field, it means we won't
|
||||
# find what we're looking for at this point; give up the search
|
||||
# and return the original decl unmodified.
|
||||
return decl, False
|
||||
|
||||
assert isinstance(parent, c_ast.TypeDecl)
|
||||
grandparent.type = node.type
|
||||
if '_Atomic' not in node.type.quals:
|
||||
node.type.quals.append('_Atomic')
|
||||
return decl, True
|
1125
.venv/Lib/site-packages/pycparser/c_ast.py
Normal file
1125
.venv/Lib/site-packages/pycparser/c_ast.py
Normal file
File diff suppressed because it is too large
Load Diff
502
.venv/Lib/site-packages/pycparser/c_generator.py
Normal file
502
.venv/Lib/site-packages/pycparser/c_generator.py
Normal file
@@ -0,0 +1,502 @@
|
||||
#------------------------------------------------------------------------------
|
||||
# pycparser: c_generator.py
|
||||
#
|
||||
# C code generator from pycparser AST nodes.
|
||||
#
|
||||
# Eli Bendersky [https://eli.thegreenplace.net/]
|
||||
# License: BSD
|
||||
#------------------------------------------------------------------------------
|
||||
from . import c_ast
|
||||
|
||||
|
||||
class CGenerator(object):
|
||||
""" Uses the same visitor pattern as c_ast.NodeVisitor, but modified to
|
||||
return a value from each visit method, using string accumulation in
|
||||
generic_visit.
|
||||
"""
|
||||
def __init__(self, reduce_parentheses=False):
|
||||
""" Constructs C-code generator
|
||||
|
||||
reduce_parentheses:
|
||||
if True, eliminates needless parentheses on binary operators
|
||||
"""
|
||||
# Statements start with indentation of self.indent_level spaces, using
|
||||
# the _make_indent method.
|
||||
self.indent_level = 0
|
||||
self.reduce_parentheses = reduce_parentheses
|
||||
|
||||
def _make_indent(self):
|
||||
return ' ' * self.indent_level
|
||||
|
||||
def visit(self, node):
|
||||
method = 'visit_' + node.__class__.__name__
|
||||
return getattr(self, method, self.generic_visit)(node)
|
||||
|
||||
def generic_visit(self, node):
|
||||
if node is None:
|
||||
return ''
|
||||
else:
|
||||
return ''.join(self.visit(c) for c_name, c in node.children())
|
||||
|
||||
def visit_Constant(self, n):
|
||||
return n.value
|
||||
|
||||
def visit_ID(self, n):
|
||||
return n.name
|
||||
|
||||
def visit_Pragma(self, n):
|
||||
ret = '#pragma'
|
||||
if n.string:
|
||||
ret += ' ' + n.string
|
||||
return ret
|
||||
|
||||
def visit_ArrayRef(self, n):
|
||||
arrref = self._parenthesize_unless_simple(n.name)
|
||||
return arrref + '[' + self.visit(n.subscript) + ']'
|
||||
|
||||
def visit_StructRef(self, n):
|
||||
sref = self._parenthesize_unless_simple(n.name)
|
||||
return sref + n.type + self.visit(n.field)
|
||||
|
||||
def visit_FuncCall(self, n):
|
||||
fref = self._parenthesize_unless_simple(n.name)
|
||||
return fref + '(' + self.visit(n.args) + ')'
|
||||
|
||||
def visit_UnaryOp(self, n):
|
||||
if n.op == 'sizeof':
|
||||
# Always parenthesize the argument of sizeof since it can be
|
||||
# a name.
|
||||
return 'sizeof(%s)' % self.visit(n.expr)
|
||||
else:
|
||||
operand = self._parenthesize_unless_simple(n.expr)
|
||||
if n.op == 'p++':
|
||||
return '%s++' % operand
|
||||
elif n.op == 'p--':
|
||||
return '%s--' % operand
|
||||
else:
|
||||
return '%s%s' % (n.op, operand)
|
||||
|
||||
# Precedence map of binary operators:
|
||||
precedence_map = {
|
||||
# Should be in sync with c_parser.CParser.precedence
|
||||
# Higher numbers are stronger binding
|
||||
'||': 0, # weakest binding
|
||||
'&&': 1,
|
||||
'|': 2,
|
||||
'^': 3,
|
||||
'&': 4,
|
||||
'==': 5, '!=': 5,
|
||||
'>': 6, '>=': 6, '<': 6, '<=': 6,
|
||||
'>>': 7, '<<': 7,
|
||||
'+': 8, '-': 8,
|
||||
'*': 9, '/': 9, '%': 9 # strongest binding
|
||||
}
|
||||
|
||||
def visit_BinaryOp(self, n):
|
||||
# Note: all binary operators are left-to-right associative
|
||||
#
|
||||
# If `n.left.op` has a stronger or equally binding precedence in
|
||||
# comparison to `n.op`, no parenthesis are needed for the left:
|
||||
# e.g., `(a*b) + c` is equivalent to `a*b + c`, as well as
|
||||
# `(a+b) - c` is equivalent to `a+b - c` (same precedence).
|
||||
# If the left operator is weaker binding than the current, then
|
||||
# parentheses are necessary:
|
||||
# e.g., `(a+b) * c` is NOT equivalent to `a+b * c`.
|
||||
lval_str = self._parenthesize_if(
|
||||
n.left,
|
||||
lambda d: not (self._is_simple_node(d) or
|
||||
self.reduce_parentheses and isinstance(d, c_ast.BinaryOp) and
|
||||
self.precedence_map[d.op] >= self.precedence_map[n.op]))
|
||||
# If `n.right.op` has a stronger -but not equal- binding precedence,
|
||||
# parenthesis can be omitted on the right:
|
||||
# e.g., `a + (b*c)` is equivalent to `a + b*c`.
|
||||
# If the right operator is weaker or equally binding, then parentheses
|
||||
# are necessary:
|
||||
# e.g., `a * (b+c)` is NOT equivalent to `a * b+c` and
|
||||
# `a - (b+c)` is NOT equivalent to `a - b+c` (same precedence).
|
||||
rval_str = self._parenthesize_if(
|
||||
n.right,
|
||||
lambda d: not (self._is_simple_node(d) or
|
||||
self.reduce_parentheses and isinstance(d, c_ast.BinaryOp) and
|
||||
self.precedence_map[d.op] > self.precedence_map[n.op]))
|
||||
return '%s %s %s' % (lval_str, n.op, rval_str)
|
||||
|
||||
def visit_Assignment(self, n):
|
||||
rval_str = self._parenthesize_if(
|
||||
n.rvalue,
|
||||
lambda n: isinstance(n, c_ast.Assignment))
|
||||
return '%s %s %s' % (self.visit(n.lvalue), n.op, rval_str)
|
||||
|
||||
def visit_IdentifierType(self, n):
|
||||
return ' '.join(n.names)
|
||||
|
||||
def _visit_expr(self, n):
|
||||
if isinstance(n, c_ast.InitList):
|
||||
return '{' + self.visit(n) + '}'
|
||||
elif isinstance(n, c_ast.ExprList):
|
||||
return '(' + self.visit(n) + ')'
|
||||
else:
|
||||
return self.visit(n)
|
||||
|
||||
def visit_Decl(self, n, no_type=False):
|
||||
# no_type is used when a Decl is part of a DeclList, where the type is
|
||||
# explicitly only for the first declaration in a list.
|
||||
#
|
||||
s = n.name if no_type else self._generate_decl(n)
|
||||
if n.bitsize: s += ' : ' + self.visit(n.bitsize)
|
||||
if n.init:
|
||||
s += ' = ' + self._visit_expr(n.init)
|
||||
return s
|
||||
|
||||
def visit_DeclList(self, n):
|
||||
s = self.visit(n.decls[0])
|
||||
if len(n.decls) > 1:
|
||||
s += ', ' + ', '.join(self.visit_Decl(decl, no_type=True)
|
||||
for decl in n.decls[1:])
|
||||
return s
|
||||
|
||||
def visit_Typedef(self, n):
|
||||
s = ''
|
||||
if n.storage: s += ' '.join(n.storage) + ' '
|
||||
s += self._generate_type(n.type)
|
||||
return s
|
||||
|
||||
def visit_Cast(self, n):
|
||||
s = '(' + self._generate_type(n.to_type, emit_declname=False) + ')'
|
||||
return s + ' ' + self._parenthesize_unless_simple(n.expr)
|
||||
|
||||
def visit_ExprList(self, n):
|
||||
visited_subexprs = []
|
||||
for expr in n.exprs:
|
||||
visited_subexprs.append(self._visit_expr(expr))
|
||||
return ', '.join(visited_subexprs)
|
||||
|
||||
def visit_InitList(self, n):
|
||||
visited_subexprs = []
|
||||
for expr in n.exprs:
|
||||
visited_subexprs.append(self._visit_expr(expr))
|
||||
return ', '.join(visited_subexprs)
|
||||
|
||||
def visit_Enum(self, n):
|
||||
return self._generate_struct_union_enum(n, name='enum')
|
||||
|
||||
def visit_Alignas(self, n):
|
||||
return '_Alignas({})'.format(self.visit(n.alignment))
|
||||
|
||||
def visit_Enumerator(self, n):
|
||||
if not n.value:
|
||||
return '{indent}{name},\n'.format(
|
||||
indent=self._make_indent(),
|
||||
name=n.name,
|
||||
)
|
||||
else:
|
||||
return '{indent}{name} = {value},\n'.format(
|
||||
indent=self._make_indent(),
|
||||
name=n.name,
|
||||
value=self.visit(n.value),
|
||||
)
|
||||
|
||||
def visit_FuncDef(self, n):
|
||||
decl = self.visit(n.decl)
|
||||
self.indent_level = 0
|
||||
body = self.visit(n.body)
|
||||
if n.param_decls:
|
||||
knrdecls = ';\n'.join(self.visit(p) for p in n.param_decls)
|
||||
return decl + '\n' + knrdecls + ';\n' + body + '\n'
|
||||
else:
|
||||
return decl + '\n' + body + '\n'
|
||||
|
||||
def visit_FileAST(self, n):
|
||||
s = ''
|
||||
for ext in n.ext:
|
||||
if isinstance(ext, c_ast.FuncDef):
|
||||
s += self.visit(ext)
|
||||
elif isinstance(ext, c_ast.Pragma):
|
||||
s += self.visit(ext) + '\n'
|
||||
else:
|
||||
s += self.visit(ext) + ';\n'
|
||||
return s
|
||||
|
||||
def visit_Compound(self, n):
|
||||
s = self._make_indent() + '{\n'
|
||||
self.indent_level += 2
|
||||
if n.block_items:
|
||||
s += ''.join(self._generate_stmt(stmt) for stmt in n.block_items)
|
||||
self.indent_level -= 2
|
||||
s += self._make_indent() + '}\n'
|
||||
return s
|
||||
|
||||
def visit_CompoundLiteral(self, n):
|
||||
return '(' + self.visit(n.type) + '){' + self.visit(n.init) + '}'
|
||||
|
||||
|
||||
def visit_EmptyStatement(self, n):
|
||||
return ';'
|
||||
|
||||
def visit_ParamList(self, n):
|
||||
return ', '.join(self.visit(param) for param in n.params)
|
||||
|
||||
def visit_Return(self, n):
|
||||
s = 'return'
|
||||
if n.expr: s += ' ' + self.visit(n.expr)
|
||||
return s + ';'
|
||||
|
||||
def visit_Break(self, n):
|
||||
return 'break;'
|
||||
|
||||
def visit_Continue(self, n):
|
||||
return 'continue;'
|
||||
|
||||
def visit_TernaryOp(self, n):
|
||||
s = '(' + self._visit_expr(n.cond) + ') ? '
|
||||
s += '(' + self._visit_expr(n.iftrue) + ') : '
|
||||
s += '(' + self._visit_expr(n.iffalse) + ')'
|
||||
return s
|
||||
|
||||
def visit_If(self, n):
|
||||
s = 'if ('
|
||||
if n.cond: s += self.visit(n.cond)
|
||||
s += ')\n'
|
||||
s += self._generate_stmt(n.iftrue, add_indent=True)
|
||||
if n.iffalse:
|
||||
s += self._make_indent() + 'else\n'
|
||||
s += self._generate_stmt(n.iffalse, add_indent=True)
|
||||
return s
|
||||
|
||||
def visit_For(self, n):
|
||||
s = 'for ('
|
||||
if n.init: s += self.visit(n.init)
|
||||
s += ';'
|
||||
if n.cond: s += ' ' + self.visit(n.cond)
|
||||
s += ';'
|
||||
if n.next: s += ' ' + self.visit(n.next)
|
||||
s += ')\n'
|
||||
s += self._generate_stmt(n.stmt, add_indent=True)
|
||||
return s
|
||||
|
||||
def visit_While(self, n):
|
||||
s = 'while ('
|
||||
if n.cond: s += self.visit(n.cond)
|
||||
s += ')\n'
|
||||
s += self._generate_stmt(n.stmt, add_indent=True)
|
||||
return s
|
||||
|
||||
def visit_DoWhile(self, n):
|
||||
s = 'do\n'
|
||||
s += self._generate_stmt(n.stmt, add_indent=True)
|
||||
s += self._make_indent() + 'while ('
|
||||
if n.cond: s += self.visit(n.cond)
|
||||
s += ');'
|
||||
return s
|
||||
|
||||
def visit_StaticAssert(self, n):
|
||||
s = '_Static_assert('
|
||||
s += self.visit(n.cond)
|
||||
if n.message:
|
||||
s += ','
|
||||
s += self.visit(n.message)
|
||||
s += ')'
|
||||
return s
|
||||
|
||||
def visit_Switch(self, n):
|
||||
s = 'switch (' + self.visit(n.cond) + ')\n'
|
||||
s += self._generate_stmt(n.stmt, add_indent=True)
|
||||
return s
|
||||
|
||||
def visit_Case(self, n):
|
||||
s = 'case ' + self.visit(n.expr) + ':\n'
|
||||
for stmt in n.stmts:
|
||||
s += self._generate_stmt(stmt, add_indent=True)
|
||||
return s
|
||||
|
||||
def visit_Default(self, n):
|
||||
s = 'default:\n'
|
||||
for stmt in n.stmts:
|
||||
s += self._generate_stmt(stmt, add_indent=True)
|
||||
return s
|
||||
|
||||
def visit_Label(self, n):
|
||||
return n.name + ':\n' + self._generate_stmt(n.stmt)
|
||||
|
||||
def visit_Goto(self, n):
|
||||
return 'goto ' + n.name + ';'
|
||||
|
||||
def visit_EllipsisParam(self, n):
|
||||
return '...'
|
||||
|
||||
def visit_Struct(self, n):
|
||||
return self._generate_struct_union_enum(n, 'struct')
|
||||
|
||||
def visit_Typename(self, n):
|
||||
return self._generate_type(n.type)
|
||||
|
||||
def visit_Union(self, n):
|
||||
return self._generate_struct_union_enum(n, 'union')
|
||||
|
||||
def visit_NamedInitializer(self, n):
|
||||
s = ''
|
||||
for name in n.name:
|
||||
if isinstance(name, c_ast.ID):
|
||||
s += '.' + name.name
|
||||
else:
|
||||
s += '[' + self.visit(name) + ']'
|
||||
s += ' = ' + self._visit_expr(n.expr)
|
||||
return s
|
||||
|
||||
def visit_FuncDecl(self, n):
|
||||
return self._generate_type(n)
|
||||
|
||||
def visit_ArrayDecl(self, n):
|
||||
return self._generate_type(n, emit_declname=False)
|
||||
|
||||
def visit_TypeDecl(self, n):
|
||||
return self._generate_type(n, emit_declname=False)
|
||||
|
||||
def visit_PtrDecl(self, n):
|
||||
return self._generate_type(n, emit_declname=False)
|
||||
|
||||
def _generate_struct_union_enum(self, n, name):
|
||||
""" Generates code for structs, unions, and enums. name should be
|
||||
'struct', 'union', or 'enum'.
|
||||
"""
|
||||
if name in ('struct', 'union'):
|
||||
members = n.decls
|
||||
body_function = self._generate_struct_union_body
|
||||
else:
|
||||
assert name == 'enum'
|
||||
members = None if n.values is None else n.values.enumerators
|
||||
body_function = self._generate_enum_body
|
||||
s = name + ' ' + (n.name or '')
|
||||
if members is not None:
|
||||
# None means no members
|
||||
# Empty sequence means an empty list of members
|
||||
s += '\n'
|
||||
s += self._make_indent()
|
||||
self.indent_level += 2
|
||||
s += '{\n'
|
||||
s += body_function(members)
|
||||
self.indent_level -= 2
|
||||
s += self._make_indent() + '}'
|
||||
return s
|
||||
|
||||
def _generate_struct_union_body(self, members):
|
||||
return ''.join(self._generate_stmt(decl) for decl in members)
|
||||
|
||||
def _generate_enum_body(self, members):
|
||||
# `[:-2] + '\n'` removes the final `,` from the enumerator list
|
||||
return ''.join(self.visit(value) for value in members)[:-2] + '\n'
|
||||
|
||||
def _generate_stmt(self, n, add_indent=False):
|
||||
""" Generation from a statement node. This method exists as a wrapper
|
||||
for individual visit_* methods to handle different treatment of
|
||||
some statements in this context.
|
||||
"""
|
||||
typ = type(n)
|
||||
if add_indent: self.indent_level += 2
|
||||
indent = self._make_indent()
|
||||
if add_indent: self.indent_level -= 2
|
||||
|
||||
if typ in (
|
||||
c_ast.Decl, c_ast.Assignment, c_ast.Cast, c_ast.UnaryOp,
|
||||
c_ast.BinaryOp, c_ast.TernaryOp, c_ast.FuncCall, c_ast.ArrayRef,
|
||||
c_ast.StructRef, c_ast.Constant, c_ast.ID, c_ast.Typedef,
|
||||
c_ast.ExprList):
|
||||
# These can also appear in an expression context so no semicolon
|
||||
# is added to them automatically
|
||||
#
|
||||
return indent + self.visit(n) + ';\n'
|
||||
elif typ in (c_ast.Compound,):
|
||||
# No extra indentation required before the opening brace of a
|
||||
# compound - because it consists of multiple lines it has to
|
||||
# compute its own indentation.
|
||||
#
|
||||
return self.visit(n)
|
||||
elif typ in (c_ast.If,):
|
||||
return indent + self.visit(n)
|
||||
else:
|
||||
return indent + self.visit(n) + '\n'
|
||||
|
||||
def _generate_decl(self, n):
|
||||
""" Generation from a Decl node.
|
||||
"""
|
||||
s = ''
|
||||
if n.funcspec: s = ' '.join(n.funcspec) + ' '
|
||||
if n.storage: s += ' '.join(n.storage) + ' '
|
||||
if n.align: s += self.visit(n.align[0]) + ' '
|
||||
s += self._generate_type(n.type)
|
||||
return s
|
||||
|
||||
def _generate_type(self, n, modifiers=[], emit_declname = True):
|
||||
""" Recursive generation from a type node. n is the type node.
|
||||
modifiers collects the PtrDecl, ArrayDecl and FuncDecl modifiers
|
||||
encountered on the way down to a TypeDecl, to allow proper
|
||||
generation from it.
|
||||
"""
|
||||
typ = type(n)
|
||||
#~ print(n, modifiers)
|
||||
|
||||
if typ == c_ast.TypeDecl:
|
||||
s = ''
|
||||
if n.quals: s += ' '.join(n.quals) + ' '
|
||||
s += self.visit(n.type)
|
||||
|
||||
nstr = n.declname if n.declname and emit_declname else ''
|
||||
# Resolve modifiers.
|
||||
# Wrap in parens to distinguish pointer to array and pointer to
|
||||
# function syntax.
|
||||
#
|
||||
for i, modifier in enumerate(modifiers):
|
||||
if isinstance(modifier, c_ast.ArrayDecl):
|
||||
if (i != 0 and
|
||||
isinstance(modifiers[i - 1], c_ast.PtrDecl)):
|
||||
nstr = '(' + nstr + ')'
|
||||
nstr += '['
|
||||
if modifier.dim_quals:
|
||||
nstr += ' '.join(modifier.dim_quals) + ' '
|
||||
nstr += self.visit(modifier.dim) + ']'
|
||||
elif isinstance(modifier, c_ast.FuncDecl):
|
||||
if (i != 0 and
|
||||
isinstance(modifiers[i - 1], c_ast.PtrDecl)):
|
||||
nstr = '(' + nstr + ')'
|
||||
nstr += '(' + self.visit(modifier.args) + ')'
|
||||
elif isinstance(modifier, c_ast.PtrDecl):
|
||||
if modifier.quals:
|
||||
nstr = '* %s%s' % (' '.join(modifier.quals),
|
||||
' ' + nstr if nstr else '')
|
||||
else:
|
||||
nstr = '*' + nstr
|
||||
if nstr: s += ' ' + nstr
|
||||
return s
|
||||
elif typ == c_ast.Decl:
|
||||
return self._generate_decl(n.type)
|
||||
elif typ == c_ast.Typename:
|
||||
return self._generate_type(n.type, emit_declname = emit_declname)
|
||||
elif typ == c_ast.IdentifierType:
|
||||
return ' '.join(n.names) + ' '
|
||||
elif typ in (c_ast.ArrayDecl, c_ast.PtrDecl, c_ast.FuncDecl):
|
||||
return self._generate_type(n.type, modifiers + [n],
|
||||
emit_declname = emit_declname)
|
||||
else:
|
||||
return self.visit(n)
|
||||
|
||||
def _parenthesize_if(self, n, condition):
|
||||
""" Visits 'n' and returns its string representation, parenthesized
|
||||
if the condition function applied to the node returns True.
|
||||
"""
|
||||
s = self._visit_expr(n)
|
||||
if condition(n):
|
||||
return '(' + s + ')'
|
||||
else:
|
||||
return s
|
||||
|
||||
def _parenthesize_unless_simple(self, n):
|
||||
""" Common use case for _parenthesize_if
|
||||
"""
|
||||
return self._parenthesize_if(n, lambda d: not self._is_simple_node(d))
|
||||
|
||||
def _is_simple_node(self, n):
|
||||
""" Returns True for nodes that are "simple" - i.e. nodes that always
|
||||
have higher precedence than operators.
|
||||
"""
|
||||
return isinstance(n, (c_ast.Constant, c_ast.ID, c_ast.ArrayRef,
|
||||
c_ast.StructRef, c_ast.FuncCall))
|
555
.venv/Lib/site-packages/pycparser/c_lexer.py
Normal file
555
.venv/Lib/site-packages/pycparser/c_lexer.py
Normal file
@@ -0,0 +1,555 @@
|
||||
#------------------------------------------------------------------------------
|
||||
# pycparser: c_lexer.py
|
||||
#
|
||||
# CLexer class: lexer for the C language
|
||||
#
|
||||
# Eli Bendersky [https://eli.thegreenplace.net/]
|
||||
# License: BSD
|
||||
#------------------------------------------------------------------------------
|
||||
import re
|
||||
|
||||
from .ply import lex
|
||||
from .ply.lex import TOKEN
|
||||
|
||||
|
||||
class CLexer(object):
|
||||
""" A lexer for the C language. After building it, set the
|
||||
input text with input(), and call token() to get new
|
||||
tokens.
|
||||
|
||||
The public attribute filename can be set to an initial
|
||||
filename, but the lexer will update it upon #line
|
||||
directives.
|
||||
"""
|
||||
def __init__(self, error_func, on_lbrace_func, on_rbrace_func,
|
||||
type_lookup_func):
|
||||
""" Create a new Lexer.
|
||||
|
||||
error_func:
|
||||
An error function. Will be called with an error
|
||||
message, line and column as arguments, in case of
|
||||
an error during lexing.
|
||||
|
||||
on_lbrace_func, on_rbrace_func:
|
||||
Called when an LBRACE or RBRACE is encountered
|
||||
(likely to push/pop type_lookup_func's scope)
|
||||
|
||||
type_lookup_func:
|
||||
A type lookup function. Given a string, it must
|
||||
return True IFF this string is a name of a type
|
||||
that was defined with a typedef earlier.
|
||||
"""
|
||||
self.error_func = error_func
|
||||
self.on_lbrace_func = on_lbrace_func
|
||||
self.on_rbrace_func = on_rbrace_func
|
||||
self.type_lookup_func = type_lookup_func
|
||||
self.filename = ''
|
||||
|
||||
# Keeps track of the last token returned from self.token()
|
||||
self.last_token = None
|
||||
|
||||
# Allow either "# line" or "# <num>" to support GCC's
|
||||
# cpp output
|
||||
#
|
||||
self.line_pattern = re.compile(r'([ \t]*line\W)|([ \t]*\d+)')
|
||||
self.pragma_pattern = re.compile(r'[ \t]*pragma\W')
|
||||
|
||||
def build(self, **kwargs):
|
||||
""" Builds the lexer from the specification. Must be
|
||||
called after the lexer object is created.
|
||||
|
||||
This method exists separately, because the PLY
|
||||
manual warns against calling lex.lex inside
|
||||
__init__
|
||||
"""
|
||||
self.lexer = lex.lex(object=self, **kwargs)
|
||||
|
||||
def reset_lineno(self):
|
||||
""" Resets the internal line number counter of the lexer.
|
||||
"""
|
||||
self.lexer.lineno = 1
|
||||
|
||||
def input(self, text):
|
||||
self.lexer.input(text)
|
||||
|
||||
def token(self):
|
||||
self.last_token = self.lexer.token()
|
||||
return self.last_token
|
||||
|
||||
def find_tok_column(self, token):
|
||||
""" Find the column of the token in its line.
|
||||
"""
|
||||
last_cr = self.lexer.lexdata.rfind('\n', 0, token.lexpos)
|
||||
return token.lexpos - last_cr
|
||||
|
||||
######################-- PRIVATE --######################
|
||||
|
||||
##
|
||||
## Internal auxiliary methods
|
||||
##
|
||||
def _error(self, msg, token):
|
||||
location = self._make_tok_location(token)
|
||||
self.error_func(msg, location[0], location[1])
|
||||
self.lexer.skip(1)
|
||||
|
||||
def _make_tok_location(self, token):
|
||||
return (token.lineno, self.find_tok_column(token))
|
||||
|
||||
##
|
||||
## Reserved keywords
|
||||
##
|
||||
keywords = (
|
||||
'AUTO', 'BREAK', 'CASE', 'CHAR', 'CONST',
|
||||
'CONTINUE', 'DEFAULT', 'DO', 'DOUBLE', 'ELSE', 'ENUM', 'EXTERN',
|
||||
'FLOAT', 'FOR', 'GOTO', 'IF', 'INLINE', 'INT', 'LONG',
|
||||
'REGISTER', 'OFFSETOF',
|
||||
'RESTRICT', 'RETURN', 'SHORT', 'SIGNED', 'SIZEOF', 'STATIC', 'STRUCT',
|
||||
'SWITCH', 'TYPEDEF', 'UNION', 'UNSIGNED', 'VOID',
|
||||
'VOLATILE', 'WHILE', '__INT128',
|
||||
)
|
||||
|
||||
keywords_new = (
|
||||
'_BOOL', '_COMPLEX',
|
||||
'_NORETURN', '_THREAD_LOCAL', '_STATIC_ASSERT',
|
||||
'_ATOMIC', '_ALIGNOF', '_ALIGNAS',
|
||||
'_PRAGMA',
|
||||
)
|
||||
|
||||
keyword_map = {}
|
||||
|
||||
for keyword in keywords:
|
||||
keyword_map[keyword.lower()] = keyword
|
||||
|
||||
for keyword in keywords_new:
|
||||
keyword_map[keyword[:2].upper() + keyword[2:].lower()] = keyword
|
||||
|
||||
##
|
||||
## All the tokens recognized by the lexer
|
||||
##
|
||||
tokens = keywords + keywords_new + (
|
||||
# Identifiers
|
||||
'ID',
|
||||
|
||||
# Type identifiers (identifiers previously defined as
|
||||
# types with typedef)
|
||||
'TYPEID',
|
||||
|
||||
# constants
|
||||
'INT_CONST_DEC', 'INT_CONST_OCT', 'INT_CONST_HEX', 'INT_CONST_BIN', 'INT_CONST_CHAR',
|
||||
'FLOAT_CONST', 'HEX_FLOAT_CONST',
|
||||
'CHAR_CONST',
|
||||
'WCHAR_CONST',
|
||||
'U8CHAR_CONST',
|
||||
'U16CHAR_CONST',
|
||||
'U32CHAR_CONST',
|
||||
|
||||
# String literals
|
||||
'STRING_LITERAL',
|
||||
'WSTRING_LITERAL',
|
||||
'U8STRING_LITERAL',
|
||||
'U16STRING_LITERAL',
|
||||
'U32STRING_LITERAL',
|
||||
|
||||
# Operators
|
||||
'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MOD',
|
||||
'OR', 'AND', 'NOT', 'XOR', 'LSHIFT', 'RSHIFT',
|
||||
'LOR', 'LAND', 'LNOT',
|
||||
'LT', 'LE', 'GT', 'GE', 'EQ', 'NE',
|
||||
|
||||
# Assignment
|
||||
'EQUALS', 'TIMESEQUAL', 'DIVEQUAL', 'MODEQUAL',
|
||||
'PLUSEQUAL', 'MINUSEQUAL',
|
||||
'LSHIFTEQUAL','RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL',
|
||||
'OREQUAL',
|
||||
|
||||
# Increment/decrement
|
||||
'PLUSPLUS', 'MINUSMINUS',
|
||||
|
||||
# Structure dereference (->)
|
||||
'ARROW',
|
||||
|
||||
# Conditional operator (?)
|
||||
'CONDOP',
|
||||
|
||||
# Delimiters
|
||||
'LPAREN', 'RPAREN', # ( )
|
||||
'LBRACKET', 'RBRACKET', # [ ]
|
||||
'LBRACE', 'RBRACE', # { }
|
||||
'COMMA', 'PERIOD', # . ,
|
||||
'SEMI', 'COLON', # ; :
|
||||
|
||||
# Ellipsis (...)
|
||||
'ELLIPSIS',
|
||||
|
||||
# pre-processor
|
||||
'PPHASH', # '#'
|
||||
'PPPRAGMA', # 'pragma'
|
||||
'PPPRAGMASTR',
|
||||
)
|
||||
|
||||
##
|
||||
## Regexes for use in tokens
|
||||
##
|
||||
##
|
||||
|
||||
# valid C identifiers (K&R2: A.2.3), plus '$' (supported by some compilers)
|
||||
identifier = r'[a-zA-Z_$][0-9a-zA-Z_$]*'
|
||||
|
||||
hex_prefix = '0[xX]'
|
||||
hex_digits = '[0-9a-fA-F]+'
|
||||
bin_prefix = '0[bB]'
|
||||
bin_digits = '[01]+'
|
||||
|
||||
# integer constants (K&R2: A.2.5.1)
|
||||
integer_suffix_opt = r'(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?'
|
||||
decimal_constant = '(0'+integer_suffix_opt+')|([1-9][0-9]*'+integer_suffix_opt+')'
|
||||
octal_constant = '0[0-7]*'+integer_suffix_opt
|
||||
hex_constant = hex_prefix+hex_digits+integer_suffix_opt
|
||||
bin_constant = bin_prefix+bin_digits+integer_suffix_opt
|
||||
|
||||
bad_octal_constant = '0[0-7]*[89]'
|
||||
|
||||
# character constants (K&R2: A.2.5.2)
|
||||
# Note: a-zA-Z and '.-~^_!=&;,' are allowed as escape chars to support #line
|
||||
# directives with Windows paths as filenames (..\..\dir\file)
|
||||
# For the same reason, decimal_escape allows all digit sequences. We want to
|
||||
# parse all correct code, even if it means to sometimes parse incorrect
|
||||
# code.
|
||||
#
|
||||
# The original regexes were taken verbatim from the C syntax definition,
|
||||
# and were later modified to avoid worst-case exponential running time.
|
||||
#
|
||||
# simple_escape = r"""([a-zA-Z._~!=&\^\-\\?'"])"""
|
||||
# decimal_escape = r"""(\d+)"""
|
||||
# hex_escape = r"""(x[0-9a-fA-F]+)"""
|
||||
# bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-7])"""
|
||||
#
|
||||
# The following modifications were made to avoid the ambiguity that allowed backtracking:
|
||||
# (https://github.com/eliben/pycparser/issues/61)
|
||||
#
|
||||
# - \x was removed from simple_escape, unless it was not followed by a hex digit, to avoid ambiguity with hex_escape.
|
||||
# - hex_escape allows one or more hex characters, but requires that the next character(if any) is not hex
|
||||
# - decimal_escape allows one or more decimal characters, but requires that the next character(if any) is not a decimal
|
||||
# - bad_escape does not allow any decimals (8-9), to avoid conflicting with the permissive decimal_escape.
|
||||
#
|
||||
# Without this change, python's `re` module would recursively try parsing each ambiguous escape sequence in multiple ways.
|
||||
# e.g. `\123` could be parsed as `\1`+`23`, `\12`+`3`, and `\123`.
|
||||
|
||||
simple_escape = r"""([a-wyzA-Z._~!=&\^\-\\?'"]|x(?![0-9a-fA-F]))"""
|
||||
decimal_escape = r"""(\d+)(?!\d)"""
|
||||
hex_escape = r"""(x[0-9a-fA-F]+)(?![0-9a-fA-F])"""
|
||||
bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-9])"""
|
||||
|
||||
escape_sequence = r"""(\\("""+simple_escape+'|'+decimal_escape+'|'+hex_escape+'))'
|
||||
|
||||
# This complicated regex with lookahead might be slow for strings, so because all of the valid escapes (including \x) allowed
|
||||
# 0 or more non-escaped characters after the first character, simple_escape+decimal_escape+hex_escape got simplified to
|
||||
|
||||
escape_sequence_start_in_string = r"""(\\[0-9a-zA-Z._~!=&\^\-\\?'"])"""
|
||||
|
||||
cconst_char = r"""([^'\\\n]|"""+escape_sequence+')'
|
||||
char_const = "'"+cconst_char+"'"
|
||||
wchar_const = 'L'+char_const
|
||||
u8char_const = 'u8'+char_const
|
||||
u16char_const = 'u'+char_const
|
||||
u32char_const = 'U'+char_const
|
||||
multicharacter_constant = "'"+cconst_char+"{2,4}'"
|
||||
unmatched_quote = "('"+cconst_char+"*\\n)|('"+cconst_char+"*$)"
|
||||
bad_char_const = r"""('"""+cconst_char+"""[^'\n]+')|('')|('"""+bad_escape+r"""[^'\n]*')"""
|
||||
|
||||
# string literals (K&R2: A.2.6)
|
||||
string_char = r"""([^"\\\n]|"""+escape_sequence_start_in_string+')'
|
||||
string_literal = '"'+string_char+'*"'
|
||||
wstring_literal = 'L'+string_literal
|
||||
u8string_literal = 'u8'+string_literal
|
||||
u16string_literal = 'u'+string_literal
|
||||
u32string_literal = 'U'+string_literal
|
||||
bad_string_literal = '"'+string_char+'*'+bad_escape+string_char+'*"'
|
||||
|
||||
# floating constants (K&R2: A.2.5.3)
|
||||
exponent_part = r"""([eE][-+]?[0-9]+)"""
|
||||
fractional_constant = r"""([0-9]*\.[0-9]+)|([0-9]+\.)"""
|
||||
floating_constant = '(((('+fractional_constant+')'+exponent_part+'?)|([0-9]+'+exponent_part+'))[FfLl]?)'
|
||||
binary_exponent_part = r'''([pP][+-]?[0-9]+)'''
|
||||
hex_fractional_constant = '((('+hex_digits+r""")?\."""+hex_digits+')|('+hex_digits+r"""\.))"""
|
||||
hex_floating_constant = '('+hex_prefix+'('+hex_digits+'|'+hex_fractional_constant+')'+binary_exponent_part+'[FfLl]?)'
|
||||
|
||||
##
|
||||
## Lexer states: used for preprocessor \n-terminated directives
|
||||
##
|
||||
states = (
|
||||
# ppline: preprocessor line directives
|
||||
#
|
||||
('ppline', 'exclusive'),
|
||||
|
||||
# pppragma: pragma
|
||||
#
|
||||
('pppragma', 'exclusive'),
|
||||
)
|
||||
|
||||
def t_PPHASH(self, t):
|
||||
r'[ \t]*\#'
|
||||
if self.line_pattern.match(t.lexer.lexdata, pos=t.lexer.lexpos):
|
||||
t.lexer.begin('ppline')
|
||||
self.pp_line = self.pp_filename = None
|
||||
elif self.pragma_pattern.match(t.lexer.lexdata, pos=t.lexer.lexpos):
|
||||
t.lexer.begin('pppragma')
|
||||
else:
|
||||
t.type = 'PPHASH'
|
||||
return t
|
||||
|
||||
##
|
||||
## Rules for the ppline state
|
||||
##
|
||||
@TOKEN(string_literal)
|
||||
def t_ppline_FILENAME(self, t):
|
||||
if self.pp_line is None:
|
||||
self._error('filename before line number in #line', t)
|
||||
else:
|
||||
self.pp_filename = t.value.lstrip('"').rstrip('"')
|
||||
|
||||
@TOKEN(decimal_constant)
|
||||
def t_ppline_LINE_NUMBER(self, t):
|
||||
if self.pp_line is None:
|
||||
self.pp_line = t.value
|
||||
else:
|
||||
# Ignore: GCC's cpp sometimes inserts a numeric flag
|
||||
# after the file name
|
||||
pass
|
||||
|
||||
def t_ppline_NEWLINE(self, t):
|
||||
r'\n'
|
||||
if self.pp_line is None:
|
||||
self._error('line number missing in #line', t)
|
||||
else:
|
||||
self.lexer.lineno = int(self.pp_line)
|
||||
|
||||
if self.pp_filename is not None:
|
||||
self.filename = self.pp_filename
|
||||
|
||||
t.lexer.begin('INITIAL')
|
||||
|
||||
def t_ppline_PPLINE(self, t):
|
||||
r'line'
|
||||
pass
|
||||
|
||||
t_ppline_ignore = ' \t'
|
||||
|
||||
def t_ppline_error(self, t):
|
||||
self._error('invalid #line directive', t)
|
||||
|
||||
##
|
||||
## Rules for the pppragma state
|
||||
##
|
||||
def t_pppragma_NEWLINE(self, t):
|
||||
r'\n'
|
||||
t.lexer.lineno += 1
|
||||
t.lexer.begin('INITIAL')
|
||||
|
||||
def t_pppragma_PPPRAGMA(self, t):
|
||||
r'pragma'
|
||||
return t
|
||||
|
||||
t_pppragma_ignore = ' \t'
|
||||
|
||||
def t_pppragma_STR(self, t):
|
||||
'.+'
|
||||
t.type = 'PPPRAGMASTR'
|
||||
return t
|
||||
|
||||
def t_pppragma_error(self, t):
|
||||
self._error('invalid #pragma directive', t)
|
||||
|
||||
##
|
||||
## Rules for the normal state
|
||||
##
|
||||
t_ignore = ' \t'
|
||||
|
||||
# Newlines
|
||||
def t_NEWLINE(self, t):
|
||||
r'\n+'
|
||||
t.lexer.lineno += t.value.count("\n")
|
||||
|
||||
# Operators
|
||||
t_PLUS = r'\+'
|
||||
t_MINUS = r'-'
|
||||
t_TIMES = r'\*'
|
||||
t_DIVIDE = r'/'
|
||||
t_MOD = r'%'
|
||||
t_OR = r'\|'
|
||||
t_AND = r'&'
|
||||
t_NOT = r'~'
|
||||
t_XOR = r'\^'
|
||||
t_LSHIFT = r'<<'
|
||||
t_RSHIFT = r'>>'
|
||||
t_LOR = r'\|\|'
|
||||
t_LAND = r'&&'
|
||||
t_LNOT = r'!'
|
||||
t_LT = r'<'
|
||||
t_GT = r'>'
|
||||
t_LE = r'<='
|
||||
t_GE = r'>='
|
||||
t_EQ = r'=='
|
||||
t_NE = r'!='
|
||||
|
||||
# Assignment operators
|
||||
t_EQUALS = r'='
|
||||
t_TIMESEQUAL = r'\*='
|
||||
t_DIVEQUAL = r'/='
|
||||
t_MODEQUAL = r'%='
|
||||
t_PLUSEQUAL = r'\+='
|
||||
t_MINUSEQUAL = r'-='
|
||||
t_LSHIFTEQUAL = r'<<='
|
||||
t_RSHIFTEQUAL = r'>>='
|
||||
t_ANDEQUAL = r'&='
|
||||
t_OREQUAL = r'\|='
|
||||
t_XOREQUAL = r'\^='
|
||||
|
||||
# Increment/decrement
|
||||
t_PLUSPLUS = r'\+\+'
|
||||
t_MINUSMINUS = r'--'
|
||||
|
||||
# ->
|
||||
t_ARROW = r'->'
|
||||
|
||||
# ?
|
||||
t_CONDOP = r'\?'
|
||||
|
||||
# Delimiters
|
||||
t_LPAREN = r'\('
|
||||
t_RPAREN = r'\)'
|
||||
t_LBRACKET = r'\['
|
||||
t_RBRACKET = r'\]'
|
||||
t_COMMA = r','
|
||||
t_PERIOD = r'\.'
|
||||
t_SEMI = r';'
|
||||
t_COLON = r':'
|
||||
t_ELLIPSIS = r'\.\.\.'
|
||||
|
||||
# Scope delimiters
|
||||
# To see why on_lbrace_func is needed, consider:
|
||||
# typedef char TT;
|
||||
# void foo(int TT) { TT = 10; }
|
||||
# TT x = 5;
|
||||
# Outside the function, TT is a typedef, but inside (starting and ending
|
||||
# with the braces) it's a parameter. The trouble begins with yacc's
|
||||
# lookahead token. If we open a new scope in brace_open, then TT has
|
||||
# already been read and incorrectly interpreted as TYPEID. So, we need
|
||||
# to open and close scopes from within the lexer.
|
||||
# Similar for the TT immediately outside the end of the function.
|
||||
#
|
||||
@TOKEN(r'\{')
|
||||
def t_LBRACE(self, t):
|
||||
self.on_lbrace_func()
|
||||
return t
|
||||
@TOKEN(r'\}')
|
||||
def t_RBRACE(self, t):
|
||||
self.on_rbrace_func()
|
||||
return t
|
||||
|
||||
t_STRING_LITERAL = string_literal
|
||||
|
||||
# The following floating and integer constants are defined as
|
||||
# functions to impose a strict order (otherwise, decimal
|
||||
# is placed before the others because its regex is longer,
|
||||
# and this is bad)
|
||||
#
|
||||
@TOKEN(floating_constant)
|
||||
def t_FLOAT_CONST(self, t):
|
||||
return t
|
||||
|
||||
@TOKEN(hex_floating_constant)
|
||||
def t_HEX_FLOAT_CONST(self, t):
|
||||
return t
|
||||
|
||||
@TOKEN(hex_constant)
|
||||
def t_INT_CONST_HEX(self, t):
|
||||
return t
|
||||
|
||||
@TOKEN(bin_constant)
|
||||
def t_INT_CONST_BIN(self, t):
|
||||
return t
|
||||
|
||||
@TOKEN(bad_octal_constant)
|
||||
def t_BAD_CONST_OCT(self, t):
|
||||
msg = "Invalid octal constant"
|
||||
self._error(msg, t)
|
||||
|
||||
@TOKEN(octal_constant)
|
||||
def t_INT_CONST_OCT(self, t):
|
||||
return t
|
||||
|
||||
@TOKEN(decimal_constant)
|
||||
def t_INT_CONST_DEC(self, t):
|
||||
return t
|
||||
|
||||
# Must come before bad_char_const, to prevent it from
|
||||
# catching valid char constants as invalid
|
||||
#
|
||||
@TOKEN(multicharacter_constant)
|
||||
def t_INT_CONST_CHAR(self, t):
|
||||
return t
|
||||
|
||||
@TOKEN(char_const)
|
||||
def t_CHAR_CONST(self, t):
|
||||
return t
|
||||
|
||||
@TOKEN(wchar_const)
|
||||
def t_WCHAR_CONST(self, t):
|
||||
return t
|
||||
|
||||
@TOKEN(u8char_const)
|
||||
def t_U8CHAR_CONST(self, t):
|
||||
return t
|
||||
|
||||
@TOKEN(u16char_const)
|
||||
def t_U16CHAR_CONST(self, t):
|
||||
return t
|
||||
|
||||
@TOKEN(u32char_const)
|
||||
def t_U32CHAR_CONST(self, t):
|
||||
return t
|
||||
|
||||
@TOKEN(unmatched_quote)
|
||||
def t_UNMATCHED_QUOTE(self, t):
|
||||
msg = "Unmatched '"
|
||||
self._error(msg, t)
|
||||
|
||||
@TOKEN(bad_char_const)
|
||||
def t_BAD_CHAR_CONST(self, t):
|
||||
msg = "Invalid char constant %s" % t.value
|
||||
self._error(msg, t)
|
||||
|
||||
@TOKEN(wstring_literal)
|
||||
def t_WSTRING_LITERAL(self, t):
|
||||
return t
|
||||
|
||||
@TOKEN(u8string_literal)
|
||||
def t_U8STRING_LITERAL(self, t):
|
||||
return t
|
||||
|
||||
@TOKEN(u16string_literal)
|
||||
def t_U16STRING_LITERAL(self, t):
|
||||
return t
|
||||
|
||||
@TOKEN(u32string_literal)
|
||||
def t_U32STRING_LITERAL(self, t):
|
||||
return t
|
||||
|
||||
# unmatched string literals are caught by the preprocessor
|
||||
|
||||
@TOKEN(bad_string_literal)
|
||||
def t_BAD_STRING_LITERAL(self, t):
|
||||
msg = "String contains invalid escape code"
|
||||
self._error(msg, t)
|
||||
|
||||
@TOKEN(identifier)
|
||||
def t_ID(self, t):
|
||||
t.type = self.keyword_map.get(t.value, "ID")
|
||||
if t.type == 'ID' and self.type_lookup_func(t.value):
|
||||
t.type = "TYPEID"
|
||||
return t
|
||||
|
||||
def t_error(self, t):
|
||||
msg = 'Illegal character %s' % repr(t.value[0])
|
||||
self._error(msg, t)
|
1950
.venv/Lib/site-packages/pycparser/c_parser.py
Normal file
1950
.venv/Lib/site-packages/pycparser/c_parser.py
Normal file
File diff suppressed because it is too large
Load Diff
10
.venv/Lib/site-packages/pycparser/lextab.py
Normal file
10
.venv/Lib/site-packages/pycparser/lextab.py
Normal file
File diff suppressed because one or more lines are too long
5
.venv/Lib/site-packages/pycparser/ply/__init__.py
Normal file
5
.venv/Lib/site-packages/pycparser/ply/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
# PLY package
|
||||
# Author: David Beazley (dave@dabeaz.com)
|
||||
|
||||
__version__ = '3.9'
|
||||
__all__ = ['lex','yacc']
|
905
.venv/Lib/site-packages/pycparser/ply/cpp.py
Normal file
905
.venv/Lib/site-packages/pycparser/ply/cpp.py
Normal file
@@ -0,0 +1,905 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
# cpp.py
|
||||
#
|
||||
# Author: David Beazley (http://www.dabeaz.com)
|
||||
# Copyright (C) 2017
|
||||
# All rights reserved
|
||||
#
|
||||
# This module implements an ANSI-C style lexical preprocessor for PLY.
|
||||
# -----------------------------------------------------------------------------
|
||||
import sys
|
||||
|
||||
# Some Python 3 compatibility shims
|
||||
if sys.version_info.major < 3:
|
||||
STRING_TYPES = (str, unicode)
|
||||
else:
|
||||
STRING_TYPES = str
|
||||
xrange = range
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Default preprocessor lexer definitions. These tokens are enough to get
|
||||
# a basic preprocessor working. Other modules may import these if they want
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
tokens = (
|
||||
'CPP_ID','CPP_INTEGER', 'CPP_FLOAT', 'CPP_STRING', 'CPP_CHAR', 'CPP_WS', 'CPP_COMMENT1', 'CPP_COMMENT2', 'CPP_POUND','CPP_DPOUND'
|
||||
)
|
||||
|
||||
literals = "+-*/%|&~^<>=!?()[]{}.,;:\\\'\""
|
||||
|
||||
# Whitespace
|
||||
def t_CPP_WS(t):
|
||||
r'\s+'
|
||||
t.lexer.lineno += t.value.count("\n")
|
||||
return t
|
||||
|
||||
t_CPP_POUND = r'\#'
|
||||
t_CPP_DPOUND = r'\#\#'
|
||||
|
||||
# Identifier
|
||||
t_CPP_ID = r'[A-Za-z_][\w_]*'
|
||||
|
||||
# Integer literal
|
||||
def CPP_INTEGER(t):
|
||||
r'(((((0x)|(0X))[0-9a-fA-F]+)|(\d+))([uU][lL]|[lL][uU]|[uU]|[lL])?)'
|
||||
return t
|
||||
|
||||
t_CPP_INTEGER = CPP_INTEGER
|
||||
|
||||
# Floating literal
|
||||
t_CPP_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?'
|
||||
|
||||
# String literal
|
||||
def t_CPP_STRING(t):
|
||||
r'\"([^\\\n]|(\\(.|\n)))*?\"'
|
||||
t.lexer.lineno += t.value.count("\n")
|
||||
return t
|
||||
|
||||
# Character constant 'c' or L'c'
|
||||
def t_CPP_CHAR(t):
|
||||
r'(L)?\'([^\\\n]|(\\(.|\n)))*?\''
|
||||
t.lexer.lineno += t.value.count("\n")
|
||||
return t
|
||||
|
||||
# Comment
|
||||
def t_CPP_COMMENT1(t):
|
||||
r'(/\*(.|\n)*?\*/)'
|
||||
ncr = t.value.count("\n")
|
||||
t.lexer.lineno += ncr
|
||||
# replace with one space or a number of '\n'
|
||||
t.type = 'CPP_WS'; t.value = '\n' * ncr if ncr else ' '
|
||||
return t
|
||||
|
||||
# Line comment
|
||||
def t_CPP_COMMENT2(t):
|
||||
r'(//.*?(\n|$))'
|
||||
# replace with '/n'
|
||||
t.type = 'CPP_WS'; t.value = '\n'
|
||||
return t
|
||||
|
||||
def t_error(t):
|
||||
t.type = t.value[0]
|
||||
t.value = t.value[0]
|
||||
t.lexer.skip(1)
|
||||
return t
|
||||
|
||||
import re
|
||||
import copy
|
||||
import time
|
||||
import os.path
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# trigraph()
|
||||
#
|
||||
# Given an input string, this function replaces all trigraph sequences.
|
||||
# The following mapping is used:
|
||||
#
|
||||
# ??= #
|
||||
# ??/ \
|
||||
# ??' ^
|
||||
# ??( [
|
||||
# ??) ]
|
||||
# ??! |
|
||||
# ??< {
|
||||
# ??> }
|
||||
# ??- ~
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
_trigraph_pat = re.compile(r'''\?\?[=/\'\(\)\!<>\-]''')
|
||||
_trigraph_rep = {
|
||||
'=':'#',
|
||||
'/':'\\',
|
||||
"'":'^',
|
||||
'(':'[',
|
||||
')':']',
|
||||
'!':'|',
|
||||
'<':'{',
|
||||
'>':'}',
|
||||
'-':'~'
|
||||
}
|
||||
|
||||
def trigraph(input):
|
||||
return _trigraph_pat.sub(lambda g: _trigraph_rep[g.group()[-1]],input)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Macro object
|
||||
#
|
||||
# This object holds information about preprocessor macros
|
||||
#
|
||||
# .name - Macro name (string)
|
||||
# .value - Macro value (a list of tokens)
|
||||
# .arglist - List of argument names
|
||||
# .variadic - Boolean indicating whether or not variadic macro
|
||||
# .vararg - Name of the variadic parameter
|
||||
#
|
||||
# When a macro is created, the macro replacement token sequence is
|
||||
# pre-scanned and used to create patch lists that are later used
|
||||
# during macro expansion
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
class Macro(object):
|
||||
def __init__(self,name,value,arglist=None,variadic=False):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.arglist = arglist
|
||||
self.variadic = variadic
|
||||
if variadic:
|
||||
self.vararg = arglist[-1]
|
||||
self.source = None
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Preprocessor object
|
||||
#
|
||||
# Object representing a preprocessor. Contains macro definitions,
|
||||
# include directories, and other information
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
class Preprocessor(object):
|
||||
def __init__(self,lexer=None):
|
||||
if lexer is None:
|
||||
lexer = lex.lexer
|
||||
self.lexer = lexer
|
||||
self.macros = { }
|
||||
self.path = []
|
||||
self.temp_path = []
|
||||
|
||||
# Probe the lexer for selected tokens
|
||||
self.lexprobe()
|
||||
|
||||
tm = time.localtime()
|
||||
self.define("__DATE__ \"%s\"" % time.strftime("%b %d %Y",tm))
|
||||
self.define("__TIME__ \"%s\"" % time.strftime("%H:%M:%S",tm))
|
||||
self.parser = None
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# tokenize()
|
||||
#
|
||||
# Utility function. Given a string of text, tokenize into a list of tokens
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
def tokenize(self,text):
|
||||
tokens = []
|
||||
self.lexer.input(text)
|
||||
while True:
|
||||
tok = self.lexer.token()
|
||||
if not tok: break
|
||||
tokens.append(tok)
|
||||
return tokens
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# error()
|
||||
#
|
||||
# Report a preprocessor error/warning of some kind
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def error(self,file,line,msg):
|
||||
print("%s:%d %s" % (file,line,msg))
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# lexprobe()
|
||||
#
|
||||
# This method probes the preprocessor lexer object to discover
|
||||
# the token types of symbols that are important to the preprocessor.
|
||||
# If this works right, the preprocessor will simply "work"
|
||||
# with any suitable lexer regardless of how tokens have been named.
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def lexprobe(self):
|
||||
|
||||
# Determine the token type for identifiers
|
||||
self.lexer.input("identifier")
|
||||
tok = self.lexer.token()
|
||||
if not tok or tok.value != "identifier":
|
||||
print("Couldn't determine identifier type")
|
||||
else:
|
||||
self.t_ID = tok.type
|
||||
|
||||
# Determine the token type for integers
|
||||
self.lexer.input("12345")
|
||||
tok = self.lexer.token()
|
||||
if not tok or int(tok.value) != 12345:
|
||||
print("Couldn't determine integer type")
|
||||
else:
|
||||
self.t_INTEGER = tok.type
|
||||
self.t_INTEGER_TYPE = type(tok.value)
|
||||
|
||||
# Determine the token type for strings enclosed in double quotes
|
||||
self.lexer.input("\"filename\"")
|
||||
tok = self.lexer.token()
|
||||
if not tok or tok.value != "\"filename\"":
|
||||
print("Couldn't determine string type")
|
||||
else:
|
||||
self.t_STRING = tok.type
|
||||
|
||||
# Determine the token type for whitespace--if any
|
||||
self.lexer.input(" ")
|
||||
tok = self.lexer.token()
|
||||
if not tok or tok.value != " ":
|
||||
self.t_SPACE = None
|
||||
else:
|
||||
self.t_SPACE = tok.type
|
||||
|
||||
# Determine the token type for newlines
|
||||
self.lexer.input("\n")
|
||||
tok = self.lexer.token()
|
||||
if not tok or tok.value != "\n":
|
||||
self.t_NEWLINE = None
|
||||
print("Couldn't determine token for newlines")
|
||||
else:
|
||||
self.t_NEWLINE = tok.type
|
||||
|
||||
self.t_WS = (self.t_SPACE, self.t_NEWLINE)
|
||||
|
||||
# Check for other characters used by the preprocessor
|
||||
chars = [ '<','>','#','##','\\','(',')',',','.']
|
||||
for c in chars:
|
||||
self.lexer.input(c)
|
||||
tok = self.lexer.token()
|
||||
if not tok or tok.value != c:
|
||||
print("Unable to lex '%s' required for preprocessor" % c)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# add_path()
|
||||
#
|
||||
# Adds a search path to the preprocessor.
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def add_path(self,path):
|
||||
self.path.append(path)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# group_lines()
|
||||
#
|
||||
# Given an input string, this function splits it into lines. Trailing whitespace
|
||||
# is removed. Any line ending with \ is grouped with the next line. This
|
||||
# function forms the lowest level of the preprocessor---grouping into text into
|
||||
# a line-by-line format.
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def group_lines(self,input):
|
||||
lex = self.lexer.clone()
|
||||
lines = [x.rstrip() for x in input.splitlines()]
|
||||
for i in xrange(len(lines)):
|
||||
j = i+1
|
||||
while lines[i].endswith('\\') and (j < len(lines)):
|
||||
lines[i] = lines[i][:-1]+lines[j]
|
||||
lines[j] = ""
|
||||
j += 1
|
||||
|
||||
input = "\n".join(lines)
|
||||
lex.input(input)
|
||||
lex.lineno = 1
|
||||
|
||||
current_line = []
|
||||
while True:
|
||||
tok = lex.token()
|
||||
if not tok:
|
||||
break
|
||||
current_line.append(tok)
|
||||
if tok.type in self.t_WS and '\n' in tok.value:
|
||||
yield current_line
|
||||
current_line = []
|
||||
|
||||
if current_line:
|
||||
yield current_line
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# tokenstrip()
|
||||
#
|
||||
# Remove leading/trailing whitespace tokens from a token list
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def tokenstrip(self,tokens):
|
||||
i = 0
|
||||
while i < len(tokens) and tokens[i].type in self.t_WS:
|
||||
i += 1
|
||||
del tokens[:i]
|
||||
i = len(tokens)-1
|
||||
while i >= 0 and tokens[i].type in self.t_WS:
|
||||
i -= 1
|
||||
del tokens[i+1:]
|
||||
return tokens
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# collect_args()
|
||||
#
|
||||
# Collects comma separated arguments from a list of tokens. The arguments
|
||||
# must be enclosed in parenthesis. Returns a tuple (tokencount,args,positions)
|
||||
# where tokencount is the number of tokens consumed, args is a list of arguments,
|
||||
# and positions is a list of integers containing the starting index of each
|
||||
# argument. Each argument is represented by a list of tokens.
|
||||
#
|
||||
# When collecting arguments, leading and trailing whitespace is removed
|
||||
# from each argument.
|
||||
#
|
||||
# This function properly handles nested parenthesis and commas---these do not
|
||||
# define new arguments.
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def collect_args(self,tokenlist):
|
||||
args = []
|
||||
positions = []
|
||||
current_arg = []
|
||||
nesting = 1
|
||||
tokenlen = len(tokenlist)
|
||||
|
||||
# Search for the opening '('.
|
||||
i = 0
|
||||
while (i < tokenlen) and (tokenlist[i].type in self.t_WS):
|
||||
i += 1
|
||||
|
||||
if (i < tokenlen) and (tokenlist[i].value == '('):
|
||||
positions.append(i+1)
|
||||
else:
|
||||
self.error(self.source,tokenlist[0].lineno,"Missing '(' in macro arguments")
|
||||
return 0, [], []
|
||||
|
||||
i += 1
|
||||
|
||||
while i < tokenlen:
|
||||
t = tokenlist[i]
|
||||
if t.value == '(':
|
||||
current_arg.append(t)
|
||||
nesting += 1
|
||||
elif t.value == ')':
|
||||
nesting -= 1
|
||||
if nesting == 0:
|
||||
if current_arg:
|
||||
args.append(self.tokenstrip(current_arg))
|
||||
positions.append(i)
|
||||
return i+1,args,positions
|
||||
current_arg.append(t)
|
||||
elif t.value == ',' and nesting == 1:
|
||||
args.append(self.tokenstrip(current_arg))
|
||||
positions.append(i+1)
|
||||
current_arg = []
|
||||
else:
|
||||
current_arg.append(t)
|
||||
i += 1
|
||||
|
||||
# Missing end argument
|
||||
self.error(self.source,tokenlist[-1].lineno,"Missing ')' in macro arguments")
|
||||
return 0, [],[]
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# macro_prescan()
|
||||
#
|
||||
# Examine the macro value (token sequence) and identify patch points
|
||||
# This is used to speed up macro expansion later on---we'll know
|
||||
# right away where to apply patches to the value to form the expansion
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def macro_prescan(self,macro):
|
||||
macro.patch = [] # Standard macro arguments
|
||||
macro.str_patch = [] # String conversion expansion
|
||||
macro.var_comma_patch = [] # Variadic macro comma patch
|
||||
i = 0
|
||||
while i < len(macro.value):
|
||||
if macro.value[i].type == self.t_ID and macro.value[i].value in macro.arglist:
|
||||
argnum = macro.arglist.index(macro.value[i].value)
|
||||
# Conversion of argument to a string
|
||||
if i > 0 and macro.value[i-1].value == '#':
|
||||
macro.value[i] = copy.copy(macro.value[i])
|
||||
macro.value[i].type = self.t_STRING
|
||||
del macro.value[i-1]
|
||||
macro.str_patch.append((argnum,i-1))
|
||||
continue
|
||||
# Concatenation
|
||||
elif (i > 0 and macro.value[i-1].value == '##'):
|
||||
macro.patch.append(('c',argnum,i-1))
|
||||
del macro.value[i-1]
|
||||
continue
|
||||
elif ((i+1) < len(macro.value) and macro.value[i+1].value == '##'):
|
||||
macro.patch.append(('c',argnum,i))
|
||||
i += 1
|
||||
continue
|
||||
# Standard expansion
|
||||
else:
|
||||
macro.patch.append(('e',argnum,i))
|
||||
elif macro.value[i].value == '##':
|
||||
if macro.variadic and (i > 0) and (macro.value[i-1].value == ',') and \
|
||||
((i+1) < len(macro.value)) and (macro.value[i+1].type == self.t_ID) and \
|
||||
(macro.value[i+1].value == macro.vararg):
|
||||
macro.var_comma_patch.append(i-1)
|
||||
i += 1
|
||||
macro.patch.sort(key=lambda x: x[2],reverse=True)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# macro_expand_args()
|
||||
#
|
||||
# Given a Macro and list of arguments (each a token list), this method
|
||||
# returns an expanded version of a macro. The return value is a token sequence
|
||||
# representing the replacement macro tokens
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def macro_expand_args(self,macro,args):
|
||||
# Make a copy of the macro token sequence
|
||||
rep = [copy.copy(_x) for _x in macro.value]
|
||||
|
||||
# Make string expansion patches. These do not alter the length of the replacement sequence
|
||||
|
||||
str_expansion = {}
|
||||
for argnum, i in macro.str_patch:
|
||||
if argnum not in str_expansion:
|
||||
str_expansion[argnum] = ('"%s"' % "".join([x.value for x in args[argnum]])).replace("\\","\\\\")
|
||||
rep[i] = copy.copy(rep[i])
|
||||
rep[i].value = str_expansion[argnum]
|
||||
|
||||
# Make the variadic macro comma patch. If the variadic macro argument is empty, we get rid
|
||||
comma_patch = False
|
||||
if macro.variadic and not args[-1]:
|
||||
for i in macro.var_comma_patch:
|
||||
rep[i] = None
|
||||
comma_patch = True
|
||||
|
||||
# Make all other patches. The order of these matters. It is assumed that the patch list
|
||||
# has been sorted in reverse order of patch location since replacements will cause the
|
||||
# size of the replacement sequence to expand from the patch point.
|
||||
|
||||
expanded = { }
|
||||
for ptype, argnum, i in macro.patch:
|
||||
# Concatenation. Argument is left unexpanded
|
||||
if ptype == 'c':
|
||||
rep[i:i+1] = args[argnum]
|
||||
# Normal expansion. Argument is macro expanded first
|
||||
elif ptype == 'e':
|
||||
if argnum not in expanded:
|
||||
expanded[argnum] = self.expand_macros(args[argnum])
|
||||
rep[i:i+1] = expanded[argnum]
|
||||
|
||||
# Get rid of removed comma if necessary
|
||||
if comma_patch:
|
||||
rep = [_i for _i in rep if _i]
|
||||
|
||||
return rep
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# expand_macros()
|
||||
#
|
||||
# Given a list of tokens, this function performs macro expansion.
|
||||
# The expanded argument is a dictionary that contains macros already
|
||||
# expanded. This is used to prevent infinite recursion.
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def expand_macros(self,tokens,expanded=None):
|
||||
if expanded is None:
|
||||
expanded = {}
|
||||
i = 0
|
||||
while i < len(tokens):
|
||||
t = tokens[i]
|
||||
if t.type == self.t_ID:
|
||||
if t.value in self.macros and t.value not in expanded:
|
||||
# Yes, we found a macro match
|
||||
expanded[t.value] = True
|
||||
|
||||
m = self.macros[t.value]
|
||||
if not m.arglist:
|
||||
# A simple macro
|
||||
ex = self.expand_macros([copy.copy(_x) for _x in m.value],expanded)
|
||||
for e in ex:
|
||||
e.lineno = t.lineno
|
||||
tokens[i:i+1] = ex
|
||||
i += len(ex)
|
||||
else:
|
||||
# A macro with arguments
|
||||
j = i + 1
|
||||
while j < len(tokens) and tokens[j].type in self.t_WS:
|
||||
j += 1
|
||||
if tokens[j].value == '(':
|
||||
tokcount,args,positions = self.collect_args(tokens[j:])
|
||||
if not m.variadic and len(args) != len(m.arglist):
|
||||
self.error(self.source,t.lineno,"Macro %s requires %d arguments" % (t.value,len(m.arglist)))
|
||||
i = j + tokcount
|
||||
elif m.variadic and len(args) < len(m.arglist)-1:
|
||||
if len(m.arglist) > 2:
|
||||
self.error(self.source,t.lineno,"Macro %s must have at least %d arguments" % (t.value, len(m.arglist)-1))
|
||||
else:
|
||||
self.error(self.source,t.lineno,"Macro %s must have at least %d argument" % (t.value, len(m.arglist)-1))
|
||||
i = j + tokcount
|
||||
else:
|
||||
if m.variadic:
|
||||
if len(args) == len(m.arglist)-1:
|
||||
args.append([])
|
||||
else:
|
||||
args[len(m.arglist)-1] = tokens[j+positions[len(m.arglist)-1]:j+tokcount-1]
|
||||
del args[len(m.arglist):]
|
||||
|
||||
# Get macro replacement text
|
||||
rep = self.macro_expand_args(m,args)
|
||||
rep = self.expand_macros(rep,expanded)
|
||||
for r in rep:
|
||||
r.lineno = t.lineno
|
||||
tokens[i:j+tokcount] = rep
|
||||
i += len(rep)
|
||||
del expanded[t.value]
|
||||
continue
|
||||
elif t.value == '__LINE__':
|
||||
t.type = self.t_INTEGER
|
||||
t.value = self.t_INTEGER_TYPE(t.lineno)
|
||||
|
||||
i += 1
|
||||
return tokens
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# evalexpr()
|
||||
#
|
||||
# Evaluate an expression token sequence for the purposes of evaluating
|
||||
# integral expressions.
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def evalexpr(self,tokens):
|
||||
# tokens = tokenize(line)
|
||||
# Search for defined macros
|
||||
i = 0
|
||||
while i < len(tokens):
|
||||
if tokens[i].type == self.t_ID and tokens[i].value == 'defined':
|
||||
j = i + 1
|
||||
needparen = False
|
||||
result = "0L"
|
||||
while j < len(tokens):
|
||||
if tokens[j].type in self.t_WS:
|
||||
j += 1
|
||||
continue
|
||||
elif tokens[j].type == self.t_ID:
|
||||
if tokens[j].value in self.macros:
|
||||
result = "1L"
|
||||
else:
|
||||
result = "0L"
|
||||
if not needparen: break
|
||||
elif tokens[j].value == '(':
|
||||
needparen = True
|
||||
elif tokens[j].value == ')':
|
||||
break
|
||||
else:
|
||||
self.error(self.source,tokens[i].lineno,"Malformed defined()")
|
||||
j += 1
|
||||
tokens[i].type = self.t_INTEGER
|
||||
tokens[i].value = self.t_INTEGER_TYPE(result)
|
||||
del tokens[i+1:j+1]
|
||||
i += 1
|
||||
tokens = self.expand_macros(tokens)
|
||||
for i,t in enumerate(tokens):
|
||||
if t.type == self.t_ID:
|
||||
tokens[i] = copy.copy(t)
|
||||
tokens[i].type = self.t_INTEGER
|
||||
tokens[i].value = self.t_INTEGER_TYPE("0L")
|
||||
elif t.type == self.t_INTEGER:
|
||||
tokens[i] = copy.copy(t)
|
||||
# Strip off any trailing suffixes
|
||||
tokens[i].value = str(tokens[i].value)
|
||||
while tokens[i].value[-1] not in "0123456789abcdefABCDEF":
|
||||
tokens[i].value = tokens[i].value[:-1]
|
||||
|
||||
expr = "".join([str(x.value) for x in tokens])
|
||||
expr = expr.replace("&&"," and ")
|
||||
expr = expr.replace("||"," or ")
|
||||
expr = expr.replace("!"," not ")
|
||||
try:
|
||||
result = eval(expr)
|
||||
except Exception:
|
||||
self.error(self.source,tokens[0].lineno,"Couldn't evaluate expression")
|
||||
result = 0
|
||||
return result
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# parsegen()
|
||||
#
|
||||
# Parse an input string/
|
||||
# ----------------------------------------------------------------------
|
||||
def parsegen(self,input,source=None):
|
||||
|
||||
# Replace trigraph sequences
|
||||
t = trigraph(input)
|
||||
lines = self.group_lines(t)
|
||||
|
||||
if not source:
|
||||
source = ""
|
||||
|
||||
self.define("__FILE__ \"%s\"" % source)
|
||||
|
||||
self.source = source
|
||||
chunk = []
|
||||
enable = True
|
||||
iftrigger = False
|
||||
ifstack = []
|
||||
|
||||
for x in lines:
|
||||
for i,tok in enumerate(x):
|
||||
if tok.type not in self.t_WS: break
|
||||
if tok.value == '#':
|
||||
# Preprocessor directive
|
||||
|
||||
# insert necessary whitespace instead of eaten tokens
|
||||
for tok in x:
|
||||
if tok.type in self.t_WS and '\n' in tok.value:
|
||||
chunk.append(tok)
|
||||
|
||||
dirtokens = self.tokenstrip(x[i+1:])
|
||||
if dirtokens:
|
||||
name = dirtokens[0].value
|
||||
args = self.tokenstrip(dirtokens[1:])
|
||||
else:
|
||||
name = ""
|
||||
args = []
|
||||
|
||||
if name == 'define':
|
||||
if enable:
|
||||
for tok in self.expand_macros(chunk):
|
||||
yield tok
|
||||
chunk = []
|
||||
self.define(args)
|
||||
elif name == 'include':
|
||||
if enable:
|
||||
for tok in self.expand_macros(chunk):
|
||||
yield tok
|
||||
chunk = []
|
||||
oldfile = self.macros['__FILE__']
|
||||
for tok in self.include(args):
|
||||
yield tok
|
||||
self.macros['__FILE__'] = oldfile
|
||||
self.source = source
|
||||
elif name == 'undef':
|
||||
if enable:
|
||||
for tok in self.expand_macros(chunk):
|
||||
yield tok
|
||||
chunk = []
|
||||
self.undef(args)
|
||||
elif name == 'ifdef':
|
||||
ifstack.append((enable,iftrigger))
|
||||
if enable:
|
||||
if not args[0].value in self.macros:
|
||||
enable = False
|
||||
iftrigger = False
|
||||
else:
|
||||
iftrigger = True
|
||||
elif name == 'ifndef':
|
||||
ifstack.append((enable,iftrigger))
|
||||
if enable:
|
||||
if args[0].value in self.macros:
|
||||
enable = False
|
||||
iftrigger = False
|
||||
else:
|
||||
iftrigger = True
|
||||
elif name == 'if':
|
||||
ifstack.append((enable,iftrigger))
|
||||
if enable:
|
||||
result = self.evalexpr(args)
|
||||
if not result:
|
||||
enable = False
|
||||
iftrigger = False
|
||||
else:
|
||||
iftrigger = True
|
||||
elif name == 'elif':
|
||||
if ifstack:
|
||||
if ifstack[-1][0]: # We only pay attention if outer "if" allows this
|
||||
if enable: # If already true, we flip enable False
|
||||
enable = False
|
||||
elif not iftrigger: # If False, but not triggered yet, we'll check expression
|
||||
result = self.evalexpr(args)
|
||||
if result:
|
||||
enable = True
|
||||
iftrigger = True
|
||||
else:
|
||||
self.error(self.source,dirtokens[0].lineno,"Misplaced #elif")
|
||||
|
||||
elif name == 'else':
|
||||
if ifstack:
|
||||
if ifstack[-1][0]:
|
||||
if enable:
|
||||
enable = False
|
||||
elif not iftrigger:
|
||||
enable = True
|
||||
iftrigger = True
|
||||
else:
|
||||
self.error(self.source,dirtokens[0].lineno,"Misplaced #else")
|
||||
|
||||
elif name == 'endif':
|
||||
if ifstack:
|
||||
enable,iftrigger = ifstack.pop()
|
||||
else:
|
||||
self.error(self.source,dirtokens[0].lineno,"Misplaced #endif")
|
||||
else:
|
||||
# Unknown preprocessor directive
|
||||
pass
|
||||
|
||||
else:
|
||||
# Normal text
|
||||
if enable:
|
||||
chunk.extend(x)
|
||||
|
||||
for tok in self.expand_macros(chunk):
|
||||
yield tok
|
||||
chunk = []
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# include()
|
||||
#
|
||||
# Implementation of file-inclusion
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def include(self,tokens):
|
||||
# Try to extract the filename and then process an include file
|
||||
if not tokens:
|
||||
return
|
||||
if tokens:
|
||||
if tokens[0].value != '<' and tokens[0].type != self.t_STRING:
|
||||
tokens = self.expand_macros(tokens)
|
||||
|
||||
if tokens[0].value == '<':
|
||||
# Include <...>
|
||||
i = 1
|
||||
while i < len(tokens):
|
||||
if tokens[i].value == '>':
|
||||
break
|
||||
i += 1
|
||||
else:
|
||||
print("Malformed #include <...>")
|
||||
return
|
||||
filename = "".join([x.value for x in tokens[1:i]])
|
||||
path = self.path + [""] + self.temp_path
|
||||
elif tokens[0].type == self.t_STRING:
|
||||
filename = tokens[0].value[1:-1]
|
||||
path = self.temp_path + [""] + self.path
|
||||
else:
|
||||
print("Malformed #include statement")
|
||||
return
|
||||
for p in path:
|
||||
iname = os.path.join(p,filename)
|
||||
try:
|
||||
data = open(iname,"r").read()
|
||||
dname = os.path.dirname(iname)
|
||||
if dname:
|
||||
self.temp_path.insert(0,dname)
|
||||
for tok in self.parsegen(data,filename):
|
||||
yield tok
|
||||
if dname:
|
||||
del self.temp_path[0]
|
||||
break
|
||||
except IOError:
|
||||
pass
|
||||
else:
|
||||
print("Couldn't find '%s'" % filename)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# define()
|
||||
#
|
||||
# Define a new macro
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def define(self,tokens):
|
||||
if isinstance(tokens,STRING_TYPES):
|
||||
tokens = self.tokenize(tokens)
|
||||
|
||||
linetok = tokens
|
||||
try:
|
||||
name = linetok[0]
|
||||
if len(linetok) > 1:
|
||||
mtype = linetok[1]
|
||||
else:
|
||||
mtype = None
|
||||
if not mtype:
|
||||
m = Macro(name.value,[])
|
||||
self.macros[name.value] = m
|
||||
elif mtype.type in self.t_WS:
|
||||
# A normal macro
|
||||
m = Macro(name.value,self.tokenstrip(linetok[2:]))
|
||||
self.macros[name.value] = m
|
||||
elif mtype.value == '(':
|
||||
# A macro with arguments
|
||||
tokcount, args, positions = self.collect_args(linetok[1:])
|
||||
variadic = False
|
||||
for a in args:
|
||||
if variadic:
|
||||
print("No more arguments may follow a variadic argument")
|
||||
break
|
||||
astr = "".join([str(_i.value) for _i in a])
|
||||
if astr == "...":
|
||||
variadic = True
|
||||
a[0].type = self.t_ID
|
||||
a[0].value = '__VA_ARGS__'
|
||||
variadic = True
|
||||
del a[1:]
|
||||
continue
|
||||
elif astr[-3:] == "..." and a[0].type == self.t_ID:
|
||||
variadic = True
|
||||
del a[1:]
|
||||
# If, for some reason, "." is part of the identifier, strip off the name for the purposes
|
||||
# of macro expansion
|
||||
if a[0].value[-3:] == '...':
|
||||
a[0].value = a[0].value[:-3]
|
||||
continue
|
||||
if len(a) > 1 or a[0].type != self.t_ID:
|
||||
print("Invalid macro argument")
|
||||
break
|
||||
else:
|
||||
mvalue = self.tokenstrip(linetok[1+tokcount:])
|
||||
i = 0
|
||||
while i < len(mvalue):
|
||||
if i+1 < len(mvalue):
|
||||
if mvalue[i].type in self.t_WS and mvalue[i+1].value == '##':
|
||||
del mvalue[i]
|
||||
continue
|
||||
elif mvalue[i].value == '##' and mvalue[i+1].type in self.t_WS:
|
||||
del mvalue[i+1]
|
||||
i += 1
|
||||
m = Macro(name.value,mvalue,[x[0].value for x in args],variadic)
|
||||
self.macro_prescan(m)
|
||||
self.macros[name.value] = m
|
||||
else:
|
||||
print("Bad macro definition")
|
||||
except LookupError:
|
||||
print("Bad macro definition")
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# undef()
|
||||
#
|
||||
# Undefine a macro
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def undef(self,tokens):
|
||||
id = tokens[0].value
|
||||
try:
|
||||
del self.macros[id]
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# parse()
|
||||
#
|
||||
# Parse input text.
|
||||
# ----------------------------------------------------------------------
|
||||
def parse(self,input,source=None,ignore={}):
|
||||
self.ignore = ignore
|
||||
self.parser = self.parsegen(input,source)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# token()
|
||||
#
|
||||
# Method to return individual tokens
|
||||
# ----------------------------------------------------------------------
|
||||
def token(self):
|
||||
try:
|
||||
while True:
|
||||
tok = next(self.parser)
|
||||
if tok.type not in self.ignore: return tok
|
||||
except StopIteration:
|
||||
self.parser = None
|
||||
return None
|
||||
|
||||
if __name__ == '__main__':
|
||||
import ply.lex as lex
|
||||
lexer = lex.lex()
|
||||
|
||||
# Run a preprocessor
|
||||
import sys
|
||||
f = open(sys.argv[1])
|
||||
input = f.read()
|
||||
|
||||
p = Preprocessor(lexer)
|
||||
p.parse(input,sys.argv[1])
|
||||
while True:
|
||||
tok = p.token()
|
||||
if not tok: break
|
||||
print(p.source, tok)
|
133
.venv/Lib/site-packages/pycparser/ply/ctokens.py
Normal file
133
.venv/Lib/site-packages/pycparser/ply/ctokens.py
Normal file
@@ -0,0 +1,133 @@
|
||||
# ----------------------------------------------------------------------
|
||||
# ctokens.py
|
||||
#
|
||||
# Token specifications for symbols in ANSI C and C++. This file is
|
||||
# meant to be used as a library in other tokenizers.
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Reserved words
|
||||
|
||||
tokens = [
|
||||
# Literals (identifier, integer constant, float constant, string constant, char const)
|
||||
'ID', 'TYPEID', 'INTEGER', 'FLOAT', 'STRING', 'CHARACTER',
|
||||
|
||||
# Operators (+,-,*,/,%,|,&,~,^,<<,>>, ||, &&, !, <, <=, >, >=, ==, !=)
|
||||
'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MODULO',
|
||||
'OR', 'AND', 'NOT', 'XOR', 'LSHIFT', 'RSHIFT',
|
||||
'LOR', 'LAND', 'LNOT',
|
||||
'LT', 'LE', 'GT', 'GE', 'EQ', 'NE',
|
||||
|
||||
# Assignment (=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=)
|
||||
'EQUALS', 'TIMESEQUAL', 'DIVEQUAL', 'MODEQUAL', 'PLUSEQUAL', 'MINUSEQUAL',
|
||||
'LSHIFTEQUAL','RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL', 'OREQUAL',
|
||||
|
||||
# Increment/decrement (++,--)
|
||||
'INCREMENT', 'DECREMENT',
|
||||
|
||||
# Structure dereference (->)
|
||||
'ARROW',
|
||||
|
||||
# Ternary operator (?)
|
||||
'TERNARY',
|
||||
|
||||
# Delimeters ( ) [ ] { } , . ; :
|
||||
'LPAREN', 'RPAREN',
|
||||
'LBRACKET', 'RBRACKET',
|
||||
'LBRACE', 'RBRACE',
|
||||
'COMMA', 'PERIOD', 'SEMI', 'COLON',
|
||||
|
||||
# Ellipsis (...)
|
||||
'ELLIPSIS',
|
||||
]
|
||||
|
||||
# Operators
|
||||
t_PLUS = r'\+'
|
||||
t_MINUS = r'-'
|
||||
t_TIMES = r'\*'
|
||||
t_DIVIDE = r'/'
|
||||
t_MODULO = r'%'
|
||||
t_OR = r'\|'
|
||||
t_AND = r'&'
|
||||
t_NOT = r'~'
|
||||
t_XOR = r'\^'
|
||||
t_LSHIFT = r'<<'
|
||||
t_RSHIFT = r'>>'
|
||||
t_LOR = r'\|\|'
|
||||
t_LAND = r'&&'
|
||||
t_LNOT = r'!'
|
||||
t_LT = r'<'
|
||||
t_GT = r'>'
|
||||
t_LE = r'<='
|
||||
t_GE = r'>='
|
||||
t_EQ = r'=='
|
||||
t_NE = r'!='
|
||||
|
||||
# Assignment operators
|
||||
|
||||
t_EQUALS = r'='
|
||||
t_TIMESEQUAL = r'\*='
|
||||
t_DIVEQUAL = r'/='
|
||||
t_MODEQUAL = r'%='
|
||||
t_PLUSEQUAL = r'\+='
|
||||
t_MINUSEQUAL = r'-='
|
||||
t_LSHIFTEQUAL = r'<<='
|
||||
t_RSHIFTEQUAL = r'>>='
|
||||
t_ANDEQUAL = r'&='
|
||||
t_OREQUAL = r'\|='
|
||||
t_XOREQUAL = r'\^='
|
||||
|
||||
# Increment/decrement
|
||||
t_INCREMENT = r'\+\+'
|
||||
t_DECREMENT = r'--'
|
||||
|
||||
# ->
|
||||
t_ARROW = r'->'
|
||||
|
||||
# ?
|
||||
t_TERNARY = r'\?'
|
||||
|
||||
# Delimeters
|
||||
t_LPAREN = r'\('
|
||||
t_RPAREN = r'\)'
|
||||
t_LBRACKET = r'\['
|
||||
t_RBRACKET = r'\]'
|
||||
t_LBRACE = r'\{'
|
||||
t_RBRACE = r'\}'
|
||||
t_COMMA = r','
|
||||
t_PERIOD = r'\.'
|
||||
t_SEMI = r';'
|
||||
t_COLON = r':'
|
||||
t_ELLIPSIS = r'\.\.\.'
|
||||
|
||||
# Identifiers
|
||||
t_ID = r'[A-Za-z_][A-Za-z0-9_]*'
|
||||
|
||||
# Integer literal
|
||||
t_INTEGER = r'\d+([uU]|[lL]|[uU][lL]|[lL][uU])?'
|
||||
|
||||
# Floating literal
|
||||
t_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?'
|
||||
|
||||
# String literal
|
||||
t_STRING = r'\"([^\\\n]|(\\.))*?\"'
|
||||
|
||||
# Character constant 'c' or L'c'
|
||||
t_CHARACTER = r'(L)?\'([^\\\n]|(\\.))*?\''
|
||||
|
||||
# Comment (C-Style)
|
||||
def t_COMMENT(t):
|
||||
r'/\*(.|\n)*?\*/'
|
||||
t.lexer.lineno += t.value.count('\n')
|
||||
return t
|
||||
|
||||
# Comment (C++-Style)
|
||||
def t_CPPCOMMENT(t):
|
||||
r'//.*\n'
|
||||
t.lexer.lineno += 1
|
||||
return t
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
1099
.venv/Lib/site-packages/pycparser/ply/lex.py
Normal file
1099
.venv/Lib/site-packages/pycparser/ply/lex.py
Normal file
File diff suppressed because it is too large
Load Diff
3494
.venv/Lib/site-packages/pycparser/ply/yacc.py
Normal file
3494
.venv/Lib/site-packages/pycparser/ply/yacc.py
Normal file
File diff suppressed because it is too large
Load Diff
74
.venv/Lib/site-packages/pycparser/ply/ygen.py
Normal file
74
.venv/Lib/site-packages/pycparser/ply/ygen.py
Normal file
@@ -0,0 +1,74 @@
|
||||
# ply: ygen.py
|
||||
#
|
||||
# This is a support program that auto-generates different versions of the YACC parsing
|
||||
# function with different features removed for the purposes of performance.
|
||||
#
|
||||
# Users should edit the method LParser.parsedebug() in yacc.py. The source code
|
||||
# for that method is then used to create the other methods. See the comments in
|
||||
# yacc.py for further details.
|
||||
|
||||
import os.path
|
||||
import shutil
|
||||
|
||||
def get_source_range(lines, tag):
|
||||
srclines = enumerate(lines)
|
||||
start_tag = '#--! %s-start' % tag
|
||||
end_tag = '#--! %s-end' % tag
|
||||
|
||||
for start_index, line in srclines:
|
||||
if line.strip().startswith(start_tag):
|
||||
break
|
||||
|
||||
for end_index, line in srclines:
|
||||
if line.strip().endswith(end_tag):
|
||||
break
|
||||
|
||||
return (start_index + 1, end_index)
|
||||
|
||||
def filter_section(lines, tag):
|
||||
filtered_lines = []
|
||||
include = True
|
||||
tag_text = '#--! %s' % tag
|
||||
for line in lines:
|
||||
if line.strip().startswith(tag_text):
|
||||
include = not include
|
||||
elif include:
|
||||
filtered_lines.append(line)
|
||||
return filtered_lines
|
||||
|
||||
def main():
|
||||
dirname = os.path.dirname(__file__)
|
||||
shutil.copy2(os.path.join(dirname, 'yacc.py'), os.path.join(dirname, 'yacc.py.bak'))
|
||||
with open(os.path.join(dirname, 'yacc.py'), 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
parse_start, parse_end = get_source_range(lines, 'parsedebug')
|
||||
parseopt_start, parseopt_end = get_source_range(lines, 'parseopt')
|
||||
parseopt_notrack_start, parseopt_notrack_end = get_source_range(lines, 'parseopt-notrack')
|
||||
|
||||
# Get the original source
|
||||
orig_lines = lines[parse_start:parse_end]
|
||||
|
||||
# Filter the DEBUG sections out
|
||||
parseopt_lines = filter_section(orig_lines, 'DEBUG')
|
||||
|
||||
# Filter the TRACKING sections out
|
||||
parseopt_notrack_lines = filter_section(parseopt_lines, 'TRACKING')
|
||||
|
||||
# Replace the parser source sections with updated versions
|
||||
lines[parseopt_notrack_start:parseopt_notrack_end] = parseopt_notrack_lines
|
||||
lines[parseopt_start:parseopt_end] = parseopt_lines
|
||||
|
||||
lines = [line.rstrip()+'\n' for line in lines]
|
||||
with open(os.path.join(dirname, 'yacc.py'), 'w') as f:
|
||||
f.writelines(lines)
|
||||
|
||||
print('Updated yacc.py')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
|
||||
|
||||
|
133
.venv/Lib/site-packages/pycparser/plyparser.py
Normal file
133
.venv/Lib/site-packages/pycparser/plyparser.py
Normal file
@@ -0,0 +1,133 @@
|
||||
#-----------------------------------------------------------------
|
||||
# plyparser.py
|
||||
#
|
||||
# PLYParser class and other utilities for simplifying programming
|
||||
# parsers with PLY
|
||||
#
|
||||
# Eli Bendersky [https://eli.thegreenplace.net/]
|
||||
# License: BSD
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
import warnings
|
||||
|
||||
class Coord(object):
|
||||
""" Coordinates of a syntactic element. Consists of:
|
||||
- File name
|
||||
- Line number
|
||||
- (optional) column number, for the Lexer
|
||||
"""
|
||||
__slots__ = ('file', 'line', 'column', '__weakref__')
|
||||
def __init__(self, file, line, column=None):
|
||||
self.file = file
|
||||
self.line = line
|
||||
self.column = column
|
||||
|
||||
def __str__(self):
|
||||
str = "%s:%s" % (self.file, self.line)
|
||||
if self.column: str += ":%s" % self.column
|
||||
return str
|
||||
|
||||
|
||||
class ParseError(Exception): pass
|
||||
|
||||
|
||||
class PLYParser(object):
|
||||
def _create_opt_rule(self, rulename):
|
||||
""" Given a rule name, creates an optional ply.yacc rule
|
||||
for it. The name of the optional rule is
|
||||
<rulename>_opt
|
||||
"""
|
||||
optname = rulename + '_opt'
|
||||
|
||||
def optrule(self, p):
|
||||
p[0] = p[1]
|
||||
|
||||
optrule.__doc__ = '%s : empty\n| %s' % (optname, rulename)
|
||||
optrule.__name__ = 'p_%s' % optname
|
||||
setattr(self.__class__, optrule.__name__, optrule)
|
||||
|
||||
def _coord(self, lineno, column=None):
|
||||
return Coord(
|
||||
file=self.clex.filename,
|
||||
line=lineno,
|
||||
column=column)
|
||||
|
||||
def _token_coord(self, p, token_idx):
|
||||
""" Returns the coordinates for the YaccProduction object 'p' indexed
|
||||
with 'token_idx'. The coordinate includes the 'lineno' and
|
||||
'column'. Both follow the lex semantic, starting from 1.
|
||||
"""
|
||||
last_cr = p.lexer.lexer.lexdata.rfind('\n', 0, p.lexpos(token_idx))
|
||||
if last_cr < 0:
|
||||
last_cr = -1
|
||||
column = (p.lexpos(token_idx) - (last_cr))
|
||||
return self._coord(p.lineno(token_idx), column)
|
||||
|
||||
def _parse_error(self, msg, coord):
|
||||
raise ParseError("%s: %s" % (coord, msg))
|
||||
|
||||
|
||||
def parameterized(*params):
|
||||
""" Decorator to create parameterized rules.
|
||||
|
||||
Parameterized rule methods must be named starting with 'p_' and contain
|
||||
'xxx', and their docstrings may contain 'xxx' and 'yyy'. These will be
|
||||
replaced by the given parameter tuples. For example, ``p_xxx_rule()`` with
|
||||
docstring 'xxx_rule : yyy' when decorated with
|
||||
``@parameterized(('id', 'ID'))`` produces ``p_id_rule()`` with the docstring
|
||||
'id_rule : ID'. Using multiple tuples produces multiple rules.
|
||||
"""
|
||||
def decorate(rule_func):
|
||||
rule_func._params = params
|
||||
return rule_func
|
||||
return decorate
|
||||
|
||||
|
||||
def template(cls):
|
||||
""" Class decorator to generate rules from parameterized rule templates.
|
||||
|
||||
See `parameterized` for more information on parameterized rules.
|
||||
"""
|
||||
issued_nodoc_warning = False
|
||||
for attr_name in dir(cls):
|
||||
if attr_name.startswith('p_'):
|
||||
method = getattr(cls, attr_name)
|
||||
if hasattr(method, '_params'):
|
||||
# Remove the template method
|
||||
delattr(cls, attr_name)
|
||||
# Create parameterized rules from this method; only run this if
|
||||
# the method has a docstring. This is to address an issue when
|
||||
# pycparser's users are installed in -OO mode which strips
|
||||
# docstrings away.
|
||||
# See: https://github.com/eliben/pycparser/pull/198/ and
|
||||
# https://github.com/eliben/pycparser/issues/197
|
||||
# for discussion.
|
||||
if method.__doc__ is not None:
|
||||
_create_param_rules(cls, method)
|
||||
elif not issued_nodoc_warning:
|
||||
warnings.warn(
|
||||
'parsing methods must have __doc__ for pycparser to work properly',
|
||||
RuntimeWarning,
|
||||
stacklevel=2)
|
||||
issued_nodoc_warning = True
|
||||
return cls
|
||||
|
||||
|
||||
def _create_param_rules(cls, func):
|
||||
""" Create ply.yacc rules based on a parameterized rule function
|
||||
|
||||
Generates new methods (one per each pair of parameters) based on the
|
||||
template rule function `func`, and attaches them to `cls`. The rule
|
||||
function's parameters must be accessible via its `_params` attribute.
|
||||
"""
|
||||
for xxx, yyy in func._params:
|
||||
# Use the template method's body for each new method
|
||||
def param_rule(self, p):
|
||||
func(self, p)
|
||||
|
||||
# Substitute in the params for the grammar rule and function name
|
||||
param_rule.__doc__ = func.__doc__.replace('xxx', xxx).replace('yyy', yyy)
|
||||
param_rule.__name__ = func.__name__.replace('xxx', xxx)
|
||||
|
||||
# Attach the new method to the class
|
||||
setattr(cls, param_rule.__name__, param_rule)
|
369
.venv/Lib/site-packages/pycparser/yacctab.py
Normal file
369
.venv/Lib/site-packages/pycparser/yacctab.py
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user