Complete Download Understanding Programming Languages 1st Edition Cliff B. Jones PDF All Chapters
Complete Download Understanding Programming Languages 1st Edition Cliff B. Jones PDF All Chapters
com
https://textbookfull.com/product/understanding-programming-
languages-1st-edition-cliff-b-jones/
OR CLICK BUTTON
DOWNLOAD NOW
https://textbookfull.com/product/programming-languages-and-
systems-1st-edition-nobuko-yoshida/
textboxfull.com
https://textbookfull.com/product/foundations-of-programming-languages-
second-edition-lee/
textboxfull.com
https://textbookfull.com/product/concepts-of-programming-languages-
twelfth-edition-sebesta/
textboxfull.com
https://textbookfull.com/product/concepts-of-programming-
languages-11th-edition-sebesta/
textboxfull.com
Programming Languages and Systems Amal Ahmed
https://textbookfull.com/product/programming-languages-and-systems-
amal-ahmed/
textboxfull.com
https://textbookfull.com/product/foundations-of-programming-languages-
kent-d-lee/
textboxfull.com
https://textbookfull.com/product/programming-interviews-for-
dummies-1st-edition-eric-t-jones/
textboxfull.com
https://textbookfull.com/product/practical-foundations-for-
programming-languages-2nd-edition-robert-harper/
textboxfull.com
https://textbookfull.com/product/domain-specific-languages-in-r-
advanced-statistical-programming-1st-edition-thomas-mailund/
textboxfull.com
Cliff B. Jones
Understanding
Programming
Languages
Understanding Programming Languages
Cliff B. Jones
Understanding Programming
Languages
Cliff B. Jones
School of Computing
Newcastle University
Newcastle upon Tyne, UK
This Springer imprint is published by the registered company Springer Nature Switzerland AG
The registered company address is: Gewerbestrasse 11, 6330 Cham, Switzerland
Preface
The principal objective of this book is to teach a skill; to equip the reader with a way
to understand programming languages at a deep level.
There exist far more programming languages than it makes sense even to attempt
to enumerate. Very few of these languages can be considered to be free from issues
that complicate –rather than ease– communication of ideas.
Designing a language is a non-trivial task and building tools to process the lan-
guage requires a significant investment of time and resources. The formalism de-
scribed in this book makes it possible to experiment with features of a programming
language far more cheaply than by building a compiler. This makes it possible to
think through combinations of language features and avoid unwanted interactions
that can confuse users of the language. In general, engineers work long and hard on
designs before they commit to create a physical artefact; software engineers need to
embrace formal methods in order to avoid wasted effort.
The principal communication mode that humans use to make computers perform
useful functions is to write programs — normally in “high-level” programming lan-
guages. The actual instruction sets of computers are low-level and constructing pro-
grams at that level is tedious and unintuitive (I say this from personal experience
having even punched such instructions directly into binary cards). Furthermore these
instruction sets vary widely so another bonus from programming in a language like
Java is that the effort can migrate smoothly to computer architectures that did not
even exist when the program was written.
General-purpose programming languages such as Java are referred to simply as
“High-Level Languages” (HLLs). Languages for specific purposes are called “Do-
main Specific” (DSLs). HLLs facilitate expression of a programmer’s intentions by
abstracting away from details of particular machine architectures: iteration can be
expressed in an HLL by an intuitive construct — entry and return from common
code can be achieved by procedure calls or method invocation. Compilers for HLLs
also free a programmer from worrying about when to use fast registers versus slower
store accesses.
Designing an HLL is a challenging engineering task: the bigger the gap between
its abstraction level and the target hardware architecture, the harder the task for the
v
vi Preface
compiler designers. A large gap can also result in programmers complaining that
they cannot get the same efficiency writing in the HLL as if they were to descend to
the machine level.
An amazing number of HLLs have been devised. There are many concepts that
recur in different languages but often deep similarities are disguised by arbitrary
syntactic differences. Sadly, combinations of known concepts with novel ideas often
interact badly and create hidden traps for users of the languages (both writers and
readers).
Fortunately, there is a less expensive way of sorting out the meaning of a pro-
gramming language than writing a compiler. This book is about describing the
meaning (semantics) of programming languages. A major objective is to teach the
skill of writing semantic descriptions because this provides a way to think out and
make choices about the semantic features of a programming language in a cost-
effective way. In one sense a compiler (or an interpreter) offers a complete formal
description of the semantics of its source language. But it is not something that
can be used as a basis for reasoning about the source language; nor can it serve
as a definition of a programming language itself since this must allow a range of
implementations. Writing a formal semantics of a language can yield a far shorter
description and one about which it is possible to reason. To think that it is a sensible
engineering process to go from a collection of sample programs directly to coding a
compiler would be naive in the extreme. What a formal semantic description offers
is a way to think out, record and analyse design choices in a language; such a de-
scription can also be the basis of a systematic development process for subsequent
compilers. To record a description of the semantics of a language requires a notation
— a “meta-language”. The meta-language used in this book is simple and is covered
in easy steps throughout the early chapters.
The practical approach adopted throughout this book is to consider a list of issues
that arise in extant programming languages. Although there are over 60 such issues
mentioned in this book, there is no claim that the list is exhaustive; the issues are
chosen to throw up the challenges that their description represents. This identifies a
far smaller list of techniques that must be mastered in order to write formal semantic
descriptions. It is these techniques that are the main takeaway of the current book.
Largely in industry (mainly in IBM), I have worked on formal semantic descrip-
tions since the 1960s1 and have taught the subject in two UK universities. The payoff
of being able to write formal abstract descriptions of programming languages is that
this skill has a far longer half-life than programming languages that come and go:
one can write a description of any language that one wants to understand; a lan-
guage designer can experiment with combinations of ideas and eliminate “feature
interactions” at far less cost and time than would be the case with writing a compiler.
The skill that this book aims to communicate will equip the reader with a way
to understand programming languages at a deep level. If the reader then wants to
1 This included working with the early operational semantic descriptions of PL/I and writing the
later denotational description of that language. PL/I is a huge language and, not surprisingly, con-
tains many examples of what might be regarded as poor design decisions. These are often taken as
cautionary tales in the book but other languages such as Ada or CHILL are not significantly better.
Preface vii
design a programming language (DSL or HLL), the skill can be put to use in creating
a language with little risk of having hidden feature interactions that will complicate
writing a compiler and/or confuse subsequent users of the language.
In fact, having mastered the skill of writing a formal semantic description, the
reader should be able to sketch the state and environment of a formal model for
most languages in a few pages. Communicating this practical skill is the main aim
of this book; it seeks neither to explore theoretical details nor to teach readers how
to build compilers.
The reader is assumed to know at least one (imperative) HLL and to be aware of
discrete maths notations such as those for logic and set theory — [MS13], for ex-
ample, covers significantly more than is expected of the reader. On the whole, the
current book is intended to be self-contained with respect to notation.
The material in this book has been used in final-year undergraduate teaching for
over a decade; it has evolved and the current text is an almost complete rewrite.
Apart from a course environment, it is hoped that the book will influence design-
ers of programming languages. As indicated in Chapter 1, current languages offer
many unfortunate feature interactions which make their use in building major com-
puter systems both troublesome and unreliable. Programming languages offer the
essential means of expression for programmers — as such they should be as clean
and free from hidden traps as possible. The repeated message throughout this book
is that it is far cheaper and more efficient to think out issues of language design be-
fore beginning to construct compilers or interpreters that might lock in incompletely
thought-out design ideas.
Most chapters in the book offer projects, which vary widely in their challenge.
They are not to be thought of as offering simple finger exercises — some of them
ask for complete descriptions of languages — the projects are there to suggest what
a reader might want to think about at that stage of study.
Some sections are starred as not being essential to the main argument; most chap-
ters include a section of “further material”. Both can be omitted on first reading.
Writing style
“The current author” normally eschews the first person (singular or plural) in tech-
nical writing; clearly, I have not followed this constraint in this preface. Some of the
sections that close each chapter and occasional footnotes also use the first person
singular when a particular observation warrants such employment.
viii Preface
Acknowledgements
I have had the pleasure of working with many colleagues and friends on the subject
of programming language semantics. Rather than list them here, their names will
crop up throughout the book. I have gained inspiration from students who have fol-
lowed my courses at both Newcastle University and the University of Manchester.
I’m extremely grateful to Jamie Charsley for his insertion of indexing commands.
I owe a debt to Troy Astarte, Andrzej Blikle, Tom Helyer, Adrian Johnson and Jim
Woodcock, who kindly offered comments on various drafts of this book. (All re-
maining errors are of course my responsibility.) My collaboration with Springer
–especially with Ronan Nugent– has been a pleasure. I have received many grants
from EPSRC over the years — specifically, the “Strata” Platform Grant helped sup-
port recent work on this book.
Contents
2 Delimiting a language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.1 Concrete syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2 Abstract syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.3 Further material . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3 Operational semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.1 Operational semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.2 Structural Operational Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
3.3 Further material . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
4 Constraining types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.1 Static vs. dynamic error detection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.2 Context conditions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
4.3 Semantic objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.4 Further material . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
5 Block structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
5.1 Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
5.2 Abstract locations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
5.3 Procedures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
5.4 Parameter passing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
5.5 Further material . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
ix
x Contents
11 Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
11.1 Review of challenges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
11.2 Capabilities of formal description methods . . . . . . . . . . . . . . . . . . . . . 160
11.3 Envoi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
Contents xi
D COOL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
D.1 Auxiliary objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
D.2 Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
D.3 Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
D.4 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
D.5 Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
D.6 Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Chapter 1
Programming languages and their description
This chapter sets the scene for the rest of the book. Sections 1.1–1.3 outline the
problems presented by programming languages and their related tools; Section 1.4
points out that there is material from the study of natural languages that is relevant
to the problems of describing artificial languages such as those used to program
computers; an overview of a range of techniques for recording the meaning of pro-
gramming languages is given in Section 1.5 and Section 1.6 introduces the specific
notation used throughout this book. In common with most of the following chap-
ters, this one closes with a section (1.7) that contains further material — in particular
such sections point to related reading.
Picking up the point about the productivity of programmers from the preceding sec-
tion, there was a panel discussion2 on Programming Languages and their Semantics
at a conference in Pittsburgh in May 2004 to which Vaughan Pratt put the intriguing
question of how much money the panelists thought that high-level programming lan-
guages had saved the world. Pratt was aware of the difficulty of the question because
he added a subsidiary query as to whether an answer to the main question would
qualify for a Nobel Prize in economics. Without hoping –even with time to reflect–
to provide a number, considering Pratt’s question is illuminating. There are almost
certainly millions of people in the world for whom programming forms a significant
part of their job. Programmers tend to be well paid. A good programmer today can
create –using a high-level programming language such as Java– systems that were
unthinkable when programs could only be communicated in machine code. A good
programming language can, moreover, ensure that many mistakes made by even an
average-level programmer are easily detected and corrected. To these powerful sav-
ings, ease of program migration can be added: avoiding the need to write versions of
essentially the same program for different machine instruction sets must itself have
saved huge wastage of time and money.
It is also important to appreciate the distribution of costs around programs: even
back in the days of mainframes, the majority of programs cost more to develop than
their lifetime machine usage costs. Since that period, decades of tracking Moore’s
Law [Moo65] have dramatically reduced the cost of executing programs. With mod-
ern interactive applications, and factoring in the human cost of user time, the actual
machine time costs are close to irrelevant. The productivity of programmers and
their ability to create systems that are useful to the end user are the paramount con-
cerns.
2 The panelists were John McCarthy, John Reynolds, Dana Scott and the current author.
1.2 The importance of HLLs 3
The mere fact that there are thousands3 of programming languages is an indica-
tion that their design is a subject of interest. The additional observation that there is
no one clear “best buy” suggests that designing a high-level programming language
is non-trivial. One tension in design is between offering access to the power of the
underlying machine facilities so that programs can be made to be efficient versus
providing abstractions that make it easier to create programs. The problems of writ-
ing programs are also likely to be exceeded by the costs of their maintenance, where
the intentions of the original programmer must be understood if the person changing
the program is to do so safely.
A good programming language offers several aids to the programmers who use
it to express their ideas:
• data structuring
• common forms of control can be built into a language (with only the compiler
having to fiddle with the specific machine-level instruction sequences that realise
the higher-level expressions)
• protecting programmers from mistakes
It is worth expanding on the issue of how programming languages provide ab-
stractions. Most computers have a small number of registers, in which all basic
computations are performed and instructions can only access one additional storage
cell. A calculation involving several values must –at the machine level– involve a
series of instructions and might require the storage of intermediate results. A first
level of abstraction allows programmers to write arbitrary expressions that have to
be translated into strings of machine operations.
Clear layers of abstraction can be seen with regard to data representation. The
storage of a computer can most easily be viewed as a sequence of small contain-
ers (bits, bytes or words).4 From its inception, the FORTRAN language supported
declaring arrays of multiple dimensions whose elements could be addressed in a
style familiar to mathematicians (e.g. A[I, J ∗ 3]). Such operands have to be trans-
lated into sequences of machine instructions that compute the machine address
in the sequence of addressable storage cells of the machine. The APL language
pushed arrays to extremes and even PL/I provides ways of manipulating slices of n-
dimensional arrays — such sub-arrays can then be manipulated as if they had been
declared to be arrays of lesser dimensions.
Array elements are all of one type. Both the COBOL and Pascal languages of-
fer ways of defining inhomogeneous records5 that facilitate grouping data elements
whose types differ from each other. Furthermore the whole inhomogeneous object
can be used (e.g. as parameters or in input/output) as a single data item or its com-
ponents can be addressed separately.
3 As early as 1969, Jean Sammet’s book [Sam69] recognised 500 programming languages; a web
site that claimed to be listing all known languages got to 8,512 in 2010 then gave up.
4 Of course, computer architectures normally include registers and might themselves provide ab-
straction such as “virtual memory” (supported by “paging”). Some of these features are discussed
when issues relating to code generation are considered in subsequent chapters.
5 Some languages, including PL/I, use the term “structures” rather than records.
4 1 Programming languages and their description
List processing facilitates the creation of arbitrary graphs of data by allowing the
manipulation of something like machine addresses as data. Early languages such as
IPL-V, Lisp and Scheme had to develop a lot of techniques for garbage collection
before list processing could be adopted into more mainstream languages.
The concept of objects is a major contribution to the abstractions, as offered in
object-oriented languages such as Simula, Smalltalk and Java. Object orientation is
discussed in Section 6.2 and its role in taming concurrent computation is the main
topic of Chapter 9.
A similar story of programming languages developing abstraction mechanisms
above the raw conditional jumps of machine level programming could be developed:
conditional if constructs, compound statement lists, for and while looping constructs
and –above all– recursion make it possible to present complicated programs as a
structured and readable text. Tony Hoare reported [Hoa81, p.76] that he could not
express his innovative Quicksort [Hoa61] algorithm until he learned the ALGOL 60
programming language.6 This almost certainly contributed to his judgement on AL-
GOL 60 in [Hoa74b]:
Here is a language so far ahead of its time, that it was not only an improvement on its
predecessors, but also on nearly all its successors. Of particular interest are its introduction
of all the main program structuring concepts, the simplicity and clarity of its description,
rarely equalled and never surpassed.
A final, but crucial, area where programming language designers have sought
to offer abstractions is that of concurrency.7 Most computers offer rather low-level
primitives such as a compare-and-swap instruction; programming even at the level
of Dijkstra’s semaphores [Dij62, Dij68a] is extremely error-prone. The whole sub-
ject of concurrency in programming languages is still in evolution and its modelling
occupies several chapters later in this book. References to histories of the evolution
of programming languages are given in Section 1.7.
Here, the concern is with the problem of knowing how to record the meaning –or
semantics– of useful abstractions such as those sketched above. Semantic descrip-
tion is a non-trivial problem and occupies Chapters 3–10 of this book. The payoff
for mastering these techniques is large and can have effects far beyond the language
design team. It is just not realistic to expect anyone to be able to design a program-
ming language that will overcome the challenges listed above by sketching sample
programs and then proceeding to compiler writing. In fact, such a procedure is a
denial of everything known about engineering. The ability to record the meaning of
a language at an abstract level means that designers can think out, document and
refine their ideas far less expensively than by coding processors. Furthermore, the
6 Although initially designed as a publication language (and the vast majority of algorithms pub-
lished in the Algorithms section of Communications of the ACM were written in ALGOL 60) the
language contributed so many fundamental concepts to programming language design that it has
had enormous influence (see [AJ18, §1.4]).
7 In contrast, so-called “weak” (or “relaxed”) memory is a hardware feature which might in-
flict considerable damage on software development because it is hard to find apposite abstrac-
tions [ŠVZN+ 13, LV16].
1.3 Translators, etc. 5
far bigger bonus is that users of better thought-through languages will become more
productive and stumble into far fewer unexpected “feature interactions”.
Before techniques for describing semantics and the case for formalism are dis-
cussed in Section 1.5, it is worth considering the software tools that process pro-
gramming languages.
In the early years of programming languages, the most frequent phrase that we heard was
that the only way to program a computer was in octal.
9 The origin of this word is explained in [Bey09] as deriving from the first attempts to automate
program construction by collecting (compiling) a group of subroutines. It is surprising that this is
the term that continues to be more commonly used for what is clearly a process of translation.
10 In a detailed study [vdH19] of the ALGOL 60 implementation by Dijkstra and his colleagues,
Gauthier van den Hove makes clear that, even early in the history of language processing, the
question of compiling and interpreting was seen less as a dichotomy and more as a spectrum.
11 Functional languages such as Miranda [Tur85] and Haskell [Hut16] make it easier to reason
about programs as though they were mathematical functions. Early implementations of functional
languages tended to perform considerably more slowly than imperative languages but this gap has
reduced and some serious applications are now written in functional languages. Logic languages
such as Prolog [SS86] make a further step both in expressiveness and in their challenge to offer
performance. (In fact, Prolog still has imperative features.) The techniques presented in this book
6 1 Programming languages and their description
imperative languages might move the arm of a robot, project an image or update a
database.) Most of the statement types actually only orchestrate the order in which
updates to variables are made by assignments.
As outlined above, straightforward expression evaluation has to be implemented
by loads, stores and single-address operations of the machine. But a compiler will
often try to optimise expression evaluation. For example “common sub-expressions”
might be evaluated only once. Even in early FORTRAN compilers, expressions
that occurred inside FOR loops but did not depend on variables whose values were
changed in the loop could be evaluated once before the loop. More subtly, expres-
sions such as those which compute array indexes in the loop could be subject to
strength reduction so that the effect of multiplication could be achieved by addi-
tion each time round a loop. Many of these optimisations are known as “source-
to-source” in the sense that there is an equivalent source program that represents
the optimised code. There are other optimisations such as those concerned with
maximising efficiency by minimising the number of loads and saves for registers
(especially index registers for address calculation) that cannot be expressed in the
source language. In either case, it is clearly essential that the “optimisations” do
not result in a program doing something different from the programmer’s legitimate
expectations. In other words, any optimisations must respect the semantics of the
given high-level language.
Similar points could be made about compiling control constructs. Most machines
provide a primitive (conditional) jump instruction. High-level languages offer far
more structured control constructs. The task of a compiler is to translate the latter
into the former in a way that results in efficient code. But, again, that low-level code
must respect the semantics of the programming language.
Three final points can be made about tools for executing high-level languages:
The languages that are spoken by human beings were not designed by committees13
— they just evolved and they continue to change. The evolution process is all too
obvious from the irregularities in most natural languages. The task of describing
such natural languages is therefore very challenging but, because they have been
around longer, it is precisely on natural languages that the first systematic studies
were undertaken. Charles Sanders Peirce (1839-1914) used the term Semiotics for
the study of languages. Peirce14 divided the study of languages into:
Some general points about semantics can be made concrete by looking at a few
specific programs. The following program (in a syntax close to that of ALGOL 60):
13 Of course, there are a small number of exceptions such as Volapük and Esperanto.
14 Pronounced “Purse”.
8 1 Programming languages and their description
non-determinism: most HLLs actually permit a range of results (e.g. because of concurrency or
to leave implementors some flexibility): even if a user is interested in the result of a program on
1.6 A meta-language 11
As indicated in Section 1.2, a premature leap from example programs for a new
language to beginning to write a compiler for the language does not constitute sound
engineering. A process of experimenting with language choices within a formal
model can iron out many potential consistencies more quickly and far less expen-
sively. The formal model can also serve as the starting point for a systematic design
process for compilers once the language has been thought out.
Chapter 11 lists a number of formal descriptions of extant programming lan-
guages. One possibility opened up by making these descriptions formal is to pro-
vide tools that use them. There is quite good support for reasoning about program
correctness from various forms of property-oriented semantics, although this nor-
mally applies to restricted subsets of major languages such as SPARK-Ada. There
are far more tools based on formal ideas that check specific properties of programs
in languages (e.g. possible dereferencing of null pointers, deadlock detection).
Having listed the technical criteria of being able to reason about programs written
in L and acting as a base for compilers for L , there remains a bigger objective. The
main aim of this book is to ensure that formal models are more widely used in the
design of future programming languages. It is to be regretted that most of the current
main-line programming languages have semantic traps that surprise programmers
and/or complicate the task of providing compilers for the language. Such anomalies
can be detected early by writing formal semantic descriptions before tackling the
far more costly job of programming a compiler.
What then is the impediment to writing, for example, an operational semantics
of a language? Section 1.6 introduces a meta-language that should not prove dif-
ficult for any programmer to understand. With that one meta-language, he or she
can describe any (imperative) programming language. Chapter 3 covers the basic
method of writing an operational semantics and subsequent chapters consider new
challenges and eight known techniques for coping with them. Experience of teach-
ing these methods over the years suggests that the real hurdle is learning to employ
the right degree of abstraction in tackling language descriptions. That can proba-
bly only be learned by looking at examples, and Chapters 3–9 provide many such
examples.
1.6 A meta-language
The term object language can be used to refer to the language whose syntactic and
semantic description is to be undertaken17 and the script letter L is used when
making a general point rather than discussing a specific object language such as
FORTRAN or Java. In contrast, languages that are used in the description of an
object language are referred to as meta-languages.
a single input item, knowing that the result is as required in one implementation of L does not
guarantee that the program will give the same result on a different correct implementation of L .
17 The qualification “object” indicates that it is the object of study; this is not to be confused with
“object code”, which is what a translator generates when it compiles a source program.
12 1 Programming languages and their description
Notation for VDM sequences is introduced in Section 2.2 (see Figure 2.2) and
maps in Section 3.1 (Figure 3.1) when they are needed.
18 Many useful textbooks exist on the notations of discrete mathematics including [Gro09].
1.6 A meta-language 13
B {true, false}
¬E negation (not)
E 1 ∧ E2 conjunction (and)
E1 , E2 are conjuncts
E1 ∨ E2 disjunction (or)
E1 , E2 are disjuncts
E 1 ⇒ E2 implication
E1 antecedent, E2 consequent
E1 ⇔ E2 equivalence
∀x ∈ S · E universal quantification
∃x ∈ S · E existential quantification
The symbols used for logic (technically, first-order predicate calculus) vary be-
tween textbooks and Figure 1.2 indicates the symbols used in this book.
It is common to set out proof rules that define valid deductions about the logical
operators. Examples that are assumed below include a definition of implication:
¬ E1 ∨ E2
⇒ -I
E1 ⇒ E2
equivalence as bi-implication:
E1 ⇒ E2
E ⇒ E1
⇔ 2
E1 ⇔ E2
What can be thought of as a definition of disjunction in terms of conjunction is
characterised by the bi-directional rule:
¬ (¬ E1 ∧ ¬ E2 )
de-Morgan-1
E1 ∨ E2
is one of de Morgan’s laws; another is:
¬ (E1 ∨ E2 )
de-Morgan-2
¬ E1 ∧ ¬ E2
14 1 Programming languages and their description
This book tackles the semantics of imperative languages such as ALGOL and Java.
Descriptions of functional and logic programming languages (e.g. Scheme [ASS85],
Prolog [SS86]) would use the same ideas but it is worth saying a few words about
the differences. Rather than design algorithmic solutions to solve problems, it would
be attractive to write logical assertions and have a program find solutions. Even
in the restricted world of mathematics or logic19 this is impossible in general, but
Prolog-style logic programming moves in this direction. Unfortunately, in order to
circumvent the problems of massive search spaces, imperative features have been
included in Prolog itself [SS86].
An intermediate approach between fully imperative and logic languages is the
class of functional programming languages. In the extreme, such languages avoid
all imperative constructs such as assignment. This makes it possible to reason
about functional programs as though they are mathematical functions. Among other
things, this avoids the need for a Hoare-style logic. Most functional languages actu-
ally offer some limited ability to “change the world”.
The reason that the reader should have no difficulty with these extended meanings
is that key properties such as the symmetry of conjunction and disjunction hold:
E1 ∨ E2
∨ -sym
E2 ∨ E1
E1 ∧ E2
∧-sym
E2 ∧ E1
In fact, the key difference with conventional propositional logic is that the so-called
“law of the excluded middle” (P ∨ ¬ P) only holds in LPF where it is established
that P denotes a truth value.
a b ¬a a∧b a∨b a ⇒ b a ⇔ b
true true false true true true true
∗ true ∗ ∗ true true ∗
false true true false true true false
true ∗ ∗ true ∗ ∗
∗ ∗ ∗ ∗ ∗ ∗
false ∗ false ∗ true ∗
true false false true false false
∗ false false ∗ ∗ ∗
false false false false true true
Quantifiers are in no way mysterious. Over finite sets, they are just convenient
abbreviations:
(∃i ∈ {1, · · · , 3} · p(i)) ⇔ (p(1) ∨ p(2) ∨ p(3))
(∀i ∈ {1, · · · , 3} · p(i)) ⇔ (p(1) ∧ p(2) ∧ p(3))
Even the infinite cases should present no difficulty:
∀i ∈ N · ∃j ∈ N · i < j
With all of the quantifiers, the scope is assumed to extend as far as possible to
the right; parentheses are not required for this case but they can be used to define
different grouping.
This leaves only the end cases with the empty range for the bound variable to
note:
∃i ∈ { } · p(i) ⇔ false
∀i ∈ { } · p(i) ⇔ true
which are obviously related from the quantifier versions of de Morgan’s laws:
¬ (∃x · p(x))
de-Morgan-3
∀x · ¬ p(x)
1.7 Further material 17
¬ (∀x · p(x))
de-Morgan-4
∃x · ¬ p(x)
There are other logics that attempt to handle terms that fail to denote a value
and a comparison is given in [CJ91]. Details of the specific LPF used in VDM
are addressed in [BCJ84, JM94]. Kleene (in [Kle52]) attributes the propositional
operator definitions in Figure 1.3 to [Łuk20]. Other papers that address the issue of
undefinedness include [Kol76, Bla86, KTB88, Bli88].
Chapter 2
Delimiting a language
The body of this book addresses the task of describing –or designing– the semantics
of programming languages. This chapter prepares the way for that task by introduc-
ing the division between syntax and semantics. A tiny language is introduced which,
because it has few surprises, can be used to explain the description method. As more
complicated language features are considered in later chapters of this book, they are
treated independently as far as is possible (e.g. input/output is modelled in Sec-
tion 4.3.1 and a similar extension could be made to the concurrent object-oriented
language in Chapter 9).
For an extant language,1 it is necessary to delimit the set of allowed programs
before their meaning can be discussed: Section 2.1 outlines “concrete syntax” no-
tations for fixing the textual strings of an object language (such strings include var-
ious marks that make it possible to parse the strings); Section 2.2 addresses a way
of defining the “abstract syntax” of programs without the symbols needed to facili-
tate parsing. This chapter also covers most of the VDM notation used in the current
book. The topic of semantics is first tackled in Chapter 3 and runs throughout the
remainder of this book.
24 avril.
Our website is not just a platform for buying books, but a bridge
connecting readers to the timeless values of culture and wisdom. With
an elegant, user-friendly interface and an intelligent search system,
we are committed to providing a quick and convenient shopping
experience. Additionally, our special promotions and home delivery
services ensure that you save time and fully enjoy the joy of reading.
textbookfull.com