Introduction to JMPL

Language Version: 4.0

Andrew Bachmann

May 31, 2001

 

Introduction to JMPL

 

JMPL is the Java-based Model Programming Language.  JMPL is designed to be easier to use for modellers.  The Java/C++ syntax is more familiar to users than the previous Lisp syntax.  JMPL also represents a number of extensions beyond the original Lisp-based MPL.

 

History of JMPL

 

In the beginning, there was Lisp-based MPL, and it was good.  However, engineers who may want to use the Livingstone system are generally not familiar or comfortable with Lisp syntax.  They are more familiar with C++ and java conventions.  So, Brian Williams designed a language that was more in keeping with these conventions.  Jim Kurien presented the language to Andrew Bachmann (me) and we worked through some issues.  We made the language conform more to java conventions.  Throughout this process we tried to make the syntax as straightforward and simple as possible.  As we developed the parser and compiler, we realized that certain structures were redundant and we removed them.  We made these changes along with others.  The result is JMPL today.

 

Differences between JMPL and the Lisp-based MPL

 

When JMPL was designed, syntactic structures were provided for improvements in the engine, either realized or future.  Where the Lisp-based MPL had a single "mode" variable, JMPL has a state vector.

 

In conjunction with the change from a single mode variable, the way to describe transitions and modes has changed somewhat.  In the Lisp-based MPL, a single mode was represented by a syntactic structure that enclosed the descriptions of transitions out of that mode.  In JMPL, transitions are represented by syntactic structures at the topmost level of a component type.  Transitions are defined by a "from vector" and a "to vector", each of which corresponds to a set of values for the state vector, before and after the transition, respectively.

 

Some other syntactic constructs have been removed.  Lisp-based MPL had a construct called "initially", which would set up the initial state of any component of that component type.  In JMPL, there is no such construct.  It was removed and replaced by an independent mechanism to supply the initial state of each component individually.  The new system is more flexible and expressive.

 

The Lisp-based MPL construct called "always" has been replaced by a java syntactic convention.  Constraints enclosed in curly braces (constraint groups) at the topmost level of a component type serve the same role.  These constraints are imposed when a component of that component type is instantiated.

 

Another difference is in failure probabilities.  Failure probabilities used to be associated with failure modes.  Essentially, these were a priori probabilities.  As such, they were only useful at the start of the diagnosis.  The new probabilities are more expressive and flexible.

 

Modelling in JMPL

 

All models in JMPL are broken down into two categories of source objects.  A source object is either an enumeration or class.  Both of these source objects allow specification of relations that operate on them.  It is not necessary to define a class or enumeration before its use.

 

Template:

 

jmpl ::= (enumeration | class)*

 

Enumerations

 

Enumeration

 

Enumerations describe attribute types.  They specify the domain of the attribute. (the set of possible values)  Enumerations are finite and discrete.  Enumerations are atomic.  They can not be constructed from composing other enumerations.  Enumerations do not form an inheritance heirarchy.

 

Template:

 

enumeration ::= enum enumName { enumValue (, enumValue)* } ({ relation* })? ;

enumName ::= ident

enumValue ::= ident

 

Examples:

 

enum onOffState {on, off};

 

enum relativeValues {low, nominal, high} {

  // some relations

  relation relativeValuesEqual ( relativeValues to) {

    this = to;

  }

  relation relativeValuesLessThan ( relativeValues to) {

    ((this = low) & ((to = nominal) | (to = high))) |

    ((this = nominal) & (to = high));

  }

  relation relativeValuesMoreThan ( relativeValues to) {

    ((this = high) & ((to = nominal) | (to =low))) |

    ((this = nominal) & (to = low));

  }

  relation relativeValuesNotEqual ( relativeValues to) {

    this != to;

  }

};

 

Classes

 

Class

 

Classes describe component types, modules, and composite attribute types.

 

Template:

 

class ::= class className (extends superName)? { classEntry* }

className ::= ident

superName ::= identifier

classEntry ::= (attribute | stateVector | classEnumeration |

                transition | failure | relation | constraintGroup)

 

Example:

 

class led {

  onOffState currentIn;

  onOffState ledState;

  commandIn repairFlag;

  private enum ModeType {nominal,failed};

  private ModeType mode;

  stateVector [mode];

  transition fix(failed,nominal) {

    repairFlag=repaired;

  }

  failure break(*,failed,low) {}

  {

    switch (mode) {

      case nominal:

        ledState = currentIn;

    }

  }

}

 

Attribute

 

An attribute can be an instance of an enumeration, or an instance of another class.

 

Template:

 

attribute ::= (modifier)? enumIdentifier attributeName ;

enumIdentifier ::= identifier

attributeName ::= ident

 

Example:

 

public onOffState ledState;

 

State Vector

 

Each class can have at most one state vector.  The state vector defines a set of variables that are used to encapsulate the mode of the component.  When transitions are defined, they use the state vector as a reference for the order of the arguments.

 

Templates:

 

stateVector ::= stateVector stateVectorVariables ;

stateVectorVariables ::= (stateVectorVariable |

                          [ stateVectorVariable (, stateVectorVariable)* ])

stateVectorVariable ::= attributeName

 

Examples:

 

stateVector modeVar;

stateVector [xCondition,yCondition,zCondition];

 

Class Enumeration

 

Enumerations can be defined within the context of a class.  An enumeration defined this way can define attributes at this point.  If at least one attribute is defined simultaneously, then the enumeration can be anonymous. (defined without a name)  Otherwise, these are the same as attributes defined outside a class.

 

Templates:

 

classEnumeration ::= (modifier)? enum (enumName)?

                                   { (enumValue (, enumValue)*)? }

                                   (enumVar (, enumVar)*)? 

                                   ({ enumEntry* })?

enumVar ::= variable

 

Example:

 

private enum {north, east, south, west} direction1, direction2;

public enum onOffState {on, off};

 

Transitions and Failures

 

Transitions and failures are birds of a feather.  From states can be specified as "*" (asterisk/star) to indicate any from state.  To states can be specified as "+" (plus) to indicate that the to state is the same as the from state.  Identifiers should belong to the domain of the variable that occurs at their location in the state vector.  The constraints in the statement block must hold in order for the transition or failure to be possible.

 

Transitions have an additional argument that represents the cost of that transition.  Failures have an additional argument that represents the probability of that failure occurring.

 

Templates:

 

transition ::= transition (transitionName)?

               '(' (fromVector ',' (toVector ',' (transitionCost)?)?)? ')'

               statementBlock

transitionCost ::= (identifier | constant)

 

failure ::= failure (failureName)?

            '(' (fromVector ',' (toVector ',' (failureProbability)?)?)? ')'

            statementBlock

failureProbability ::= (identifier | constant)

 

toVector ::= (toStateId | '[' toStateId (',' toStateId)* ']' )

toStateId ::= ( '.' | '+' | identifier )

 

fromVector ::= (fromStateId | '[' fromStateId (',' fromStateId)* ']' )

fromStateId ::= ( '.' | '*' | identifier )

 

Examples:

 

transition turnAround([north],[south]) {

  commandIn=turnAround;

}

failure fallDown(*,[north],unlikely) {

}

 

Relations

 

Relations are named sets of constraints.  They are essentially processed as macros, although they have some scope.  For example, a relation in a class can refer to attributes of that class at the location of the relation invocation.  Relations are invoked from instances of the class of which they are a part.  (see Constraints)

 

Templates:

 

relation ::= relation relationName '(' (argument (',' argument)*)? ')' statementBlock

relationName ::= ident

argument ::= argumentType argumentVar

argumentType ::= identifier

argumentVar ::= variable

 

Examples:

 

// attach the two boxes through this class's local attributes:

// string1, string2

relation attach(Can can1, Can can2) {

  can1.inString = string1;

  can2.outString = string1;

  can1.outString = string2;

  can2.inString = string2;

} 

 

Constraints

 

Although constraints may sometimes look like executable code, they are not.  The compiler converts these constraints into clauses for the engine.  As part of this process, all the constraints in a particular context (transition, failure, etc.) are logically combined.  During this combining, certain forms of trivial logical simplification might take place.  (i.e. A or not A implies true)  The clauses formed by the compiler each conform to disjunctive normal form.   (i.e. A or B or not C or...)  The clauses output are logically equivalent to the constraints input, but may or may not bear resemblance to them, depending on the logical operators used to combine the terms.

 

Templates:

 

constraintGroup ::= { (constraint)* }

constraint ::= ( constraintGroup

               | (if (expression) statement (else statement)?)

               | (iff (expression) statement)

               | (switch '(' (identifier | this) ')' { (casesGroup)* } )

               | ';'

               | (expression ';')

casesGroup ::= ( case identifier ':' )+

               caseConstraintList

caseConstraintList ::= (constraint)*

 

Examples:

 

if (a=b) { c=d; } else {e=f;}

iff (a=b) { c=d; }

// the following is equivalent to "a=b & c=d;"

{

  a=b;

  c=d;

}

switch (onOff) {

  case on: a=b;

  case off: c=d;

}

 

Expressions

 

Expressions are the most basic part of a constraint.  Equality and inequality expressions must operate on attributes or components of the same type.  Said another way, if "a=b", then a and b must both be components or attributes of the same type.  Also, redundant syntactic forms are supplied for the logical constructs, in order to make it easier for modellers to use syntax that is most familiar and comfortable to them.

 

Relations are used like method invocations in C++ or java.  It is important to note that relations are not functions.  Relations do not return values.  Please see the example for an invalid usage of a relation as a function.  Also of note is that relations only accept identifiers as arguments.  Expressions are not identifiers, and can not be used as arguments.

 

Templates:

 

expression := ( (expression '|' expression)

              | (expression '||' expression)

              | (expression '&' expression)

              | (expression '&&' expression)

              | (primaryExpression '=' primaryExpression)

              | (primaryExpression '==' primaryExpression)

              | (primaryExpression '!=' primaryExpression)

              | ('!' expression)

              | ('~' expression)

              | ( (identifier'.')?relationName

                  '(' (primaryExpression (','primaryExpression )*)? ')' )

              )

primaryExpression := (identifier | this)

 

Examples:

 

// equality

this=on;

this==on;

// conjunction

(a=b) & (c=d);

a=b && (c=d);

// disjunction

a=b | c=d;

(a=b) || c=d;

// negation/inequality

~a=b;

!a=b;

a!=b;

// relations

object.relation(arg1,arg2);

relation2(arg);

// relations are not functions (this is syntactically invalid)

sum(x,y)=z;

// arguments to relations can not be expressions

// (this is syntactically invalid)

allTrue(x=y,z=f);

 

Symbols/References

 

Names are of the fairly standard convention from Java and C++.

 

Templates:

 

identifier ::= ident('.'ident)*

ident ::= [A-Z,a-z,_,$][A-Z,a-z,0-9,_,$]*

 

Examples:

 

foo.bar.baz (identifier)

foo (ident)

 

Comments

 

There are two kinds of comments.  Line comments and group comments.  They are similar to comments in C++ and Java.

 

Examples:

 

// this is a line comment

/* this is a

   group comment */

 

Modifiers

 

Modifiers establish the rules of access for attrbiutes or enumerated types that are defined within the context of a component type.  The absence of a modifier, or the "public" modifier allows access to from any context.  The "private" modifier only allows access from the component type.