In this section we shall present the formal semantics we have
given to a hierarchy of DFDs.
The more complicated parts will be illustrated by means of small examples. Thus, this section contains a collection of VDM functions which transform SA (with the structure described in
Appendix A) to a collection of VDM modules (represented in the
structure described in Appendix B).
\subsubsection{Top level functions}
The top-level function, which transforms a hierarchy of data flow
diagrams (an $HDFD$), also takes as arguments the mini-specifications supplied by the
user and the style in which the composing operations are tobe generated%
\footnote{The argument with this information will be called $style$ thoughout the
definition indicating that it is the ``style" (implicit or explicit)
it should be transformed.}.
\begin{vdm_al} functions
TransHDFD : HDFD * MSs * (<EXPL>|<IMPL>) -> setofModule
TransHDFD(hdfd,mss,style) == let mainmod=MakeDFDModule(hdfd,mss,style) in let mk_(-,-,-,dfdmap,-)=hdfd in let mods= dunion {TransHDFD(dfd,mss,style)
| dfd insetrng dfdmap} in
{mainmod} union mods;
\end{vdm_al} For each module the interface and the body (the definitions) of
the module must be created.
\begin{vdm_al}
MakeDFDModule : HDFD * MSs * (<EXPL>|<IMPL>) -> Module
MakeDFDModule(mk_(dfdid,dss,dfdtopo,dfdmap,dfdsig),
mss,style) == let i = MakeInterface(dfdid,dss,dfdtopo,dfdsig,dfdmap),
defs = MakeDefinitions(dfdid,dss,dfdtopo,
dfdsig,mss,style) in
mk_(ModIdConf(dfdid),i,defs);
\end{vdm_al}
\noindent The name of the module (the first component of the
returned triple) is generated by means of a configuration function.
Any name ending in $Conf$ denotes
something which can be configured by a user of a tool translating
DFDs to VDM.
These configuration functions should be defined (by the user orby a
tool%
\footnote{Such a tool would obviously also need tobe able to produce
the abvstract syntax of strutured analysis and produce the concrete
syntax of the VDM specifications which are produced.}
automatically translating DFDs into VDM specifications) in order tobe able to
conform to any specific conventions (e.g. for naming identifiers) that are used.
At the endof the formal definition we present an example of how these functions can be defined.
\subsection{Interface functions}
The interface of each module contains an import part for a modulewith the
type information forall the data stores and the data flows (only the types actually used in the given module are imported), an import part for each of the data transformers that are further decomposed (and
are described in their own module), and an export part for the
operation which specifies the functionality of the given DFD.
\begin{vdm_al}
MakeInterface: DFDId * DSs * DFDTopo * DFDSig * DFDMap ->
Interface
MakeInterface(dfdid,dss,dfdtopo,dfdsig,dfdmap) == let tmimp = MakeTypeModImp(dss, dom dfdtopo),
dfdmimps = MakeDFDModImps(dom dfdmap,dfdsig),
exp = MakeOpExp(dfdid,dfdsig(dfdid)) in
mk_(({tmimp} union dfdmimps),exp) pre dfdid insetdom dfdsig;
MakeTypeModImp : DSs * setof FlowId -> Import
MakeTypeModImp(dss,fids) == let tysigs= {mk_TypeSig(DSIdConf(dsid))
|dsid inset dss} union
{mk_TypeSig(FlowIdTypeConf(fid))
|fid inset fids} in
mk_(TypeModConf(),tysigs);
MakeDFDModImps: setof DFDId * DFDSig -> setof Import
MakeDFDModImps(dfdids,dfdsig) ==
{mk_(ModIdConf(id),{MakeOpSig(id,dfdsig(id))})
| id inset dfdids} pre dfdids subsetdom dfdsig;
MakeOpSig : DFDId * Signature -> OpSig
MakeOpSig(dfdid,sig) == let opty = MakeOpType(sig),
opst = MakeOpState(sig) in
mk_OpSig(OpIdConf(dfdid),opty,opst);
MakeOpType : Signature -> OpType
MakeOpType(mk_(il,ol,-)) ==
mk_OpType(MakeType(il),MakeType(ol));
\end{vdm_al} In $MakeType$ it can be seen that multiple flows toorfrom a data
transformer are modelled as product types.
MakeOpState : Signature -> seqof Id
MakeOpState(mk_(-,-,sl)) ==
[let mk_(s,-)=i in
StateVarConf(s)
|i inseq sl];
\end{vdm_al}
\subsection{The main function fordefinitions}
The body of each module contains a number ofdefinitions.
The body will contain a state definition andif the
DFD contains any data stores, these are included together with the
data flows between the systemand the external process. If the DFD contains any data transformers which are not further decomposed, the body will also contain definitionsfor
these. Finally the module will always contain a definition of the
operation which describes the functionality of that DFD.
\begin{vdm_al}
MakeDefinitions: DFDId * DSs * DFDTopo * DFDSig * MSs *
(<EXPL>|<IMPL>) -> Definitions
MakeDefinitions(dfdid,dss,dfdtopo,dfdsig,mss,style) == let dst = MakeState(dfdid,dss,CollectExtDFs(dfdtopo)),
msdescs = MakeMSDescs(dfdsig,mss),
dfdop = MakeDFDOp(dfdid,dfdtopo,dfdsig,style) in if dst=nil then {dfdop} union msdescs else {dst,dfdop} union msdescs;
\end{vdm_al}
\subsection{Functionsfor the state definition}
Each data store in a
DFD and each data flow toorfrom an external process is transformed
into a state component of the state definition.
\begin{vdm_al}
MakeState : DFDId * DSs * setof FlowId -> [StateDef]
MakeState(dfdid,dss,fids) == if dss={} and fids={} thennil elselet fl=MakeFieldList(dss union fids) in
mk_StateDef(StateIdConf(dfdid),fl);
MakeFieldList : setof StId -> seqof Field
MakeFieldList(ids) == if ids={} then [] elselet id inset ids in
[MakeField(id)]^MakeFieldList(ids \ {id}) measureCard;
Card: setof StId -> nat Card(s) == card s;
MakeField : StId -> Field
MakeField(id) ==
mk_Field(StateVarConf(id),StateTypeConf(id));
\end{vdm_al}
\subsection{Functionsfor primitive MSs}
The data transformers which are not further decomposed (they can be considered as being
primitive) can be supplied by the user in the form of a
mini-specification in VDM-SL. In the case where the user has not supplied such a
mini-specification, a simple implicitly defined operation is generated. Since the user has supplied only the type
information for the data transformer (by means of the data flow types) the generated definition will have the right type with
\textbf{\ttfamily true} as a post-condition.
\begin{vdm_al}
MakeMSDescs : DFDSig * MSs -> setof Definition
MakeMSDescs(dfdsig,mss) == ifforall id insetdom dfdsig& is_DFDId(id) then {} elselet id insetdom dfdsig best is_MSId(id) in letdef'= if id in set dom mss then mss(id) else MakeOp(id,dfdsig(id)) in
{def'} union MakeMSDescs({id} <-: dfdsig,mss);
MakeOp: MSId * (seqof FlowId * seqof FlowId * State)
-> ImplOp
MakeOp(msid,mk_(din,out,dst)) == let partpl = MakeInpPar(din),
residtp = MakeOutPair(out),
dext = MakeExt(dst),
body = mk_ImplOpBody(nil, mk_BoolLit(true)) in
mk_ImplOp(OpIdConf(msid),partpl,residtp,dext,body);
In the function $MakeOutPair$ it should be noticed that if a data
transformer contains more that one data flow out from it, it is
necessary to ``invent" a new identifier to denote the result of the
data transformer ($ResultIdConf$).
\begin{vdm_al}
MakeOutPair : seqof FlowId -> [IdType]
MakeOutPair(fidl) == caseslen fidl:
0 -> nil ,
1 -> mk_IdType(FlowIdVarConf( hd fidl),
FlowIdTypeConf( hd fidl)), others -> let t=mk_ProductType([FlowIdTypeConf(i) | i inseq fidl]) in
mk_IdType(ResultIdConf(),t) end;
\subsection{Functionsfor composing data transformers implicitly}
\label{depend}
An operation describing the functionality of a DFD uses
the operationsfor the lower-level DFDs.
The combination that must be constructed depends upon the topology of the
DFD. Whenever a data transformer receives data from another data
transformer through a data flow (in the same DFD) this dependency
must be incorporated in the combination, by using the output value from the first data transformer (and possibly changed state
component(s)) as input for the second data transformer. However,
since a data transformer in principle is a loose construct (see
\cite{Wieth89} for a thorough
treatment of the semantics of looseness)
it is necessary when generating preand
post-conditions to take this possible looseness into
account. This is done by specifying that there must exist an output
value (and possibly one or more changed statevalues) such that the
post-condition of the first data transformer is fulfilled andthen
use this value (orvalues) for the data transformer which depends upon the
first one (see \cite{Plat&91a}).
By means of three small examples we will illustrate
what has tobe taken into account to describe the functionality of a
DFD as a whole.
\subsubsection*{Example 1}
Consider the \DFD\ in figure~\ref{example1}.
%\makefigure{example1}{DFD for example 1}{example1}
It is a simple
\DFD\ consisting of two data transformers $P$ and $Q$,
each having one input data flow ($a$ and $b$ respectively) and one
output data flow ($b$ and $c$ respectively).
$Q$ receives data from $P$ and thus
$Q$ depends on $P$. When this \DFD\ is intended to model a sequential system it is
obvious that $P$ must be executed before $Q$ can be executed. This
dependency between $P$ and $Q$ also can be found in the pre- and post-condition of the composite \DFD:
%\begin{vdm}
%\begin{op}[i]{PQ}
%\parms{a: A}[c: C]
%\begin{precond}
%\exists{b : B}\\
% {pre-P(a) \And\\
% post-P(a, b) \And \\
% pre-Q(b)}
%\end{precond}
%\begin{postcond}
%\exists{b : B}\\
% {pre-P(a) \And
% post-P(a, b) \And \\
% pre-Q(b) \And
% post-Q(b, c)}
%\end{postcond}
%\end{op}
%\end{vdm}
\begin{lstlisting}
PQ(a: A) c : C preexists b : B & pre_P(a) and
post_P(a,b) and
pre_Q(b) postexists b : B & pre_P(a) and
post_P(a,b) and
pre_Q(b) and
post_Q(b,c)
\end{lstlisting}
It is necessary to quote the post-condition\footnote{%
`Quoting' pre- and post-conditions of (implicitly defined) functions and operationsis a {\small VDM} technique to `invoke' other functionsoroperationsfrom within a pre- or post-condition (i.e. a
predicate): each implicitly defined function or operation $f$ has
associated boolean {\em functions} $pre-f$ and $post-f$ which, given the
appropriate arguments, yield $true$ if the pre- or post-condition respectively of $f$ holds for those arguments, and $false$ otherwise.
A quoted pre-condition of an operation takes
the input arguments of the operation and the state components
used by the operation as its arguments.
A quotation of a post-condition of an operation first takes
the input arguments of the operation, then some arguments
representing the valuesof the state components before the operation is
executed, the output resultof the operation, and finally the newstate
components (only those to which the operation has write access).%
} of $P$ to
produce a value that must satisfy the pre-condition of $Q$. Since $P$
may be loosely specified there may be several values satisfying the
post-condition of $P$ given some argument $a$. However, since only
some of these values might satisfy the pre-condition of $Q$ an
existential quantification over this `internal data flow', $b$, is
necessary.
Alternative solutions can be envisaged, differing in the strength of the constraints put upon the combination.
\noindent $\Box$\\
\subsubsection*{Example 2}
Example~1 is now expanded by introducing a data store that both
data transformer $P$ and data transformer $Q$ have write access to.
This \DFD\ is given in figure~\ref{example2}.
The data store $ds$ is-- as has been mentioned -- interpreted as a state
component.
%\makefigure{example2}{DFD for example 2}{example2}
This composite \DFD\ can bespecifiedby the following implicit
definition:
%\begin{vdm}
%\begin{op}[i]{PQ_{DS}}
%\parms{a: A}[c: C]
%\ext{\Wr ds: DS\\}
%\begin{precond}
%\exists{b : B, ds' : DS}\\
% {pre-P(a, ds) \And\\
% post-P(a, ds, b, ds') \And \\
% pre-Q(b, ds')}
%\end{precond}
%\begin{postcond}
%\exists{b : B, ds' : DS}\\
% {pre-P(a, ~{ds}) \And
% post-P(a, ~{ds}, b, ds') \And \\
% pre-Q(b, ds') \And
% post-Q(b, ds', c, ds)}
%\end{postcond}
%\end{op}
%\end{vdm}
\begin{lstlisting}
PQ_DS(a: A) c : C wr ds : DS preexists b : B, ds' : DS &
pre_P(a, ds) and
post_P(a,ds,b,ds') and
pre_Q(b,ds') postexists b : B, ds' : DS &
pre_P(a,ds~) and
post_P(a,ds~,b.ds') and
pre_Q(b,ds') and
post_Q(b,ds',c,ds)
\end{lstlisting}
It is necessary to introduce an intermediate state component, $ds'$, which holds the value of $ds$ in between execution of the different data
transformers, $P$ and $Q$. This situation occurs when several data
transformers are allowed to modify the same data store.
In addition, this example illustrates another technicality that must be taken into account in the transformation from \DFD s to \VDM. The
value of the state component, $ds$, before activation of the operation is referred to differently inside the pre-condition (as $ds$) and the
post-condition (as $~{ds}$). When a pre- or post-condition (using
an old state value) is quoted it is necessary to supply information
about whether it was quoted inside a pre-condition or inside a
post-condition.
\noindent $\Box$\\
\subsubsection*{Example 3}
The \DFD\ from example~2 is now expanded by adding an extra data
transformer, $R$, which also modifies data store $ds$, but
otherwise isnot connected to the two other data transformers ($P$ and
$Q$). The \DFD\ is given in figure~\ref{example3}.
%\makefigure{example3}{DFD for example 3}{example3}
Although the \DFD\ at first sight still looks rather simple, it turns
out that the \VDM\ specification for the \DFD\ is quite complicated. The \DFD\ is illustrative for the situation in which
the {\em writer} of the \DFD\ may understand it
differently than the {\em reader} of the \DFD. The ambiguity comes from the fact that nothing is said about in which order the three data
transformers should modify the data store. Maybe it isnot important,
but maybe it is essential that one specific execution order is chosen in the implementation.
The notation `$*: DS$' (in the figure)
means that a value of type $DS$ will be used at this
point, but we don't know exactly {\em which} value that will be. Consider
$P$ and $R$. One of them uses the old value of $ds$ in the quotation of its post-condition, but we don't know which one because
that depends on the execution order.
The possible execution orders are visible in the
generated \VDM\ specification.
The following implicit definition of the composite \DFD\ can be generated:
%\begin{vdm}
%\begin{op}[i]{PQR_{DS}}
%\parms{a: A, d: D}[r: C \X E]
%\ext{\Wr ds : DS\\}
%\begin{precond}
%\exists{b : B, c : C, e : E, ds', ds'' : DS}\\ {%
%\lineup[c]{(}{pre-R(d, ds) \And post-R(d, ds, e, ds') \And \\
% pre-P(a, ds') \And post-P(a, ds', b, ds'') \And pre-Q(b, ds''))} \Or\\
%\lineup[c]{(}{pre-P(a, ds) \And post-P(a, ds, b, ds') \And \\
% pre-R(d, ds') \And post-R(d, ds', e, ds'') \And pre-Q(b, ds''))} \Or\\
%\lineup[c]{(}{pre-P(a, ds) \And post-P(a, ds, b, ds') \And \\
% pre-Q(b, ds') \And post-Q(b, ds', c, ds'') \And pre-R(d, ds''))}}
%\end{precond}
%\begin{postcond}
%\Let (c, e) = r
%\Lin\\
%\exists{b:B, ds', ds'' : DS}\\
%{\lineup[c]{(}{pre-R(d, ~{ds}) \And post-R(d, ~{ds}, e, ds') \And
% pre-P(a, ds') \And \\ post-P(a, ds', b, ds'') \And
% pre-Q(b, ds'') \And post-Q(b, ds'', c, ds))} \Or\\
%\lineup[c]{(}{pre-P(a, ~{ds}) \And post-P(a, ~{ds}, b, ds') \And
% pre-R(d, ds') \And \\ post-R(d, ds', e, ds'') \And
% pre-Q(b, ds'') \And post-Q(b, ds'', c, ds))} \Or\\
%\lineup[c]{(}{pre-P(a, ~{ds}) \And post-P(a, ~{ds}, b, ds') \And
% pre-Q(b, ds') \And \\ post-Q(b, ds', c, ds'') \And
% pre-R(d, ds'') \And post-R(d, ds'', e, ds))}}
%\end{postcond}
%\end{op}
%\end{vdm}
\begin{lstlisting}
PQR_DS(a : A, d : D) r : C * E wr ds : DS preexists b : B, c : C, e : E, ds', ds'' : DS &
(pre_R(d, ds) and post_R(d, ds, e, ds') and
pre_P(a, ds') and post_P(a, ds', b, ds'') and
pre_Q(b, ds'')) or
(pre_P(a, ds) and post_P(a, ds, b, ds') and
pre_R(d, ds') and post_R(d, ds', e, ds'') and
pre_Q(b, ds'')) or
(pre_P(a, ds) and post_P(a, ds, b, ds') and
pre_Q(b, ds') and post_Q(b, ds', c, ds'') and
pre_R(d, ds'')) postlet (c, e) = r in exists b:B, ds', ds'' : DS &
(pre_R(d, ds~) and post_R(d, ds~, e, ds') and
pre_P(a, ds') and post_P(a, ds', b, ds'') and
pre_Q(b, ds'') and post_Q(b, ds'', c, ds)) or
(pre_P(a, ds~) and post_P(a, ds~, b, ds') and
pre_R(d, ds') and post_R(d, ds', e, ds'') and
pre_Q(b, ds'') and post_Q(b, ds'', c, ds)) or
(pre_P(a, ds~) and post_P(a, ds~, b, ds') and
pre_Q(b, ds') and post_Q(b, ds', c, ds'') and
pre_R(d, ds'') and post_R(d, ds'', e, ds))
\end{lstlisting}
The post-condition shows that there are three possible
execution orders: $[P, Q, R]$, $[P, R, Q]$ and $[R, P, Q]$.
The pre- and post-conditions defined above ensure that at least one
possible execution order can be used.
$r$ is a new name, introduced to denote the output as a whole.
\noindent $\Box$\\
Below, we present the functions that are used tocompose
data transformers into implicit specifications. These functions illustrate how the problematic issues from the three
examples above are dealt with.
\begin{vdm_al}
MakeDFDOp: DFDId * DFDTopo * DFDSig * (<EXPL>|<IMPL>) ->
OpDef
MakeDFDOp(dfdid,dfdtopo,dfdsig,style) == if style=<EXPL> then MakeDFDExplOp(dfdid,dfdtopo,dfdsig) else MakeDFDImplOp(dfdid,dfdtopo,dfdsig) preif style=<EXPL> then pre_MakeDFDExplOp(dfdid,dfdtopo,dfdsig) else pre_MakeDFDImplOp(dfdid,dfdtopo,dfdsig);
MakeDFDImplOp : DFDId * DFDTopo * DFDSig -> ImplOp
MakeDFDImplOp(dfdid,dfdtopo,dfdsig) == let mk_(din,out,dst)=dfdsig(dfdid) in let partpl = MakeInpPar(din),
residtp = MakeOutPair(out),
dext = MakeExt(dst),
body = MakeImplOpBody(dfdid,dfdtopo,dfdsig) in
mk_ImplOp(OpIdConf(dfdid),partpl,residtp,dext,body) pre dfdid insetdom dfdsig and
pre_MakeImplOpBody(dfdid,dfdtopo,dfdsig);
\end{vdm_al}
The function $MakeImplOpBody$ is used to generate both the pre-condition and the post-condition of an implicit operation definition. In order to take intermediate data store values into account,
$MakeImplOpBody$ and its auxiliary functions will use a mapfromstate
components to the current number of intermediate values ($intm$).
The mapis initialized by mapping allstate components to zero
(indicating that no intermediate statevalues have been introduced yet)%
\footnote{The configuration function $StateVarIntConf$
inserts a number of quotes corresponding to the number of the
intermediate value, as it was done in the examples.}. In addition, a map $maxm$ with the same domain of state components is used to ensure that a
post-condition uses the state after an operation as the last of a
series of intermediate state components. Each state
component in $maxm$ is mapped to the number of data transformers
having write access (and thus potentially introduce an
intermediate state value) to that state component.
The $IntM$ domain is an auxiliary domain which is used in the formal
transformation from SA to VDM. It is used to provide information
about intermediate statevalues.
The function $MakePreExpr$ is used to generate the pre-condition of an
implicit operation body. The function will first determine whether an
existential quantified expressions is needed
%(which is the case if there are independent
%partitions consisiting of more than one data transformer each) by calling $QuantNec$, and depending on that, create either a existential
quantified expression, or just the predicate part of such an expression.
\begin{vdm_al} functions
MakePreExpr: DFDId * DFDTopo * DFDSig * IntM * IntM ->
Expr
MakePreExpr(dfdid,dfdtopo,dfdsig,intm,maxm) == let mk_(-,out,dst)=dfdsig(dfdid) in let fids = NeedsQuant(dfdtopo,dfdsig,{},{}),
pred = MakePrePred(dfdtopo,dfdsig,intm,maxm) in if QuantNec(out,dst,fids,intm,maxm) thenlet bind = MakeExistsBind(fids,dst,intm,
maxm,<PRE>) in
mk_ExistsExpr(bind,pred) else pred pre dfdid insetdom dfdsig;
\end{vdm_al}
The function $MakePrePred$ is used to create the `body' of the pre-condition of an implicit operation. First, all possible orders of execution are
determined. Then, for each data transformer it is ensured that its pre-condition
can be satisfied by generating a predicate in which the pre-condition of that
data transformer is quoted in a context in which all its predecessors in a possible executation order have
been executed.
Finally, all such predicates or combined in a disjunction.
The function $MakePreForEO$ generates a pre-expression for a specific
execution order $piseq$. An application of both the
quoted preand the quoted post-condition of the first data transformer in the execution order is generated (by $MakeQuotedApply$) andthen $MakePreForEO$ is called
recursively with the remainder of the data transformers in $piseq$.
A collection of intermediate statevalues $intm'$ is constructed in each
recursion step in order to use the correct intermediate statevalues in the construction of a quotation for an operation. All quotations are combined in a conjunction.
\begin{vdm_al}
MakePreForEO: seq1of ProcId * DFDSig * IntM * IntM ->
Expr
MakePreForEO(piseq,dfdsig,intm,maxm) == let nid= hd piseq in let intm'={stid |-> if mk_(stid, ) in set
CollectStIds({dfdsig(nid)}) then intm(stid) + 1 else intm(stid)
| stid insetdom intm} in let dpre = MakeQuotedApply(nid,dfdsig(nid),intm',maxm,
<PRE>,<PRE>),
dpost = MakeQuotedApply(nid,dfdsig(nid),intm',maxm,
<PRE>,<POST>) in iflen piseq=1 then dpre elselet pred=mk_BinaryExpr(dpre,<AND>,dpost) in
mk_BinaryExpr(pred,<AND>,
MakePreForEO(tl piseq,dfdsig,
intm',maxm));
\end{vdm_al}
$MakePostExpr$ is used to generate the post-condition of an implicit operation.
When a DFD has more than one output, these outputs are combined into
a tuple expression. A new name for this tuple expression is created by
generating a let-expression. $MakePostExpr$ first determines
whether such a let-expression should be generated. Then, the body of the post-expression is generated by calling $MakeInExpr$.
\begin{vdm_al}
MakePostExpr: DFDId * DFDTopo * DFDSig * IntM * IntM ->
Expr
MakePostExpr(dfdid,dfdtopo,dfdsig,intm,maxm) == let mk_(-,out,dst)=dfdsig(dfdid),
fids = NeedsQuant(dfdtopo,dfdsig, elems out,{}),
body = MakeInExpr(out,dst,fids,dfdtopo,dfdsig,
intm,maxm) in iflen out<= 1 then body else mk_LetExpr(MakePattern(out),ResultIdConf(),body) prelet mk_(-,out,dst)=dfdsig(dfdid),
fids = NeedsQuant(dfdtopo,dfdsig, elems out,{}) in
pre_MakeInExpr(out,dst,fids,dfdtopo,dfdsig,
intm,maxm);
\end{vdm_al}
The function $MakeInExpr$ operates in much the same way as $MakePreExpr$ does for the generation of pre-conditions. The function examines whether an
existential quantification is needed, andif this is the case such a
quantified expression is generated. The remainder of the post-condition is
generated by calling $MakePostPred$.
\begin{vdm_al}
MakeInExpr: seqof FlowId * State * setof FlowId *
DFDTopo * DFDSig * IntM * IntM -> Expr
MakeInExpr(out,dst,fids,dfdtopo,dfdsig,intm,maxm) == let pred=MakePostPred(dfdtopo,dfdsig,intm,maxm) in if QuantNec(out,dst,fids,intm,maxm) thenlet bind = MakeExistsBind(fids,dst,intm,maxm,
<POST>) in
mk_ExistsExpr(bind,pred) else pred pre pre_MakeExistsBind(fids,dst,intm,maxm,<POST>);
\end{vdm_al}
The function $MakePostPred$ is used to create the `body' of the post-condition of an implicit operation. First, all possible orders of execution are
determined, andfor each execution order a conjunction of quoted function
applications are generated using the intermediate statevalues (this is done in $MakePostForEO$).
The separate conjunctions are then combined in one large disjunction, in this way specifying that the implementor can choose either one of the
execution orders to implement the DFD.
The function $MakePostForEO$ generates a post-expression for a specific
execution order $piseq$. An application of both the
quoted preand the quoted post-condition of the first data transformer in the execution order is generated (by $MakeQuotedApply$) andthen $MakePostForEO$ is called
recursively with the remainder of the data transformers in $piseq$.
A collection of intermediate statevalues $intm'$ is constructed in each
recursion step in order to use the correct intermediate statevalues in the construction of a quotation for an operation. All quotations are combined in a conjunction.
\begin{vdm_al}
MakePostForEO: seq1of ProcId * DFDSig * IntM * IntM ->
Expr
MakePostForEO(piseq,dfdsig,intm,maxm) == let nid= hd piseq in let intm'={stid |-> if mk_(stid, ) in set
CollectStIds({dfdsig(nid)}) then intm(stid) + 1 else intm(stid)
| stid insetdom intm} in let dpre = MakeQuotedApply(nid,dfdsig(nid),intm',maxm,
<POST>, <PRE>),
dpost = MakeQuotedApply(nid,dfdsig(nid),intm',maxm,
<POST>,<POST>) in iflen piseq=1 then mk_BinaryExpr(dpre,<AND>,dpost) elselet pred=mk_BinaryExpr(dpre,<AND>,dpost) in
mk_BinaryExpr(pred,<AND>,MakePostForEO(tl piseq,dfdsig,
intm',maxm)) prelet nid= hd piseq in
nid insetdom dfdsig and
pre_MakeQuotedApply(nid,dfdsig(nid),intm,maxm,
<POST>,<PRE>) and
pre_MakeQuotedApply(nid,dfdsig(nid),intm,maxm,
<POST>,<POST>);
\end{vdm_al}
The function $MakeExistsBind$ is used by both $MakePreExpr$ and
$MakeInExpr$ for the construction of a multiple type binding (tobe used in an
existential quantification), and a correspondingly updated collection of intermediate statevalues.
%The intermediate state
%values are first updated ($intm$) by increasing each state component in $st$
%(which has write access) by one, if it isnot equivalent to the last
%one.
Two lists ($outl$ and $stl$) of pairs (the variable name and
its type) are created in which the intermediate statevalues
(collected in $intm$) are taken into account. Then a multiple type binding with these two lists is returned.
\begin{vdm_al}
MakeExistsBind: setof FlowId * State * IntM * IntM *
(<PRE>|<POST>) -> MultTypeBind
MakeExistsBind(fs,dst,intm,maxm,c) == let outl = MakeTypeBindList(fs),
stl = [let mk_(s,-)=i,
p = MakePatternIds(s,intm(s)+1,maxm(s),c) in
mk_TypeBind(p,StateTypeConf(s))
|i inseq dst
& let mk_(-,m)=i in m=<READWRITE>] in
mk_MultTypeBind(outl^stl) preforall mk_(s,<READWRITE>) inseq dst&
s insetdom intm and s insetdom maxm;
\end{vdm_al}
The function $ExecutionOrders$ generates a setof `possible
execution orders'. An
execution order is a sequence of $ProcId$s. The order of $ProcId$s in such
an execution order is a valid order in which the data transformers in a
DFD with topology $dfdtopo$ can be executed.
\begin{vdm_al}
ExecutionOrders: DFDTopo -> setofseq1of ProcId
ExecutionOrders(dfdtopo) == let top={mk_(fid,tid)
|mk_(fid,tid) insetrng dfdtopo &
(is_DFDId(fid) or is_MSId(fid) or (fid = nil)) and
(is_DFDId(tid) or is_MSId(tid) or (tid = nil))},
top2={mk_(fid,tid)|mk_(fid,tid) insetrng dfdtopo &
(is_DFDId(fid) or is_MSId(fid)) and
(is_DFDId(tid) or is_MSId(tid))} in let piset= dunion {{pi_1,pi_2}
|mk_(pi_1,pi_2) inset top}\{nil} in
{piseq | piseq inset PossibleSeqs(piset) & forall i,j insetinds piseq &
j<i => (piseq(j) notinset
TransClosure(piseq(i),top2,{}))};
\end{vdm_al}
$MakeQuotedApply$ generates the application of the quotation of
a preor a post-condition of an operation. Note that the configuration
function $StateVarIntConf$ is given information about where it is
quoted from. The necessity for this was shown in example~2.
\begin{vdm_al}
MakeQuotedApply: (DFDId|MSId) * Signature * IntM * IntM *
(<PRE>|<POST>) * (<PRE>|<POST>) -> Apply
MakeQuotedApply(id,mk_(din,out,dst),intm,maxm,c,c2) == let inarg = [FlowIdVarConf(i)|i inseq din],
oldstarg = [let mk_(s,m)=i in if m=<READ> then StateVarIntConf(s,intm(s),
maxm(s),c) else StateVarIntConf(s,intm(s) - 1,
maxm(s),c)
|i inseq dst],
outarg = [FlowIdVarConf(i)|i inseq out],
starg = [let mk_(s,-)=i in
StateVarIntConf(s,intm(s),maxm(s),c)
|i inseq dst & let mk_(-,m)=i in m=<READWRITE>] in if c2=<PRE> then mk_Apply("pre_"^OpIdConf(id),inarg^oldstarg) else mk_Apply("post_"^OpIdConf(id),inarg^oldstarg^
outarg^starg) preforall mk_(s,m) inseq dst&
s insetdom intm and
s insetdom maxm and
m=<READWRITE> => intm(s)>0;
\end{vdm_al}
\subsection{Functionsfor composing data transformers explicitly}
The explicit definitionsof operationsfor composing data transformers in a DFD are generated
following the same dependency strategy which is used for
generating the implicit definitions. The principle for combining the
data transformers uses the same dependency information from the
DFD. However, since the stateof the DFD isnot explicitly mentioned in the call of an operation, there is no problem with intermediate statevaluesfor the explicit definitions. Thus, the explicit definitions will in general be shorter and easier to read than the
implicit ones. The different execution orders are dealt withby using the
non-deterministic statement\footnote{VDM-SL has a non-deterministic statement which
takes a setof statements and executes each of them them in a
non-deterministic order.}. In this way the choice of execution order is left open.
\subsubsection*{Example 4}
Before presenting the formal description of how \DFD s as a whole can be
transformed into explicit operation definitions, we show how
the \DFDs\ from the first three examples can be described explicitly.
The first \DFD\ from figure~\ref{example1} can bespecifiedby the following explicit
operation definition:
%\begin{vdm}
%\begin{op}[e]{PQ}
%\signature{A \Oto C}
%\parms{a}
%\Def b = P(a)
%\Din
%\Def c = Q(b)
%\Din
%\return{c}
%\end{op}
%\end{vdm}
\begin{lstlisting}
PQ: A ==> C
PQ(a) == def b = P(a) in def c = Q(b) in return c
\end{lstlisting}
\noindent
Def-statements\footnote{A def-statement corresponds to a let-statement
(or let-expression) except that it is legal at the right-hand-side of
the equal sign to use an operation call that may modify the state.}
are used to introduce the (intermediate) data flows.
For the \DFD\ in figure~\ref{example2} the following explicit operation can be
generated:
%\begin{vdm}
%\begin{op}[e]{PQ_{DS}}
%\signature{A \Oto C}
%\parms{a}
%\Def b = P(a)
%\Din
%\Def c = Q(b)
%\Din
%\return{c}
%\end{op}
%\end{vdm}
\begin{lstlisting}
PQ_DS: A ==> C
PQ_DS(a) == def b = P(a) in def c = Q(b) in return c
\end{lstlisting}
\noindent
This operation is equivalent to the one generated for the \DFD\ in example~\ref{example1}, because
the state components that are modified by the different
operation need notbe explicitly mentioned in the call of these operations. In this respect, explicit operationsin \VDMSL\ are very much similar to procedures in imperative programming languages accessing global variables.
The following explicit operation can be generated for the \DFD\ in figure~\ref{example3}:
\begin{lstlisting}
PQR_DS: A * D ==> C * E
PQR_DS(a,d) ==
||
((def b = P(a) in def c = Q(b) in def e = R(d) in return mk_(c,e)),
(def e = R(d) in def b = P(a) in def c = Q(b) in return mk_(c,e)),
(def b = P(a) in def e = R(d) in def c = Q(b) in return mk_(c,e))
)
\end{lstlisting}
%\begin{vdm}
%\begin{op}[e]{PQR_{DS}}
%\signature{A \X D \Oto C \X E}
%\parms{a, d}
%\begin{nondetstmt}
%\lineup[c]{(}
%{\Def b = P(a)
%\Din
%\Def c = Q(b)
%\Din
%\Def e = R(d)
%\Din
%\return{mk-(c, e)}),}\\
%\lineup[c]{(}
%{\Def e = R(d)
%\Din
%\Def b = P(a)
%\Din
%\Def c = Q(b)
%\Din
%\return{mk-(c, e)}),}\\
%\lineup[c]{(}
%{\Def b = P(a)
%\Din
%\Def e = R(d)
%\Din
%\Def c = Q(b)
%\Din
%\return{mk-(c, e)})}
%\end{nondetstmt}
%\end{op}
%\end{vdm}
\noindent
The three different execution orders
are incorporated in a
non-deterministic statement. It is necessary to use a return
statement at the endof each sequence statement in the
nondeterministic statement (each represents a possible execution order) to ensure that a correct return value is created.
\noindent $\Box$\\
The function used to create operationsfor \DFD s in
the explicit style is called $MakeDFDExplOp$.
The strategy is somewhat similar to the one that
has been used for the implicit style. Here we also have a number of
possible execution orders that must be taken into account.
\begin{vdm_al}
MakeDFDExplOp : DFDId * DFDTopo * DFDSig -> ExplOp
MakeDFDExplOp(dfdid,dfdtopo,dfdsig) == let mk_(din,-,-) = dfdsig(dfdid),
eos = ExecutionOrders(dfdtopo),
intm = {stid |-> 0
| mk_(stid,-) inset
CollectStIds( rng dfdsig)},
maxm = {stid |-> Reduce(NoOfWr(rng dfdsig,stid))
|mk_(stid,-) inset CollectStIds(rng dfdsig)} in let optype = MakeOpType(dfdsig(dfdid)),
parms = [mk_PatternId(FlowIdVarConf(i))
|i inseq din],
bodys = {MakeStmtForEO(piseq,dfdid,dfdsig)
|piseq inset eos},
dpre = MakePreExpr(dfdid,dfdtopo,dfdsig,intm,maxm) in let body = MakeNonDetStmt(bodys) in
mk_ExplOp(OpIdConf(dfdid),optype,parms,body,dpre) pre dfdid insetdom dfdsig and let intm = {stid |-> 0
|mk_(stid,-) inset CollectStIds(rng dfdsig)},
maxm = {stid |-> Reduce(NoOfWr(rng dfdsig,stid))
|mk_(stid,-) inset CollectStIds(rng dfdsig)} in
pre_MakePreExpr(dfdid,dfdtopo,dfdsig,intm,maxm) and forall piseq inset ExecutionOrders(dfdtopo)&
pre_MakeStmtForEO(piseq,dfdid,dfdsig);
\end{vdm_al}
The function $MakeExplOpBody$ is defined recursively. In each
recursion step one
data transformer is processed until all data transformers
(collected in $pids$) in the given partition $p$ have been
incorporated. The strategy is the same asfor $MakePreExpr$ and
$MakePostExpr$ where a new (independent) data transformer ($nid$) is
chosen. The function $MakeCallAndPat$ creates
a call of the operation for the given
data transformer and the corresponding pattern which the call must be matched against. If the operation returns a value, the call is used in a define statement. Otherwise it is a call statement
which must be included as a part of a sequence of statements.
\begin{vdm_al}
MakeStmtForEO: seq1of ProcId * DFDId * DFDSig -> Stmt
MakeStmtForEO(piseq,dfdid,dfdsig) == let nid= hd piseq in let mk_(call,pat) = MakeCallAndPat(nid,dfdsig(nid)),
kind = FindKind(dfdsig(nid)) in iflen piseq=1 thenlet mk_(-,out,-)=dfdsig(dfdid) in let ret=mk_Return(MakeResult(out)) in if kind=<OPRES> then mk_DefStmt(pat,call,ret) else mk_Sequence([call,ret]) elselet rest=MakeStmtForEO( tl piseq,dfdid,dfdsig) in if kind=<OPRES> then mk_DefStmt(pat,call,rest) elseif is_Sequence(rest) thenlet mk_Sequence(sl)=rest in
mk_Sequence([call]^sl) else mk_Sequence([call,rest]) prehd piseq insetdom dfdsig;
MakePattern : seqof Id -> [Pattern]
MakePattern(idl) == caseslen idl:
0 -> nil ,
1 -> mk_PatternId( hd idl), others -> mk_TuplePattern([mk_PatternId(i) | i inseq idl]) end;
MakeResult : seq1of Id -> Expr
MakeResult(idl) == iflen idl=1 then FlowIdVarConf( hd idl) else mk_TupleConstructor([FlowIdVarConf(i) | i inseq idl]);
\end{vdm_al}
\subsection{General Auxiliary Functions}
The function $DBinOp$ generates an expression by distributing a binary operator
over a setof expressions.
\begin{vdm_al}
DBinOp : BinaryOp * set1 of Expr -> Expr
DBinOp(op,es) == let e inset es in ifcard es=1 then e else mk_BinaryExpr(e,op,DBinOp(op, es \ {e}));
\end{vdm_al}
The function $CollectExtDFs$ is intended to collect the external data
flow identifiers from the topology of a DFD.
\begin{vdm_al}
CollectExtDFs : DFDTopo -> setof FlowId
CollectExtDFs(dfdtopo) ==
{fid|fid insetdom dfdtopo
& let mk_(pid_1,pid_2)=dfdtopo(fid) in
is_EPId(pid_1) or is_EPId(pid_2)};
NeedsQuant: DFDTopo * DFDSig * setof FlowId * setof ProcId -> setof FlowId
NeedsQuant(dfdtopo,dfdsig,notneeded,pids) == let top={mk_(fid,tid)|mk_(fid,tid) insetrng dfdtopo &
(is_DFDId(fid) or is_MSId(fid)) and
(is_DFDId(tid) or is_MSId(tid))} in ifdom dfdsig=pids then {} elselet pid insetdom dfdsig \ pids in if TransClosure(pid,top,{})={} and
EquivClass(top,{pid})= dom dfdsig then NeedsQuant(dfdtopo,dfdsig,notneeded,
pids union {pid}) elselet mk_(-,out,-)=dfdsig(pid) in
NeedsQuant(dfdtopo,dfdsig,notneeded,
pids union {pid}) union elems out \ notneeded;
\end{vdm_al}
The function $QuantNec$ is responsible for determining whether it is
necessary to use an existential quantification at a given place in a
post-condition.
\begin{vdm_al}
QuantNec: seqof FlowId * State * setof FlowId *
IntM * IntM -> bool
QuantNec(out,dst,fids,intm,maxm) ==
fids <> {} or -- (exists id in seq out& id in set fids) or
(exists mk_(s,m) inseq dst&
m=<READWRITE> and intm(s)<maxm(s)) preforall mk_(s,-) inseq dst&
s insetdom intm and s insetdom maxm;
MakeTypeBindList : setof FlowId -> seqof TypeBind
MakeTypeBindList(fids) == if fids={} then [] elselet fid inset fids in let pat = [mk_PatternId(FlowIdVarConf(fid))],
first=mk_TypeBind(pat,FlowIdTypeConf(fid)) in
[first]^MakeTypeBindList(fids \ {fid}) measure CardFId;
MakePatternIds: (Id | DSId) * nat * nat *
(<PRE>|<POST>) -> seqof PatternId
MakePatternIds(id, n, max, c) == if (n = max) and (c = <POST>) then [mk_PatternId(StateVarConf(id))] elsecases n:
0 -> if c = <PRE> then [mk_PatternId(StateVarConf(id))] else [mk_PatternId(StateOldVarConf(id))], others -> MakePatternSeq(StateVarConf(id), n, max) end;
MakePatternSeq: Id * nat * nat -> seqof PatternId
MakePatternSeq(id, n, max) == if n = max then [mk_PatternId(id ^ "'")] else [mk_PatternId(id ^ "'")] ^
MakePatternSeq(id ^ "'", n+1, max) pre n <= max measure TowardsMax;
TowardsMax: Id * nat * nat -> nat
TowardsMax(-,n,max) ==
max - n;
\end{vdm_al}
The function $EquivClass$ collects all data transformers from a
topology which are connected in an equivalence class.
\begin{vdm_al}
EquivClass: setof (ProcId * ProcId) * setof (MSId|DFDId) -> setof (MSId|DFDId)
EquivClass(top,ids) == ifexists mk_(fid,tid) inset top&
(fid inset ids and tid notinset ids) or
(tid inset ids and fid notinset ids) thenlet mk_(fid,tid) inset top best
(fid inset ids and tid notinset ids) or
(tid inset ids and fid notinset ids) in
EquivClass(top,ids union {fid,tid}) else ids;
\end{vdm_al}
$MakeNonDetStmt$ takes a setof statements, and
generate a non-deterministic statement from them if there is more than
one partition.
\begin{vdm_al}
MakeNonDetStmt : setof Stmt -> Stmt
MakeNonDetStmt(stmts) == casescard stmts:
1 -> let {s}=stmts in s, others -> mk_NonDetStmt(stmts) end precard stmts<>0;
\end{vdm_al}
The function $CollectStIds$ collects allstate
component identifiers from a DFD.
The purpose of $NoOfWr$ isto determine how many data transformers there
are in a given partition that have write access to a given state component.
This information is used to deal with the intermediate statevalues.
\begin{vdm_al}
NoOfWr: setof Signature * StId -> nat
NoOfWr(sigs,stid) == if sigs={} then 0 elselet sig inset sigs in let mk_(-,-,dst)=sig in if mk_(stid,<READWRITE>) insetelems dst then 1+NoOfWr(sigs \ {sig},stid) else NoOfWr(sigs \ {sig},stid);
Reduce: nat -> nat
Reduce(n) == if (n = 0) or (n = 1) then n else n - 1;
\end{vdm_al}
\subsection{Configuration functions}
\begin{vdm_al}
ModIdConf : DFDId -> Id
ModIdConf(mk_DFDId(id)) ==
id^"Module";
StateIdConf : DFDId -> Id
StateIdConf(mk_DFDId(id)) ==
id^"State";
DSIdConf : DSId -> Id
DSIdConf(mk_DSId(id)) ==
id;
OpIdConf : MSId | DFDId | Id -> Id
OpIdConf(id) == cases id:
mk_MSId(id'),
mk_DFDId(id') -> id', others -> id end;
\end{vdm_al}
The $StateVarIntConf$ function needs to know whether a state component is being referred toin a pre-condition orin a
post-condition of an operation. This is caused by the fact that the state before the call of the operation is denoted differently in a
pre-condition than in a post-condition (in a pre-condition $v$ means
the state before calling the operation, while that is denoted by $~v$ in a post-condition).
\begin{vdm_al}
StateVarIntConf: (Id | DSId) * nat * nat * (<PRE>|<POST>)
-> Id
StateVarIntConf(id,n,max,c) == if (max=n) and (c=<POST>) then StateVarConf(id) elsecases n:
0 -> if c=<PRE> then StateVarConf(id) else StateOldVarConf(id),
1 -> StateVarConf(id)^"'", others -> StateVarIntConf(id,n - 1,max,c)^"'" end;
VarConf : StId -> Id
VarConf(id) == if is_DSId(id) then StateVarConf(id) else FlowIdVarConf(id);
TypeConf : DSId|FlowId -> Id
TypeConf(id) == if is_DSId(id) then StateTypeConf(id) else FlowIdTypeConf(id);
FlowIdVarConf : Id -> Id
FlowIdVarConf(id) ==
ToLower(id);
FlowIdTypeConf : Id -> Id
FlowIdTypeConf(id) ==
ToUpper(id);
StateTypeConf : Id | DSId -> Id
StateTypeConf(id) ==
ToUpper(id);
StateVarConf : Id | DSId -> Id
StateVarConf(id) ==
ToLower(id);
StateOldVarConf : Id | DSId -> Id
StateOldVarConf(id) ==
ToLower(id)^"old";
TypeModConf : () -> Id
TypeModConf() == "TypeModule";
ResultIdConf : () -> Id
ResultIdConf() == "r";
PossibleSeqs: setof ProcId -> setofseqof ProcId
PossibleSeqs(pids) == if pids = {} then {} elseifcard pids = 1 then {[pid]| pid inset pids} elselet pid inset pids in let rest = PossibleSeqs(pids \ {pid}) in dunion {InsertPId(pid, seq')
| seq' in set rest} measure CardPSet;
ToLower: Id | DSId | DFDId | EPId | MSId -> Id
ToLower(id) == let realid = cases id:
mk_DSId(id'),
mk_DFDId(id'),
mk_EPId(id'),
mk_MSId(id') -> id', others -> id end in
[LowerChar(i) | i inseq realid];
\end{vdm_al}
The auxiliary functions ($ToLower$ and $ToUpper$) are to change all letters to lower-case and upper-case letters respectively.
ToUpper: Id | DSId | DFDId | EPId | MSId -> Id
ToUpper(id) == let realid = cases id:
mk_DSId(id'),
mk_DFDId(id'),
mk_EPId(id'),
mk_MSId(id') -> id', others -> id end in
[UpperChar(i) | i inseq realid];
In this paper we have defined a semantics for \DFDs\ by formally
specifying a transformation from \DFDs\ to \VDM\ specifications. In this
section we give a brief overview of related work in the area of
defining semantics for \DFDs, and we conclude with some
observations on our work and some ideas for further research.
\subsection{Related work}
When \DFDs\ were originally introduced, they were presented as a
graphical notation. The intended semantics of this notation
was defined verbally, but the need for a formal base is now more
commonly recognized, see e.g.~\cite{Hofstede&92}.
Work has been done on formalizing \DFDs, with the intention of either disambiguating their meaning, orof using
the formal semantics as a base for a combined formal/structured method.
In \cite{Randell90} a translation back and forth between \DFDs\ and Z specifications is described.
\cite{Alabiso88} contains an explanation of how \DFDs\ can manually be transformed into an object-oriented design. The paper touches
upon some problematic issues arising in a transformation from \DFDs. In \cite{Semmens&91c} a small example of how a \DFD\ can be transformed in Z is presented. However, no formal semantics of the \DFDs\ is
presented and it isnot clear to what extent the transformation can be automated. In \cite{Bruza&89} some guidelines for how semantics can be attached to \DFDs\ are given. It is sketched how \DFDs\ can be transformed into a
Petri net variant combined with path expressions. In \cite{Elmstrom&93} a complete semantics is provided for the Ward and Mellor version of SA/RT by means of high-level timed Petri nets.
Here an executable subsetof VDM-SL is also used to describe the
mini-specifications of an SA/RT model. In \cite{Adler88} a semantic base for guiding the decomposition
process in the construction of a hierarchy of \DFDs\ is presented. This
work is based on graph theory in an algebraic setting.
Kevin Jones uses \VDM\ to provide a denotational style semantics of a
non-conventional machine architecture (The Manchester DataFlow Machine)
based on data flow graphs \cite{Jones87e}. In \cite{Fraser&91} a rule-based approach for transforming \SA\ products
into \VDM\ specifications is presented. Their \VDM\ specifications are
very explicit and hard to read, mainly because of the way
decision tables have been taken into account.
Polack concentrate on the methodological aspects of combining
\SA\ notations and {\small Z}\ specifications \cite{Polack92}, the resulting
combination is known as {\small SAZ}.
Tse and Pong use extended Petri nets for formalizing \DFDs\ \cite{Tse&89}.
France discusses an algebraic approach to modeling control-extended
DFDs in~\cite{France92}. In~\cite{Semmens&92a} an overview of several approaches to combining \SA\
techniques and notations with formal methods (including our approach) is
given.
The main resultof the work presented in this paper with respect to
other work in this area is that we have been able to capture the semantics of
a \DFD\ as a whole in a compositional way at a high level of
abstraction, taking into account the whole hierarchy of \DFDs\ that is
created during an \SA\ development, which to our knowledge
has not been done before.
\subsection{Status and Perspectives}
With respect to the semantics of \DFDs\ in terms of a formal
transformation to \VDM\ specifications the following observations can be made:
\begin{itemize}
\item
An unambiguous interpretation of \DFDs\ is available, which -- due to the particular transformation chosen -- is abstract.
Consequently, there are few restrictions on the further development of the \DFD\ into a software design.
\item
The transformation is executable, which opens up possibilities for
automatically generating \VDM\ specifications from \DFDs. In this way,
the initial effort needed to produce a formal specification is
significantly decreased.
\item
The \DFDs\ and their \VDM\ counterparts can be regarded as equivalent
views on the system, using different representations.
\end{itemize}
A few restrictions apply to our transformation, however.
One of these is the exclusion of concurrent systems,
whereas some \SA\ extensions provide facilities for specifying such
systems.
We briefly mentioned how some of the \DFD\ constructs would be interpreted if we had taken concurrency into account.
A transformation from a real-time \SA\ variant to a combination of \VDM\ and
e.g. CCS \cite{Milner80}, CSP \cite{Hoare85} or Petri nets \cite{Peterson77}
would be an interesting area for future research. We foresee that
the main problem in automatically providing a concurrent specification
description would be that such a description would have a very low level of
abstraction.
Intuitively it would be expected that each data transformer is transformed into
a {\em process} and that all these processes are executed in parallel. This
would resultin a large number of processes due to the number of data
transformers usually present in a \DFD.
Concerns might also arise with respect to the size of the classof \DFDs\
having no cyclic internal data flows and obeying the one-to-one mapping from input valuesto output values. In our experience, cyclic data flows are often used to
model error situations which could also have been modeled by means ofstate
components in data stores. Therefore, most \DFDs\ with such cyclic
structures can be rewritten using only acyclic structures, and
therefore we believe that this restriction isnot very important. With respect to the restriction to one-to-one mappings between input valuesand output values, we can say that usually the need for other
mappings only occurs when \DFDs\ are used as a design notation, but not
when they are used as an (abstract) specification notation. Therefore,
this restriction cannot be considered very important in our situation.
\newpage
\bibliographystyle{nnewalpha}
%% note: retrieve dan.bib fromhttps://github.com/overturetool/overture/tree/development/documentation/bib
\bibliography{dan}
\appendix
\newpage
\section{Abstract Syntax for Structured Analysis}
The version of Structured Analysis considered in this \documenttype\
consists of a hierarchy of data
flow diagrams ($HDFD$), a data dictionary ($DD$), and a collection of
uniquely identified mini-specifications ($MSs$). The typesofall data flows in the data flow diagrams must be defined in the data
dictionary. In addition to this,
the signature of the top-level DFD must conform to
its topology.
\begin{vdm_al} types
SA = HDFD * DD * MSs inv mk_(hdfd,dd,-) ==
FlowTypeDefined(hdfd,dd) and TopLevelSigOK(hdfd);
\end{vdm_al}
The hierarchy of data flow diagrams is defined recursively. Each
$HDFD$ has a name, an unordered collection of data stores used in
the DFD, a description of its topology, a collection of
uniquely identified data transformers (``bubbles")
which are further decomposed as $HDFD$s, and a description of the signatures ofall the data transformers.
The invariant for $HDFD$ ensures that the signatures of the data
transformers (and the DFD as a whole) are consistent with the topology and the data stores, and that all the DFDs which are further
decomposed are described.
The topology of a DFD is a collection of uniquely identified data
flows. Each data flow is directed from a data transformer to another
data transformer. The data transformers can either be further decomposed
($DFDId$) or they can be primitive ($MSId$). An external process
($EPId$) is identified by its name.
At lower level DFDs where the data flow goes to (or comes from) another data transformer which is outside the DFD the name is
omitted (the value {\textbf{\ttfamily nil}} is used).
The invariant requires that the topology of the
internal connections is acyclic.
\begin{vdm_al}
DFDTopo = map FlowId to ([ProcId] * [ProcId]) inv dfdtopo == let top={mk_(fid,tid)
|mk_(fid,tid) insetrng dfdtopo
& (is_DFDId(fid) or is_MSId(fid)) and
(is_DFDId(tid) or is_MSId(tid))} in
NotRecursive(top) and forall flowid insetdom dfdtopo &
FlowConnectOK(dfdtopo(flowid));
FlowId = seqofchar;
ProcId = DFDId|MSId|EPId;
DFDMap = map DFDId to HDFD;
DFDSig = map (DFDId|MSId) to Signature;
\end{vdm_al}
A signature for a description of a data transformer consists of
input, output, andstate information. If a data transformer does not
have any connection to a state component which it is changing, it must produce
some output value instead.
\begin{vdm_al}
Signature = Input * Output * State inv mk_(-,out,sta) ==
(sta=[]) => (out<>[]) and
(out=[]) => (exists mk_(-,m) inseq sta & m=<READWRITE>);
Input = seqof FlowId;
Output = seqof FlowId;
\end{vdm_al}
The {\em State} part of a signature is a sequence of pairs ofstate
variable identifiers (either data store identifiers or
data flows between the systemand the external
processes) and the modes in which they are accessed.
The data flows between the external processes and
the specifiedsystem are treated asstate components.
Therefore, the top-level
operation specifying the whole system contains no input or output
(checked by $TopLevelSigOK$). All data flows must be present in the state component being either read or
write components, depending upon whether they are ingoing or outgoing data
flows.
\begin{vdm_al}
TopLevelSigOK: HDFD -> bool
TopLevelSigOK(mk_(sysid,-,dfdtop,-,dfdsig)) ==
sysid insetdom dfdsig and let mk_(din,out,dst)=dfdsig(sysid) in
din=[] and out=[] and forall flowid insetdom dfdtop& let mk_(fid,tid)=dfdtop(flowid) in
(is_EPId(fid) =>
mk_(flowid,<READ>) insetelems dst) and
(is_EPId(tid) =>
mk_(flowid,<READWRITE>) insetelems dst);
\end{vdm_al}
In order for the signature mapping tobe consistent it is necessary to ensure that all data stores are connected to data transformers,
that all signatures reflect the information about flows from the
topology, that all identifiers mentioned in the signatures are
available, and finally that signatures are provided forall data
transformers used in the DFD (checked by $DFDSigConsistent$ and its
auxiliary functions).
\begin{vdm_al}
DFDSigConsistent: DFDId * DFDTopo * DSs * DFDMap * DFDSig
-> bool
DFDSigConsistent(id,dfdtop,dss,dfdmap,dfdsig) ==
DSConnected(dss,dfdsig) and
SigsAllRight(dfdtop,dfdsig) and
IdsInSigsAvail(dss,dfdtop, rng dfdsig) and
SigsForAllUsedIds(id, rng dfdtop,dfdmap,dfdsig);
DSConnected : DSs * DFDSig -> bool
DSConnected(dss,dfdsig) == forall dsid inset dss& exists mk_(-,-,dst) insetrng dfdsig& exists i inseq dst& let mk_(id,-)=i in
dsid=id;
SigsAllRight : DFDTopo * DFDSig -> bool
SigsAllRight(dfdtop,dfdsig) == forall flowid insetdom dfdtop & cases dfdtop(flowid):
mk_(id,mk_EPId(-)) -> let mk_(-,-,dst)=dfdsig(id) in
mk_(flowid,<READWRITE>) inset elems dst,
mk_(mk_EPId(-),id) -> let mk_(-,-,dst)=dfdsig(id) in
mk_(flowid,<READ>) inset elems dst,
mk_(nil, id) -> let mk_(din,-,-) = dfdsig(id) in
flowid insetelems din,
mk_(id, nil) -> let mk_(-,out,-) = dfdsig(id) in
flowid insetelems out,
mk_(fid,tid) -> let mk_(-,out,-) = dfdsig(fid),
mk_(din,-,-) = dfdsig(tid) in
(flowid insetelems out) and
(flowid insetelems din) end;
IdsInSigsAvail : DSs * DFDTopo * setof Signature -> bool
IdsInSigsAvail(dss,dfdtop,sigs) == let fids=CollectExtDFs(dfdtop) in forall mk_(din,out,dst) inset sigs& elems din subsetdom dfdtop and elems out subsetdom dfdtop and elems dst subset {mk_(id,m)
|id inset dss union fids,
m inset {<READ>,<READWRITE>}};
LowerLevelUsed : DFDTopo * DFDMap -> bool
LowerLevelUsed(dfdtop,dfdmap) == let ids = dom dfdmap in forall mk_(fid,tid) insetrng dfdtop &
(is_DFDId(fid) => fid inset ids) and
(is_DFDId(tid) => tid inset ids);
SigsForAllUsedIds: DFDId * setof ([ProcId] * [ProcId]) *
DFDMap * DFDSig -> bool
SigsForAllUsedIds(id,top,dfdmap,dfdsig) ==
(forall dfdid insetdom dfdmap& let mk_(-,-,-,-,dfdsig')=dfdmap(dfdid) in
dfdsig'(dfdid)=dfdsig(dfdid)) and let sigs= dom dfdsig in
id inset sigs and-- dfds subset sigs and forall mk_(fid,tid) inset top&
((is_MSId(fid) or is_DFDId(fid)) =>
(fid inset sigs)) and
((is_MSId(tid) or is_DFDId(tid)) =>
(tid inset sigs));
FlowConnectOK : ([ProcId] * [ProcId]) -> bool
FlowConnectOK(mk_(fid,tid)) ==
((is_EPId(fid) or fid=nil ) =>
(is_DFDId(tid) or is_MSId(tid))) and
((is_EPId(tid) or tid=nil ) =>
(is_DFDId(fid) or is_MSId(fid)));
TransClosure: (DFDId|MSId) * setof ((DFDId|MSId) *
(DFDId|MSId)) * setof (DFDId|MSId) -> setof (DFDId|MSId)
TransClosure(pid,top,dset) == ifexists mk_(fromid,toid) inset top&
((fromid=pid) or (fromid inset dset)) and
(toid notinset dset) thenlet mk_(fromid,toid) inset top best
((fromid=pid) or (fromid inset dset)) and
(toid notinset dset) in TransClosure(pid,top,dset union {toid}) else dset
\end{vdm_al}
\newpage
\section{The Abstract Syntax for VDM-SL}
In this appendix we provide an abstract syntax for the part of VDM-SL
which we actually use in the definition of the formal semantics of
DFDs. The abstract syntax for the structuring part is an extension to
the abstract syntax from the VDM-SL standard because structuring is notyet a part of the standard. However, this abstract syntax
correspond closely to a part of the abstract syntax used in the IFAD
VDM-SL language. The abstract syntax for the flat language is simply a subsetof the one used in the VDM-SL standard. None of the subsections
below are annotated because this is done elsewhere already.
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.