Concrete Syntax
This section describes the concrete syntax of the OCL. This allows
modelers to write down OCL expressions in a standardized way. A formal
mapping from the concrete syntax to the abstract syntax from Chapter 8
("Abstract Syntax") is given. Although not required by the UML 2.0 for
OCL RFP, Section 9.6 describes a mapping from the abstract syntax to
the concrete syntax. This allows one to produce a standard human
readable version of any OCL expression that is
represented as an instance of the abstract syntax.
Section 9.1 ("Structure of the Concrete Syntax") describes the
structure of the grammar and the motivation for the use of an
attribute grammar.
Structure of the Concrete Syntax
The concrete syntax of OCL is described in the form of an a full
attribute grammar. Each production in an attribute grammar may have
synthesized attributes attached to it. The value of synthesized
attributes of elements on the left hand side of a production rule is
always derived from attributes of elements at the right hand side of
that production rule. Each production may also have inherited
attributes attached to it. The value of inherited attributes of
elements on the right hand side of a production rule is always
derived from attributes of elements on the left hand side of that
production.
In the attribute grammar that specifies the concrete syntax, every
production rule is denoted using the EBNF formalism and annotated
with synthesised and inherited attributes, and disambiguating rules.
There are a number of special annotations:
Synthesized attributes. Each production rule has one
synthesized attribute called ast (short for abstract syntax
tree), that holds the instance of the OCL Abstract Syntax that is
returned by the rule. The type of ast is different for every
rule, but it always is an element of the abstract syntax. The type is
stated with each production rule under the heading "Abstract Syntax
Mapping". The ast attribute constitutes the formal mapping from
concrete syntax to abstract syntax.
The motivation for the use of an attribute grammar is the easiness of
the construction and the clarity of this mapping. Note that each name
in the EBNF format of the production rule is postfixed with 'CS' to
clearly distinguish between the concrete syntax elements and their
abstract syntax counterparts.
Inherited attributes. Each production rule has one inherited
attribute called env (short for environment), that holds a list
of names that are visible from the expression. All names are references
to elements in the model. In fact, env is a name space
environment for the expression or expression part denoted according to
the production rule. The type of the env attribute is
Environment, as shown in Figure 13 . A
number of operations are defined for this type. Their
definitions and more details on the Environment type can be found in
Section 9.4 ("Environment definition").
The manner in which both the ast
and env attributes are determined, is given using OCL
expressions.
Note that the contents of the env attribute are fully
determined by the context of the OCL expression. When an OCL
expression is used as an invariant to class X, its environment will be
different than in the case the expression is used as a postcondition to
an operation of class Y. In Chapter 12 ("The Use of Ocl Expressions in
UML Models") the context of OCL expressions is defined in detail.
Multiple production rules. For some elements there is a choice
of multiple production rules. In that case the EBNF format of
each production rule is prefixed by a capital letter between square
brackets. The same prefix is used for the corresponding
determination rules for the ast and env attributes.
Multiple occurences of production names. In some production
rules the same element name is used more than once. To distinguish
between these occurences the names will be postfixed by a number in
square brackets, as in the following example.
CollectionRangeCS ::= OclExpressionCS[1] '..' OclExpressionCS[2]
Disambiguating rules. Some of the production rules are
syntactically ambiguous. For such productions disambiguating
rules have been defined. Using these rules, each production and thus
the complete grammar becomes nonambiguous. For example in parsing a.b(),
there are at least three possible parsing solutions:
- a is a VariableExpr (a reference to a let or an
iterator
variable)
- a is an AttributeCallExp (self is implicit)
- a is a NavigationCallExp (self is implicit)
A decision on which grammar production rule to use, can only be made
when the environment of the expression is taken into account. The
disambiguating rules describe these choices based on the environment
and allow unambiguous parsing of a.b(). In this case the rules
(in plain English) would be:
- If a is a defined variable in the current scope, a
is a VariableExp.
-
If not, check self and all iterator variables in scope. The inner-most
scope for which as is either
- an attribute with the name a, resulting in
an AttributeCallExp,
- or an opposite association-end with the name a,
resulting in a NavigationCallExp,
- defines the meaning of a.b().
- If neither of the above is true, the expression is illegal
/ incorrect and cannot be parsed.
Disambiguating rules may be based on the UML model to which the OCL
expresion is attached (e.g does an attribute exist or not). Because of
this, the UML model must be available when an OCL expression is parsed,
otherwise it cannot be validated as a correct expression. The grammar
is structured in such a way that at most one of the production rules
will fullfil all the disambiguating rules, thus ensuring that the
grammar as a whole is unambiguous. The disambiguating rules are written
in OCL, and use some metaclasses and additional operations from the UML
1.4 semantics.