Generating an Implementation of a Parallel Programming Language from a Formal Semantic Definition

Generating an Implementation of a Parallel Programming Language from a Formal Semantic Definition M.J. OUDSHOORN, K.J. RANSOM and C.D. MARLIN Departme...
0 downloads 2 Views 127KB Size
Generating an Implementation of a Parallel Programming Language from a Formal Semantic Definition M.J. OUDSHOORN, K.J. RANSOM and C.D. MARLIN Department of Computer Science, University of Adelaide, G.P.O. Box 498, Adelaide, S.A. 5001, Australia

ABSTRACT ATLANTIS is a tool for the semi-automatic generation of interpretive language implementations from formal semantic definitions. This tool was originally designed to facilitate the implementation of sequential programming languages and the present paper describes how it has been adapted to also generate implementations of parallel programming languages. ATLANTIS is founded on a layered, information structure model and this paper examines the changes introduced into each layer of the model, focussing on the structure of the generated parse tree and the nature of the parallel interpreter generated from the programming language definition.

1 . Introduction As the demand for more reliable software increases, so too does the need for formal definitions of programming languages. The difficulty experienced by language designers is that existing formalisms offer little assistance in the language design and implementation process. In fact, formalisms such as attribute grammars,9,21 denotational semantics5,19,20 and the Vienna Development Method (VDM)1,8 are ignored by the designers of the programming languages in wide use. This means that a formal description of the language semantics is often produced after the programming language has been defined in natural language, and already implemented. ATLANTIS, A Tool for LANguage definiTion and Interpreter Synthesis,15,16 provides language designers with a tool which aids in the design process by checking that the semantics are defined and used in a consistent manner, as well as automatically generating a language prototype from the language definition. This allows language designers to experiment with new features and observe the interactions between the new features and pre-existing ones. Furthermore, ATLANTIS produces an interpreter which exactly mimics the language definition and hence provides designers with the opportunity to “execute” the semantic definition of the programming language and verify that the observed semantics (and, hence, the actual semantics given in the definition) are in fact the intended semantics. ATLANTIS is a multi-layer, multi-pass system in which the programming language semantics are defined operationally by way of transformations on information structures.13,15 The model is founded on the use of algebraically specified abstract data types (ADT's)3,4,6 which are used to construct the information structures which reflect the state of an executing program. Surrounding the ADT definitions is a layer of high level operations (HLO's) which provides a shorthand notation for complex ADT operations, or some common sequence of ADT manipulations. Finally, the semantic actions used in the

language definition are provided. These semantic actions invoke the HLO's to manipulate the underlying information structures. The layering employed in an ATLANTIS definition of a programming language is shown in Figure 1.

ADT's

SDA's

HLO's

Semantic Actions

Figure 1. The structure of the underlying information structure model. In addition to providing multiple layers for the definition of programming languages, an ATLANTIS definition is potentially multi-pass. This allows the information structures to be built up through multiple passes over the source code of a program written in the new language, permitting the description of language features in a more natural and precise manner than would otherwise be possible. In particular, languages such as Modula-2,22 which were designed to be compiled by multi-pass compilers, can be defined more naturally through a multi-pass definition. The model described above is adequate for the definition of sequential programming languages,14,15,16 but has certain shortcomings when defining the semantics of parallel programming languages. The principal problem is that ADT definitions assume sequential access. A technique initially developed by Mallgren,11,12 and later refined by Freidel,2 Marlin13 and Oudshoorn et al,17 known as shared data abstractions (SDA's) protects the data structures within a concurrent environment and ensures that data integrity is preserved. SDA’s are considered in more detail in Section 2. Migrating from a sequential to a parallel environment also affects the nature of the high level operations. Additional constructs are added for use within this layer and the description of semantic actions to provide greater expressiveness and to cater for the special needs of parallel language definitions. Section 3 briefly describes these additional constructs. Section 4 examines the changes made to ATLANTIS definitions to describe the manner in which they may now be executed: sequentially, or in parallel with other semantic actions. Section 5 outlines the generated interpreter based on the revised ATLANTIS definition for parallel languages. This section constitutes the focus of the present paper, examining the annotated parse tree on which the interpreter works, and the way in which the interpreter behaves in order to follow precisely the semantic definition of the parallel programming language. Finally, some conclusions are presented in Section 6. 2 . Shared Data Abstractions ATLANTIS is based on an operational semantic model where the semantics of a programming language is defined in terms of transformations made to information

structures. The formal nature of ATLANTIS rests on the use of the algebraic specification technique for abstract data types3,4,6 to describe the information structures, and allows the language designer to develop clear and unambiguous definitions. ADT's are suitable for use in a sequential environment, but require protection within a parallel environment to ensure that multiple concurrent access does not corrupt the data. Mallgren developed an approach for the definition of shared data abstractions (SDA's) 11,12 which defines the data object and provides synchronisation details informing user processes when they must wait and when they may proceed. Mallgren's approach encapsulates the data object within the SDA definition and hence requires a new SDA definition for each object. Mallgren's approach also has severe practical limitations which make it unsuitable for use within ATLANTIS; these limitations are discussed more fully in Oudshoorn et al.17 It has been necessary to develop an alternative method for ADT protection in a parallel environment for use in ATLANTIS. The approach has been to automatically generate a protective envelope surrounding each ADT. By adhering to the following rules, the integrity of any shared ADT variable declared in a language definition is ensured at the level of a single ADT operation (that is, no two operations performed simultaneously on a data object will be permitted to interfere with each other): • At any given time, there may be at most one thread of control attempting to modify the value of a data object. • Multiple threads of control may simultaneously read the value of a data object, provided that no thread is attempting to modify the data object at that time. These rules ensure that all modifications made to ADT data objects, are performed as atomic operations. However, despite this protection afforded by the SDA layer, it is often necessary to specify further synchronisation (at the algorithmic level) within the HLO and semantic description layers. The generation of the protection mechanism and the details of its behaviour are fully defined in Oudshoorn et al.17 The automatic generation of an ADT protection envelope means that the ADT layer of an ATLANTIS definition remains unaffected in moving from sequential language definition to parallel language definition. The ADT protection envelopes, or SDA layer, is completely transparent to the rest of the ATLANTIS system, as it provides protection for ADT objects at the level of individual operations. 3 . High Level Operations The SDA layer introduced above provides for the protection of ADT's at the level of individual operations, but is insufficient to ensure that the data integrity is preserved between two ADT operations. Consider the following example, where two processes share a common stack, as an illustration of the problem. One process may read the value at the top of a stack and then attempt to remove it from the stack. In between the reading of the value at the top of the stack and its removal, the second process may add another value to the stack. The object which is then removed from the stack is the newly added value, not the value most recently read. In this situation, the ADT protection envelope has ensured that the ADT object has been protected as each operation is performed, but it has failed to help capture the intended semantics. To provide assistance in the definition of

parallel features of programming languages, additional constructs are needed for the definition of high level operations and semantic actions. The constructs added to ATLANTIS to enable the definition of parallel languages include the specification of critical regions where only a single process may be active at any one time, a “waituntil” clause which delays a process until some condition holds true, and a “do” clause which provides a nondeterministic choice between several alternatives. These constructs have been used successfully to model the semantics of the Ada rendezvous mechanism.18 The three constructs together provide the necessary expressiveness for the specification of concurrent features of parallel languages, by providing the ability to group statements and calls to ADT operations together and to ensure mutually exclusive execution of these actions. This provides an additional level of protection for the underlying ADT operations, protected individually by the SDA layer. The constructs added to ATLANTIS for the benefit of the definition of parallel programming languages simply map onto code invoked by the implementation prototype generated from the language definition. Hence, it is the generated interpreter which is the focus of interest in extending ATLANTIS from the definition of purely sequential programming languages to the definition of parallel programming languages. 4 . Semantic Actions A language definition submitted to ATLANTIS has three main components: the lexical, syntactic and semantic aspects of the language. The syntactic aspect of a language is specified using EBNF notation, a notation which is widely used and understood within the computer science community. Interspersed throughout the EBNF description of the language syntax are semantic actions, as illustrated in Figure 2 (which shows a fragment of the definition of the syntax and semantics for an expression). Uppercase identifiers are used to represent the nonterminal elements of the language. Semantic regions in the definition are delimited by “%%” and, in the example shown, constitute calls to a single action in each case referring to a routine within the HLO layer. For languages involving more than a single pass, a declaration of the form “Pass n:” may appear in a semantic region, indicating that the specified actions are performed solely during the nth pass.Omission of such a declaration implies that the specified actions are performed on the first pass only. EXPR : TERM | EXPR plus_sym EXPR | EXPR minus_sym EXPR ;

%% Pass 1: call add_results; %% %% Pass 1: call subtract_results; %%

Figure 2. A fragment of the definition of an expression. The dynamic semantics of a programming language is defined in the final pass over the language definition. As with the static semantics, the dynamic semantics is specified in terms of transformations applied to the underlying information structures. When defining the semantics of a sequential programming language, it is assumed that such execution is performed by a single thread of control. However, when defining parallel programming

languages, it is necessary to specify the existence of additional threads of control, in order to model the semantics of constructs requiring concurrent execution. In order to support the definition of parallel programming languages, ATLANTIS allows the language designer to specify the stages of program execution at which additional threads of execution control are to be initiated. Figureּ3 demonstratesthe manner in which such stages are identified. In this example, an expression permitting the parallel evaluation of the subexpressions is introduced. In the case of a simple expression, the nonterminal “TERM” is simply invoked, but in the case of an expression involving addition or subtraction, the two subexpressions are evaluated concurrently and the results manipulated after evaluation of both subexpressions has completed. For any given EBNF alternative of a syntactic rule of the programming language, the dynamic semantic actions of the elements, or group of elements, appearing in that alternative which are followed by text of the form “// ... //” are assumed to be handled by a separate thread of control. The notation “// ... //” is known as a control thread initiator. The term initiating thread is also introduced, to refer to the thread of control modelling the dynamic semantics of the alternative in which these control thread initiations are found. Control thread initiations can be classified as either synchronous or asynchronous, as specified by the embedded keyword (“sync” or “async” respectively). A synchronous control thread initiator indicates that the invoking thread is not to proceed until the newly created control thread terminates. An asynchronous control thread initiator implies that the initiating thread need not wait for the new thread to terminate before proceeding. PAR_EXPR

: TERM | PAR_EXPR | PAR_EXPR

// sync // plus_sym PAR_EXPR // sync // %% Pass 1: call add_results; %% // sync // minus_sym PAR_EXPR // sync // %% Pass 1: call subtract_results; %%

; Figure 3. Definition of an expression whose subexpressions may be evaluated in parallel. Referring to Figure 3, the preceding discussion implies that the thread of control modelling the execution of the parallel expression (lefthand side of the production) will initiate two new threads of control if addition or subtraction is encountered. In each case, the thread will be used to evaluate the nonterminal immediately before the “//...//” item in the relevant alternative in the productions in Figureּ3. For example, in the case of an addition, two synchronous threads are initiated to evaluate the two “PAR_EXPR” nonterminals. Each of these threads is to execute concurrently and models the evaluation of one of the two subexpressions to be computed. The initiating thread will wait until both of the subexpressions have been evaluated before proceeding; the semantic action for the production will then be evaluated (to add together the results of the subexpressions). Figure 4 illustrates the use of asynchronous control thread initiators. Here, two methods of procedure call are defined for a language, without the need for any additional HLO’s. The first alternative of the “PROCEDURE_CALL” production describes the traditional concept of subroutine call, where the called routine is executed and then

execution continues from the point immediately after the call. The second alternative uses the same high level operation “Proc_Call” to model a procedure call, but applies an asynchronous control thread initiator, indicating that the procedure call is to be modelled by a new thread of control, and that the initiating thread (modelling the caller) need not wait. Thus, the second alternative models an asynchronous procedure call. Further details of the representation and initiation of control threads are given in the following section.

PROCEDURE_CALL : invoke PROCEDURE_NAME %% Pass 2; call Proc_Call; %% | schedule PROCEDURE_NAME %% Pass 2; call Proc_Call; %% // async // ; Figure 4. Asynchronous initiation of control threads.

5 . Generating the implementation prototype In order for a new programming language to receive serious consideration, there is an increasing need for the new language to have a formal definition, as well as some form of implementation which is available for experimentation. ATLANTIS provides a formalism suitable for the definition of programming languages in a rigorous and unambiguous manner, and furthermore allows the generation of implementation prototypes derived directly from such definitions. ATLANTIS aims to support experimentation with new language designs, rather than the generation of production quality compilers. Consequently, the implementations generated by ATLANTIS take the form of interpreters, rather than compilers. 5.1

Overview of the interpreter

Figure 5 illustrates the general structure of the ATLANTIS system and the components generated from a language description. The components with square corners represent those parts of the language implementation which are derived (either directly or indirectly) from the programming language definition. The remaining components are independent of the programming language being defined and form the ATLANTIS system. The shaded region of the diagram shows the structure of a typical language implementation produced by the system. Within the shaded region, the dashed arrows indicate the flow of data into the main module that drives the interpreter. The solid arrows reflect the layered operational model upon which ATLANTIS is based.

source file

scanner tokens parser

inp

ut

lex

lex

yacc

ut

np ci

ac

y language definition

ATLANTIS

tree-builder annotated parse tree interpreter driver

semantic routines

high-level operations

SDA's

ADT's

Figure 5. The ATLANTIS system. As Figure 5 shows, ATLANTIS produces the scanner and parser for a language implementation indirectly by generating input for lex10 and yacc7 respectively. The generated scanner is responsible for reading a stream of characters from a source program

results

written in the newly defined programming language and converting it into a stream of tokens to be supplied to the parser. The parser analyses the token stream and produces error messages as appropriate. Provided no syntactic errors are detected, the parser will supply the tree-builder module with sufficient information to construct a parse tree which is suitably annotated with calls to semantic routines. The generated parse tree is then passed to the interpreter driver for interpretation. The flow of execution control during the interpretive execution of a source program reflects the nature of the information structure model used by ATLANTIS (as discussed in Section 2). When the interpreter driver encounters semantic actions to be executed, it invokes the corresponding routines from the pool of such routines generated from the language definition. In keeping with the layered nature of the model, these semantic routines then call the necessary HLO routines which, in turn, invoke the appropriate SDA/ADT operations to manipulate the information structures representing the state of the program. The fact that the interpreter generated by ATLANTIS follows the underlying operational model of the programming language definition ensures the correct interpretation of the source code. If any unexpected behaviour is uncovered while using the generated interpreter, then the language designer may assume that the language definition does not provide an accurate reflection of the intended language semantics, rather than that the interpreter is behaving inappropriately. Such help is invaluable to a language designer and is only possible if the interpreter generated by ATLANTIS mimics the building and transformation of the information structures precisely. 5.2

The parse tree

Interpretation of source programs written in the newly defined programming language is achieved by performing a post-order traversal of the annotated parse tree supplied to a generated interpreter. The interpreter executes the appropriate semantic actions whenever they are encountered; hence, the information structures manipulated in the language definition are similarly built and manipulated. The parse tree may be traversed a number of times, depending on the number of passes used in the definition of the programming language semantics. Each semantic action is only executed during the pass for which it was specified. The parse tree, as the structured representation of a source program, constitutes the most important and fundamental data structure used by the interpreter. The general structure of the generated parse tree is illustrated in Figure 6. The definition of a simple language is given in Figure 6(a), and Figure 6(b) shows the parse tree that would be constructed if the source string "XZZ" were supplied as input to the generated interpreter. Each node in the parse tree contains a symbol which indicates the particular element in the language grammar to which the node corresponds, and an attribute storing the information with which the parse tree is annotated. Figureּ6(b) shows the symbol and any semantic action associated with the node. Nodes labelled with the null symbol “•” are inserted into the parse tree as place-holders. EBNF expressions in the language definition that are preceded by a semantic action (such as “A1” occurring at the beginning of the definition of the start symbol “ST”) will require a null node to which the semantic action can be attached. Such nodes may also be necessary within subexpressions of the EBNF rules (such as the node to which semantic action “A7” is attached).

ST

ST:

R1:

R2:

%% A1 %% R1 %% A2 %% R2 %% A3 %% ;

R1 A2



A1

%% A4 %% [ 'X' %% A5 %% ] | 'Y' %% A6 %% ;



'X' A5

A4

{ 'Z' } %% A7 %% ;

R2 A3

'Z'

(a)

'Z'



A7

(b)

Figure 6. A simple language definition and its associated parse tree. For a programming language requiring multiple passes, the attribute associated with each node contains an array of semantic actions: one entry for each pass. Performing one post-order traversal over the annotated parse tree for each pass ensures that the execution of semantic actions occurs as specified by the language definition. R1:

R2 R3 // sync // 'X' %% A1 ; %% R4 // async // R3 // sync // ;

R2:

.... ;

R3:

.... ;

R4:

.... ;

(a)

R1

R2

R3

....

....

'X' A1

R4

R3

....

....

(b)

Figure 7. A parallel language construct and its associated parse tree section. In order to support parallelism within an interpreter created by ATLANTIS, it is necessary to annotate the parse tree with additional information indicating those parts of a program that require concurrent interpretation. Figure 7(a) shows an excerpt from a simple language definition involving the initiation of additional threads of control. The parse tree fragment, generated when a sentence derived from “R1” was found in the input, is shown in Figure 7(b). Note, that there are three elements in the definition of “R1” in Figure 7(a) that are followed by control thread initiators. As Figure 7(b) illustrates, the nodes in the

parse tree corresponding to the elements to be evaluated in parallel are linked together via an auxiliary link. In addition, the attribute of the first of these nodes is modified to reflect the fact that the given node is at the start of a chain of such nodes. Thus, in the general case, all elements within an alternative that are followed by a control thread initiator will be linked together in such a manner. Figure 7(a) shows that the nonterminal “R2” would be handled first and then “R3” encountered. This node is to be evaluated in parallel with a number of other elements of the production. It is when “R3” is encountered that all the necessary control threads are initiated. The control thread initiators associated with the two instances of “R3” are synchronous in nature and hence they must complete before the terminal “X” is evaluated and its associated semantic action executed. “R4” is evaluated concurrently in an asynchronous manner, allowing evaluation of the production “R1” to continue without waiting for that of “R4” to complete. During the final pass, additional threads of control are created when the traversal of the parse tree reaches the start node in a chain of nodes linked in the manner described above. When the initiating thread resumes execution (which may be immediately in the case when all sub-elements are to be handled in an asynchronous manner), the traversal of the parse tree will continue from the start node in the chain in the usual manner. However, as additional members of the chain are encountered, they are not traversed again. In the example presented in Figure 7, the initiation of three additional control threads would occur when the traversal visited the second child of the node “R1”. After the first and third of these threads (corresponding to the two “R3” nodes) terminates, the initiating thread would continue the traversal, moving to the third child of the node “R1” and executing the semantic action “A1” as a result. After visiting this node, the remaining two child nodes of “R1” will be skipped, leaving the initiating thread to execute any semantic action associated with the given instance of “R1”, and continue the traversal in the normal manner. 5.3

Driving the interpreter

The component labelled "interpreter driver" in Figure 5 is the part of the generated interpreter responsible for performing the parse tree traversal; the algorithm for performing this traversal is given in Figureּ8.In order to interpret source code written in a sequential programming language, it is sufficient for an interpreter driver to be implemented in terms of a procedure which is invoked once for each pass specified in the language definition. However, in order to support the interpretation of parallel programming language constructs, the interpreter driver is implemented as a process (an Ada task, for example). Thus, each thread of control corresponds to an instantiation of the interpreter driver task. Figure 9 presents the definition of the routine “InvokeNewThreads” used in the algorithm in Figureּ8. As this routine shows, a new thread of control is initiated by creating a new instantiation of the interpreter driver task, and passing it the appropriate subtree to be traversed. The initiating thread will then wait for each of the tasks that were created as a result of synchronous control thread initiation. The "wait" in the figure is represented in the implementation by the Ada task rendezvous mechanism.

current_node := root of sub-tree being interpreted; Mark current_node as not visited; loop if the current pass is the last pass (the dynamic semantics pass) and current_node not marked as visited and current_node is the first in a chain of “parallel” nodes and current_node ≠ root of sub-tree being interpreted then InvokeNewThreads; else if FirstChild(current_node) ≠ null and current_node not marked as visited then current_node := FirstChild(current_node); elsif current_node not marked as visited then Process current_node and mark it as visited; elsif Sibling(current_node) ≠ null then Process current_node and mark it as visited; current_node := Sibling(current_node); elsif current_node ≠ root of the sub-tree being interpreted then current_node := Parent(current_node); Process tcurrent_node and mark it as visited; else Process current_node and mark it as visited; end if; end if; exit when current_node = root of sub-tree being interpreted or else current_node = root of parse tree end loop; Figure 8. The parse tree traversal algorithm. procedure InvokeNewThreads is begin for each node in the chain of nodes loop Instantiate a new traversal task appropriately; Send the new task the sub-tree emanating from the current node; if control thread invocator for current node is synchronous then record this node in a list of such nodes; end if; end loop; for each node in the list constructed above loop wait for the termination of the task created when the current node was processed above; end loop; end InvokeNewThreads; Figure 9. The algorithm to generate more interpreter processes.

6 Conclusions and Future Work Extending ATLANTIS to handle the definition of parallel programming languages as well as sequential programming languages has taken place with minimal disruption to the notation currently in place. In fact, users only observe the addition of some constructs to aid in the description of concurrent language features in the layers describing high level operations and semantic actions. The SDA layer, consisting of ADT protection envelopes, is completely transparent to the user and is generated automatically by the ATLANTIS system from the ADT definitions. The mechanics of the interpreter driver have been redesigned to cater for parallel languages and, again, users of ATLANTIS remain unaware of the change. The interpreter, however, remains faithful to the underlying information structure model by building and manipulating the information structures as dictated by the language definition. The ability of the interpreter to spawn off additional interpreter processes to handle those aspects of the language relating to parallelism provides the language designer with an implementation prototype against which the intended language semantics may be compared. The generated implementation prototype provides the language designer with a tool which allows the evaluation of the new programming language and encourages experimentation with language design. As each new language feature is introduced into a programming language, the designer is able to generate a new interpreter and evaluate how the newly added language feature combines and interacts with existing language features. Clearly, such a tool is only as good as the test data with which it is provided. ATLANTIS has an advantage over other formalisms in that it allows the generation of a language prototype and permits a degree of testing and evaluation prior to finalising the language definition. The language prototype is also useful to programmers and compiler writers. Programmers are provided with a language implementation which is a direct implementation of the language definition. This allows the development of test programs which will provide definitive answers to questions about the language. Compiler writers also benefit from the existence of a prototype language implementation which can act as a reference implementation against which more efficient compilers may be compared. Future work on ATLANTIS includes the provision of a graphical tool for the definition of the programming language, linked to the generated interpreter so that the language designer can observe the progress of the interpreter through the source program and see which semantic routines have an effect at a particular point in the source code. Such a facility will further enhance the capability of ATLANTIS to aid the language designer in the language design process by further ensuring that the observed semantics are, in fact, the intended semantics. 7 . References 1. D. Bjørner and C.B. Jones (Eds.), The Vienna Development Method: The MetaLanguage. Lecture Notes in Computer Science, Vol. 61, Springer-Verlag, Berlin, 1978.

2.

D.H. Freidel, Modelling Communication and Synchronization in Parallel Programming Languages. Technical Report 84–01 {Ph.D. Thesis}, Department of Computer Science, The University of Iowa, Iowa City, Iowa, May 1984. 3. J.A. Goguen, J.W. Thatcher, E.G. Wagner and J.B. Wright, Initial Algebra Semantics and Continuous Algebras. Journal of the ACM, 24(1):68-95, 1977. 4. J.A. Goguen, J.W. Thatcher and E.G. Wagner, An initial algebra approach to the specification, correctness and implementation of abstract data types. In R.T. Yeh (Ed.), Current Trends in Programming Methodology, Vol. 4, Chap. 5, pp. 80–149, Prentice-Hall, Englewood Cliffs, New Jersey, 1978. 5. M.J.C. Gordon, The Denotational Description of Programming Languages. An Introduction. Springer-Verlag, New York, 1979. 6. J.V. Guttag and J.J. Horning, The algebraic specification of abstract data types. Acta Informatica, 10(1):27-52, 1978. 7. S.C. Johnson, Yacc – Yet Another Compiler Compiler. Technical Report Number 23, Bell Laboratories, Murray Hill, New Jersey, October 1975. 8. C.B. Jones, Systematic Software Development Using VDM. Prentice-Hall, London, 1986. 9. U. Kastens, B. Hutte and E. Zimmermann, GAG: A Practical Compiler Generator. Lecture Notes in Computer Science, Vol. 141, Springer-Verlag, Berlin, 1982. 10. M.E. Lesk, Lex –ּA Lexical Analyser Generator. Technical Report Number 59, Bell Laboratories, Murray Hill, New Jersey, October 1975. 11. W.R. Mallgren, Formal Specification of Graphic Data Types. ACM Transactions on Programming Languages and Systems, 4(4):687-710, Oct. 1982. 12. W.R. Mallgren, Formal Specification of Interactive Graphics Programming Languages, MIT Press, Cambridge, Massachusetts, 1983. 13. C.D. Marlin, M.J. Oudshoorn and D.H. Freidel, A Model of Communication in Ada using Shared Data Abstractions. In S.G. Aki, F. Fiala and W.W. Koczkodaj (Eds). Advances in Computing and Information – ICCI'90. Lecture Notes in Computer Science, Vol. 468, Springer-Verlag, Berlin, 199, pp. 443–452. 14. C.D. Marlin and M.J. Oudshoorn, Using Abstract Data Types in a Model of the Data Control Aspect of Programming Languages. Australian Computer Science Communications, 7 (1):19.1-19.10, Feb.1985. 15. M.J. Oudshoorn and C.D. Marlin, Language Definition and Implementation. Australian Computer Science Communications, 11(1):26-36, Feb. 1989. 16. M.J. Oudshoorn and C.D. Marlin, A Layered, Operational Model of Data Control in Programming Languages. Journal of Computer Languages, 16(2):147-165, 1991. 17. M.J. Oudshoorn, K.J. Ransom and C.D. Marlin, Abstract Data Types: Converting from Sequential to Parallel. In P.A. Bailes (Ed.), Engineering Safe Software, Proceedings of the 1991 Australian Software Engineering Conference, Sydney, New South Wales, July 1991, pp. 285–298. 18. M.J. Oudshoorn and C.D. Marlin, Describing the Semantics of Parallel Languages Using Shared Data Abstractions. Submitted for publication. 19. D.A. Schmidt, Denotational Semantics: A Methodology for Language Development. Allyn and Bacon Inc., Newton, Massachusetts, 1986. 20. J.E. Stoy, Denotational Semantics: The Scott-Strachey Approach to Programming Language Theory. MIT Press, Cambridge, Massachusetts, 1977.

21. J. Uhl, S. Drossopoulou, G. Persch, G.Goos, M. Dausmann, G. Winterstein and W.ּKirchgässner, An Attribute Grammar for the Semantic Analysis of Ada. Lecture Notes in Computer Science, Vol. 139, Springer-Verlag, Berlin, 1982. 22. N. Wirth, Programming in Modula-2. Third, corrected edition, Springer-Verlag, Berlin, 1985.

Suggest Documents