Notation
The following Syntax description uses the conventions of the XBNF extended BNF including:
- O(_)::=an optional (_).
- #(_)::=any number of (_) including none.
- N(_)::=one or more of (_).
- For op, term, L(op, term)::=term #(op term), -- Infix operator expression.
- List(_)::= L( "," , (_) ).
Grammar
- expression::= logical_expression.
- logical_expression::= L( logical_operator, relational_expression ).
score > 88.33 and score <= 93.33
gender = #female or gender = #male
p implies q
left xor right
next->isEmpty or value <= next.value
self.grading and self.questions->size<10
p1 <> p2 implies p1.name <> p2.name
Student.allInstances->forAll( p1, p2 | p1 <> p2 implies p1.name <> p2.name )
- relational_expression::= additive_expression O( relational_operator additive_expression ).
score > 88.33
value <= next.value
self.questions->size<10
- additive_expression::= L( add_operator, multiplicative_expression ).
golgafrensham + 42
golgafrensham - 42
self.questions->size+1
- multiplicative_expression::= L( multiply_operator, unary_expression ).
2 * head
22 / 7
20*self.questions->size
- unary_expression::= #( unary_operator ) postfix_expression.
- prefect
not ford
not self.questions->isEmpty
- postfix_expression::= primary_expression #(navigation_operator feature_call ).
self.questions
employees->includes(#dent)
self.questions->size
self.employer->size
self.employer->includes(self)
self.employee->select (v | v.wages>10000 )->size
Student.allInstances->forAll( p1, p2 | p1 <> p2 implies p1.name <> p2.name )
- primary_expression::= literal_collection | literal | feature_call | "(" expression ")" | if_expression.
42
self
#dent
employer@pre
MyClass::myMethod(myArgument)
self.employee->exists (v | v = self )
Student.allInstances
- if_expression::= "if" expression "then" expression "else" expression "endif".
if name = 'beeblebrox' then body->count(head) = 2 endif
- feature_call_parameters::= "(" O( declarator ) ( actual_parameter_list) ")".
( 42, #dent, x, self, 'beeblebrox')
( v:people | v.height < 6 )
( p1, p2 | p1 <> p2 implies p1.name <> p2.name )
- literal::= string | number | "#" name.
'beeblebrox'
42
#dent
- enumeration_type::= "enum" "{" List("#" name ) "}".
enum{ #female, #male }
- simple_type_Specifier::= path_type_name | enumeration_type.
- literal_collection::= collection_kind "{" O(expression_list_or_range) "}".
(Set):
Set { 1 , 2 , 5 , 88 }
Set { 'apple' , 'orange', 'strawberry' }
(Sequence):
Sequence { 1, 3, 45, 2, 3 }
Sequence { 'ape', 'nut' }
Sequence{ 10..24 }
(Bag):
Bag {1 , 3 , 4, 3, 5 }
- expression_list_or_range::= expression O( N( "," expression ) | ( ".." expression )).
12,13,15
12..15
- feature_call::= path_name O(time_expression) O(qualifiers) O(feature_call_parameters).
- qualifiers::= "[" actual_parameter_list "]".
- declarator::= List(name) O( ":" simple_type_Specifier ) "|".
- path_type_name::= type_name #( double_colon type_name).
- path_name::= ( type_name | name ) #( double_colon ( type_name | name ) ).
MyClass::myAttribute
- time_expression::= "@" name.
@pre
- actual_parameter_list::= List( expression ).
1+1, 2, 3
Lexicon
- double_colon::= colon colon.
- colon::= ":".
- logical_operator::= "and" | "or" | "xor" | "implies".
- collection_kind::= "Set" | "Bag" | "Sequence" | "Collection".
- relational_operator::= "=" | ">" | "<" | ">=" | "<=" | "<>".
- add_operator::= "+" | "-".
- multiply_operator::= "*" | "/".
- unary_operator::= "-" | "not".
- navigation_operator::= "." | "->"
- number::= digit #digit.
- type_name::= upper #idchar.
- name::= lower #idchar.
- idchar::= letter | digit | "_".
- string::="'" #( normal_char | backslash escape_sequence ) "'".
- backslash::="\\".
- normal_char::=char ~ ("'" | backslash | newline ).
- newline::= Newline and carriage return characters shown as "\n""\r" in C and C++ .
- escape_sequence::= "n" | "t" | "b" | "r" | "f" | backslash | "'" | doublequote | ASCII_code_number. -- compare with C/C++/Java
- ASCII_code_number::=odigit O( odigit ) | ("0".."3") odigit odigit
- doublequote::="\"".
- digit::="0".."9".
- odigit::=octal_digit.
- octal_digit::="0".."7".
- letter::= upper | lower.
- upper::="a".."z".
- lower::="A".."Z".
Comments
Comments in OCL are written after two dashes. Everything after the two
dashes up to and including the end of line is comment. For example:
- -- this is a comment
Class Invariants
The context is shown first and then the invariant:
context Student
inv: self.age >= 0
Operation Specifications
In text the context of the operation is listed and then the pre-, and post-, conditions.
context Typename::operationName(parameter1 : Type1, ... ): ReturnType
pre : parameter1 > ...
post: result = ...
(In an ealy version the context was underlined and the word "contxt" not used).
In a post condition use "@pre" to indicate the value before the operation and "result" is the value returned.
Special Symbols
- self::Object.
self refers to an object of the class being constrained
self.numberOfEmployees
In most cases, self can be left out, because the context is clear, as in the above examples.
- result::Object.
result is used in a post-condition inside an operation specification, to
indicate the object returned
Local Variables
Within a context the lexeme "Let" allows the definition of a
shorthand "variable" name for an expression:
let variable : type = expression.
OCL Types
Each class in a model that uses the UML defines a new type. There are
other predefined types:
- type::= pathname | predefined_type.
- predefined_type::= basic_type | "OclExpression" | "OclType" | "OclAny".
- basic_type::= "Integer" | "Real" | "String" | "Boolean".
OCL Types have the following features:
- For T:OclType.
- T.allInstances::Set(T), set of all instances of a type T in model.
- T.name::String, the name of the type.
- T.attributes::Set(String), the set of names of attributes of T as defined in the model.
- T.associationEnds: Set(string), set of names of navigable roles in model.
- T.operations::Set(string),
- type.supertypes::Ste(OclType).
Properties of objects of Any Type
- OclAny::=following
Net
(End of Net)
Navigation
Starting from a specific object (or a path_name), we can navigate an association on the
class diagram to refer to other objects and their properties. To do so, we
navigate the association by using the opposite association-end:
object.rolename
[ primary_expression ]
The value of this expression is the set of objects on the other side of the
role name association. If the multiplicity of the association-end has a
maximum of one ("0..1" or "1"), then the value of this expression is an
object. If the multiplicity is "0..1" then the result is an
empty set if there is no object, otherwise it is the unique objet.
Navigation expression generate Collections of objects.
By default, navigation will result in a Set. When the association on the
Class Diagram is adorned with {ordered}, the navigation results in a
Sequence. Collections, like Sets, Bags and Sequences, are predefined types
in OCL. They have a large number of predefined operations on them.
collection->operation(arguments)
Collections
- Collection(T)::=Set(T) | Sequence(T) | Bag(T).
Collections can be Sets, Sequences, and Bags.
An object can occur at most once in a Set but many times in a Bag. It occurs at a particular position in Sequence.
See literal_collection in the grammar above.
Given a typename T then T.allInstances is the Set of all objects of type T.
Operations on Collections
Collections have many useful properties. These are added to the OCL
to give it the power of symbolic logic (LPC) and naive set theory.
Properties of collections are accessed by using an arrow '->' followed by
the name of the property. There are many of these. They seem to have been
borrowed from Smalltalk [Smalltalk_methods].
Many of the properties of collections result in collections. This means
that they can be chained together:
self.employee->select (v | v.wages>10000 )->size
See postfix_expression in the grammar above.
The following is a list of some of the commonest ones.
For c,c1:Collection(T), v:variable, T,T1,T2:Type, e:expression, b:BooleanExpression, o1:object.
(casts):
- c->asSequence::Sequence(T),
- c->sortBy(...)::Sequence(T).
(size):
- c->size::Integer.
(isEmpty):
- c->isEmpty::Boolean.
- |- (axiom1): c->isEmpty = (c->size = 0).
(count):
- c->count(o1):: Integer=count of occurrences of o1 in c.
- |- (axiom2): c->count(o1) = c->select(v | v=o1)->size.
(sum):
- c->sum::T.
(filters): select, reject, collect, etc
(select):
c->select( v : T | b(v) ):: Collection(T), generates a collection by taking only elements v in c for which e(v) is true.
- c->select( b ):: Collection(T).
v->select( v | e(v) ):: Collection(T).
(reject):
c->reject( v : T | b(v) ):: Collection(T) = c->select(v:T | not(e(v))).
(collect):
c->collect( v : T | e(v) ):: Collection(T), generates a collection made up of e(v) as v goes thru collection c.
c->collect( v | e(v) ):: Collection(T).
- c->collect( e ):: Collection(T).
(quantifiers): forAll and exists. Compare with LPC
(forAll):
- c->forAll( v : T | b ):: Boolean = for all v:c&T ( b ).
- c->forAll( v | b ):: Boolean.
- c->forAll( b ):: Boolean.
(exists):
- c->exists( v : T | b ):: Boolean = for some v:c&T ( b ).
- c->exists( v | b ):: Boolean.
- c->exists( b ):: Boolean.
(iterate):
c->iterate( v : T1; a : T2 = e0 | e(v,a) )::collection(T).
The variable v is the iterator, as in the definition of select, forAll,
etc. v takes each value in the collection c in turn.
The variable a is the accumulator. The a gets an initial value
e0. The e(v,a) is an expression that is repeatedly assigned to a.
For example
- |-c->sum = c->iterate(v, a=0 | a+v).
- |-c->size = c->iterate(v, a=0 | a+1).
(sets): includes, excludes, union, intersection, including, and
excluding. Compare with set_theory. Plus (new in 1.3) unique, excludes, excludesAll
(includes):
- c->includes(o):: Boolean= true if and only if o in c.
(union):
- c->union(c2):: Set(T) = c|c2.
(intersection):
- c->intersection(c2):: Set(T)= c&c2.
(including):
- c->including(o)::Set(T) = c | {o}.
(excluding):
- c->excluding(o):: Set(T) = c ~ {o}.
(sequences): append, prepend, at, etc etc. See my strings and string_theory.
(append):
- s->append(o):: Sequence(T) = s ! o, put o after s.
(prepend):
- s->prepend(o):: Sequence(T) = o ! s, put o at the front of s.
(at):
- s->at(i ):: T = s[i], pick out the i'th item in s.
Undefined expressions
Whenever an OCL expression is being evaluated, there is a possibility that
one or more of the queries in the expression are undefined. If this is the
case, then the complete expression will be undefined.
Two exceptions are the boolean operators and + or:
- True OR-ed with anything is True
- False AND-ed with anything is False
Notice, that if-then-else-endif does not evaulate all its arguments and so
can be used to avoid an undefined object.
In later OCL versions
- oclUndefined()
will recognize an undefined object and return True otherwise it returns
False.
Shorthand notation for collect
In general, when we apply a property to a Collection of Objects, then the OCL
will automatically be interpret it as a collect over the members of the
Collection with the specified property. So
self.employee->collect(birthdate)
is normally written
self.employee.birthdate
and it means all the employee's birthdates.