We use cookies to distinguish you from other users and to provide you with a better experience on our websites. Close this message to accept cookies or find out how to manage your cookie settings.
To save content items to your account,
please confirm that you agree to abide by our usage policies.
If this is the first time you use this feature, you will be asked to authorise Cambridge Core to connect with your account.
Find out more about saving content to .
To save content items to your Kindle, first ensure no-reply@cambridge.org
is added to your Approved Personal Document E-mail List under your Personal Document Settings
on the Manage Your Content and Devices page of your Amazon account. Then enter the ‘name’ part
of your Kindle email address below.
Find out more about saving to your Kindle.
Note you can select to save to either the @free.kindle.com or @kindle.com variations.
‘@free.kindle.com’ emails are free but can only be saved to your device when it is connected to wi-fi.
‘@kindle.com’ emails can be delivered even when you are not connected to wi-fi, but note that service fees apply.
Action notation includes an imperative action notation for specifying changes to storage.
Imperative actions are concerned with stable information.
Chapter 15 illustrates the use of imperative action notation in the semantic description of variable declarations, assignment statements, and expressions.
Imperative actions are concerned with processing stable information, which generally gets propagated further than transient and scoped information, remaining current until some primitive action changes it. Stable information consists of a collection of independent items, and each change only affects one item. Changes are destructive, rather than temporary, so they can only be reversed if a copy of the destroyed item is available.
Stable information represents the values assigned to variables in programs. An assignment is regarded as an order to the computer, rather than as an assertion, hence the adjective ‘imperative’ for the processing of stable information.
Implementations represent stable information using random-access memory, or secondary storage devices such as magnetic tapes and discs, which in fact can only store single bits—but lots of them! Particular bit-patterns in memory correspond to values, although different occurrences of the same bit-pattern may represent many different abstract values, such as characters and numbers. However, the representation of values by bit-patterns is generally implementation-dependent, so we are justified in ignoring it in semantic descriptions.
In action notation we represent stable information by a map from storage cells to individual items of storable data.
Action notation includes a functional action notation for specifying data flow.
Functional actions are concerned with transient information.
Chapter 13 illustrates the use of functional action notation in the semantic description of expressions and statements.
So far, we have considered actions and data separately: basic action notation does not involve data at all, nor does data notation involve actions. This chapter introduces functional actions that do involve data, as well as yielders that can occur in actions.
The information that actions can process is classified as transient, scoped, stable, or permanent, according to how far it tends to be propagated. Functional actions are concerned with processing transient information, which generally isn't propagated very far during action performance. The transient information current at the start of an action may still be current at the start of the action's direct subactions. However, almost all primitive actions, and a few combinators, prevent the propagation of transient information, so it usually disappears rather quickly.
Transient information represents intermediate values computed during program execution. For example, it may represent the values of some subexpressions, prior to the application of an arithmetic operation to them. Transient information typically corresponds to data which implementations put in registers, or on a stack.
Each item of transient information consists of a single individual of sort datum. We represent a collection of transients as a tuple, so as to be able to keep track of each item.
Declarations include definitions of constants and packages. Expressions include identifiers, as well as aggregates for arrays and records and selection of components. Statements include blocks with local declarations.
The semantic description of declarations, etc., illustrates the use of the declarative action notation introduced in Chapter 7.
Semantic entities now include general array and record values, and packages.
Declarations in programs are constructs that are similar to mathematical definitions, in that they introduce symbols and determine their interpretation. The symbols introduced are called identifiers. Declarations are said to bind identifiers to their interpretation.
The scope of a declaration is the part of the program in which its binding is valid. This is usually the rest of the block statement in which it occurs, except for inner blocks where the same identifier is re-declared, thus making a hole in the scope of the outer-level declaration. Declarations can also occur in modules, which are called packages in ADA. A module may limit the scope of the declarations in it, and it declares a module identifier which may be used to import the bindings of the declarations into other modules.
The processing of a declaration is called its elaboration. Declarations may involve expressions that are to be evaluated, so their elaboration generally has a computational aspect—in contrast to mathematical definitions.
A record value is similar to a module or package entity, in that its field identifiers can be regarded as bound to component values.
Expressions include arithmetic and Boolean operations. Statements involving expressions include conditionals, while-loops, and guarded alternatives.
The semantic description of expressions and statements illustrates the use of the functional action notation introduced in Chapter 6.
Semantic entities are the same as for literals, although further operations on them are needed now.
Expressions in programming languages resemble mathematical terms. Syntactically, both expressions and terms are generally formed from literal constants, such as numerals, and variables, using mathematical operators, such as +. Semanticaily, both expressions and terms are evaluated to particular values.
There are, however, some distinctive differences between expressions and terms. Function calls in expressions may lead to divergence, whereas the application of a mathematical function in a term to arguments outside its domain of definition is merely undefined. Expression evaluation may even involve execution of statements. The notion of a program variable in an expression is quite different from that of a mathematical variable in a term, as explained in detail in Chapter 15. The order of evaluation of subexpressions in an expression may affect the value given—although in most programming languages, the order of evaluation is deliberately left implementation-dependent, which prevents program(mer)s from relying on any particular order. Whether or not a particular subexpression gets evaluated may also affect the outcome of the evaluation, and some expression constructs conditionally avoid the evaluation of particular subexpressions.
AD is a medium-scale, high-level programming language. Syntactically, it is a sublanguage of ADA.
The specified action semantics for AD constructs does not always correspond exactly to the semantics described in the ADA Reference Manual. For instance, parameter passing modes are left implementation-dependent in ADA, but not in AD.
The action semantic description of AD given here collects and extends the fragmented illustrations given throughout Part III. It also provides the full algebraic specifications of all the semantic entities used in Part III.
Some of the actions specified in Part III are respecified here with some extra subactions. For example, the action representing the execution of a block statement now synchronizes with locally-declared tasks before starting to execute the block body, and relinquishes locally-declared variables afterwards.
The description of AD allows assessment of the pragmatic qualities of action semantics: readability, comprehensibility, modularity, modifiability, etc.
To extend the description of AD to an accurate description of ADA would be a major project, the feasibility of which remains to be seen.
The modular structure of the description is specified first, in the order in which the modules are presented, which is mostly bottom-up.
In this first part of the book we introduce a few categories as fundamental semantic frameworks and explore the formulation of general choice operators and iteration in arbitrary categories with finite coproducts.
The specification of the algebraic properties of action notation in this Appendix is divided into modules as shown on the next page.
The body of each module introduces symbols, highlights their principal properties, and lists their secondary properties.
The properties specified are sufficient to reduce the full action notation to a kernel, whose abstract syntax and structural operational semantics are defined in Appendix C.
The specified properties of the kernel action notation hold for the notion of action equivalence defined at the end of Appendix C. The algebraic theory of action notation is not yet fully developed, although it may already be adequate for verifying the semantic correctness of simple program optimizations.
Action notation supports concurrency, but without introducing a combinator for it, so there are no algebraic properties concerning the sending and receipt of messages, or the fulfilment of contracts.
The moduleFacetsdeserves special mention. It defines a moderately expressive notation for subsorts of actions, with reference to outcomes and incomes, and similarly for yielders. This notation is specified axiomatically; the expected correspondence with the operational semantics of action notation has not yet been proved. Nevertheless, the axiomatic specification of the subsort notation provides useful documentation of the sorts of yielders to be used in primitive actions, and of the various facets of the action combinators.
Descriptions of programming languages have several important applications.
Different applications require different features of descriptions.
Informal descriptions are inadequate: formality is essential.
Good pragmatic features are needed to make formal descriptions usefu.
Comprehensive language descriptions involve both syntax and semantics.
Standards relate syntax and semantics to implementations.
This chapter provides some background for the main topic of this book, namely the action semantic description of programming languages. We start by identifying some important uses for descriptions of programming languages. Although various uses require different features of descriptions, these requirements are not necessarily conflicting. Formality is a particularly important feature. We go on to consider the factorization of language descriptions into syntax and semantics, and discuss how they relate to the pragmatic issue of setting standards for programming languages.
Motivation
Programming languages are artificial languages. Programs written in them are used to control the execution of computers. There are many programming languages in existence. Some are simple, intended for special purposes; others are complex and general-purpose, for use in a wide variety of applications.
Even though programming languages lack many of the features of natural languages, such as vagueness, it is not at all easy to give accurate, comprehensive descriptions of them. Which applications require descriptions of programming languages—and hence motivate the study of appropriate frameworks for such descriptions?
First, there is the programming language design process.
Task declarations initiate concurrent processes, which can synchronize using entry calls and accept statements. The select statement may now iterate until an entry call can be accepted.
The semantics of task declarations illustrates the use of the communicative action notation introduced in Chapter 10.
Semantic entities now include agents, and various signals sent in messages. They also include entities that represent open alternatives of selections.
Let us first consider the treatment of input and output in programming languages. By definition, the input of a program is the information that is supplied to it by the user; the output is the information that the user gets back. However, it is important to take into account not only what information is supplied, but also when the supply takes place. We may distinguish between so-called batch and interactive input-output.
With batch input, all the input to the program is supplied at the start of the program. The input may then be regarded as stored, in a file. Batch output is likewise accumulated in a file, and only given to the user when (if ever) the program terminates. On the other hand, interactive input is provided gradually, as a stream of data, while the program is running; the program may have to wait for further input data to be provided before it can proceed. Similarly, interactive output is provided to the user while the program is running, as soon as it has been determined.
Action notation includes a data notation for general use.
Standard data consists of tuples, truth-values, numbers, characters, strings, lists, trees, sets, and maps.
Some of the operations of data notation are intended for use mainly on proper sorts, rather than on mere individuals.
Appendix E gives the full algebraic specification of data notation.
Consider an implementation of a high-level programming language. When a program is run, the information processed by it is represented entirely by sequences of bits: O's and 1's. The programmer, however, does not usually have to deal with this representation directly. The program can be regarded as processing abstract entities, such as numbers, arrays, and sets. Indeed, standards for high-level programming languages generally leave the binary representation of information unspecified. Recall from Chapter 1 that the semantics of a program is an entity which represents the implementation-independent aspects of its information processing behaviour, and that the semantics of a phrase is an entity which represents its contribution to overall behaviour. In action semantics, these entities are generally actions. The information processed by programs is represented by items of data, which correspond directly to abstract entities such as numbers rather than to bit sequences.
Various sorts of data are needed for the semantics of general-purpose high-level programming languages, not only ‘mathematical’ values such as numbers and lists, but also abstract entities of computational origins such as variables, procedures, packages, and so on.
Action notation includes a declarative action notation for specifying scopes of bindings.
Declarative actions are concerned with scoped information.
Chapter H illustrates the use of declarative action notation in the semantic description of declarations, expressions, and statements.
Declarative actions axe concerned with processing scoped information, which generally gets propagated further than transient information. The scoped information current at the start of an action is often current throughout the action—although it may get temporarily hidden by other scoped information within the action. It disappears at the end of its scope.
Scoped information represents the associations, called bindings, which declarations in programs establish between identifiers and entities such as constants, variables, procedures, etc. Implementations usually represent bindings by some form of symbol table.
Programming languages are often characterized as having static or dynamic scopes for bindings. The difference concerns whether or not it is possible to determine from the program text, before running the program with its input, which declarations establish the bindings of which identifiers. For efficiency, compilers extract the required information from the symbol table during compilation, so that it isn't needed when the compiled code is run.
The possibility of dynamic bindings only arises when the programming language contains procedures, or similar constructs, where the body of the procedure can be called from various parts of the program: the bindings current at each call might be different.