pcpp.parser
module
Source code
#!/usr/bin/python
# Python C99 conforming preprocessor parser config
# (C) 2017-2020 Niall Douglas http://www.nedproductions.biz/
# and (C) 2007-2017 David Beazley http://www.dabeaz.com/
# Started: Feb 2017
#
# This C preprocessor was originally written by David Beazley and the
# original can be found at https://github.com/dabeaz/ply/blob/master/ply/cpp.py
# This edition substantially improves on standards conforming output,
# getting quite close to what clang or GCC outputs.
from __future__ import generators, print_function, absolute_import, division
import sys, re, os
in_production = 1 # Set to 0 if editing pcpp implementation!
# Some Python 3 compatibility shims
if sys.version_info.major < 3:
STRING_TYPES = (str, unicode)
else:
STRING_TYPES = str
# -----------------------------------------------------------------------------
# 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_LINECONT', 'CPP_COMMENT1', 'CPP_COMMENT2',
'CPP_POUND','CPP_DPOUND', 'CPP_PLUS', 'CPP_MINUS', 'CPP_STAR', 'CPP_FSLASH', 'CPP_PERCENT', 'CPP_BAR',
'CPP_AMPERSAND', 'CPP_TILDE', 'CPP_HAT', 'CPP_LESS', 'CPP_GREATER', 'CPP_EQUAL', 'CPP_EXCLAMATION',
'CPP_QUESTION', 'CPP_LPAREN', 'CPP_RPAREN', 'CPP_LBRACKET', 'CPP_RBRACKET', 'CPP_LCURLY', 'CPP_RCURLY',
'CPP_DOT', 'CPP_COMMA', 'CPP_SEMICOLON', 'CPP_COLON', 'CPP_BSLASH', 'CPP_SQUOTE', 'CPP_DQUOTE',
'CPP_DEREFERENCE', 'CPP_MINUSEQUAL', 'CPP_MINUSMINUS', 'CPP_LSHIFT', 'CPP_LESSEQUAL', 'CPP_RSHIFT',
'CPP_GREATEREQUAL', 'CPP_LOGICALOR', 'CPP_OREQUAL', 'CPP_LOGICALAND', 'CPP_ANDEQUAL', 'CPP_EQUALITY',
'CPP_INEQUALITY', 'CPP_XOREQUAL', 'CPP_MULTIPLYEQUAL', 'CPP_DIVIDEEQUAL', 'CPP_PLUSEQUAL', 'CPP_PLUSPLUS',
'CPP_PERCENTEQUAL', 'CPP_LSHIFTEQUAL', 'CPP_RSHIFTEQUAL'
)
literals = "+-*/%|&~^<>=!?()[]{}.,;:\\\'\""
# Whitespace, but don't match past the end of a line
def t_CPP_WS(t):
r'([ \t]+|\n)'
t.lexer.lineno += t.value.count("\n")
return t
# Line continuation, accept whitespace between the backslash and new line
def t_CPP_LINECONT(t):
r'\\[ \t]*\n'
t.value = t.value[1:-1]
t.lexer.lineno += 1
return t
_string_literal_linecont_pat = re.compile(r'\\[ \t]*\n')
t_CPP_POUND = r'\#'
t_CPP_DPOUND = r'\#\#'
t_CPP_PLUS = r'\+'
t_CPP_MINUS = r'-'
t_CPP_STAR = r'\*'
t_CPP_FSLASH = r'/'
t_CPP_PERCENT = r'%'
t_CPP_BAR = r'\|'
t_CPP_AMPERSAND = r'&'
t_CPP_TILDE = r'~'
t_CPP_HAT = r'\^'
t_CPP_LESS = r'<'
t_CPP_GREATER = r'>'
t_CPP_EQUAL = r'='
t_CPP_EXCLAMATION = r'!'
t_CPP_QUESTION = r'\?'
t_CPP_LPAREN = r'\('
t_CPP_RPAREN = r'\)'
t_CPP_LBRACKET = r'\['
t_CPP_RBRACKET = r'\]'
t_CPP_LCURLY = r'{'
t_CPP_RCURLY = r'}'
t_CPP_DOT = r'\.'
t_CPP_COMMA = r','
t_CPP_SEMICOLON = r';'
t_CPP_COLON = r':'
t_CPP_BSLASH = r'\\'
t_CPP_SQUOTE = r"'"
t_CPP_DQUOTE = r'"'
t_CPP_DEREFERENCE = r'->'
t_CPP_MINUSEQUAL = r'-='
t_CPP_MINUSMINUS = r'--'
t_CPP_LSHIFT = r'<<'
t_CPP_LESSEQUAL = r'<='
t_CPP_RSHIFT = r'>>'
t_CPP_GREATEREQUAL = r'>='
t_CPP_LOGICALOR = r'\|\|'
t_CPP_OREQUAL = r'\|='
t_CPP_LOGICALAND = r'&&'
t_CPP_ANDEQUAL = r'&='
t_CPP_EQUALITY = r'=='
t_CPP_INEQUALITY = r'!='
t_CPP_XOREQUAL = r'^='
t_CPP_MULTIPLYEQUAL = r'\*='
t_CPP_DIVIDEEQUAL = r'/='
t_CPP_PLUSEQUAL = r'\+='
t_CPP_PLUSPLUS = r'\+\+'
t_CPP_PERCENTEQUAL = r'%='
t_CPP_LSHIFTEQUAL = r'<<='
t_CPP_RSHIFTEQUAL = 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.value, subs_made = _string_literal_linecont_pat.subn('', t.value)
t.lexer.lineno += subs_made + 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
return t
# Line comment
def t_CPP_COMMENT2(t):
r'(//[^\n]*)'
return t
def t_error(t):
t.type = t.value[0]
t.value = t.value[0]
t.lexer.skip(1)
return t
# Python 2/3 compatible way of importing a subpackage
oldsyspath = sys.path
sys.path = [ os.path.join( os.path.dirname( os.path.abspath(__file__) ), "ply" ) ] + sys.path
from ply import lex, yacc
from ply.lex import LexToken
sys.path = oldsyspath
del oldsyspath
# -----------------------------------------------------------------------------
# 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)
def default_lexer():
return lex.lex(optimize=in_production)
# ------------------------------------------------------------------
# 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
self.lineno = None
def __repr__(self):
return "%s(%s)=%s" % (self.name, self.arglist, self.value)
# ------------------------------------------------------------------
# Preprocessor event hooks
#
# Override these to customise preprocessing
# ------------------------------------------------------------------
class Action(object):
"""What kind of abort processing to do in OutputDirective"""
IgnoreAndPassThrough = 0
"""Abort processing (don't execute), but pass the directive through to output"""
IgnoreAndRemove = 1
"""Abort processing (don't execute), and remove from output"""
class OutputDirective(Exception):
"""Raise this exception to abort processing of a preprocessor directive and
to instead output it as is into the output"""
def __init__(self, action):
self.action = action
class PreprocessorHooks(object):
"""Override these in your subclass of Preprocessor to customise preprocessing"""
def __init__(self):
self.lastdirective = None
def on_error(self,file,line,msg):
"""Called when the preprocessor has encountered an error, e.g. malformed input.
The default simply prints to stderr and increments the return code.
"""
print("%s:%d error: %s" % (file,line,msg), file = sys.stderr)
self.return_code += 1
def on_file_open(self,is_system_include,includepath):
"""Called to open a file for reading.
This hook provides the ability to use ``chardet``, or any other mechanism,
to inspect a file for its text encoding, and open it appropriately. Be
aware that this function is used to probe for possible include file locations,
so ``includepath`` may not exist. If it does not, raise the appropriate
``IOError`` exception.
The default calls ``io.open(includepath, 'r', encoding = self.assume_encoding)``,
examines if it starts with a BOM (if so, it removes it), and returns the file
object opened. This raises the appropriate exception if the path was not found.
"""
if sys.version_info.major < 3:
assert self.assume_encoding is None
ret = open(includepath, 'r')
else:
ret = open(includepath, 'r', encoding = self.assume_encoding)
bom = ret.read(1)
#print(repr(bom))
if bom != '\ufeff':
ret.seek(0)
return ret
def on_include_not_found(self,is_malformed,is_system_include,curdir,includepath):
"""Called when a #include wasn't found.
Raise OutputDirective to pass through or remove, else return
a suitable path. Remember that Preprocessor.add_path() lets you add search paths.
The default calls ``self.on_error()`` with a suitable error message about the
include file not found if ``is_malformed`` is False, else a suitable error
message about a malformed #include, and in both cases raises OutputDirective
(pass through).
"""
if is_malformed:
self.on_error(self.lastdirective.source,self.lastdirective.lineno, "Malformed #include statement: %s" % includepath)
else:
self.on_error(self.lastdirective.source,self.lastdirective.lineno, "Include file '%s' not found" % includepath)
raise OutputDirective(Action.IgnoreAndPassThrough)
def on_unknown_macro_in_defined_expr(self,tok):
"""Called when an expression passed to an #if contained a defined operator
performed on something unknown.
Return True if to treat it as defined, False if to treat it as undefined,
raise OutputDirective to pass through without execution, or return None to
pass through the mostly expanded #if expression apart from the unknown defined.
The default returns False, as per the C standard.
"""
return False
def on_unknown_macro_in_expr(self,ident):
"""Called when an expression passed to an #if contained an unknown identifier.
Return what value the expression evaluator ought to use, or return None to
pass through the mostly expanded #if expression.
The default returns an integer 0, as per the C standard.
"""
return 0
def on_unknown_macro_function_in_expr(self,ident):
"""Called when an expression passed to an #if contained an unknown function.
Return a callable which will be invoked by the expression evaluator to
evaluate the input to the function, or return None to pass through the
mostly expanded #if expression.
The default returns a lambda which returns integer 0, as per the C standard.
"""
return lambda x : 0
def on_directive_handle(self,directive,toks,ifpassthru,precedingtoks):
"""Called when there is one of
define, include, undef, ifdef, ifndef, if, elif, else, endif
Return True to execute and remove from the output, raise OutputDirective
to pass through or remove without execution, or return None to execute
AND pass through to the output (this only works for #define, #undef).
The default returns True (execute and remove from the output).
directive is the directive, toks is the tokens after the directive,
ifpassthru is whether we are in passthru mode, precedingtoks is the
tokens preceding the directive from the # token until the directive.
"""
self.lastdirective = directive
return True
def on_directive_unknown(self,directive,toks,ifpassthru,precedingtoks):
"""Called when the preprocessor encounters a #directive it doesn't understand.
This is actually quite an extensive list as it currently only understands:
define, include, undef, ifdef, ifndef, if, elif, else, endif
Return True to remove from the output, raise OutputDirective
to pass through or remove, or return None to
pass through into the output.
The default handles #error and #warning by printing to stderr and returning True
(remove from output). For everything else it returns None (pass through into output).
directive is the directive, toks is the tokens after the directive,
ifpassthru is whether we are in passthru mode, precedingtoks is the
tokens preceding the directive from the # token until the directive.
"""
if directive.value == 'error':
print("%s:%d error: %s" % (directive.source,directive.lineno,''.join(tok.value for tok in toks)), file = sys.stderr)
self.return_code += 1
return True
elif directive.value == 'warning':
print("%s:%d warning: %s" % (directive.source,directive.lineno,''.join(tok.value for tok in toks)), file = sys.stderr)
return True
return None
def on_potential_include_guard(self,macro):
"""Called when the preprocessor encounters an #ifndef macro or an #if !defined(macro)
as the first non-whitespace thing in a file. Unlike the other hooks, macro is a string,
not a token.
"""
pass
def on_comment(self,tok):
"""Called when the preprocessor encounters a comment token. You can modify the token
in place. You must return True to let the comment pass through, else it will be removed.
Returning False or None modifies the token to become whitespace, becoming a single space
if the comment is a block comment, else a single new line if the comment is a line comment.
"""
return None
Functions
def CPP_INTEGER(t)
-
(((((0x)|(0X))[0-9a-fA-F]+)|(\d+))([uU][lL]|[lL][uU]|[uU]|[lL])?)
Source code
def CPP_INTEGER(t): r'(((((0x)|(0X))[0-9a-fA-F]+)|(\d+))([uU][lL]|[lL][uU]|[uU]|[lL])?)' return t
def default_lexer()
-
Source code
def default_lexer(): return lex.lex(optimize=in_production)
def t_CPP_CHAR(t)
-
(L)?'([^\\n]|(\(.|\n)))*?'
Source code
def t_CPP_CHAR(t): r'(L)?\'([^\\\n]|(\\(.|\n)))*?\'' t.lexer.lineno += t.value.count("\n") return t
def t_CPP_COMMENT1(t)
-
(/*(.|\n)*?*/)
Source code
def t_CPP_COMMENT1(t): r'(/\*(.|\n)*?\*/)' ncr = t.value.count("\n") t.lexer.lineno += ncr return t
def t_CPP_COMMENT2(t)
-
(//[^\n]*)
Source code
def t_CPP_COMMENT2(t): r'(//[^\n]*)' return t
def t_CPP_INTEGER(t)
-
(((((0x)|(0X))[0-9a-fA-F]+)|(\d+))([uU][lL]|[lL][uU]|[uU]|[lL])?)
Source code
def CPP_INTEGER(t): r'(((((0x)|(0X))[0-9a-fA-F]+)|(\d+))([uU][lL]|[lL][uU]|[uU]|[lL])?)' return t
def t_CPP_LINECONT(t)
-
\[ \t]*\n
Source code
def t_CPP_LINECONT(t): r'\\[ \t]*\n' t.value = t.value[1:-1] t.lexer.lineno += 1 return t
def t_CPP_STRING(t)
-
"([^\\n]|(\(.|\n)))*?"
Source code
def t_CPP_STRING(t): r'\"([^\\\n]|(\\(.|\n)))*?\"' t.value, subs_made = _string_literal_linecont_pat.subn('', t.value) t.lexer.lineno += subs_made + t.value.count("\n") return t
def t_CPP_WS(t)
-
([ \t]+|\n)
Source code
def t_CPP_WS(t): r'([ \t]+|\n)' t.lexer.lineno += t.value.count("\n") return t
def t_error(t)
-
Source code
def t_error(t): t.type = t.value[0] t.value = t.value[0] t.lexer.skip(1) return t
def trigraph(input)
-
Source code
def trigraph(input): return _trigraph_pat.sub(lambda g: _trigraph_rep[g.group()[-1]],input)
Classes
class Action
-
What kind of abort processing to do in OutputDirective
Source code
class Action(object): """What kind of abort processing to do in OutputDirective""" IgnoreAndPassThrough = 0 """Abort processing (don't execute), but pass the directive through to output""" IgnoreAndRemove = 1 """Abort processing (don't execute), and remove from output"""
Class variables
var IgnoreAndPassThrough
-
Abort processing (don't execute), but pass the directive through to output
var IgnoreAndRemove
-
Abort processing (don't execute), and remove from output
class Macro
-
Source code
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 self.lineno = None def __repr__(self): return "%s(%s)=%s" % (self.name, self.arglist, self.value)
Methods
def __init__(self, name, value, arglist=None, variadic=False)
-
Initialize self. See help(type(self)) for accurate signature.
Source code
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 self.lineno = None
class OutputDirective (ancestors: builtins.Exception, builtins.BaseException)
-
Raise this exception to abort processing of a preprocessor directive and to instead output it as is into the output
Source code
class OutputDirective(Exception): """Raise this exception to abort processing of a preprocessor directive and to instead output it as is into the output""" def __init__(self, action): self.action = action
Methods
def __init__(self, action)
-
Initialize self. See help(type(self)) for accurate signature.
Source code
def __init__(self, action): self.action = action
class PreprocessorHooks
-
Override these in your subclass of Preprocessor to customise preprocessing
Source code
class PreprocessorHooks(object): """Override these in your subclass of Preprocessor to customise preprocessing""" def __init__(self): self.lastdirective = None def on_error(self,file,line,msg): """Called when the preprocessor has encountered an error, e.g. malformed input. The default simply prints to stderr and increments the return code. """ print("%s:%d error: %s" % (file,line,msg), file = sys.stderr) self.return_code += 1 def on_file_open(self,is_system_include,includepath): """Called to open a file for reading. This hook provides the ability to use ``chardet``, or any other mechanism, to inspect a file for its text encoding, and open it appropriately. Be aware that this function is used to probe for possible include file locations, so ``includepath`` may not exist. If it does not, raise the appropriate ``IOError`` exception. The default calls ``io.open(includepath, 'r', encoding = self.assume_encoding)``, examines if it starts with a BOM (if so, it removes it), and returns the file object opened. This raises the appropriate exception if the path was not found. """ if sys.version_info.major < 3: assert self.assume_encoding is None ret = open(includepath, 'r') else: ret = open(includepath, 'r', encoding = self.assume_encoding) bom = ret.read(1) #print(repr(bom)) if bom != '\ufeff': ret.seek(0) return ret def on_include_not_found(self,is_malformed,is_system_include,curdir,includepath): """Called when a #include wasn't found. Raise OutputDirective to pass through or remove, else return a suitable path. Remember that Preprocessor.add_path() lets you add search paths. The default calls ``self.on_error()`` with a suitable error message about the include file not found if ``is_malformed`` is False, else a suitable error message about a malformed #include, and in both cases raises OutputDirective (pass through). """ if is_malformed: self.on_error(self.lastdirective.source,self.lastdirective.lineno, "Malformed #include statement: %s" % includepath) else: self.on_error(self.lastdirective.source,self.lastdirective.lineno, "Include file '%s' not found" % includepath) raise OutputDirective(Action.IgnoreAndPassThrough) def on_unknown_macro_in_defined_expr(self,tok): """Called when an expression passed to an #if contained a defined operator performed on something unknown. Return True if to treat it as defined, False if to treat it as undefined, raise OutputDirective to pass through without execution, or return None to pass through the mostly expanded #if expression apart from the unknown defined. The default returns False, as per the C standard. """ return False def on_unknown_macro_in_expr(self,ident): """Called when an expression passed to an #if contained an unknown identifier. Return what value the expression evaluator ought to use, or return None to pass through the mostly expanded #if expression. The default returns an integer 0, as per the C standard. """ return 0 def on_unknown_macro_function_in_expr(self,ident): """Called when an expression passed to an #if contained an unknown function. Return a callable which will be invoked by the expression evaluator to evaluate the input to the function, or return None to pass through the mostly expanded #if expression. The default returns a lambda which returns integer 0, as per the C standard. """ return lambda x : 0 def on_directive_handle(self,directive,toks,ifpassthru,precedingtoks): """Called when there is one of define, include, undef, ifdef, ifndef, if, elif, else, endif Return True to execute and remove from the output, raise OutputDirective to pass through or remove without execution, or return None to execute AND pass through to the output (this only works for #define, #undef). The default returns True (execute and remove from the output). directive is the directive, toks is the tokens after the directive, ifpassthru is whether we are in passthru mode, precedingtoks is the tokens preceding the directive from the # token until the directive. """ self.lastdirective = directive return True def on_directive_unknown(self,directive,toks,ifpassthru,precedingtoks): """Called when the preprocessor encounters a #directive it doesn't understand. This is actually quite an extensive list as it currently only understands: define, include, undef, ifdef, ifndef, if, elif, else, endif Return True to remove from the output, raise OutputDirective to pass through or remove, or return None to pass through into the output. The default handles #error and #warning by printing to stderr and returning True (remove from output). For everything else it returns None (pass through into output). directive is the directive, toks is the tokens after the directive, ifpassthru is whether we are in passthru mode, precedingtoks is the tokens preceding the directive from the # token until the directive. """ if directive.value == 'error': print("%s:%d error: %s" % (directive.source,directive.lineno,''.join(tok.value for tok in toks)), file = sys.stderr) self.return_code += 1 return True elif directive.value == 'warning': print("%s:%d warning: %s" % (directive.source,directive.lineno,''.join(tok.value for tok in toks)), file = sys.stderr) return True return None def on_potential_include_guard(self,macro): """Called when the preprocessor encounters an #ifndef macro or an #if !defined(macro) as the first non-whitespace thing in a file. Unlike the other hooks, macro is a string, not a token. """ pass def on_comment(self,tok): """Called when the preprocessor encounters a comment token. You can modify the token in place. You must return True to let the comment pass through, else it will be removed. Returning False or None modifies the token to become whitespace, becoming a single space if the comment is a block comment, else a single new line if the comment is a line comment. """ return None
Subclasses
Methods
def __init__(self)
-
Initialize self. See help(type(self)) for accurate signature.
Source code
def __init__(self): self.lastdirective = None
def on_comment(self, tok)
-
Called when the preprocessor encounters a comment token. You can modify the token in place. You must return True to let the comment pass through, else it will be removed.
Returning False or None modifies the token to become whitespace, becoming a single space if the comment is a block comment, else a single new line if the comment is a line comment.
Source code
def on_comment(self,tok): """Called when the preprocessor encounters a comment token. You can modify the token in place. You must return True to let the comment pass through, else it will be removed. Returning False or None modifies the token to become whitespace, becoming a single space if the comment is a block comment, else a single new line if the comment is a line comment. """ return None
def on_directive_handle(self, directive, toks, ifpassthru, precedingtoks)
-
Called when there is one of
define, include, undef, ifdef, ifndef, if, elif, else, endif
Return True to execute and remove from the output, raise OutputDirective to pass through or remove without execution, or return None to execute AND pass through to the output (this only works for #define, #undef).
The default returns True (execute and remove from the output).
directive is the directive, toks is the tokens after the directive, ifpassthru is whether we are in passthru mode, precedingtoks is the tokens preceding the directive from the # token until the directive.
Source code
def on_directive_handle(self,directive,toks,ifpassthru,precedingtoks): """Called when there is one of define, include, undef, ifdef, ifndef, if, elif, else, endif Return True to execute and remove from the output, raise OutputDirective to pass through or remove without execution, or return None to execute AND pass through to the output (this only works for #define, #undef). The default returns True (execute and remove from the output). directive is the directive, toks is the tokens after the directive, ifpassthru is whether we are in passthru mode, precedingtoks is the tokens preceding the directive from the # token until the directive. """ self.lastdirective = directive return True
def on_directive_unknown(self, directive, toks, ifpassthru, precedingtoks)
-
Called when the preprocessor encounters a #directive it doesn't understand. This is actually quite an extensive list as it currently only understands:
define, include, undef, ifdef, ifndef, if, elif, else, endif
Return True to remove from the output, raise OutputDirective to pass through or remove, or return None to pass through into the output.
The default handles #error and #warning by printing to stderr and returning True (remove from output). For everything else it returns None (pass through into output).
directive is the directive, toks is the tokens after the directive, ifpassthru is whether we are in passthru mode, precedingtoks is the tokens preceding the directive from the # token until the directive.
Source code
def on_directive_unknown(self,directive,toks,ifpassthru,precedingtoks): """Called when the preprocessor encounters a #directive it doesn't understand. This is actually quite an extensive list as it currently only understands: define, include, undef, ifdef, ifndef, if, elif, else, endif Return True to remove from the output, raise OutputDirective to pass through or remove, or return None to pass through into the output. The default handles #error and #warning by printing to stderr and returning True (remove from output). For everything else it returns None (pass through into output). directive is the directive, toks is the tokens after the directive, ifpassthru is whether we are in passthru mode, precedingtoks is the tokens preceding the directive from the # token until the directive. """ if directive.value == 'error': print("%s:%d error: %s" % (directive.source,directive.lineno,''.join(tok.value for tok in toks)), file = sys.stderr) self.return_code += 1 return True elif directive.value == 'warning': print("%s:%d warning: %s" % (directive.source,directive.lineno,''.join(tok.value for tok in toks)), file = sys.stderr) return True return None
def on_error(self, file, line, msg)
-
Called when the preprocessor has encountered an error, e.g. malformed input.
The default simply prints to stderr and increments the return code.
Source code
def on_error(self,file,line,msg): """Called when the preprocessor has encountered an error, e.g. malformed input. The default simply prints to stderr and increments the return code. """ print("%s:%d error: %s" % (file,line,msg), file = sys.stderr) self.return_code += 1
def on_file_open(self, is_system_include, includepath)
-
Called to open a file for reading.
This hook provides the ability to use
chardet
, or any other mechanism, to inspect a file for its text encoding, and open it appropriately. Be aware that this function is used to probe for possible include file locations, soincludepath
may not exist. If it does not, raise the appropriateIOError
exception.The default calls
io.open(includepath, 'r', encoding = self.assume_encoding)
, examines if it starts with a BOM (if so, it removes it), and returns the file object opened. This raises the appropriate exception if the path was not found.Source code
def on_file_open(self,is_system_include,includepath): """Called to open a file for reading. This hook provides the ability to use ``chardet``, or any other mechanism, to inspect a file for its text encoding, and open it appropriately. Be aware that this function is used to probe for possible include file locations, so ``includepath`` may not exist. If it does not, raise the appropriate ``IOError`` exception. The default calls ``io.open(includepath, 'r', encoding = self.assume_encoding)``, examines if it starts with a BOM (if so, it removes it), and returns the file object opened. This raises the appropriate exception if the path was not found. """ if sys.version_info.major < 3: assert self.assume_encoding is None ret = open(includepath, 'r') else: ret = open(includepath, 'r', encoding = self.assume_encoding) bom = ret.read(1) #print(repr(bom)) if bom != '\ufeff': ret.seek(0) return ret
def on_include_not_found(self, is_malformed, is_system_include, curdir, includepath)
-
Called when a #include wasn't found.
Raise OutputDirective to pass through or remove, else return a suitable path. Remember that Preprocessor.add_path() lets you add search paths.
The default calls
self.on_error()
with a suitable error message about the include file not found ifis_malformed
is False, else a suitable error message about a malformed #include, and in both cases raises OutputDirective (pass through).Source code
def on_include_not_found(self,is_malformed,is_system_include,curdir,includepath): """Called when a #include wasn't found. Raise OutputDirective to pass through or remove, else return a suitable path. Remember that Preprocessor.add_path() lets you add search paths. The default calls ``self.on_error()`` with a suitable error message about the include file not found if ``is_malformed`` is False, else a suitable error message about a malformed #include, and in both cases raises OutputDirective (pass through). """ if is_malformed: self.on_error(self.lastdirective.source,self.lastdirective.lineno, "Malformed #include statement: %s" % includepath) else: self.on_error(self.lastdirective.source,self.lastdirective.lineno, "Include file '%s' not found" % includepath) raise OutputDirective(Action.IgnoreAndPassThrough)
def on_potential_include_guard(self, macro)
-
Called when the preprocessor encounters an #ifndef macro or an #if !defined(macro) as the first non-whitespace thing in a file. Unlike the other hooks, macro is a string, not a token.
Source code
def on_potential_include_guard(self,macro): """Called when the preprocessor encounters an #ifndef macro or an #if !defined(macro) as the first non-whitespace thing in a file. Unlike the other hooks, macro is a string, not a token. """ pass
def on_unknown_macro_function_in_expr(self, ident)
-
Called when an expression passed to an #if contained an unknown function.
Return a callable which will be invoked by the expression evaluator to evaluate the input to the function, or return None to pass through the mostly expanded #if expression.
The default returns a lambda which returns integer 0, as per the C standard.
Source code
def on_unknown_macro_function_in_expr(self,ident): """Called when an expression passed to an #if contained an unknown function. Return a callable which will be invoked by the expression evaluator to evaluate the input to the function, or return None to pass through the mostly expanded #if expression. The default returns a lambda which returns integer 0, as per the C standard. """ return lambda x : 0
def on_unknown_macro_in_defined_expr(self, tok)
-
Called when an expression passed to an #if contained a defined operator performed on something unknown.
Return True if to treat it as defined, False if to treat it as undefined, raise OutputDirective to pass through without execution, or return None to pass through the mostly expanded #if expression apart from the unknown defined.
The default returns False, as per the C standard.
Source code
def on_unknown_macro_in_defined_expr(self,tok): """Called when an expression passed to an #if contained a defined operator performed on something unknown. Return True if to treat it as defined, False if to treat it as undefined, raise OutputDirective to pass through without execution, or return None to pass through the mostly expanded #if expression apart from the unknown defined. The default returns False, as per the C standard. """ return False
def on_unknown_macro_in_expr(self, ident)
-
Called when an expression passed to an #if contained an unknown identifier.
Return what value the expression evaluator ought to use, or return None to pass through the mostly expanded #if expression.
The default returns an integer 0, as per the C standard.
Source code
def on_unknown_macro_in_expr(self,ident): """Called when an expression passed to an #if contained an unknown identifier. Return what value the expression evaluator ought to use, or return None to pass through the mostly expanded #if expression. The default returns an integer 0, as per the C standard. """ return 0