CppCond

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:

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"

class cpip.core.CppCond.ConditionalState(theState, theIdOrCondExpr)

Holds a single conditional state.

constExprStr(invert=False)

Returns self as a string which is the concatenation of constant-expressions.

flip()

Inverts the boolean such as for #else directive.

flipAndAdd(theBool, theConstExpr)

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.

hasBeenTrue

Return True if the state has been True at any time in the lifetime of this object.

negateLastState()

Inverts the state of the last item on the stack.

state

Returns boolean state of self.

class cpip.core.CppCond.CppCond

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;

theBool
Is a boolean that is the result of the callers evaluation of a constant-expression.
theIce
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.
close()

Finalisation, may raise ExceptionCppCond is stack non-empty.

hasBeenTrueAtCurrentDepth()

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.

isTrue()

Returns True if all of the states in the stack are True, False otherwise.

oElif(theBool, theConstExpr)

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.
oElse()

Deal with the result of a #else.

oEndif()

Deal with the result of a #endif.

oIf(theBool, theConstExpr)

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.
oIfdef(theBool, theConstExpr)

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.
oIfndef(theBool, theConstExpr)

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.
stackDepth

Returns the depth of the conditional stack as an integer.

class cpip.core.CppCond.CppCondGraph

Represents a graph of conditional preprocessing directives.

isComplete

True if the last if-section, if present is completed with an #endif.

oElif(theFlc, theTuIdx, theBool, theCe)

Deal with the result of a #elif.

theFlc
A cpip.core.FileLocation.FileLineColumn object that identifies the position in the file.
theTuIndex
An integer that represents the position in the translation unit.
theBool
The current state of the conditional stack.
theCe
The constant expression as a string (not evaluated).
oElse(theFlc, theTuIdx, theBool)

Deal with the result of a #else.

theFlc
A cpip.core.FileLocation.FileLineColumn object that identifies the position in the file.
theTuIndex
An integer that represents the position in the translation unit.
theBool
The current state of the conditional stack.
oEndif(theFlc, theTuIdx, theBool)

Deal with the result of a #endif.

theFlc
A cpip.core.FileLocation.FileLineColumn object that identifies the position in the file.
theTuIndex
An integer that represents the position in the translation unit.
theBool
The current state of the conditional stack.
oIf(theFlc, theTuIdx, theBool, theCe)

Deal with the result of a #if.

theFlc
A cpip.core.FileLocation.FileLineColumn object that identifies the position in the file.
theTuIndex
An integer that represents the position in the translation unit.
theBool
The current state of the conditional stack.
theCe
The constant expression as a string (not evaluated).
oIfdef(theFlc, theTuIdx, theBool, theCe)

Deal with the result of a #ifdef.

theFlc
A cpip.core.FileLocation.FileLineColumn object that identifies the position in the file.
theTuIndex
An integer that represents the position in the translation unit.
theBool
The current state of the conditional stack.
theCe
The constant expression as a string (not evaluated).
oIfndef(theFlc, theTuIdx, theBool, theCe)

Deal with the result of a #ifndef.

theFlc
A cpip.core.FileLocation.FileLineColumn object that identifies the position in the file.
theTuIndex
An integer that represents the position in the translation unit.
theBool
The current state of the conditional stack.
theCe
The constant expression as a string (not evaluated).
visit(theVisitor)

Take a visitor object and pass it around giving it each CppCondGraphNode object.

class cpip.core.CppCond.CppCondGraphIfSection(theIfCppD, theFlc, theTuIdx, theBool, theCe)

Class that represents a conditionally compiled section starting with #if... and ending with #endif.

theIfCppD
A string, one of ‘#if’, ‘#ifdef’, ‘#ifndef’.
theFlc
A cpip.core.FileLocation.FileLineColumn object that identifies the position in the file.
theTuIndex
An integer that represents the position in the translation unit.
theBool
The current state of the conditional stack.
theCe
The constant expression as a string (not evaluated).
oElif(theFlc, theTuIdx, theBool, theCe)

Deal with the result of a #elif.

oElse(theFlc, theTuIdx, theBool)

Deal with the result of a #else.

oEndif(theFlc, theTuIdx, theBool)

Deal with the result of a #endif.

oIf(theFlc, theTuIdx, theBool, theCe)

Deal with the result of a #if.

oIfdef(theFlc, theTuIdx, theBool, theCe)

Deal with the result of a #ifdef.

oIfndef(theFlc, theTuIdx, theBool, theCe)

Deal with the result of a #ifndef.

visit(theVisitor, theDepth)

Take a visitor object make the pre/post calls.

class cpip.core.CppCond.CppCondGraphNode(theCppDirective, theFileLineCol, theTuIdx, theBool, theConstExpr=None)

Base class for all nodes in the CppCondGraph.

canAccept(theCppD)

True if I can accept a Preprocessing Directive; theCppD.

oElif(theFlc, theTuIdx, theBool, theCe)

Deal with the result of a #elif.

oElse(theFlc, theTuIdx, theBool)

Deal with the result of a #else.

oEndif(theFlc, theTuIdx, theBool)

Deal with the result of a #endif.

oIf(theFlc, theTuIdx, theBool, theCe)

Deal with the result of a #if.

oIfdef(theFlc, theTuIdx, theBool, theCe)

Deal with the result of a #ifdef.

oIfndef(theFlc, theTuIdx, theBool, theCe)

Deal with the result of a #ifndef.

retStrList(theDepth)

Returns a list of string representation.

visit(theVisitor, theDepth)

Take a visitor object make the pre/post calls.

class cpip.core.CppCond.CppCondGraphVisitorBase

Base class for a CppCondGraph visitor object.

visitPost(theCcgNode, theDepth)

Post-traversal call with a CppCondGraphNode and the integer depth in the tree.

visitPre(theCcgNode, theDepth)

Pre-traversal call with a CppCondGraphNode and the integer depth in the tree.

class cpip.core.CppCond.CppCondGraphVisitorConditionalLines

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() and this returns -1 or 0 or 1. 0 means NO. 1 means YES and -1 means sometimes - for re-included files in a different macro environment perhaps.

fileIdS

An unordered list of file IDs.

isCompiled(fileId, lineNum)

Returns 1 if this line is compiled, 0 if not or -1 if it is ambiguous i.e. sometimes it is and somtimes not when multiple inclusions.

visitPre(theCcgNode, theDepth)

Capture the fileID, line number and state.

exception cpip.core.CppCond.ExceptionCppCond

Simple specialisation of an exception class for the CppCond.

exception cpip.core.CppCond.ExceptionCppCondGraph

Simple specialisation of an exception class for the CppCondGraph.

exception cpip.core.CppCond.ExceptionCppCondGraphElif

When the CppCondGraph sees an #elif preprocessing directive in the wrong sequence.

exception cpip.core.CppCond.ExceptionCppCondGraphElse

When the CppCondGraph sees an #endif preprocessing directive in the wrong sequence.

exception cpip.core.CppCond.ExceptionCppCondGraphIfSection

Exception for a CppCondGraphIfSection.

exception cpip.core.CppCond.ExceptionCppCondGraphNode

When the CppCondGraphNode sees an preprocessing directive in the wrong sequence.

class cpip.core.CppCond.LineConditionalInterpretation(theList)

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. In any case not every line number is present, the state of any unmentioned line is the state of the last mentioned line. Thus a simple dict is not useful.

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 that point is ambiguous.

isCompiled(lineNum)

Returns 1 if this line is compiled, 0 if not or -1 if it is ambiguous i.e. sometimes it is and sometimes not when multiply included.

This requires a search for the previously mentioned line state.

Will raise a ValueError if no prior state can be found, for example if there are no conditional compilation directives in the file. In this case it is up to the caller to handle this. CppCondGraphVisitorConditionalLines does this during visitPre() by artificially inserting line 1. See CppCondGraphVisitorConditionalLines.isCompiled()

cpip.core.CppCond.StateConstExprFileLine

alias of StateConstExprLoc