SYNOPSIS

pycca [OPTIONS] FILE [FILE FILE …]

DESCRIPTION

The pycca command translates a specification of classes into "C" code that is suitable for use with the Single Threaded Software Architecture (STSA). The language that pycca translates is a simple configuration language that allows the specification of domains, classes and state machines. Any associated processing is specified in ordinary "C" code. The language is translated into the necessary data structures required by STSA with the included "C" code passed through into the definitions.

Pycca generates two files from its input. One file is the generated "C" code, named by appending a .c suffix to the basename of the first input file. The other file is a generated header file which has a .h suffix. More than one input file may be given in the invocation. Subsequent files are processes as if all the files had been concatenated together. Typically, second and subsequent files hold domain population information so that a domain may be populated differently without modifying the file containing its logic. However, additional domains may also be processed with the resulting generated code placed in a single output file.

Despite the use of py in the name, pycca has nothing to do with the Python language

OPTIONS

-help, -?

Print the help message.

-version

Print the version number and license for pycca, then exit.

-noline

Do not output #line directives in the generated file that reference the pycca file. Normally, the generated "C" code contains line directives to so that compiler error messages reference the pycca source rather than the generated code. However, some compilers and debuggers are confused by these directives.

-trim

Trim leading blanks from the passed along code. By default, "C" code is passed along verbatim including the whitespace that is often present to make the pycca source more easily read. If this option is present, then excess leading blanks are removed. The number of removed blanks is calculated based on the number of leading blanks in the first or second line of passed along code. Code for functions will be left with at least a four blank indent and prolog and epilog code will have no leading blanks. This feature makes it easier to visualize the code in source level debuggers by removing extraneous blanks that were used to improve the readability of the pycca source itself. Note that this options only works on space characters and not tab characters. If you intend to use this feature, do not use tabs for indentation.

-noC99

Do not include any C99 constructs in the generated code. This is for the benefit of old compilers. Note however that pycca does not examine any of the passed through code and it may well contain constructs that a particular "C" compiler does not accept.

-extstorage

Make the storage arrays allocated for classes extern scope. Normally such storage is made file static scope to avoid global namespace collisions. However, C++ compilers do not like the forward references that pycca generates (they are indeed not valid C++) and this is a workaround for that.

-save

Save the parse and generation information to a file. This file will be the same base name as the input file with a .ral extension added. This file is useful for downstream tooling such as pyccaexplorer or pycca2dot that needs to know the encodings generated by pycca.

-output filename

Output the results to filename. This option allows finer control of the output file names. By default pycca derives the output file names from the source name by substituting any suffix in first FILE argument with .c and .h. If filename is a directory, then the output files names will be constructed as usual, except with filename as the leading part of the path. Otherwise, filename is used as the base for constructing the output file names by substituting .c and .h for any suffix that filename may have.

-rdonly

Set the generated code and header files to be read only. This is a convenience to help avoid the mistake of editing the generated files and loosing the edit when pycca is next run. When this option is present, the generated files are removed prior to processing the input and then set to be read only after the output is generated. If this option is absent, then the generated files are not removed first and are just opened in write mode. This will fail if the generated files were from a previous run of pycca with the -rdonly option. In that case it is necessary to manually remove the generated files.

-instrument pattern

Insert instrumentation code for all classes that match pattern. Pattern is a list of simple wildcard type string matching expressions. Any functions for a class whose name matches any of the given patterns will have instrumentation code inserted at the beginning of the function that is intended to help trace code execution. See the INSTRUMENTATION section below for details of how instrumentation is accomplished . Inserting instrumentation into the passed along code will usually cause older compilers to complain. Thus the -instrument option sometimes will not work well if the -noC99 option is required to passify your compiler. N.B. that pattern is interpreted as a list of whitespace separated elements and the wildcard specifications in the pattern will usually require quoting to prevent interpretation by the invoking command shell.

-ids

Insert into the generated header file a numerical encoding of all the classes and attributes of the domain. All classes that have at least one attribute are includes. References do not count as attributes. Classes are numbered from 0 and attributes are numbered from 0 within each class. Encoding the classes and attributes as numbers is often useful in bridge code for the domain.

-dataportal

Insert into the generated code file a set of data structures that allow the attributes of the classes to be accessed. The -dataportal option implies the -ids option. The generated class and attribute encoding can be used with supplied functions to read an update class attributes of the domain without executing any code within the domain. This facility provides a limited means to break encapsulation of the domain for the purpose of data access. See the DATA PORTAL section below for further details.

-portalcode

Create the files pycca_portal.h and pycca_portal.c in the current directory. Pycca exits successfully after creating the files. These two files are the “C” code used in conjunction with the data structures generated with the -dataport option.

-header file

Use file as the name of the header file that contains the interface declarations for the software architectural mechanisms. By default, the file mechs.h is assumed to be the mechanisms interface file.

LEXICAL CONVENTIONS

Comments

start with a sharp character (#) and continue to the end of the line.

Names

start with an alphabetic character and may contain an arbitrary number of alphabetic, numeric or underscore (_) characters.

Whitespace

is generally ignored and no constructs depend upon any particular arrangement of whitespace.

"C" Variables

any text appearing between matching parentheses, (…), is taken to be a list of comma separated "C" variable or parameter declarations. As of pycca version 3.0, parameter lists and attribute declarations are parsed. Because of the inherent ambiguity of typedef type aliases and the complexity of certain "C" declarations that involve constant expressions, it is possible for pycca to incorrectly parse the declaration. These issues can usually be solved with an appropriate typedef included in the implementation prolog section.

"C" Code

any text appearing between matching braces, {…}, is taken to be a "C" code sequence and is otherwise uninterpreted. The determination of the matching enclosing brace accounts for the syntax of the "C" language.

KEYWORDS

The following tokens are keywords and may not be used where an arbitrary name is required. They are represented in the syntax description below in upper case.

  • attribute

  • class

  • constant

  • constructor

  • default

  • destructor

  • domain

  • dynamic

  • end

  • epilog

  • event

  • external

  • final

  • implementation

  • initial

  • instance

  • interface

  • machine

  • operation

  • polymorphic

  • population

  • prolog

  • reference

  • set

  • slots

  • state

  • static

  • subtype

  • table

  • transition

  • union

OTHER TOKENS

The following strings are also tokens. The token name is given to match as it appears in the syntax definition below.

MPOINTS

->> or ->>c or ->>n

MLPOINTS

->>l

MCPOINTS

-ddd>>, where ddd is a sequence of decimal digits

POINTS

->

NAME

A sequence of a letter followed by an arbitrary number of letters, decimal digits or underscore characters that is not also a keyword.

NUMBER

A sequence of decimal digits.

CODE

"C" code enclosed in braces ({…}) as described above in the "C" Code section.

VARDEF

A "C" variable definition or comma separated list of variable definitions as described above in the "C" Variables section.

SYNTAX

The following is a yacc style syntax of the language accepted by pycca.

translation
    :
    |   domainDefs
    ;

domainDefs
    :   domain
    |   domainDefs domain
    ;

domain
    :   DOMAIN NAME domainDef END
    ;

domainDef
    :   domainComponent
    |   domainDef domainComponent
    ;

domainComponent
    :   prolog
    |   epilog
    |   class
    |   instance
    |   domainFunc
    |   extFunc
    |   table
    ;

prolog
    :   INTERFACE PROLOG CODE
    |   IMPLEMENTATION PROLOG CODE
    ;

epilog
    :   INTERFACE EPILOG CODE
    |   IMPLEMENTATION EPILOG CODE
    ;

class
    :   CLASS NAME classDef END
    ;

classDef
    :
    |   classProp
    |   classDef classProp
    ;

classProp
    :   attrDef
    |   referenceDef
    |   subtypeDef
    |   machineDef
    |   slotSpec
    |   storageSpec
    |   classOp
    |   instOp
    |   polyDef
    |   constructor
    |   destructor
    ;

attrDef
    :   ATTRIBUTE singleVar defaultSpec
    ;

defaultSpec
    :
    |   DEFAULT CODE
    ;

referenceDef
    :   REFERENCE NAME POINTS NAME
    |   REFERENCE NAME MPOINTS NAME
    |   REFERENCE NAME MCPOINTS NAME
    |   REFERENCE NAME MLPOINTS NAME
    ;

subtypeDef
    :   SUBTYPE NAME REFERENCE nameList END
    |   SUBTYPE NAME UNION nameList END
    ;

nameList
    :   NAME
    |   nameList NAME
    ;

machineDef
    :   MACHINE machinePropSet END
    ;

machinePropSet
    :   machineProp
    |   machinePropSet machineProp
    ;

machineProp
    :   STATE NAME varList CODE
    |   TRANSITION NAME '-' NAME POINTS NAME
    |   TRANSITION '.' '-' NAME POINTS NAME
    |   DEFAULT TRANSITION defTransName
    |   INITIAL STATE NAME
    |   FINAL STATE NAME
    ;

defTransName
    :   NAME
    ;

singleVar
    :   VARDEF
    ;

varList
    :   VARDEF
    ;

slotSpec
    :   SLOTS NUMBER
    ;

storageSpec
    :   POPULATION DYNAMIC
    |   POPULATION STATIC
    |   POPULATION CONSTANT
    ;

instance
    :   instName attrAssigns END
    ;

instName
    :   INSTANCE NAME
    |   INSTANCE NAME '@' NAME
    |   INSTANCE NAME '.' NAME
    ;

attrAssigns
    :
    |   attrSpec
    |   attrAssigns attrSpec
    ;

attrSpec
    :   singleVar CODE
    |   NAME POINTS NAME
    |   NAME POINTS NAME '.' NAME
    |   NAME MPOINTS nameList END
    ;

domainFunc
    :   DOMAIN OPERATION NAME varList returnType CODE
    |   DOMAIN OPERATION CODE NAME varList returnType CODE
    ;

classOp
    :   CLASS OPERATION NAME varList returnType CODE
    ;

extFunc
    :   EXTERNAL OPERATION NAME varList returnType Code
    |   EXTERNAL OPERATION CODE NAME varList returnType Code
    ;

instOp
    :   INSTANCE OPERATION NAME varList returnType CODE
    ;

returnType
    :
    |   ':' singleVar
    ;

polyDef
    :   POLYMORPHIC EVENT nameList END
    |   POLYMORPHIC EVENT error END
    ;

constructor
    :   CONSTRUCTOR CODE
    ;

destructor
    :   DESTRUCTOR CODE
    ;

table
    :   tableName heading body END
    |   tableName body END
    ;

tableName
    :   TABLE NAME
    ;

heading
    :   headingSpec
    |   heading headingSpec
    ;

headingSpec
    :   singleVar
    |   NAME
    ;

body
    :
    |   tuple
    |   body tuple
    ;

tuple
    :   tupleId tupleDef
    |   tupleId
    ;

tupleId
    :   '@'
    |   '@' NAME
    ;

tupleDef
    :   attrValue
    |   tupleDef attrValue
    ;

attrValue
    :   CODE
    |   POINTS NAME
    |   POINTS NAME '.' NAME
    |   MPOINTS nameList END
    |   '-'
    ;

SEMANTICS

The pycca language allows the definition of domains. Domains consist of classes, domain operations and instance definitions. In addition you may specify "C" code to be placed at the beginning or end of the generated files. In this section, we will discuss the semantic meaning of all the syntactic constructs of the pycca language.

DOMAIN

Domain definitions start with the domain keyword followed by the name of the domain and stops at the matching end keyword.

domain myDomain
    # Put your domain definition here
end

The procedural interface to a domain consists of a set of domain operations. Domain operations are converted into ordinary "C" functions. They are made external in scope and their prototype is inserted into the generated header file. To help manage the global namespace, the name of the domain and an underscore are prepended to the "C" function that is generated for a domain operation.

domain myDomain
    domain operation
    init(void)
    {
        // the domain initialization is done here
    }
end

So this would generate:

void myDomain_init(void)
{
    // the domain initialization is done here
}

If a domain operation returns a value, then the type is specified by following the interface with a colon (:) and a variable type. If no return type is specified, then the function is typed as void.

domain myDomain
    domain operation
    getStatus(void) : (int)
    {
        return 3 ;
    }
end

This would generate:

int myDomain_getStatus(void)
{
    return 3 ;
}

Since domain operations form the external interface of the domain, it is convenient to be able to associate comments and declarations in the pycca source and have that information placed in the generated header file. An optional "C" segment may precede the domain operation definition and this will be placed in the generated header.

domain myDomain
    domain operation {
    /*
     * This operation is used to modify parameters.
     */}
    modParam(int a)
    {
        // modify parameters here!
    }
}

This construct would result in the comment being passed to the generated header file (and only the generated header) as:

/*
 * This operation is used to modify parameters.
 */
extern void myDomain_modParam(int a) ;

Although this construct is intended primarily to pass comments associated with domain operations through to the generated header file that serves as the interface specification to the domain, the "C" code lines (like all other pycca constructs enclosed in {}) is passed through unmodified (except for whitespace trimming) and may be used to pass through pre-processor include directives or for any other useful purposes.

Complementary to domain operations are external operations. They are defined similarly to domain operations.

domain myDomain
    external operation
    getReactorTemp(int reactor)
    {
        // any code here is not passed through
    }
}

External operations define the external function dependencies of the domain. The domain expects these functions to be supplied from elsewhere. The above example generates external declarations such as:

extern void eop_myDomain_getReactorTemp(int reactor) ;

in both the generated header file and in the code file. The intent of the external operation declarations is to aid in generating bridge functions for the domain. For each external operation, a function must be provided that satisfies the semantics of the operation by bridging to domain functions of another domain. Sometimes this bridge is simply a name translation that can be done via a macro. Other bridges may be more complex, mapping encoded identifiers in one domain to encoded identifiers in another domain. Any code associated with the external operation does not get placed in the generated code. However, this code may be used in other pycca-based tools. To invoke an external operation from with in the code of a domain, use the ExternalOp() macro described below.

CLASSES

A Domain contains classes. A class is template for data and behavior. A particular instance of a class (often called an object) performs the computational actions. All instances of a given class have the same attribute data and same general behavior. A class has a name and optionally attributes and a state machine.

class c1
    # class definition appears here.
end

CLASS ATTRIBUTES

Attributes are declared in the same way as structure members in "C", but without any punctuation. Attributes are optional, but useful classes usually have attributes. Following the lexical conventions, the "C" declarations appear in parentheses.

class c1
    attribute (int a1)
end

The default value of an instance can also be specified. The default value is used only if no value is specified when an initial instance of the class is defined. Default values are used only when specifying the set of initial instances. For dynamically created instances, all attribute values are set by running code. The default value must evaluate to a valid "C" compile-time constant expression (since it will be used as an initializer) and since it is passed through must be enclosed in braces.

class c1
    attribute (int a2) default {sizeof(int) + 32}
end

CLASS REFERENCES

Sometimes classes need to refer to other classes or themselves in order to implement relationships. A reference is a special kind of attribute that is turned into a pointer to a specific class structure. The reference is given a name, which generally will be the same name as the relationship it implements (perhaps augmented with some annotation if the relationship is reflexive). The type of the attribute need not be specified since it can be deduced as a pointer to a structure that matches the class name.

SINGULAR REFERENCES

Usually a single valued reference is used to implement traversal of a relationship on the side that is one or one-conditional. In the case of a singular reference, a simple pointer member holds the address of the referenced instance and NULL may be used to indicate conditionality. So

class c1
    reference R1 -> c1
end

is translated to

struct c1 {
    ...
    struct c1 *R1 ;
    ...
} ;
MULTIPLE REFERENCES

A reference may also implement a relationship traversal for the side that is "many" or "many-conditional". This type of reference storage is more complicated since we must manage the storage for accumulating a set of pointers to instances. It turns out that it is convenient to distinguish between multiple references for static relationships and those for dynamic relationships. Static relationships don’t change in time over the course of program execution. They are occur relatively frequently in some applications and which instance are related to each other is known at compile time. Two different storage strategies are available for static multiple references.

Using the ->> symbol will cause pycca to insert an array of pointers. The ->> notation comes in several alternate forms that are used to control the details of how the array of pointers is allocated. If ->> or ->>n is used to define the multiple reference, then the class structure has a pointer member defined for it that will point to a NULL terminated array of class references. For example, the class fragment:

class c2
    reference R2 ->> c1
end

translates into the "C" structure fragment:

    struct c1 *const*const R2 ;

If ->>c is used to define the reference, then the array of class references is counted and the class structure will have two members defined for it, a pointer to the array and a count value. In this case the class fragment:

class c3
    reference R3 ->>c c5
end

translates into the "C" structure fragment:

    struct c5 *const*const R3 ;
    unsigned R3__count ;

The <name>__count member should be accessed using the RefCountMember() macro described below to insulate any code from the member naming convention.

In both cases, pycca will examine the initial instance population and build an array of pointers in constant memory that point to the related instances and will initialize the class instance with the pointer to the reference array. If a NULL terminated array was requested then the array of references will have a NULL value as its last element. If a counted array was requested, the array contains just as many pointers as indicated by the initial instance population and a count of the number of pointers in the array is set in the initializer of the referrring instance.

For dynamic relationships, two alternatives are provided. If the reference specification is of the form "-ddd>>", where the "ddd" stands for a set of decimal digits then an array of class references is allocated in non-constant memory. When the count form is used, then the class instances will have an array of pointers of the specified size defined as part of their class structure. For example, the class fragment:

class c6
    reference R4 -20>> c7
end

translates into the "C" structure fragment:

    struct c7 *R4[20] ;

Where the R4 member is an array of of class references (i.e. pointers of type struct c7 * in this case). The array will be initialized for each instance with the references specified in the instance definition for the instance and any unused slots will be set to NULL. Action code can then manage that storage to implement dynamic one-to-many type relationships where a NULL value is used to indicate that a reference storage slot is not being used.

Finally, if the reference specification is of the form ->>l, a doubly linked list is set up to manage the multiple relationship. This entails two things. First, a set of links is added as a member of the class that defines the reference. Second, a corresponding set of links is added as a member of the class to which the reference is made. This allows a list be built starting at the referring class and threading multiple instances of the referred to class along the links that are part of the referred to class. Thus the memory for the links is relatively easily managed and referenced instances may be easily added and removed from the list. A set of macros, defined below, is provided to hide the details of the linking, unlinking and traversal mechanism.

For example, the class fragment:

class c8
    reference R10 ->>l c9
end

adds the following member to struct c8:

    rlink_t R10 ;

and adds the following member to struct c9:

    rlink_t R10__links ;

Any initial instances of c9 that are referenced by c8 are linked together as part of the initializers defined by the initial instance population.

CLASS CONSTRUCTORS AND DESTRUCTORS

A class may define a constructor or a destructor. Neither constructors nor destructors can take parameters.

class c3
    attribute (int count)
    constructor {
        self->count = 0 ;
    }
    destructor {
        reportCount(self->count) ;
    }
end

If any class that contains a constructor also has an initial instance population specified, then pycca will generate a function of the form, <domain name>_Ctor where <domain name> is replaced by the name of the domain. This function will invoke the constructor for all initial instances of all classes that have defined a set of initial instances and also have defined a constructor. It is up to the user to invoke this function during the application initialization phase (e.g. in some domain operation that is invoked at initialization time).

CLASS OPERATIONS

A class may define class based operations. Class operations do reference any particular instance and provide a means of factoring common class operations into a single function.

class c4
    class operation
    common(
        int a,
        char *b) : (int)
    {
        // Common class operation code.
        // No defined instance variable.
        return -1 ;
    }
end

INSTANCE OPERATIONS

A class may define instance based operations. Instance operations have an implicit first parameter which is a pointer to the instance on which the operation is to be performed. It is not necessary to declare the self variable as pycca will insert it. However, since this is "C", it is necessary to supply a value for the implied self parameter when invoking an instance operation.

class c5
    attribute (int count)
    instance operation
    addFive()
    {
        // Can reference "self"
        self->count += 5 ;
    }
end

INSTANCE STORAGE

A separate pool of storage of instances is generated for each class. Instances may be declared as initial instances, as slots in the storage pool for dynamically created instances or the pool may contain both initially defined instances and slots for dynamic instance creation. If a class definition contains a slots statement, then the storage pool for the class will contain at least the given number of instance storage locations.

class c6
    attribute (int count)
    population dynamic
    slots 5
end

The above example insures that there are five dynamically allocatable instances of class c6.

INITIAL INSTANCES

The instance and table statements are used to define initial class instances. Initial instances are already in place when the domain is first run and are implemented as initializers of the array variable that holds the storage pool. It is necessary for the class definition to appear before instances for that class may be defined. Instances may be named or anonymous. Named instances are useful when creating initial instances that have instance references in them. Anonymous instances cannot be referred to directly by other initial instances. Named instances also have the advantage of being able to be located at run time using the Instance() macro.

The values of all attributes that do not have a defined default value must be specified. For attributes that have a defined default value, they are given that default value if not mentioned in the instance definition. Otherwise the default value is overrided when mention in the instance definition.

class c6
    attribute (int count) default {0}
    attribute (int age)
    reference R13 -> c7
end

instance c6@i1
    (int age) {22}
    (int count) {17}
    R13 -> i14
end

The above example defines a named initial instance, i1, of class, c6. The default value of (int count) is overridden to be 17 rather than the default of 0. The singular reference, R13, is set to point to the i14 instance of class, c7.

When there are a number of instances of a particular class, the instance statement can be tedious to use and obscures the nature of the instances as a group. In this case, the table command allows many instances to be defined in a tabular arrangement.

table
    c6      (int age)       (int count)     R13
    @i2     {19}            {26}            -> i8
    @       -               {27}            -> i9
    @i3     {42}            {28}            -> i10
end

The above example shows three more initial instances for class c6. The attributes are listed as a heading, followed by lines that give the name of the instance and the values of the attributes it is to have. The heading need only contain those attributes that you wish to define to be different from the default. If the instance name is not given (i.e. a plain @ is present) then the instance is anonymous. A - for a value represents the default value for that attribute. In general, the value of a cell in the table is the same as you would specify in an instance statement.

STORAGE CHARACTERISTICS

It is possible to declare the storage characteristics of instance population of a class. Class populations can be declared as dynamic, static, or constant using the population keyword in the class specification. If no population clause is present, then population static is assumed. A class population is dynamic if instances are created and deleted at run time. Class populations that are not created and deleted at run time are termed, static. This implies that the number of instances does not change over the course of running the domain. A special case of static is a class population that is constant. A constant population is both static and read only, i.e. its attributes are not updated. Pycca will declare constant populations as "C" const variables (i.e. ROMable) and all other instance populations will be placed in initialized data sections (i.e. RAM).

The total number of instances that will be defined for the pool of a class is the sum of the number of initial instances defined and the number of dynamic slots defined by a slots statement. Only classes declared as dynamic may have a slots statement to allocate additional storage slots. Most frequently, classes either have an intial instance population or are dynamic and use the slots statement to allocate the required storage. It is possible to mix initial instances and dynamic slots. Initial instances that are destroyed at run time are then available to be allocated at a later time. Neither constant nor static class storage types may have a slots statement to declare dynamic instances. Pycca will issue a warning for constant or static classes whose initial instance population is empty and no storage pool will be defined for such classes.

class c7
    attribute (int count)
    attribute (int year)
    population constant
end

instance c7
    (int count) 3
    (int year) 1977
end

instance c7
    (int count) 7
    (int year) 1978
end

The above example declares two anonymous instances of the constant class, c7. The storage pool for c7 will be declared as const and its size is fixed at two.

STATE MACHINES

Some classes exhibit lifecycle behavior. That behavior is encoded using a state machine. Pycca allows the specification of Moore type state machines. For Moore machines, action code is associated with the state and that code is executed upon the transition into the state. There are quite a number of rules for state machines as we shall see below.

class Dog
    machine
        initial state born
        final state die

        state born()
        {
            // "C" code for the "born" state goes here
        }
        transition born - Age -> grow

        state grow()
        {
            // "C" code for the "grow" state goes here
        }
        transition grow - Age -> die

        state die()
        {
            // "C" code for the "die" state goes here
        }
    end
end

Each machine may specify a default initial state using the initial state statement. If none is given, then the first state defined is taken to be the default initial state. A state may be marked as a final state using the final state statement. When a final state is entered, its action is executed and the instance is destroyed automatically upon completion of the state action. A state is defined with the state statement. A state has a name and may have parameters that are passed to it as part of the event that causes the transition into the state. These parameter are listed in parentheses following the state name. Many times, the state action has no parameters and this indicated by an empty declaration in the parentheses as in the above example. It is a corollary of Moore machines that carry event data, that the data carried by an event must match the parameters of the state action. Thus it is not allowed for the same event to cause a transition into states that have a different parameter signatures. Pycca detects this error, issuing an appropriate error message.

The state action is supplied by the "C" code in the enclosing braces. When an event causes a transition into a state, the given "C" code executes. The code may refer to self which is declared as a pointer to a class instance and contains the reference to the instance to which the event was directed.

Transitions are specified by the transition statement. This statement lists the current state, event and new state. Pycca allows the state machine to be specified in any order. You may list all the state definitions followed by the transitions or any combination you find clearest. Note that there is no separate list of events that are specified. Event names are defined when they appear in transition statements.

There are two special state names that are used to control transitions. If a transition specifies, IG, as the new state, then the event is ignored in the current state. Ignored events cause no transition and are simply discarded. If a transition specified, CH, as the new state, then receiving the event while in the current state is treated as a can't happen circumstance and the state transition engine generates a fatal error.

The transition statements for state machine need not specify all possible transitions for the state machine. The transitions form a matrix that is S x E large, where S is the number of states and E is the number of events. Any element of the transition matrix that is not specified in a transition statement is given a default value. By default, an unspecified transition is marked as CH. However, the default transition statement may be used to specify the default transition desired for those transitions not specified in a transition statement.

INSTANCE CREATION

The STSA supports dynamic instance creation. Each class has its own storage pool. This is in keeping with the minimal system assumptions that STSA makes. Class instances may be created in a synchronous manner or in an asynchronous manner. Synchronous creation involves invoking a create function from STSA. Pycca provides a convenience macro, PYCCA_createInstance, to help in the interface to STSA. Synchronously created instances may be placed in any state when they are created. Usually the default initial state is chosen using the InitialStateNumber() macro (or just 0 for creating instances that have no associated state machine). It is important to remember that synchronously created instances do not execute the action of their initial state. They are simply allocated from the class storage pool, run the constructor if any and are placed in the state. So for example:

ClassRefVar(Dog, d) ;
d = PYCCA_createInstance(Dog, InitialStateNumber(Dog)) ;

will create an instance of Dog held in the d variable in its default initial state, but state action of the default initial state has not been run.

The other form of instance creation is asynchronous instance creation. Any transition statement where the current state is named by the period character (.) is a creation transition. The period state name represents an initial pseudo state where the instance has been created and will be delivered a event causing a transition into the new state given in the transition statement. For asynchronous creation, the state action into which the instance transition upon receiving the creation event is executed.

For example:

class Dog
    machine
        transition . - Born -> born
        transition born - GrowUp -> grown
        state born()
        {
            puts("Dog is born") ;
        }

        state grown()
        {
            puts("Dog is grown") ;
        }
    end
end

defines a class with a creation event, Born. Executing the statement:

PYCCA_generateCreation(Born, Dog, self) ;

will cause the creation event Born to be queued. When that event is dispatched, an instance of Dog will be created in the initial pseudo state (with the constructor executed if any) and the Born event will be delivered to the new instance. The event will cause a transition from the initial pseudo state into the born state and the action associated with that state will be executed.

The creation rules may seem complex, but they cover all the required circumstances. It is worth noting that any state that is an initial state and which does not have any incoming transitions should have an empty state action. Since there is never any transition into the state, any action present would never be executed. Any state that has no incoming transitions and is not an initial state is a dead state as it cannot be reached. Any state that has no outgoing transitions and is not a final state is a trapping state as there is not way to exit the state.

GENERALIZATION RELATIONSHIPS

A generalization relationship is a complete disjoint set partitioning of a supertype class into subtype classes. The partitioning is complete because every instance of the supertype must have a corresponding instance of a subtype. The partitioning is disjoint because no two subtype instances correspond to the same supertype instance. Pycca has support for declaring the necessary data structures to hold generalization relationship information in class structures. Generalization relationships can be implemented in two ways in pycca, as references or as a union.

GENERALIZATIONS IMPLEMENTED BY REFERENCE

A class defines a storage for generalization relationship implmented by reference using the subtype … reference statement. This statement gives a list of classes that are to be considered as the subtype classes.

class c8
    subtype R9 reference
        cs1
        cs2
        cs3
    end
end

Pycca will translate a subtype … reference statement into two structure members:

struct c8 {
    SubtypeCode R9__code ;
    MechInstance R9 ;
} ;

The R9 member holds a pointer to one of the subtypes of the R9 reference. The R9__code member holds an integer encoding of the type of the R9 pointer. In this scheme of subtyping, the subtype instances are quite distinct from the supertype and relationship navigation is done by pointer reference.

GENERALIZATIONS IMPLEMENTED BY UNION

In simple cases, typically a generalization hierarchy that is only a single level deep, it is sometimes more convenient to hold the subtype classes of the supertype directly in the storage of the supertype instances as a union data type. Considering the above example, it would appear in union form as below:

class c8
    subtype R9 union
        cs1
        cs2
        cs3
    end
end

In this case, pycca would translate this subtype statement into:

struct c8 {
    SubtypeCode R9__code ;
    union {
        struct cs1 R9_cs1 ;
        struct cs2 R9_cs2 ;
        struct cs3 R9_cs3 ;
    } R9 ;
} ;

Keeping subtypes in a union data structure contained within a member of the supertype can make managing dynamic instances easier, especially in the case of dynamic migration of one subtype into another. As long as the size of the subtype classes is similar, there is no waste of memory and there may be some savings. However, complex hierarchies, such as those where one supertype class is the supertype for multiple generalization relationships, do not work well using the union data type.

POLYMORPHIC EVENTS

The idea of a generalization relationship is also associated with the concept of a polymorphic event. A polymorphic event is an event that is defined in a supertype and when delivered to an instance of the supertype is remapped at run time to an event in the subtype instance to which the supertype instance is currently related. Polymorphic events have no effect on the supertype. A supertype may have both ordinary events that are consumed in a state machine of the supertype and polymorphic events that are passed down and remapped to the subtypes. Eventually all polymorphic events must be consumed in one of the direct or indirect subtypes of the supertype. Polymorphic events in the subtypes are known by the same name as they are given in the supertype. Polymorphic events may pass through more than one level of generalization and each generalization hierarchy may introduce new polymorphic events. The general situation can become quite complex. In practice the full power of polymorphic events is rarely needed. Pycca includes checks to insure that the polymorphism is properly defined. The following example shows a situation with a two level generalization hierachy.

class super1
    subtype R1 reference
        sub_1_A
        sub_1_B
    end
    polymorphic event
        e1
        e2
    end
end

class sub_1_A
    subtype R2 reference
        sub_2_A
        sub_2_B
    end
    machine
        # This machine consumes "e1" since it is mentioned in a
        # transition.However "e2" is passed along since it is not
        # consumed by this state machine.
        transition s1 - e1 -> s1
        state s1()
        {
            puts("Consume e1 at this level") ;
        }
    end
end

class sub_1_B
    machine
        # This machine consumes both "e1" and "e2" as it must
        # since it does not define any further generalization
        # relationship.
        transition s1 - e1 -> s2
        state s1()
        {
            puts("Consume e1 at this level") ;
        }

        transition s2 - e2 -> s1
        state s2()
        {
            puts("Consume e2 at this level") ;
        }
    end
end

# As subtypes of sub_1_A, these classes both receive "e2" and
# both must consume the event.
class sub_2_A
    machine
        transition s1 - e2 -> s1
        state s1()
        {
            puts("Consume e2 at this level") ;
        }
    end
end

class sub_2_B
    machine
        transition s1 - e2 -> s1
        state s1()
        {
            puts("Consume e2 at this level") ;
        }
    end
end

In this example, the supertype class, super1, has a generalization relationship, R1, with two subtypes, sub_1_A and sub_1_B. The super1 class defines two polymorphic events, e1 and e2. This means that the subtypes of super1 must either consume the polymorphic events directly or they must be consumed by a generalization hierarchy that originates with the subtype. In this case, sub_1_A defines a generalization relationship, R2, with subtype sub_2_A and sub_2_B. The state machines in this example are such that e1 and e2 are both consumed in transitions in sub_1_B. Only e1 is consumed in sub_1_A, meaning that e2 is passed along to the subtypes, sub_2_A and sub_2_B. Since no further generalization relationships are defined for sub_2_A or sub_2_B, these subtypes are required to consume e2.

PROLOG AND EPILOG CODE

It is sometimes necessary to place additional code into the generated file. For example, header files needed by the "C" code in actions needs to be put into the generated file at a location dictated by the compiler. Pycca allows both prolog and epilog code to be inserted in both the interface and implementation files.

implementation prolog {
#include <stddef.h>
#include "myHeader.h"
}

Prolog code is placed in the generated "C" file before any of the passed through code. Similarly for the interface prolog. It is placed before the domain function prototypes. Epilog code is placed at the end. Note also that prolog and epilog code is cumulative in that there may be multiple prolog or epilog statements and the contents are accumulated across all such statements to be placed in the generated files at the appropriate place.

INTERFACING TO THE SOFTWARE ARCHITECTURE

Pycca handles many of the details of encoding information for the architectural mechanisms data structures. For example, pycca determines the numeric encoding of states and events. It is necessary to know some of this information in the code that is being passed through. For example, you must be able to supply the number of an event when it is generated to the state machine of an instance. Pycca uses a set of naming conventions for preprocessor defines and variable names. There are a set of "C" preprocessor macros that pycca includes in the generated code that are aware of the naming conventions used. These macros are also aware of the software architectural mechanisms interfaces and so decrease the dependencies of the passed along code. As of revision 2.6, pycca also helps ease the problem by defining a context for classes and states. When the code is output, pycca will surround all the code of a given class with a macro definition of the name of the class. A similar definition is emitted for each state action, naming the state of the action. These definitions provide a means of further insulating the code in a state action or other class operation from the name of the class or state itself. So below, many of the macros come in more than one form so that they may be used in any context or within the context of a given class or state. Older macro names have been retained for backward compatibility.

EVENT GENERATION

Event generation is a very common activity in state actions. These macros are convenient for generating events that contain no supplemental parameters. See the EVENT GENERATION WITH SUPPLEMENTAL DATA section below for as discussion of dealing with events that contain supplemental data .

PYCCA_generate(e, c, i, s)

Generate an event to an instance.

e

The name of the event to generate.

c

The name of the class of the instance receiving the event.

i

A pointer to the instance that is to receive the event.

s

A pointer to the instance that is sending the event. This may be NULL if the event originates from a domain operation.

PYCCA_generateSelf(e, c)

Generate an event to self.

e

The name of the event to generate.

c

The name of the class of self.

PYCCA_generateToSelf(e)

Generate an event to self. This macro will use the current class context.

e

The name of the event to generate.

PYCCA_generatePolymorphic(e, c, i, s)

Generate a polymorphic event.

e

The name of the event to generate.

c

The name of the class of the instance receiving the event.

i

A pointer to the instance that is to receive the event.

s

A pointer to the instance that is sending the event. This may be NULL if the event originates from a domain operation.

PYCCA_generateCreation(e, c, s)

Generate a creation event.

e

The name of the event to generate.

c

The name of the class of the instance receiving the event.

s

A pointer to the instance that is sending the event. This may be NULL" if the event originates from a 'domain operation.

PYCCA_generateDelayed(e, c, i, s, d)

Generate a delayed event.

e

The name of the event to generate.

c

The name of the class of the instance receiving the event.

i

A pointer to the instance that is to receive the event.

s

A pointer to the instance that is sending the event. This may be NULL if the event originates from a domain operation.

d

The delay time, in milliseconds.

PYCCA_generateDelayedSelf(e, c, d)

Generate a delayed event to self.

e

The name of the event to generate.

c

The name of the class of self.

d

The delay time, in milliseconds.

PYCCA_generateDelayedToSelf(e, d)

Generate a delayed event to self. This macro will use the current class context.

e

The name of the event to generate.

d

The delay time, in milliseconds.

PYCCA_cancelDelayed(e, c, i, s)

Cancel a delayed event.

e

The name of the event to generate.

c

The name of the class of the instance receiving the event.

i

A pointer to the instance that is to receive the event.

s

A pointer to the instance that is sending the event. This may be NULL if the event originates from a domain operation.

PYCCA_cancelDelayedSelf(e, c)

Cancel a delayed event that was sent to self.

e

The name of the event to generate.

c

The name of the class of self.

PYCCA_cancelDelayedToSelf(e)

Cancel a delayed event that was sent to self. This macro will use the current class context.

e

The name of the event to generate.

PYCCA_remainDelayed(e, c, i, s)

Retrive the time remaining on a delayed event.

e

The name of the event to generate.

c

The name of the class of the instance receiving the event.

i

A pointer to the instance that is to receive the event.

s

A pointer to the instance that is sending the event. This may be NULL if the event originates from a domain operation.

PYCCA_remainDelayedSelf(e, c)

Retrive the time remaining on a self-directed delayed event.

e

The name of the event to generate.

c

The name of the class of self.

PYCCA_remainDelayedToSelf(e)

Retrive the time remaining on a self-directed delayed event. This macro will use the current class context.

e

The name of the event to generate.

EVENT GENERATION WITH SUPPLEMENTAL DATA

Since events can carry supplemental data, to send such an event is a three step process.

  1. Obtain an Event Control Block (ECB). A different macro is used to obtain an ecb for each of the three different event types.

  2. Fill in the event data.

  3. Post the event to an event queue.

Sending a Bark Event

Assuming that dog is an instance of class Dog and that the Bark event takes a single parameter, howLoud, then the following will send the Bark event to the dog instance of Dog.

MechEcb bark = PYCCA_newEvent(Bark, Dog, dog, self) ;
PYCCA_eventParam(bark, Dog, Bark, howLoud) = 20 ;
PYCCA_postEvent(bark) ;
PYCCA_newEvent(e, c, i, s)

Returns a MechEcb for an ordinary event.

e

The name of the event to generate.

c

The name of the class of the instance receiving the event.

i

A pointer to the instance that is to receive the event.

s

A pointer to the instance that is sending the event. This may be NULL if the event originates from a domain operation.

PYCCA_newSelfEvent(e, c)

Returns a MechEcb for an ordinary event where the target instance and the sending instance are self.

e

The name of the event to generate.

c

The name of the class of the instance receiving the event.

PYCCA_newEventToSelf(e)

Returns a MechEcb for an ordinary event where the target instance and the sending instance are self. This macro will use the current class context.

e

The name of the event to generate.

PYCCA_newPolymorphicEvent(e, c, i, s)

Returns a MechEcb for a polymorphic event.

e

The name of the event to generate.

c

The name of the class of the instance receiving the event.

i

A pointer to the instance that is to receive the event.

s

A pointer to the instance that is sending the event. This may be NULL if the event originates from a domain operation.

PYCCA_newCreationEvent(e, c, s)

Returns a MechEcb for a creation event.

e

The name of the event to generate.

c

The name of the class from which an instance is to be created.

s

A pointer to the instance that is sending the event. This may be NULL if the event originates from a domain operation.

PYCCA_newCreationEventForThisClass(e, s)

Returns a MechEcb for a creation event for creating an instance of the current class context.

e

The name of the event to generate.

s

A pointer to the instance that is sending the event. This may be NULL if the event originates from a domain operation.

PYCCA_eventParam(ecb, c, e, p)

Retrieve the value of an event parameter.

ecb

A pointer to an event control block.

c

The name of the class of the instance receiving the event.

e

The name of the event to generate.

p

The name of the event parameter.

PYCCA_eventParamOfThisClass(ecb, e, p)

Retrieve the value of an event parameter. This macro will use the current class context.

ecb

A pointer to an event control block.

e

The name of the event to generate.

p

The name of the event parameter.

PYCCA_postEvent(ecb)

Place ecb on the non-self directed event queue.

ecb

A pointer to an event control block.

PYCCA_postSelfEvent(ecb)

Place ecb on the self directed event queue. N.B. creation events should not be posted to the self-directed event queue.

ecb

A pointer to an event control block.

PYCCA_postDelayedEvent(ecb, d)

Delay the delivery of ecb by d milliseconds.

ecb

A pointer to an event control block.

d

The delay time in milliseconds.

INSTANCE MANAGEMENT

Class instances may be created and destroyed at run time. These macros help with the interface to the mechanisms to handle instance management.

PYCCA_createInstance(c, s)

Create an instance of a class.

c

The name of the class of the instance to be created.

s

The initial state that the class is to be placed in. This argument is the actual numeric code for the state and should be specified by a macro. This is usually specified as InitialStateNumber(c), to create the instance in its default initial state but a class may be created in any of its states as specified by the StateNumber(c, s) macro. For classes that do not have an associated state machine use 0. N.B. that the action of the initial state is not executed when an instance is synchronously created in this manner. To both create an instance and execute an action, you must create the instance asynchronously using a creation event.

PYCCA_newInstance(c)

Create an instance of a class in the default initial state.

c

The name of the class of the instance to be created.

PYCCA_newInstanceOfThisClass

Create an instance of the current class in the default initial state. This macro will use the current class context.

PYCCA_destroyInstance(i)

Destroy an instance of a class.

i

A pointer to the instance that is to be destoyed.

PYCCA_setToInitialState(i, c)

Set the current state of an instance to its default initial state.

i

A pointer to the instance that is to have its state changed.

c

The name of the class of the instance corresponding to i.

PYCCA_setToInitialStateForThisClass(i)

Set the current state of an instance of the current class to its default initial state. This macro will use the current class context.

i

A pointer to the instance that is to have its state changed.

PYCCA_selectOneInstWhere(i, c, expr)

This macro expands to a linear search of class named, c, for the first instance where expr is true. The i argument is the name of a variable which is of type pointer to c structure. The expanded code searches the storage pool for c stopping at the first instance that is in use and that satisfies expr. Expr is presumed to contain accesses to the attributes of c in the form of i→a. The value of i variable is modified and at the end of the loop will either point to the first instance of c where expr evaluates to non-zero or will point past the end of the storage pool for the class (as given by the EndStorage(c) macro, i.e. if i >= EndStorage(c) then the search failed). Note that this macro tests for whether or not the instance is currently allocated and therefore is only useful for classes that are either dynamically allocated or have a state machine.

i

The name of a variable that is a pointer to c class instances.

c

The name of the class of the instance corresponding to i.

expr

A "C" expression that will be intepreted as a boolean.

PYCCA_selectOneInstOfThisClassWhere(i, expr)

This macro operates the same as PYCCA_selectOneInstWhere except that the current class context is used to supply the class name.

i

The name of a variable that is a pointer to c class instances.

expr

A "C" expression that will be intepreted as a boolean.

PYCCA_selectOneStaticInstWhere(i, c, expr)

This macro is like PYCCA_selectOneInstWhere except that it does not assume that c is has a dynamic pool associated with it. For static and constant populations, there may not be an instance allocation block created and this macro does not include the test that the instance in the storage pool is actually in use.

PYCCA_selectOneStaticInstOfThisClassWhere(i, expr)

This macro is like PYCCA_selectOneInstWhere except that it uses the current class context to supply the class name.

PYCCA_forAllInst(i, c)

This macro is a convenience macro that sets up a loop such that iterates across all instances of a class. The macro should be followed by a statement (possibly compound and enclosed in braces ({}). In the statement, i will iteratively take on the value of every instance defined for the class, c.

i

The name of a variable that is a pointer to c class instances.

c

The name of the class of the instance corresponding to i.

PYCCA_forAllInstOfThisClass(i)

This macro is like PYCCA_forAllInst except that it uses the current class context to supply the class name.

PYCCA_forAllRelated(v, i, r)

This macro is a convenience macro that sets up a loop such that iterates across the instances that are related to the class. This macro assumes that the related instances were declared using the →>c, syntax, i.e. the related instances are of the counted type. The macro should be followed by a statement (possibly compound and enclosed in braces ({}). In the macro, v should be declared as ClassRefSetVar or a ClassRefConstSetVar. Then v is iterated over the set of related instances.

v

The name of a reference set variable.

i

The name of an instance reference variable.

r

The name of the relationship across which the iteration occurs.

PYCCA_forAllRelatedTerm(v, i, r)

This macro is the same as PYCCA_forAllRelated() except that the relationship must have been declared using the →>n syntax, i.e. the relationship storage consists of a NULL terminated array of instance pointers.

GENERALIZATION NAVIGATION

When a generalization relationship is implemented as a union data type, the instances of the subtypes do not use a pointer to navigate to the supertype. Rather it is only necessary to up cast the self pointer to obtain the pointer to the supertype. This macro achieves the necessary address arithmetic.

PYCCA_unionSupertype(sub, supc, r)

Navigate to the supertype for instances contained in a union.

sub

A pointer to the subtype instance. This is frequently self.

supc

The name of the supertype class.

r

The name of the relationship.

Warning
Up casting in this context is just as dangerous and error prone as up casting in any other context. If sub is not a subtype of the class, supc, then this macro will silently yield an incorrect result since it involves an explicit cast that the compiler cannot check.

Navigation to the subtype is achieved by taking the address of the subtype member within the generated structure. This macro hides the naming conventions for the subtype member.

PYCCA_unionSubtype(sup, r, subc)

Navigate to the subtype instance contained in a union.

sup

A pointer to the supertype instance. This is frequently self.

r

The name of the relationship.

subc

The name of the subtype class.

PYCCA_referenceSubtype(sup, r, subc)

Navigate to the subtype instance by pointer reference.

sup

A pointer to the supertype instance. This is frequently self.

r

The name of the relationship.

subc

The name of the subtype class.

PYCCA_isSubtypeRelated(sup, supc, r, subc)

Determine if a supertype instance is currently related to an instance of the given subtype. The macro returns true if sup is related to an instance of the subtype class, subc across the relationship, r.

sup

A pointer to the supertype instance. This is frequently self.

supc

The name of the supertype class.

r

The name of the relationship.

subc

The name of the subtype class.

PYCCA_migrateSubtype(i, supc, r, subc)

Change the subtype of a supertype instance. For generalization relationship, the supertype maintains an encoded value of the subtype to which it is currently related. This macro changes that encoded value.

i

Instance pointer to a supertype.

supc

Name of the supertype class.

r

Name of the generalization relationship.

subc

Name of the subtype class.

PYCCA_initUnionInstance(sup, r, subc)

Initialize a subtype instance that is contained in a union based supertype to its default initial state. When subtype migration happens in subtypes contained in a union, if the subtype has a state model, then it is necessary to initialize the architectural structures to be those of the new subtype. This macro accomplishes that. Note that this macro does not run any constructor of the subtype. For subtypes that have a constructor, an explicit call is required.

sup

A pointer to the supertype instance. This is frequently self.

r

The name of the relationship.

subc

The name of the subtype class.

PYCCA_initUnionInstanceToState(sup, r, subc, st)

Initialize a subtype instance that is contained in a union based supertype specifying a particular state. This macro is like the PYCCA_initUnionInstance macro but also allows you to specify the state into which the instance is placed.

sup

A pointer to the supertype instance. This is frequently self.

r

The name of the relationship.

subc

The name of the subtype class.

st

The state name of a subc class state.

PYCCA_relateSubtypeByRef(s, supc, r, t, subc)

Relate a supertype instance to a subtype instance when the relationship is being stored by reference.

s

A pointer to the supertype instance. This is frequently self.

supc

The name of the supertype class.

r

The name of the relationship.

t

A pointer to the target subclass instance.

subc

The name of the subtype class.

DYNAMIC RELATIONSHIP MANAGEMENT

These macro aid in managing the references associated with dynamic relationships. In particular, one-to-many relationships implemented by a counted pointer array (i.e. those using the -ddd>> syntax) require you to find a slot whose value is NULL and store the reference there. Unrelating instances is similar.

PYCCA_relateToMany(n, o, r, m)

Relate two instances in an one-to-many relationship that is implemented using a counted pointer array.

n

The name of a variable of a type that can point to a set of instances. This variable should be declared with the ClassRefSetVar() or ThisClassRefSetVar() macro.

o

A pointer to the instance on the one side of the relationship.

r

The name of the relationship.

m

A pointer to an instance on the many side of the relationship.

The n variable is ranged over the counted pointer array where instances of r are stored until an empty slot is found (as indicated by a NULL value) and then many side instance is then assigned to that slot. If no slot is found, then the expression n >= o→r + COUNTOF(o→r) is true.

PYCCA_unrelateFromMany(n, o, r, m)

Unrelate two instances from a one-to-many relationship that is implemented by a counted pointer array.

n

The name of a variable of a type that can point to a set of instances. This variable should be declared with the ClassRefSetVar() or ThisClassRefSetVar() macro.

o

A pointer to the instance on the one side of the relationship.

r

The name of the relationship.

m

A pointer to an instance on the many side of the relationship.

The n variable is ranged over the counted pointer array where instances of r are stored a slot containing the value of m is found and then that slot is assigned NULL. If no matching slot is found, then the expression n >= o→r + COUNTOF(o→r) is true.

For one-to-many relationships implemented by linked lists (i.e. those using the ->>l syntax), the following macros are useful.

PYCCA_linkToMany(o, r, m)

Relate two instances in an one-to-many relationship that is implemented using linked lists.

o

A pointer to the instance on the one side of the relationship.

r

The name of the relationship.

m

A pointer to an instance on the many side of the relationship.

The m instance is added to the linked list of instances that are related to the o instance across relationship r.

PYCCA_unlinkFromMany(m, r)

Unrelate an instance in a one-to-many relationship that is implemented using linked lists.

m

A pointer to the many side instance that is to be unlinked.

r

The name of the relationship in which m participates.

The m instance is unlinked from the list associated with relationship r.

PYCCA_isLinkEmpty(o, r)

Test if there are any instance in a one-to-many relationship implemented using linked lists.

o

A pointer to a one side instance.

r

The name of the relationship.

This macro evaluates to a boolean that is true if there are no instances linked from o across the relationship, r, and false otherwise.

PYCCA_isLinkNotEmpty(o, r)

Test if there are no instances in a one-to-many relationship implemented using linked lists.

o

A pointer to a one side instance.

r

The name of the relationship.

This macro evaluates to a boolean that is true if there is at least one instance linked from o across the relationship, r, and false otherwise.

PYCCA_forAllLinkedInst(o, r, l)

Iterate over the instances of a one-to-many relationship implemented using linked lists.

o

A pointer to a one side instance.

r

The name of the relationship.

l

The name of a variable of type rlink_t *

This macro expands to a for loop construct where all the instances related to o across r are visited. The link variable, l, is successively assigned values of the links related on the many side to o. The value of the link variable, l, is not a pointer to an instance. The instance pointer must be recovered by using the PYCCA_linkToInstRef() or PYCCA_linkToInstRefOfThisClass() macros described below.

PYCCA_linkToInstRef(l, c, r)
l

The name of a variable of type rlink_t *

c

The name of the class on the many side of the relationship to which l refers.

r

The name of the relationship.

This macro converts a link pointer of type rlink_t * that is a link in relationship, r, to pointer to an instance of class, c.

PYCCA_linkToInstRefOfThisClass()
l

The name of a variable of type rlink_t *

r

The name of the relationship.

This macro is like PYCCA_linkToInstRef() except that it uses the current class context to determine the class of the referenced instance.

Instance Identifiers

It is sometimes useful to use have an means to identify an instance of a particular class outside of a domain. The pointer value of the instance is not suitable for this purpose, but the array index of the instance in its storage pool is satisfactory. The macros in this group provide a means of generating a small integer value for a instance that can be used as an identifier external to the domain or to translate an instance identifier into a pointer reference to the instance.

PYCCA_idOfSelf

This macro generates an integer identifier for the self reference.

PYCCA_idOfRef(c, r)
c

The name of the class.

r

The reference value for a member of class, c.

This macro generates an integer identifier for a reference, r of class, c.

PYCCA_idOfInst(c, n)
c

The name of the class.

n

The name of a named instance.

This macro generates an integer identifier for the named instance, n of class, c.

PYCCA_refOfId(c, i)
c

The name of the class.

i

An integer identifier for an instance of the class.

This macro returns an instance reference to an instance of class, c, that is identifed by the integer identifier, i.

PYCCA_refOfThisClassId(i)
i

An integer identifier for an instance of the class.

This macro is like PYCCA_refOfId() except that it uses the current class context to determine the class of the instance identifier.

PYCCA_checkId(c, i)
c

The name of the class.

i

An integer identifier for an instance of the class.

This macro generates an invocation of the assert() macro to test that the identifier, i is valid for class, c. This is useful if a domain operation accepts an integer identifier for an instance and wants to assert its validity.

PYCCA_checkThisClassId(i)
i

An integer identifier for an instance of the class.

This macro is like PYCCA_checkId() except that it uses the current class context to determine the class of the instance identifier.

ENCODING MACROS

This set of macros gives access to the various encodings and naming conventions used by pycca in the generated code. These macros are used by the PYCCA_ macros and are sometimes useful in normal state action code. As of version 2.6, new macros were added that can use the class context that pycca generates.

ClassRefType(c)

Expands to the type of a reference to an instance of class named, c.

ThisClassRefType

Expands to the type of a reference to an instance of the current class context.

ClassRefVar(c, v)

Declare a variable named v that refers to the class named, c.

ThisClassRefVar(v)

Declare a variable named v that refers to the current class.

ClassConstRefVar(c, v)

Declare a variable named v that refers to the class name c. This macro is used for references to classes that have constant populations.

ThisClassConstRefVar(v)

Declare a variable named v that refers to a constant instance of the current class.

ClassRefSetVar(c, v)

Declare a variable named v that refers to a set of instances of the class named c.

ThisClassRefSetVar(v)

Declare a variable named v that refers to a set of instances of the current class.

ClassConstRefSetVar(c, v)

Declare a variable named v that revers to a set of constant instances of the class named c.

ThisClassConstRefSetVar(c, v)

Declare a variable named v that revers to a set of constant instances of the current class.

SubCodeMember(r)

For generalization relationships, the super type holds an encoded values that signifies the type of the sub type to which it is currently related. This macro gives the structure member name in the super type instance for relationship, r.

SubCodeValue(c, r, s)

For generalization relationships, the super type holds an encoded values that signifies the type of the sub type to which it is currently related. This macro gives the subtype code number for class, c, relationship, r and subtype name, s.

SubTypesMember(r, s)

For subtypes held in a union, this macro gives the name of the union member for subtype, s, in relationship, r. Use the PYCCA_unionSubtype() macro to simply obtain the address of the union subtype member.

RefCountMember(r)

For multiple references defined with the ->>c construct, this macro gives the name of the class structure member that holds the count of class references along the relationship given by the r argument.

EventNumber(c, e)

The number of the ordinary event for the event named, e, in class, c.

ThisClassEventNumber(e)

The number of the ordinary event for the event named, e, in the current class.

PolyEventNumber(c, e)

The number of the polymorphic event for the event named, e, in class, c.

InitialStateNumber(c)

The number of the default initial state for instances of class, c.

StateNumber(c, s)

The number encoding the state, s, for class, c.

ThisClassStateNumber(s)

The number encoding the state, s, for the current class.

SelfStateNumber

The number encoding the state for the current state of the self instance.

EventParamType(c, e)

The type name of the data structure for event, e, in class, c. This macro is now deprecated as it interfers with a type name used in the architecture mechanisms. It is retained for backwards compatiblity but will be removed in a future release. Use the EventParamDecl() macro instead.

EventParamDecl(c, e)

The type name of the data structure for event, e, in class, c.

ThisClassEventParamDecl(e)

The type name of the data structure for event, e, in the current class.

ClassData(c)

A pointer to the class data structure for c.

ThisClassData

A pointer to the class data structure for the current class.

RefCountMember(r)

The name of the structure member that holds the reference count for a muli-reference relationship.

BeginStorage(c)

The address of the beginning of instance storage for class, c.

ThisClassBeginStorage

The address of the beginning of instance storage for the current class.

EndStorage(c)

The address of one instance past the end of instance storage for class, c. The pointer generated by this macro must not be de-referenced.

ThisClassEndStorage

The address of one instance past the end of instance storage for the current class. The pointer generated by this macro must not be de-referenced.

Instance(c, n)

A pointer to the instance of class, c, that was named n in the initial instance declaration.

ThisClassInstance(n)

A pointer to the instance of the current classs that was named n in the initial instance declaration.

IsInstInUse(i)

A boolean test to determine if the instance pointed to by i is currently in use. This is useful when iterating across the storage for a class searching for instances with particular attribute values. It is always necessary to determine if the instance is in use before testing for the required attribute values.

DomainName

The name of the current domain.

DomainOp(d, o)

The name of domain operation, o, in domain, d.

ExternalOp(o)

The name of an external operation, o.

ClassOp(c, o)

The name of class operation, o, in class, c.

ThisClassOp(c, o)

The name of class operation, o, in the current class.

InstOp(c, o)

The name of instance operation, o, in class, c.

ThisClassInstOp(c, o)

The name of instance operation, o, in the current class.

INSTRUMENTATION

If the -instrument option is used, pycca will emit instrumentation code at the beginning of each function associated with matching classes. When pycca includes instrumentation code, the preprocessor symbol, INSTRUMENT will be defined. The actual instrumentation is delegated to a macro, INSTR_FUNC(s), where s is a string indicating the specific function being invoked. Users may define the INSTR_FUNC(s) macro (e.g. in the implementation prolog code) to override the default supplied by pycca. As of version 4.1, pycca supplies a default version of INSTR_FUNC as well as one that is intended to work with a tack generated test harness. The default definition supplied by pycca is:

#ifdef INSTRUMENT
#   ifndef INSTR_FUNC
#       ifdef TACK
#           include "harness.h"
#           define INSTR_FUNC(s) harness_stub_printf("instrument",\
                "func %s file %s line %u", (s), __FILE__, __LINE__)
#       else
#           define INSTR_FUNC(s) printf("%s: %s %d\n", (s), __FILE__, __LINE__)
#       endif /* TACK */
#   endif /* INSTR_FUNC */
#endif /* INSTRUMENT */

N.B. because the instrumentation macro is inserted into the output before any passed along "C" code, old compilers that do not allow variable declarations in a block after code statements will most likely produce compiler errors if any local variables are declared in the instrumented function. Such is the limitation of a program like pycca that does not examine the "C" code passed along.

DATA PORTAL

When the -dataportal option is given, pycca will generate a set of data structures that allow access to the attributes of class instances from outside of the domain. This facility is provided for the following purposes:

  1. Bridging data values into and out of the domain for use by other domains.

  2. Bridging event generation to the instances of a domain.

  3. Bridging instance creation and deletion into a domain.

  4. Testing, where attribute values need to be read and updated as part of the test set.

  5. Testing, where generated events are used to force execution paths.

The only values available through this interface are declared attribute values and the type code of a subtype that is related to a supertype. The internal pointer references used for relationship navigation are not accessible.

The files pycca_portal.h or pycca_portal.c contain the code and declarations needed to use the portal facility. These files may be obtained by invoking pycca with the -portalcode option. These files contain the common code that can be used to read and update the class attributes within the domain and generate events to the instances of the domain. The functions require a parameter which is a pointer to the data structure that pycca generates. This variable is named <domain name>_portal, where <domain name> is replaced with the name of the domain. An external declaration of the variable is inserted into the generated header file. Classes and attributes are encoded as small integers and these definitions are also placed in the generated header file. A macro definition giving the total number of instances is also placed in the generated header file. This is the same numeric encoding that is placed in the generated header file when the -ids option is given.

Some care must be taken when accessing class attributes for classes that are subtypes of a generalization that is implemented via a union. In the case of a union, there are as many instances of each of the subtypes as there are of the ultimate supertype of the generalization. However, how the storage space of the subtype is being interpreted is determined by a type code that is stored in the supertype. Accessing a union subtype that is a different type than that currently related to the supertype will not yield the correct value. So for supertype classes, the encoded values of the type of the related subtype classes is also placed in the header file.

N.B. that the interface to access domain data and generate events via the portal data structures and code is very low level, partially breaks the encapsulation of the domain and, consequently, is subject to substantial abuse. However, it does provide a means to access domain data that uses relatively little memory, is data driven and generates no executable code in the domain itself. This is what is necessary for bridging and testing purposes and, consequently, is it strongly advised that no invocations of data portal functions appear in pycca domain code.

© Copyright 2007 - 2013 by G. Andrew Mangogna.

The following terms apply to all files associated with the software unless explicitly disclaimed in individual files.

The author hereby grants permission to use, copy, modify, distribute, and license this software and its documentation for any purpose, provided that existing copyright notices are retained in all copies and that this notice is included verbatim in any distributions. No written agreement, license, or royalty fee is required for any of the authorized uses. Modifications to this software may be copyrighted by their authors and need not follow the licensing terms described here, provided that the new terms are clearly indicated on the first page of each file where they apply.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

GOVERNMENT USE: If you are acquiring this software on behalf of the U.S. government, the Government shall have only "Restricted Rights" in the software and related documentation as defined in the Federal Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you are acquiring the software on behalf of the Department of Defense, the software shall be classified as "Commercial Computer Software" and the Government shall have only "Restricted Rights" as defined in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the authors grant the U.S. Government and others acting in its behalf permission to use and distribute the software in accordance with the terms specified in this license.