In addition to the primitive functions, the Acme predicate language supports user-defined functions with the design analysis construct. A design analysis is simply a user-defined function that can be invoked from design invariants or design heuristics. Design Analyses can be defined by composing primitive functions from the Acme Predicate Language and/or other Design Analyses. Alternatively, design analyses can be externally defined functions or tools that are accessed through the Java interpreter. External design analyses are performed out of the scope of the Acme system, leaving the semantics for such functions undefined. It is the responsibility of the user defining the external design analyses to insure that the function returns the type of entity published in the design analysis signature.
The syntax for the design analysis construct adds the following productions to the Acme predicate language grammar presented thus far:
DesignAnalysis
::= analysis Identifier ( FormalParams ) : ReturnTypeIdentifier =
Expression
| external
analysis Identifier( FormalParams ) :
ReturnTypeIdentifier
= ExternalReference ;
An ExternalReference is a reference to a function defined outside of the Acme language. In general, this will be a reference to a method available in the Java interpreter that is running Acme. The general form of an external reference that calls the Java interpreter will be package.class.method(ActualParams).
The following examples illustrate the declaration of design analyses.
analysis goesFast(fil : Filter) : boolean = (fil.rate > 1000);
analysis goesFastSecurely(fil : Filter) : boolean = (goesFast(fil) and fil.secure = true) ;
external analysis sourceCompiles(fil : Filter) : rate = java.tools.checkSrc;
Note that the external analysis needs to be integrated properly by writing a plugin to Acme. See the Adding External Design Analyses section in the help pages.
Once declared, these design analyses are available for use in design invariants and design heuristics. As an example, the following style description makes use of these design analyses.
Family sample = {
Component Type Filter = { Property rate : int; … };
// invariant that says all filters are fast and secure.
Invariant forall f : component in self.Components |
satisfiesType(f, Filter) ® goesFastSecurely(f);
// invariant that says the source code compiles for all filters
Invariant forall f : component in self.Components | sourceCompiles(f);
//
heuristic that limits fan-in and fan-out
Heuristic forall c : component in self.Components | size(connectedTo(c)) <= 2;
}