Part 4: RSDL Formal Definition
4.3 Static Semantics
4.3.2 Visibility, Names and Identifiers
This section introduces all formalisations for resolution of names, visibility rules and identifiers.
4.3.2.1 Name
Abstract Syntax
Name :: TOKEN
Agent-type-name = Name
Agent-name = Name
State-name = Name
Signal-name = Name
Literal-name = Name
Operation-name = Name
Timer-name = Name
Gate-name = Name
Connector-name = Name
Channel-name = Name
Variable-name = Name
Sort-name = Name
Concrete Syntax
<name> :: TOKEN
<literal> = <literal name>
<literal name> = <name>
<sort> = <basic sort>
<basic sort> = <name>
Mapping to Abstract Syntax
| <name>(x) => mk-Name(x)
Please note that TOKEN is present in both AS0 and AS1 such that it need not be mapped.
4.3.2.2 Identifier
Abstract Syntax
Identifier :: Qualifier Name
Qualifier = Path-item*
Agent-identifier = Identifier
Agent-type-identifier = Identifier
Signal-identifier = Identifier
Timer-identifier = Identifier
Gate-identifier = Identifier
Variable-identifier = Identifier
Concrete Syntax
<identifier> :: [<qualifier>] <name>
<qualifier> :: {<path item>}+
Auxiliary Functions
For identifiers a lot of auxiliary functions are defined. They all serve for formalising the resolution, visibility and the insertion of full qualifiers. Please find below the definitions for the AS0 and AS1 grammars.
ENTITYKIND =def
{ agentKind, agentTypeKind, channelKind, signalKind, variableKind, remoteVariableKind }
The following entity kinds exist: agents (blocks); agent types (block types); channels, gates; signals, timers;
variables (including formal parameters), literals, data types; remote variables;
fullIdentifier(i:<identifier>): <identifier> =def i.refersto0.myfullIdentifier The full identifier of an identifier is the full identifier of its defining entity.
fullPath(x: DefinitionAS0): <path item>* =def if x = undefined then empty
elseif x ∈ <rsdl specification> then empty elseif x ∈ <block type definition> then
fullPath(x.parentAS0) ∩ < <path item>(blocktype, x.s-<block type heading>.s-<name>) >
else fullPath(x.parentAS0) endif
The full path of a definition is constructed recursively. Please note that only types may contain definitions, as instances have been transformed before.
myfullIdentifier(x: DefinitionAS0): <identifier> =def <identifier>(x.fullPath, x.defName)
The full identifier of a definition is constructed using its full path and its name.
myFullIdentifierAS1(d: DefinitionAS1): DefinitionAS1 =def
d.inv-Mapping.myfullIdentifier.Mapping
The full identifier function for the AS1 is derived from that of the AS0.
resolutionByContainer(i:<identifier>, k:ENTITYKIND, s:DefinitionAS0):DefinitionAS0 =def
if s = undefined then undefined else let matchingDefs =
{ d ∈ DefinitionAS0: d.parentAS0.findScopeUnit = s ∧ i.s-<name> = d.defName ∧ matchingQualifier(i.s-<qualifier>, d.myfullIdentifier.s-<qualifier>)} in if | matchingDefs | = 1 then matchingDefs.take
else resolutionByContainer(i, k, s.parentAS0.findScopeUnit) endif
endif
The binding of a <name> to a definition through resolution by container proceeds in the following steps, starting in the scope unit where the <identifier> appears:
1. if a unique entity exists in the current scope unit with the same <name> and entity kind and matching
<qualifier>s, the <name> is bound to that entity; otherwise
2. resolution by container is attempted in the scope unit which defines the current scope unit.
matchingQualifier(q1: <qualifier>, q2: <qualifier>): BOOLEAN =def
if q1 = q2 then True
elseif q1 = undefined then True
elseif q1.length < q2.length then matchingQualifier(q1, q2.tail)
else matchingPathItem(q1.head, q2.head) ∧ matchingQualifier(q1.tail, q2.tail) endif
The definition above defines when two qualifiers are matching.
refersto0(i: <identifier>): DefinitionAS0 =def
if i.parentAS0 ∈ <typebased block heading> then
resolutionByContainer(i, agentTypeKind, i.parentAS0.findScopeUnit) elseif i.parentAS0 ∈ (<gate constraint> ∪ <channel path>) then
if resolutionByContainer(i, signalKind, i.parentAS0.findScopeUnit) ≠ undefined then resolutionByContainer(i, signalKind, i.parentAS0.findScopeUnit)
else resolutionByContainer(i, remoteVariableKind, i.parentAS0.findScopeUnit) endif
elseif i.parentAS0 ∈ <channel endpoint> then
resolutionByContainer(i, agentKind, i.parentAS0.findScopeUnit) elseif i.parentAS0 ∈ <channel to channel connection> then
resolutionByContainer(i, channelKind, i.parentAS0.findScopeUnit)
elseif i.parentAS0 ∈ <save part> then
resolutionByContainer(i, signalKind, i.parentAS0.findScopeUnit) elseif i.parentAS0 ∈ (<stimulus> ∪ <output body gen identifier>) then
resolutionByContainer(i, signalKind, i.parentAS0.findScopeUnit) elseif i.parentAS0 ∈ <assignment> then
resolutionByContainer(i, variableKind, i.parentAS0.findScopeUnit) elseif i.parentAS0 ∈ <create request> then
resolutionByContainer(i, agentKind, i.parentAS0.findScopeUnit)
elseif i.parentAS0 ∈ <import> ∧ i.parentAS0.s-<identifier> = i then // it is the variable identifier resolutionByContainer(i, variableKind, i.parentAS0.findScopeUnit)
elseif i.parentAS0 ∈ <import> then
resolutionByContainer(i, remoteVariableKind, i.parentAS0.findScopeUnit) elseif i.parentAS0 ∈ <export> then
resolutionByContainer(i, remoteVariableKind, i.parentAS0.findScopeUnit)
elseif i.parentAS0 ∈ (<set clause> ∪ <reset clause> ∪ <timer active expression>) then resolutionByContainer(i, signalKind, i.parentAS0.findScopeUnit)
elseif i.parentAS0 ∈ <operand5> then
if resolutionByContainer(i, variableKind, i.parentAS0.findScopeUnit) ≠ undefined then resolutionByContainer(i, variableKind, i.parentAS0.findScopeUnit)
elseif i.s-<qualifier> = undefined then // it is only a <name>
predefined else undefined endif
else undefined endif
The definition above lists all places where <identifier>s appear in AS0 constructors and how to resolve them using the resolution by container. Please note the special handling for <gate constraint> and <channel path>
which refers to the following text in the language definition.
A <signal list item> which is an <identifier> denotes a <signal identifier> or <timer identifier> if this is possible according to the visibility rules or else a <remote variable identifier>.
Please note that the visibility rules are already covered by the definition of the resolution by context.
refersto1(i: Identifier): DefinitionAS1 =def
i.inv-Mapping.refersto0.Mapping
The identifier reference function for the AS1 is derived from that of the AS0.
referstoName1(n: Name): DefinitionAS1 =def
if n.parentAS1∈ Nextstate-node then let candidateStates =
{ s ∈ parentAS1ofKind(n, State-transition-graph).s-State-node-set: s.s-State-name = n } in if | candidateStates | = 1 then candidateStates.take else undefined endif
elseif n.parentAS1∈ Join-node then let candidateLabels =
{ s ∈ parentAS1ofKind(n, State-transition-graph).s-Free-action-set:
s.s-Connector-name = n } in
if | candidateLabels | = 1 then candidateLabels.take else undefined endif endlet
else undefined endif
The definition above shows how single names are resolved. Recall, that in some places no identifiers are allowed but only names, namely for states or connectors.
sortCompatible(t1: <sort>, t2: <sort>): BOOLEAN =def t1 = t2 ∧ t1 ≠ undefined
The function above defines when two sorts are compatible. Due to the limited data type part an equality is sufficient.
exprSort(e: Expression): DefinitionAS1 =def if e ∈ Literal then
mk-Name(literalSort(e.s-Name.s-TOKEN)) elseif e ∈ Operation-application then
mk-Name(operationSort(e.s-Operation-name.s-TOKEN,
< exprSort(a) | a in e.s-Expression-seq >)) elseif e ∈ Variable-access then
e.refersto1.s-Sort-name
elseif e ∈ Now-expression then mk-Name(“Time”) elseif e ∈ Pid-expression then mk-Name(“PId”)
elseif e ∈ Timer-active-expression then mk-Name(“Boolean”) else undefined
endif
The function above calculates the sort of an expression.
findScopeUnit(entity: DefinitionAS0): DefinitionAS0 =def
if entity = undefined then undefined
elseif entity ∈ (<agent type definition> ∪ <agent definition>) then entity else findScopeUnit(entity.parentAS0)
endif
The function findScopeUnit searches for the next enclosing scope unit starting from the current place.
visible(entity: DefinitionAS0, scope: DefinitionAS0): BOOLEAN =def
if scope = undefined then False
else entity.parentAS0.findScopeUnit = scope ∨ visible(entity, scope.parentAS0.findScopeUnit) endif
An entity is visible in a scope unit if: it has its defining context in that scope unit; or the entity is visible in the scope unit which defines that scope unit.
defName(d: DefinitionAS0): <name> =def
if d ∈ <block type definition> then d.s-<block type heading>.s-<name>
elseif d ∈ <block definition> then d.s-<block heading>.s-<name>
elseif d ∈ <textual typebased block definition> then d.s-<typebased block heading>.s-<name>
elseif d ∈ <channel definition> then d.s-<name>
elseif d ∈ <textual gate definition> then d.s-<gate>
elseif d ∈ <signal definition> then d.s-<signal definition item>-seq.head.s-<name>
elseif d ∈ <timer definition> then d.s-<timer definition item>-seq.head
elseif d ∈ <variable definition> then d.s-<variables of sort>-seq.head.s-<name>-seq.head elseif d ∈ <remote variable definition> then
d.s-<remote variable definition gen name>-seq.head.s-<name>-seq.head else undefined
endif
The function defName extracts the name of a definition.
getEntityKind(d: DefinitionAS0): ENTITYKIND =def
if d ∈ <agent type definition> then agentTypeKind elseif d ∈ <agent type reference> then agentTypeKind elseif d ∈ <agent definition> then agentKind
elseif d ∈ <textual typebased agent definition> then agentKind elseif d ∈ <agent reference> then agentKind
elseif d ∈ <signal definition> then signalKind elseif d ∈ <timer definition> then signalKind elseif d ∈ <variable definition> then variableKind
elseif d ∈ <remote variable definition> then remoteVariableKind elseif d ∈ <channel definition> then channelKind
elseif d ∈ <gate in definition> then channelKind else undefined
endif
Conditions on Concrete Syntax
=5=> ∀ i ∈ <identifier>: i.refersto0 ≠ undefined ∧ visible(i.refersto0, i.findScopeUnit) An entity can be referenced by using an <identifier>, if the entity is visible.
=5=>∀d, d' ∈ DefinitionAS0: getEntityKind(d) ≠ undefined ∧ getEntityKind(d) = getEntityKind(d') ⇒ myfullIdentifier(d) ≠ myfullIdentifier(d')
All entities with the same entity kind must have different Identifiers.
Transformations
i=<identifier>(q, n) provided fullIdentifier(i).s-<qualifier> ≠ q =4=> <identifier>(fullIdentifier(i).s-<qualifier>, n)
For all identifiers, the corresponding full qualifiers have to be inserted.
Mapping to Abstract Syntax
| i=<identifier>(q, name) =>
if i.refersto0 = predefined then mk-Literal(Mapping(name)) else mk-Identifier(Mapping(q), Mapping(name))
endif
| <qualifier>(q) => Mapping(q)
4.3.2.3 Path Item
Path items are mapped straightforwardly to the AS1.
Abstract Syntax
Path-item = Agent-type-qualifier | Agent-qualifier Agent-type-qualifier :: Agent-type-name
Agent-qualifier :: Agent-name
Concrete Syntax
<path item> :: <scope unit kind> <name>
<scope unit kind> = block | block type Auxiliary Functions
matchingPathItem(p1: <path item>, p2: <path item>): BOOLEAN =def
if p1.s-<scope unit kind> = block then
p1.s-<name>.newName = p2.s-<name> ∧ p2.s-<scope unit kind> = blocktype else p1 = p2
endif
The function matchingPathItem provides the comparison between path items. Please note that for path items containing agent names the implicitly introduced agent type names have to be considered.
Mapping to Abstract Syntax
| <path item>(block, n) => mk-Agent-qualifier(Mapping(n))
| <path item>(blocktype, n) => mk-Agent-type-qualifier(Mapping(n))