Provides a state stack of booleans to facilitate conditional compilation as: ISO/IEC 9899:1999(E) section 6.10.1 (‘C’) and ISO/IEC 14882:1998(E) section 16.1 (‘C++’) [cpp.cond]
This does not interpret any semantics of either standard but instead provides a state class that callers that do interpret the language semantics can use.
In particular this provides state change operations that might be triggered by the following six pre-processing directives:
#if constant-expression new-line group opt
#ifdef identifier new-line group opt
#ifndef identifier new-line group opt
#elif constant-expression new-line group opt
#else new-line group opt
#endif new-line
In this module a single CppCond object has a stack of ConditionalState objects. The latter has both a boolean state and an ‘explanation’ of that state at any point in the translation. The latter is represented by a list of string representations of either constant-expression or identifier tokens.
The stack i.e. CppCond can also be queried for its net boolean state and its net ‘explanation’.
Basic boolean stack operations
Directive Argument Stack, s, boolean operation
--------- -------- -----------------------
#if constant-expression s.push(bool)
#ifdef identifier s.push(bool)
#ifndef identifier s.push(!bool)
#elif constant-expression s.pop(), s.push(bool)
#else N/A Either s.push(!s.pop()) or s.flip()
#endif N/A s.pop()
Basic boolean ‘explanation’ string operations: NOTE: The ‘!’ prefix is parameterised as TOKEN_NEGATION so that any subsequent processing can recognise ‘!!’ as ‘’ and ‘!!!’ as ‘!’.
Directive Argument Matrix, m, strings
--------- -------- ------------------
#if constant-expression m.push(['%s' % tokens,])
#ifdef identifier m.push(['(defined %s)' % identifier)])
#ifndef identifier m.push(['!(defined %s)' % identifier)])
#elif constant-expression m[-1].push('!%s' % m[-1].pop()),
m[-1].push(['%s' % tokens,])
Note: Here we flip the existing state via
a push(!pop())) then push the additional
condition so that we have multiple
contitions that are and'd together.
#else N/A m[-1].push('!%s' % m[-1].pop())
Note: This is the negation of the sum of
the previous #if, #elif statements.
#endif N/A m.pop()
Note: The above does not include error checking such as pop() from an empty stack.
Stringifying the matrix m:
flatList = []
for aList in m:
assert(len(aList) > 0)
if len(aList) > 1:
# Add parenthesis so that when flatList is flattened then booleans are
# correctly protected.
flatList.append('(%s)' % ' && '.join(aList))
else:
flatList.append(aList[0])
return ' && '.join(flatList)
This returns for something like m is: [['a < 0',], ['!b', 'c > 45'], ['d < 27',],]
Then this gives: "a < 0 && (!b && c > 45) && d < 27"
CppCondGraph adds file/line tracing so that we know where the conditional state was set. This would mean the PpLexer adding a FileLineCol as a argument to oIf() etc. Then this gets passed to ConditionalState.__init__(). Also instead of a PpLexer passing str(CppCond) it passes deepcopy of CppCond. There will be a performance hit here, might it be significant?
Holds a single conditional state.
Returns self as a string which is the concatenation of constant-expressions.
Inverts the boolean such as for #else directive.
This handles an #elif command on this item in the stack. This flips the state (if theBool is True) and negates the last expression on the condition list then appends theConstExpr onto the condition list.
Return True if the state has been True at any time in the lifetime of this object.
Inverts the state of the last item on the stack.
Returns boolean state of self.
Provides a state stack to handle conditional compilation. This could be used by an implementation of conditional inclusion e.g. ISO/IEC 14882:1998(E) section 16.1 Conditional inclusion [cpp.cond] Essentially this class provides a state machine that can be created altered and queried. The APIs available to the caller correspond to the if-section part of the the applicable standard (i.e. #if #elif etc). Most APIs take two arguments;
Finalisation, may raise ExceptionCppCond is stack non-empty.
Return True if the ConditionalState at the current depth has ever been True. This is used to decide whether to evaluate #elif expressions. They don’t need to be if the ConditionalState has already been True, and in fact, the C Rationale (6.10) says that bogus #elif expressions should _not_ be evaluated in this case - i.e. ignore syntax errors.
Returns True if all of the states in the stack are True, False otherwise.
Deal with the result of a #elif.
theBool Is a boolean that is the result of the callers evaluation of a constant-expression.
theConstExpr A string that represents the identifier or constant-expression in a way that the caller sees fit (i.e. this is not evaluated locally in any way). Combinations of such strings _are_ merged by use of boolean logic (‘!’) and LPAREN and RPAREN.
Deal with the result of a #else.
Deal with the result of a #endif.
Deal with the result of a #if.
theBool Is a boolean that is the result of the callers evaluation of a constant-expression.
theConstExpr A string that represents the identifier or constant-expression in a way that the caller sees fit (i.e. this is not evaluated locally in any way). Combinations of such strings _are_ merged by use of boolean logic (‘!’) and LPAREN and RPAREN.
Deal with the result of a #ifdef.
theBool Is a boolean that is the result of the callers evaluation of a constant-expression.
theConstExpr A string that represents the identifier or constant-expression in a way that the caller sees fit (i.e. this is not evaluated locally in any way). Combinations of such strings _are_ merged by use of boolean logic (‘!’) and LPAREN and RPAREN.
Deal with the result of a #ifndef.
theBool Is a boolean that is the result of the callers evaluation of a constant-expression.
theConstExpr A string that represents the identifier or constant-expression in a way that the caller sees fit (i.e. this is not evaluated locally in any way). Combinations of such strings _are_ merged by use of boolean logic (‘!’) and LPAREN and RPAREN.
Returns the depth of the conditional stack as an integer.
Represents a graph of conditional preprocessing directives.
True if the last if-section, if present is completed with an #endif.
Deal with the result of a #elif.
Deal with the result of a #else.
Deal with the result of a #endif.
Deal with the result of a #if.
Deal with the result of a #ifdef.
Deal with the result of a #ifndef.
Take a visitor object and pass it around giving it each CppCondGraphNode object.
Class that represents a conditionally compiled section starting with #if... and ending with #endif.
Deal with the result of a #elif.
Deal with the result of a #else.
Deal with the result of a #endif.
Deal with the result of a #if.
Deal with the result of a #ifdef.
Deal with the result of a #ifndef.
Take a visitor object make the pre/post calls.
Base class for all nodes in the CppCondGraph.
True if I can accept a Preprocessing Directive; theCppD.
Deal with the result of a #elif.
Deal with the result of a #else.
Deal with the result of a #endif.
Deal with the result of a #if.
Deal with the result of a #ifdef.
Deal with the result of a #ifndef.
Returns a list of string representation.
Take a visitor object make the pre/post calls.
Base class for a CppCondGraph visitor object.
Post-traversal call with a CppCondGraphNode and the integer depth in the tree.
Pre-traversal call with a CppCondGraphNode and the integer depth in the tree.
Allows you to find out if any particular line in a file is compiled or not. This is useful to be handed to the ITU to HTML generator that can colourize the HTML depending if any line is compiled or not.
This is a visitor class that walks the graph creating a dict of: {file_id : [(line_num, boolean), ...], ...} It then decomposes those into a map of {file_id : LineConditionalInterpretation(), ...} which can perfom the actual conditional state determination.
API is really isCompiled(file, line): and this returns -1, 0, 1. 0 means NO. 1 means YES and -1 means sometimes - for re-included files in a different macro environment perhaps.
An unordered list of file IDs.
Returns 1 if this line is compiled, 0 if not or -1 if it is ambiguos i.e. sometimes it is and somtimes not when multiply included.
Capture the fileID, line number and state.
Simple specialisation of an exception class for the CppCond.
Simple specialisation of an exception class for the CppCondGraph.
When the CppCondGraph sees an #elif preprocessing directive in the wrong sequence.
When the CppCondGraph sees an #endif preprocessing directive in the wrong sequence.
Exception for a CppCondGraphIfSection.
When the CppCondGraphNode sees an preprocessing directive in the wrong sequence.
Class that represents the conditional compilation state of every line in a file. This takes a list of [(line_num, boolean), ...] and interprets individual line numbers as to whether they are compiled or not. If the same file is included twice with a different macro environment then it is entirely possible that line_num is not monotonic. We have to sort theList by line_num and if there are duplicate line_num with different boolean values then the conditional compilation state at then point is ambiguos.
Returns 1 if this line is compiled, 0 if not or -1 if it is ambiguos i.e. sometimes it is and somtimes not when multiply included.
alias of StateConstExprLoc