SlideShare a Scribd company logo
Understanding And Writing Compilers A
Doityourself Guide Richard Bornat download
https://ebookbell.com/product/understanding-and-writing-
compilers-a-doityourself-guide-richard-bornat-36529634
Explore and download more ebooks at ebookbell.com
Here are some recommended products that we believe you will be
interested in. You can click the link to download.
Smart Thinking Skills For Critical Understanding And Writing Second
Edition Reissue Matthew Allen
https://ebookbell.com/product/smart-thinking-skills-for-critical-
understanding-and-writing-second-edition-reissue-matthew-
allen-57136102
Smart Thinking Skills For Critical Understanding And Writing 2nd Ed
Matthew Allen
https://ebookbell.com/product/smart-thinking-skills-for-critical-
understanding-and-writing-2nd-ed-matthew-allen-4121292
Smart Thinking Skills For Critical Understanding And Writing 2nd
Edition Matthew Allen
https://ebookbell.com/product/smart-thinking-skills-for-critical-
understanding-and-writing-2nd-edition-matthew-allen-1272680
Smart Thinking Skills For Critical Understanding And Writing Matthew
Allen Allen
https://ebookbell.com/product/smart-thinking-skills-for-critical-
understanding-and-writing-matthew-allen-allen-27821526
The Pester Book The Allinone Guide To Understanding And Writing Tests
For Powershell Adam Bertram
https://ebookbell.com/product/the-pester-book-the-allinone-guide-to-
understanding-and-writing-tests-for-powershell-adam-bertram-22295790
Understanding Development And Proficiency In Writing Quantitative
Corpus Linguistic Approaches Philip Durrant
https://ebookbell.com/product/understanding-development-and-
proficiency-in-writing-quantitative-corpus-linguistic-approaches-
philip-durrant-33030350
Get Funded A Practical Guide To Understanding The Grant Application
Process And Writing Winning Proposals In The Behavioral And Biomedical
Fields 1st Edition Elias Phd
https://ebookbell.com/product/get-funded-a-practical-guide-to-
understanding-the-grant-application-process-and-writing-winning-
proposals-in-the-behavioral-and-biomedical-fields-1st-edition-elias-
phd-56481488
Understanding Developing And Writing Effective Ieps A Stepbystep Guide
For Educators Roger Pierangelo George Giuliani
https://ebookbell.com/product/understanding-developing-and-writing-
effective-ieps-a-stepbystep-guide-for-educators-roger-pierangelo-
george-giuliani-48922738
Understanding Chinese Multilingual Scholars Experiences Of Writing And
Publishing In English A Socialcognitive Perspective Congjun Mu
https://ebookbell.com/product/understanding-chinese-multilingual-
scholars-experiences-of-writing-and-publishing-in-english-a-
socialcognitive-perspective-congjun-mu-10788004
Understanding And Writing Compilers A Doityourself Guide Richard Bornat
Understanding
and Writing
Compilers
A do-it-yourself guide
Richard Bornat
Middlesex University, London.
richard@bornat.me.uk
First published—1979.
Internet edition—2007; corrected 2008.
Copyright c 1979, 2007, 2008 Richard Bornat.
Permission to copy without fee all or part of this material is granted provided
that the copies are not made or distributed for direct commercial advantage,
the copyright notice above and the title of the book appear, and notice is given
that copying is by permission of Richard Bornat.
Original edition published by Macmillan Press Ltd., Basingstoke, UK.
Internet edition published by Richard Bornat, 28 Albany Road, LONDON N4
4RL, UK. richard@bornat.me.uk
Preface to the online
edition
I wrote this book on compiling in the late 1970s. It was a success. I still meet
people who learnt about compiling from it. I get three or four requests a year
from people who’d like a copy. I used to tell them to use Abebooks.com, but
now there aren’t any copies even there. Since I own the copyright (thanks to
Macmillan), I can publish it again, for free.
For a while I tried to reproduce the original book from the original nroff source,
but Unix version 6 nroff is long dead, and the printer-driver hacks that made
bold and underlining work are even deader. So I hacked it into L
A
TEX, and here
it is. I fixed the errors that I knew about, changed a very few infelicities, and
otherwise tried to change as little as possible.
Why isn’t it in C?
When I began to write this book I was heavily into BCPL. By the time it was
finished I was a C programmer. The display editor ded that I developed to write
the book was written in C. C is in many respects a direct descendant of BCPL.
I was pressed, even before the book came out, to rewrite all the examples in C.
I didn’t do that for various reasons, some good and some bad. It would be an
enormous effort to do it now, because C is now so far from its origins.
Old-fashioned opinions
I agree with quite a lot of this book, but by no means all of it. I’ve decided to
change nothing. For better or worse, here it is.
i
ii
Preface
In the past compiler writers and designers seemed to form an elite group within
computing science, set apart by their esoteric knowledge and their ability to
produce large, important system programs which really worked. The admiration
of the computing public, whether it was once deserved or not, is no longer
merited now that the principles of programming-language implementation are
so well understood. Compiler-writing is no longer a mystery.
This book attempts to explain and demystify the principles of compiler writing
so that you can go out and build a working compiler of your own. There is
enough detail in this book for you to build a compiler for quite a complicated
language – certainly PASCAL, perhaps ALGOL 68 or SIMULA 67 – but it
doesn’t attempt an encyclopaedic coverage of the field. It is intended more as an
introduction to compiler-writing and a do-it-yourself kit for the compiler-writer,
giving enough detail for you to understand the principles of the subject, than as
a survey of past history or present horizons. The principles of interpretation are
close enough to those of compilation for chapter 19 to give a simple introduction
to interpreter writing.
The method of treatment and the relative amount of attention given to various
topics in this book reflects my own views about the relative importance of those
topics. There is a separate section on run-time support, less attention is paid
than is perhaps usual to the topic of parsing or syntax analysis and the discussion
of translation is totally oriented to tree-walking. I have presented the subject
in this way for both practical and educational reasons. First, the object code
instruction sequences which implement run-time support are more important
in practice than is usually recognised. It is differences in run-time mechanisms,
as much as or more than anything else, which distinguish one language from
another – say SIMULA 67 from ALGOL 68, POP-2 from ALGOL 60 – and
the efficiency of run-time support code fragments is crucial to the efficiency of
the object program. Second, I believe it is more important to give a practical
description of syntax analysis in a book which is intended for the practical
compiler-writer than to give a more formal and complete introduction to the
topic. The syntax analysis mechanisms chosen for illustration in section IV] are
selected for their practical relevance. Of the three mechanisms presented, the
‘one-track’ and ‘operator-precedence’ mechanisms are now rather old-fashioned
but are still quite adequate to the task of parsing popular modern languages.
iii
iv
Finally, my insistence on tree-walking as the best means of translation is both
because it makes explanation of translation algorithms much easier and enables
me to bring out the topic of ‘crucial code fragments’ which forms so much of the
life of the professional compiler writer; also in my experience it is a practical
way in which both novice and expert can quickly build a working translator
containing the minimum number of errors.
Throughout the book I have emphasised the fact that the task of compilation can
be divided into separate modular sub-tasks. It is largely the identification of and
emphasis on this essential modularity that has clarified the subject. Emphasis
on modular design also helps me to avoid discussing every known technique
for each of the tasks – if you know what the task is, one or two good ways to
accomplish it and how to recognise another good way if somebody shows you
one, then you are on the way to becoming a capable compiler writer.
Throughout the book I have emphasised the need for the compiler to provide
a service to its users. It seems to me that the demands of system or compiler
efficiency are too often given precedence over the justifiable demands of the
user who wants understandable error reports, accurate and reliable object code
or strict adherence to an industry standard. The same goes, I believe, for the
demands of small compiler size or simplicity of construction. A good compiler
can be acceptably efficient and reasonably small yet still provide a good user
service. Indeed I believe that the well-designed compiler will out-perform the
‘efficient’ special-purpose construction, but even if it isn’t so the compiler writer
should stand by the principle that machines are provided to save human time
and effort. A few seconds of machine time which saves minutes (or more often
hours) of human time is machine time well spent!
Host, Object and Source Language in Examples
In the examples throughout the book most algorithms are written in a version
of the language BCPL, details of which are briefly explained in appendix A.
Some example algorithms are in PASCAL: I would have used PASCAL more
extensively were it not for the fact that its lack of block structure, lack of
conditional expressions and lack of a simple ‘union-type’ convention forces an
obscure programming style. BCPL’s advantage is that it is untyped and that
therefore the examples can show the bare bones of an algorithm without too
much unnecessary hedging round of type declarations. At the same time this
is a drawback: BCPL’s lack of data structure declarations makes it difficult to
explain some fairly simple algorithms. Early examples give some explanation
of the data structures which are manipulated by the algorithms: it is worth
pointing out that in every case the values which are manipulated are pointers
to record-structures rather than the structures themselves.
Appendix B explains the assembly code which is used to illustrate the oper-
ation of the translation algorithms in sections II and III. It is the code of a
single-address, multi-register single-segment-addressing machine. Throughout
v
the book there is an emphasis on the machine-independence of compiler design
and the fact that details of the object machine’s instruction set don’t affect the
design of the compiler. Nevertheless it is useful, when discussing translation al-
gorithms, to illustrate the code of an example object machine in order to show
the advantages of good design of code fragments.
With the present explosion in the use of microprocessors interest in compil-
ing has re-emerged, particularly interest in compiling system-programming lan-
guages. The problems of compiler-writing for small machines are mainly to do
with producing compact object code: the examples presented in this book are
not directly oriented towards this purpose but may be readily adapted to it.
The desire to produce a very small compiler, as opposed to a small object pro-
gram, should be curbed until you properly understand the principles of compiler
design (when perhaps the desire will go away!)
The source language which is used to illustrate syntax analysis and translation
algorithms has a variable syntax and semantics, floating somewhere in the space
between BCPL, ALGOL 60, ALGOL 68 and PASCAL. Some attention is given
to the detailed difficulties which arise in writing a compiler for each of these
languages. I didn’t wish to limit the discussion to those features displayed by
any particular language, however, nor to get bogged down in the details of that
language. Most examples therefore relate to the general difficulties of compiling
any language which includes recursive procedures, block-structuring, dynamic
arrays, pointer variables, and so on.
Acknowledgements
I’m enormously indebted to all the people who have argued with me about
compilers and compiling over the years, lectured to me, listened to me, corrected
me and generally helped me develop the ideas I present in this book. Many of
the good ideas are those of my mentors. Thanks especially to Jeff Rohl for
helping me in my first steps and encouraging me to develop my ideas as far as
they have gone: thanks also to Bernard Sufrin for so many invaluable discussions
about the best way to build and to explain compilers. Thanks to my colleagues
at the University of Essex – Bruce Anderson, Mike Brady, Tony Brooker, Mike
Foster, Pete Gardner, Pat Hayes, John Laski, Bob Wielinga and all the students
who listened to earlier versions of this book in the form of lectures.
This book was type-set on a DEC PDP-11/40 and printed using a Diablo printer.
Thanks to the denizens of the Queen Mary College Computer Systems Labo-
ratory for allowing me to use the machine so heavily and especially to George
Coulouris, Jon Rowson, Ben Salama and Harold Thimbleby for leading me
through the intricacies of the local software.
vi
Contents
Preface to the online edition i
Preface iii
I Modular Organisation of Compilers 1
1 Phases and Passes 5
1.1 Tasks and Sub-tasks . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2 Translation and Optimisation . . . . . . . . . . . . . . . . . . . . 9
1.3 Object Descriptions in the Symbol Table . . . . . . . . . . . . . . 10
1.4 Run-time Support . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.5 Source Program Errors . . . . . . . . . . . . . . . . . . . . . . . . 12
1.6 Two-pass Compilation . . . . . . . . . . . . . . . . . . . . . . . . 14
1.7 An Example of Compilation . . . . . . . . . . . . . . . . . . . . . 17
2 Introduction to Translation 19
2.1 Phrases and Trees . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.2 Tree Walking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.3 Linear Tree Representations . . . . . . . . . . . . . . . . . . . . . 25
2.4 Improving the Tree Walker . . . . . . . . . . . . . . . . . . . . . 27
2.5 Using the Symbol Table Descriptors . . . . . . . . . . . . . . . . 32
2.6 Translation Error Handling . . . . . . . . . . . . . . . . . . . . . 32
3 Introduction to Syntax Analysis 37
3.1 Language Descriptions (Grammars) . . . . . . . . . . . . . . . . . 39
3.2 Bottom-up Analysis of Expressions . . . . . . . . . . . . . . . . . 41
3.3 Top-down Analysis of Statements . . . . . . . . . . . . . . . . . . 47
vii
viii CONTENTS
3.4 Building a Node of the Tree . . . . . . . . . . . . . . . . . . . . . 50
3.5 Syntax Error Handling . . . . . . . . . . . . . . . . . . . . . . . . 52
4 Lexical Analysis and Loading 57
4.1 Reading the Source Program . . . . . . . . . . . . . . . . . . . . 58
4.1.1 Characters to items . . . . . . . . . . . . . . . . . . . . . 59
4.1.2 Efficiency of the input phases . . . . . . . . . . . . . . . . 63
4.1.3 Representing an item . . . . . . . . . . . . . . . . . . . . . 63
4.1.4 Identifiers and keywords . . . . . . . . . . . . . . . . . . . 64
4.1.5 Detecting lexical errors . . . . . . . . . . . . . . . . . . . 65
4.1.6 Tricks of implementation . . . . . . . . . . . . . . . . . . 66
4.2 Output for a Loader . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.2.1 Linking program sections . . . . . . . . . . . . . . . . . . 67
4.2.2 Relocating a program section . . . . . . . . . . . . . . . . 68
4.2.3 Fixup of label values . . . . . . . . . . . . . . . . . . . . . 69
4.2.4 Run-time debugging and the loader . . . . . . . . . . . . 71
4.2.5 Loaders and system efficiency . . . . . . . . . . . . . . . . 71
4.2.6 Error detection in the loader . . . . . . . . . . . . . . . . 72
4.3 After the Loader . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
II Translation and Crucial Code Fragments 75
5 Translating Arithmetic Expressions 81
5.1 Reverse Operations . . . . . . . . . . . . . . . . . . . . . . . . . . 84
5.2 Register Dumping . . . . . . . . . . . . . . . . . . . . . . . . . . 85
5.3 Tree Weighting . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
5.4 Avoiding Reverse Operations . . . . . . . . . . . . . . . . . . . . 92
5.5 Function Calls and Register Dumping . . . . . . . . . . . . . . . 94
5.6 Other Forms of the Tree . . . . . . . . . . . . . . . . . . . . . . . 95
5.7 Combinations of Arithmetic Types . . . . . . . . . . . . . . . . . 96
6 Translating Boolean Expressions 101
6.1 Evaluating a Relational Expression . . . . . . . . . . . . . . . . . 101
6.2 Boolean or Conditional Operators? . . . . . . . . . . . . . . . . . 103
6.3 Jumping Code for Boolean Expressions . . . . . . . . . . . . . . 104
6.3.1 Relational expressions . . . . . . . . . . . . . . . . . . . . 107
6.3.2 Binary Boolean operators . . . . . . . . . . . . . . . . . . 108
CONTENTS ix
6.3.3 Boolean variables and constants . . . . . . . . . . . . . . 109
6.3.4 Conditional expressions . . . . . . . . . . . . . . . . . . . 109
7 Translating Statements and Declarations 113
7.1 Assignment Statement . . . . . . . . . . . . . . . . . . . . . . . . 113
7.2 ‘While’ Statement . . . . . . . . . . . . . . . . . . . . . . . . . . 116
7.3 BCPL ‘for’ Statement . . . . . . . . . . . . . . . . . . . . . . . . 118
7.4 FORTRAN ‘DO’ Statement . . . . . . . . . . . . . . . . . . . . . 122
7.5 Compound Statements . . . . . . . . . . . . . . . . . . . . . . . . 124
7.6 ALGOL-60-like Blocks . . . . . . . . . . . . . . . . . . . . . . . . 126
7.7 Procedure Declarations . . . . . . . . . . . . . . . . . . . . . . . 126
8 Creating and Using the Symbol Table 129
8.1 Table Lookup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
8.1.1 Sequential lookup . . . . . . . . . . . . . . . . . . . . . . 131
8.1.2 Binary chop . . . . . . . . . . . . . . . . . . . . . . . . . . 131
8.1.3 Name tree . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
8.2 Hash Addressing . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
8.2.1 Re-hashing after a collision . . . . . . . . . . . . . . . . . 135
8.2.2 Chaining to avoid clustering . . . . . . . . . . . . . . . . . 138
8.2.3 Keywords in the symbol table . . . . . . . . . . . . . . . . 138
8.2.4 Tables of constants . . . . . . . . . . . . . . . . . . . . . . 139
8.3 Object Descriptions in the Symbol Table . . . . . . . . . . . . . . 139
8.3.1 Assigning addresses to data objects . . . . . . . . . . . . . 140
8.3.2 Assigning addresses to labels and procedures . . . . . . . 141
8.4 Single Names with Multiple Descriptors . . . . . . . . . . . . . . 142
8.4.1 Hierarchical qualification in COBOL . . . . . . . . . . . . 142
8.4.2 Block-structuring and scope . . . . . . . . . . . . . . . . . 144
9 Accessing an Element of a Data Structure 147
9.1 Accessing an Element of a Vector . . . . . . . . . . . . . . . . . . 148
9.1.1 Fixed-size vectors . . . . . . . . . . . . . . . . . . . . . . . 150
9.1.2 Run-time checks on vector access . . . . . . . . . . . . . . 151
9.1.3 Vectors as arguments in a procedure call . . . . . . . . . . 152
9.2 Accessing an Element of an Array . . . . . . . . . . . . . . . . . 152
9.2.1 Multiplicative subscript calculation . . . . . . . . . . . . . 153
9.2.2 Non-multiplicative array address calculation . . . . . . . . 155
x CONTENTS
9.3 Record Data Structures . . . . . . . . . . . . . . . . . . . . . . . 158
9.3.1 Record-type declarations . . . . . . . . . . . . . . . . . . . 160
9.3.2 Accessing an element of a record . . . . . . . . . . . . . . 160
9.3.3 Union types and variant records . . . . . . . . . . . . . . 163
10 Code Optimisation 165
10.0.4 Language restrictions and accurate optimisation . . . . . 167
10.0.5 Is optimisation cost-effective? . . . . . . . . . . . . . . . . 169
10.1 Net Effect and Order of Computation . . . . . . . . . . . . . . . 170
10.1.1 Basic blocks, regions and structured statements . . . . . . 172
10.2 Optimisation within a Basic Block . . . . . . . . . . . . . . . . . 174
10.2.1 Constant folding . . . . . . . . . . . . . . . . . . . . . . . 175
10.2.2 Deferred storage . . . . . . . . . . . . . . . . . . . . . . . 177
10.2.3 Global register allocation . . . . . . . . . . . . . . . . . . 177
10.2.4 Redundant code elimination . . . . . . . . . . . . . . . . . 178
10.2.5 Peephole optimisation . . . . . . . . . . . . . . . . . . . . 180
10.3 Loops and Code Motion . . . . . . . . . . . . . . . . . . . . . . . 180
10.3.1 Loop-invariant code . . . . . . . . . . . . . . . . . . . . . 181
10.3.2 Strength reduction . . . . . . . . . . . . . . . . . . . . . . 182
10.4 Hardware Design and Optimisation . . . . . . . . . . . . . . . . . 184
10.5 Language Design and Optimisation . . . . . . . . . . . . . . . . . 185
III Run-time Support 189
11 Procedure Call and Return 193
11.1 The Tasks of Procedure Call and Return Fragments . . . . . . . 193
11.2 Layout of Procedure Call and Return Fragments . . . . . . . . . 196
11.3 Recursion and the ‘Efficiency’ of FORTRAN . . . . . . . . . . . 199
11.4 Simple Stack Handling . . . . . . . . . . . . . . . . . . . . . . . . 201
11.4.1 Allocating space for vectors and arrays . . . . . . . . . . . 204
12 Arguments and Parameters 209
12.1 Different Kinds of Argument Information . . . . . . . . . . . . . 209
12.2 Passing Argument Information . . . . . . . . . . . . . . . . . . . 211
12.3 Generating Code for a Single Argument . . . . . . . . . . . . . . 215
12.4 Checking Stack Limits . . . . . . . . . . . . . . . . . . . . . . . . 217
12.5 The Evils of Run-time Argument Checking . . . . . . . . . . . . 218
CONTENTS xi
12.5.1 Mutually recursive procedures . . . . . . . . . . . . . . . . 219
12.5.2 Separately compiled sections of program . . . . . . . . . . 221
12.5.3 Incompletely specified parameters . . . . . . . . . . . . . 222
13 Environments and Closures 225
13.1 An Explicit Representation of the Environment . . . . . . . . . . 228
13.2 The Environment Link Mechanism . . . . . . . . . . . . . . . . . 231
13.3 The Display Vector Mechanism . . . . . . . . . . . . . . . . . . . 234
13.4 ALGOL 60’s ‘call by name’ . . . . . . . . . . . . . . . . . . . . . 237
13.5 Block Structure and Data Frames . . . . . . . . . . . . . . . . . . 239
13.6 Non-local ‘goto’ Statements . . . . . . . . . . . . . . . . . . . . . 243
14 Efficiency, Heaps and Lifetimes 245
14.1 Procedure Call with PUSH and POP Instructions . . . . . . . . . 246
14.2 Addressing the Stack with a Single Register . . . . . . . . . . . . 248
14.3 Heap Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
14.4 ALGOL 68, Lifetimes and Pointers . . . . . . . . . . . . . . . . . 258
14.4.1 Multi-word results . . . . . . . . . . . . . . . . . . . . . . 261
14.5 SIMULA 67 ‘classes’ . . . . . . . . . . . . . . . . . . . . . . . . . 262
IV Parsing Algorithms 267
14.6 The Dangers of Backtracking . . . . . . . . . . . . . . . . . . . . 270
15 Notation and Formal Language Theory 273
15.1 Languages and Sentences . . . . . . . . . . . . . . . . . . . . . . 274
15.2 Generating a Sentence . . . . . . . . . . . . . . . . . . . . . . . . 275
15.3 Grammars and Productions . . . . . . . . . . . . . . . . . . . . . 278
15.4 The Chomsky Hierarchy . . . . . . . . . . . . . . . . . . . . . . . 280
15.5 The Parsing Problem . . . . . . . . . . . . . . . . . . . . . . . . . 282
15.6 Derivations and Sentential Forms . . . . . . . . . . . . . . . . . . 283
15.7 Equivalence and Ambiguity . . . . . . . . . . . . . . . . . . . . . 284
15.7.1 When is a grammar ambiguous? . . . . . . . . . . . . . . 288
15.8 Lexical Analysis and type 3 grammars . . . . . . . . . . . . . . . 288
15.9 Warshall’s Closure Algorithm . . . . . . . . . . . . . . . . . . . . 292
15.10Context Dependency and Two-level Grammars . . . . . . . . . . 294
15.10.1Parsing with a context-dependent grammar . . . . . . . . 295
xii CONTENTS
16 Top-down Syntax Analysis 297
16.1 Factoring to Reduce Backtracking . . . . . . . . . . . . . . . . . 299
16.2 Removing Left Recursion . . . . . . . . . . . . . . . . . . . . . . 301
16.3 One-symbol-look-ahead and One-track Analysers . . . . . . . . . 305
16.4 Context Clashes and Null Symbols . . . . . . . . . . . . . . . . . 309
16.5 A One-track Grammar is Unambiguous . . . . . . . . . . . . . . 315
16.6 Error Detection and Processing . . . . . . . . . . . . . . . . . . . 316
16.6.1 Reporting a syntax error . . . . . . . . . . . . . . . . . . . 320
16.7 Pragmatics and Tricks . . . . . . . . . . . . . . . . . . . . . . . . 320
16.8 Interpretive Top-down Analysis . . . . . . . . . . . . . . . . . . . 323
16.9 Automatic Generation of Top-down Analysers . . . . . . . . . . . 327
17 Operator-Precedence Analysis of Expressions 329
17.1 Determining the Precedence of Operators . . . . . . . . . . . . . 331
17.2 Numerical Operator Priorities . . . . . . . . . . . . . . . . . . . . 334
17.3 Reducing a Phrase-pattern to a Single Symbol . . . . . . . . . . 337
17.4 An Operator-precedence Grammar is Unambiguous . . . . . . . . 340
17.5 Input States, Error Detection and Unary Operators . . . . . . . . 340
17.5.1 Unary expression operators . . . . . . . . . . . . . . . . . 342
17.5.2 Implementing the input-state mechanism . . . . . . . . . 342
17.6 Recursive operator-precedence analysis . . . . . . . . . . . . . . . 343
18 LR(1) Syntax Analysis 347
18.0.1 Notation and etymology . . . . . . . . . . . . . . . . . . . 348
18.0.2 LR(1) languages . . . . . . . . . . . . . . . . . . . . . . . 350
18.1 Analyser States and the Stack . . . . . . . . . . . . . . . . . . . . 350
18.2 Why It Works: Non-deterministic Syntax Analysis . . . . . . . . 353
18.2.1 Efficient implementation . . . . . . . . . . . . . . . . . . . 357
18.3 Building an SLR(1) Analyser . . . . . . . . . . . . . . . . . . . . 358
18.4 Constructing an LALR(1) Analyser . . . . . . . . . . . . . . . . . 363
18.4.1 Encoding the state table . . . . . . . . . . . . . . . . . . . 366
18.4.2 It doesn’t always work! . . . . . . . . . . . . . . . . . . . 366
18.5 Error Detection and Tree-building Actions . . . . . . . . . . . . . 367
18.6 Error Recovery . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
18.7 What are the True Advantages of LR(1) Analysis? . . . . . . . . 369
CONTENTS xiii
V Interpreting and Debugging 373
19 Interpreters and Interpretation 377
19.1 Interpreting the parse tree . . . . . . . . . . . . . . . . . . . . . . 378
19.1.1 The costs of interpretation . . . . . . . . . . . . . . . . . 378
19.2 Imitative Interpretation . . . . . . . . . . . . . . . . . . . . . . . 380
19.3 Linearising or Virtual-machine Interpreters . . . . . . . . . . . . 382
19.3.1 Linearising the program . . . . . . . . . . . . . . . . . . . 382
19.3.2 Representing the environment . . . . . . . . . . . . . . . . 385
19.3.3 Setting up the environment . . . . . . . . . . . . . . . . . 387
19.4 The Design of Languages for Interpretation . . . . . . . . . . . . 388
19.4.1 ‘Deep’ and ‘shallow’ dynamic binding . . . . . . . . . . . 388
19.5 Debugging with an Interpreter . . . . . . . . . . . . . . . . . . . 389
19.5.1 Run-time editing . . . . . . . . . . . . . . . . . . . . . . . 391
19.6 Combining Interpretation and Compilation . . . . . . . . . . . . 392
20 Run-time Debugging Aids 395
20.0.1 Assembly Code Debuggers . . . . . . . . . . . . . . . . . . 396
20.0.2 Source Language Debugging . . . . . . . . . . . . . . . . . 397
20.1 Off-line debugging and the panic dump . . . . . . . . . . . . . . . 397
20.2 Interactive debugging . . . . . . . . . . . . . . . . . . . . . . . . 399
20.3 Run-time event tracing . . . . . . . . . . . . . . . . . . . . . . . . 399
20.4 Break points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
20.5 Producing Debuggable Code . . . . . . . . . . . . . . . . . . . . . 401
20.6 Doing Without the Debugger . . . . . . . . . . . . . . . . . . . . 401
A The BCPL language 403
B Assembly code used in examples 407
Bibliography 411
xiv CONTENTS
Section I
Modular Organisation of
Compilers
1
Understanding And Writing Compilers A Doityourself Guide Richard Bornat
3
Compilers, at first appearance, seem to display a bewildering variety of organisa-
tion: there are single-pass compilers, multi-pass compilers, optimising compilers,
load-and-go compilers, interactive compilers and so on and on. Luckily for the
compiler-writer, all these apparently different forms can be seen as variations on
a simple theme. All compilers perform the same collection of tasks and apparent
variations come about because the tasks are put together in slightly different
ways or because certain of the tasks are given more emphasis than others in
some variations. Below the surface the theme is constant – all compilers per-
form the same sequence of essential tasks in the same order and by and large
the optional tasks which they may perform have a fixed position in the sequence
as well.
It’s this underlying regularity of organisation which makes it possible to describe
the principles of compiler writing in a space as small as this book, because the
question of the organisation of the compiler as a whole can be separated from
the questions of the design of its sub-sections. Each of the sub-tasks, and the
various algorithms for carrying it out, can then be studied in relative isolation.
In addition the regularity makes compiler writing largely source-language inde-
pendent: though to the novice it might seem that a COBOL compiler would
differ enormously from a PASCAL compiler I shall show that it isn’t so. Per-
haps even more surprising is that the problems of compiler writing are largely
object-machine independent as well. Both source language and object machine
affect only details of compiler construction, whilst the overall organisation re-
mains fixed. Once you have understood the principles of compiler design, most
of the details fall naturally into place.
Of the chapters in this section, chapter 1 discusses the organisation of compi-
lation as a sequence of phases, each carried out by a separate module of the
compiler, and gives brief examples of the operation of each phase. Chapter 2
gives a more detailed introduction to the principles of translation and justifies
the use of a tree-walking translation mechanism. Chapter 3 gives an introduc-
tion to the principles of syntax analysis. Chapter 4 discusses lexical analysis
and loading: apart from a discussion of lexical analysis grammars in chapter 15
and a discussion of symbol-table building algorithms in chapter 8, both of these
rather simple topics are then ignored for the rest of the book.
Later sections expand the treatment. Section II concentrates on translation
and code optimisation, section III on the code which performs run-time sup-
port functions such as stack handling during a procedure call, section IV deals
with syntax analysis and section V treats the allied topics of interpretation (as
distinct from compilation) of source programs and the run-time debugging of
compiled programs.
I discuss these topics in this order because I believe that it is useful to un-
derstand mechanisms of translation before mechanisms of syntax analysis, even
though syntax analysis occurs before translation when a program is compiled.
Translation, with code optimisation, is probably the most interesting phase of
compilation to the active compiler-writer. The novice needs some instruction in
the mechanisms of syntax analysis – but it comes much easier if you’ve seen the
4
requirements of a translator first. In that way you can see why a syntax analyser
is necessary and why it must produce the kind of output that it does. Also,
having seen the powers and limitations of simple translation, you’re in a better
position to assess the importance (or unimportance) of code optimisation.
Chapter 1
Phases and Passes
The most obvious overall task of a compiler is to read a program in one language
– the ‘source’ program in the ‘source’ language – and to translate it to produce
an equivalent program in another language – the ‘object’ program in the ‘object’
language. The object language is usually the machine language of a computer,
or something close to it, and the source program is usually in a ‘high-level’
language such as FORTRAN, PASCAL, ALGOL 68, SIMULA 67 or BCPL,
because translation from high-level language to machine language is the practical
problem which compilers exist to solve. Compilers can be written to translate
from any kind of programming language into any other, however, with varying
degrees of efficiency of the resulting object program.
Another part of the compiler’s overall task, just as important but too often
neglected, is that the compiler must check that the source program makes some
kind of sense and, when it doesn’t seem to make sense, must produce a descrip-
tion of the problem (an error report) so that the programmer can correct the
program. I’ll return to discuss the tasks of error detection and reporting later
in this chapter.
Before describing how the overall tasks are split up into sub-tasks it’s worth
discussing the ways in which the sub-sections of a compiler can be combined to
make a complete compiler. The overall activity of compilation can be divided
into a sequence of phases, during each of which one of the sub-tasks is carried
out. As emphasised in the introduction to this section, all compilers perform
the same sub-tasks in the same sequence, and therefore all compilers consist
of the same sequence of phases. Conceptually each phase transforms the pro-
gram fragment-by-fragment, taking as input a fragment of some representation
of the source program and producing as output a fragment of some other, trans-
formed, representation. Because the transformation is fragment-by-fragment it
is possible to choose how the phases are linked. If each separate fragment goes
through all the phases before compilation of the next fragment starts we have
a single-pass compiler, because all of the source program is compiled in a single
pass over the source text. Conversely, if the entire program goes through each
5
6 CHAPTER 1. PHASES AND PASSES
one of the phases before any of it is presented to the next phase we have a multi-
pass compiler, because each phase performs a separate pass over the program.
Clearly if there are N phases, you can organise the compiler into anything from
1 to N passes.
Thus every compiler must be multi-phase, but it may or may not be multi-pass.
Logically the phases must be joined together in the same order no matter how
many passes are employed, so that it might not seem to matter whether this book
concentrates on multi-pass or single-pass organisation. In practice, however, the
multi-pass organisation is simpler to describe and to understand because the
interface between the phases is so much more straightforward. Indeed for some
languages – ALGOL 60 is a prominent example – it is difficult to construct an
effective single-pass compiler at all and therefore the multi-pass organisation is
also more generally useful.
It is often believed that multi-pass compilation is inherently less efficient than
single-pass. This is simply untrue: since in any compiler each fragment must be
processed by the same sequence of phases, a single-pass compiler must perform
the same amount of work as a multi-pass compiler. Suppose, however, that each
pass of a multi-pass compiler were to write its output into a disc file (or any
backing store file) from which the next pass had to read it in again. Such a multi-
pass compiler would indeed be less efficient than a single-pass compiler which
didn’t use backing store but the inefficiency would be caused by the overhead
of input and output between passes.
A multi-pass compiler which stores the output of each pass in the main computer
memory will certainly be no slower than a single-pass compiler. On a modern
computer the only disadvantage of multi-pass compilation is that it may use
quite a lot of space to store the output of the various passes. Nowadays com-
puter memories (for everything but the smallest micro-processors!) are large
enough for this minor drawback to be overwhelmed by the enormous advan-
tages of clarity of design, and consequent ease of construction, of a multi-pass
compiler. For most of the rest of this book I describe a compiler organised in
two passes, which is an effective compromise between economy of space and
clarity of organisation. In the discussion of compilation algorithms, however,
it is usually simplest to imagine that each of the phases in a compiler always
performs a separate pass over the program.
1.1 Tasks and Sub-tasks
Figure 1.1 shows a coarse breakdown of the overall compilation task (I deal with
the parallel task of error processing separately below). A compiler must first
analyse a program to see what its intended effect is. The result of this analysis
will be a representation of the program which is adequate for later translation
into the object language. As I show below and in chapter 2, this representation
is a structure which shows how the separate fragments of program inter-relate.
It splits naturally into two parts:
1.1. TASKS AND SUB-TASKS 7
source
program
analysed
program
object
program
ANALYSE TRANSLATE
Figure 1.1: Coarse structure of compilation
(i) the ‘parse tree’ which shows the structure of the program text – how the
fragments of program join together to make up larger fragments, how those
join to make still larger fragments and so on.
(ii) the ‘symbol table’ which provides a correlation between all the different
occurrences of each name throughout the program and hence provides a
link between each name and its declaration, whether implicit or explicit,
in the source text.
These two information structures are the most important milestones in the
compilation process. Compilation as a whole is an activity which first builds up
information structures which describe how the program may be broken into frag-
ments and how these fragments inter-relate, then extracts from these structures
the information which is required in order to translate the program.
Neither analysis nor translation is yet a simple enough sub-task to describe
individually. Figure 1.2 shows a more detailed breakdown into sub-tasks. The
figure shows clearly the hierarchical-sequential nature of the compiling process:
each of the sub-tasks is entrusted to a particular phase and the phases don’t
overlap at all.1
Capital letters (e.g. LEXICAL ANALYSIS) indicate a phase
or sub-task, lower case letters (e.g. parse tree) an information structure or an
intermediate program representation.
The arrangement of phases in figure 1.2 is partly determined by the nature of
the compiling task but is also partly conventional. In particular the LEXICAL
ANALYSIS phase is present for convenience’ sake and the fact that the com-
piler’s output is most commonly presented to a LOAD phase before execution
is equally a matter of convenience.
Consider the LEXICAL ANALYSIS phase: it has been found convenient to in-
clude a phase, immediately before SYNTAX ANALYSIS, which partitions the
characters of the source program into ‘items’, roughly analogous to words and
punctuation marks in natural languages. This not only makes the design of
the syntax analyser easier, but also it happens that almost all of those sections
of the compiler whose speed of operation is important fall into the READ and
LEXICAL ANALYSIS phases. Thus the task of improving the compiler’s op-
erating efficiency can be reduced to the task of improving the efficiency of the
relatively simple input phases and in the rest of the compiler it is possible to
1 There are some minor exceptions to this rule when compiling rather old languages such as
FORTRAN. These exceptions have been eliminated in most modern languages.
8 CHAPTER 1. PHASES AND PASSES
source program
characters
lexical items
parse tree
object code
object program
s
y
m
b
o
l
t
a
b
l
e
READ
LEXICAL
ANALYSIS
SYNTAX
ANALYSIS
SIMPLE
TRANSLATION
LOAD
OBJECT
DESCRIPTION
OPTIMISE
TREE
(optional)
OPTIMISE
CODE
(optional)
Figure 1.2: Finer structure of compilation
1.2. TRANSLATION AND OPTIMISATION 9
ignore the demands of speed of operation in order to concentrate on clarity of
design and construction.
Consider next the LOAD phase shown in figure 1.2. If the source program is
split into sections which are compiled separately to produce separate sections of
object code, it is necessary eventually to use a loader program to combine all the
separate code sections into a complete executable program. At the same time
the loader can automatically incorporate sections of code from a separate code
library. People find the facility to split their program into sections convenient,
so the loader increases human efficiency although in terms of machine utilisation
alone it can be more efficient to compile the entire program in one piece and
to avoid the loading overhead entirely. At any rate the compiler may, but need
not, output code in ‘loadable’ form as the compiler-writer wishes.
I discuss the input and output phases (READ, LEXICAL ANALYSIS and
LOAD in figure 1.2) together with the question of code output formats in chap-
ter 4, so as to separate these matters from the more central and more important
phases of analysis, translation and optimisation which occupy most of this book.
1.2 Translation and Optimisation
Two of the phases in figure 1.2 are marked as optional – OPTIMISE TREE
and OPTIMISE CODE. They are included in the figure because they are so
important and so frequently included (or desired by the user if they are not
included!). In order to explain the breakdown of the diagram at this point
it’s necessary to distinguish between ‘simple translation’ and ‘optimisation’ in
general
Simple translation takes a representation of a fragment of the source program
and produces an equivalent fragment of object code. It doesn’t take much
account of the interaction between fragments, except insofar as it must do
in order to produce correct code, but it may be quite sophisticated in the
way in which it selects the particular code fragment which it generates as
the translation of each source program construct.
Optimisation takes a wider view of the program: it looks at a representation
of a larger section of the source program and reorganises the fragments
within it, or improves the way in which they interface, so as to produce a
shorter object program or a faster object program than simple translation
could produce. Sometimes it is possible to produce an optimised object
program which is both shorter and faster than one produced by simple
translation.
It’s not possible to draw a hard line between the two approaches because it’s
not clear when sophisticated simple translation becomes primitive optimisa-
tion. Some so-called ‘optimising’ compilers would be better viewed as rather
good simple translators: many straightforward compilers include some element
10 CHAPTER 1. PHASES AND PASSES
of optimisation. True optimisations, for the purposes of this book, are those
translations which exploit peculiarities of the control flow specified in the source
program. Chapter 10 discusses this topic more fully.
There are essentially two ways in which a compiler can optimise a program –
i.e. produce a better-than-simple translation
(i) It can include a phase which alters the source program algorithm in such
a way that subsequent simple translation can produce the desired effect.
This is shown as OPTIMISE TREE in figure 1.2: essentially it replaces
the source program’s algorithm with another which has the same effect
but which can be translated into more efficient code.
(ii) It can include a phase which modifies the code produced by simple trans-
lation in order to increase the efficiency of the object program. This is
shown as OPTIMISE CODE in figure 1.2: this phase looks mainly at the
ways in which the code fragments interface with each other.
These two approaches can be equivalent in their effect and sometimes com-
pilers employ both. The OPTIMISE TREE technique is the more machine-
independent of the two and is perhaps potentially the more powerful.
1.3 Object Descriptions in the Symbol Table
The names used in a program are merely used to identify run-time objects of one
kind or another – variables, arrays, records, procedures, labels and so on. Apart
from the needs of debugging, the source program names used are completely
arbitrary and the object program operates entirely upon the run-time objects
without taking any account of the names which originally introduced them. In
order to keep track of the association between names and run-time objects, the
compiler must keep a table (the symbol table in figure 1.2) in which it stores
against each name a description of the run-time object which that name denotes.
The translator uses this table to replace each occurrence of a name by a reference
to the corresponding run-time object.
As names are encountered in the source program they are inserted into the
symbol table by the lexical analyser (more discussion of this in chapters 4 and
8) but information about the associated run-time object can only be inserted
after the program has been syntactically analysed. Names are ‘declared’ ei-
ther implicitly (e.g. in FORTRAN) or explicitly (most other languages!). In
general the declarative information must be processed before any statement
using the name can be translated. Thus correlation of names with run-time
objects must precede translation, but must of course follow syntax analysis in
which the declarative information is recognised. So there is only one time in the
compilation process at which such correlation is possible – hence the OBJECT
DESCRIPTION phase in figure 1.2 falls between syntax analysis and simple
translation or optimisation.
1.4. RUN-TIME SUPPORT 11
program: begin integer a,b;
....
procedure A(x) real x;
begin real y;
.... B(‘a’); ....
end;
procedure B(u) character u;
begin string v;
.... A(3.7); ....
end;
.... A(1.0); ....
end
After outer block calls A, which calls B, which calls A again:
a b
outer
block
x y
A(i)
u v
B
x y
A(ii)
Figure 1.3: Activation Record Structure in a simple program
1.4 Run-time Support
Much of the object program consists of instructions which carry out the tasks
of calculation, assignment, comparison, etc. which are specified in the source
program. However there are also sections of the source program whose task is
run-time support of various kinds. Two important tasks of run-time support are
to implement the storage and register manipulations necessary for the execution
of a procedure call2
(discussed in detail throughout section III) and the mainte-
nance of ‘heap’ storage systems (touched on in chapter 14). The efficiency of the
run-time support fragments in the object program ought to be a major concern
of the compiler writer: users will present programs for compilation which are
based on highly efficient algorithms and will be justifiably annoyed if the object
program is inefficient purely because of the operation of run-time support code
fragments ‘behind the scenes’.
The most important task of the run-time support fragments is to maintain the
Activation Record Structure. When a procedure call is executed a procedure
2 Throughout this book I use the word ‘procedure’ to include the word ‘function’ – a function
is merely a procedure which computes and returns a result. So far as run-time support is
concerned, functions and procedures are almost identical.
12 CHAPTER 1. PHASES AND PASSES
activation is created, which exists until the corresponding procedure return is
executed. In non-recursive languages such as FORTRAN only one activation at
most can ever exist for each procedure but in languages which permit recursion
there may be several activations in existence, each in a different stage of exe-
cution of a single procedure body and each with its private storage and private
copy of the arguments with which it was called. Each of these activations can
be described by a data structure – in a recursive programming language the
data structure is an ‘activation record’, ‘data frame’ or ‘stack frame’ stored on
a stack – and the linkages between these data structures define the Activation
Record Structure. Figure 1.3 shows a simple example of the structure which
can result.
The maintenance of the Activation Record Structure in an implementation of a
recursive programming language is an important ‘overhead’ cost in the execution
of the object program. By prohibiting recursion, languages such as FORTRAN
reduce the overhead cost but the discussion in section III shows how careful
design of code fragments can minimise or even eliminate the efficiency gap be-
tween recursive and non-recursive languages. Even recursive languages must
restrict the manipulation of some values in the object program in order to allow
the Activation Record Structure to be kept in a stack: chapter 14 discusses the
way in which the necessary restrictions can be applied in an implementation of
ALGOL 68 and touches on the implementation of SIMULA 67, which can avoid
the imposition of such restrictions because it doesn’t use a data-frame stack.
1.5 Source Program Errors
“Any fool can write a compiler for correct source programs”
(J.S. Rohl 1972)
People write programs and people make mistakes. They often need help in order
to correct their mistakes and sometimes a compiler can provide it. Compiler
designers too often seem to place ‘helping people’ rather low down on their
scale of priorities, well below ‘compiler efficiency’, ‘object program efficiency’
and ‘source language facilities’. My opinion is that this order of priorities is
wrong-headed but I hope to demonstrate in this book that it is unnecessary
as well. There is no conflict in practice between, on the one hand, the goal of
providing help with errors in source programs and, on the other hand, the goal
of providing a compiler which is efficient and which produces efficient object
programs.
A compiler has two basic tasks to perform in connection with source programs
which contain errors:
(i) Compile-time error processing:
The compiler must somehow provide information to a programmer who
has submitted a program which cannot be compiled. The information
must, as far as possible, give an explanation of why the program cannot
1.5. SOURCE PROGRAM ERRORS 13
be compiled and if possible make suggestions as to how the error might
be corrected.
(ii) Error-checking code:
Many programs, though syntactically correct, specify impossible sequences
of operations when they are executed. Some object program errors (e.g.
arithmetic overflow) may be detected by the object machine’s hardware
but probably many others will not be. In order to detect run-time errors
which are not covered by hardware checks the compiler must produce ob-
ject code fragments which check whether a run-time error has occurred
each time they are run. An example is the array-bound check: it is com-
mon for a compiler to generate code for an array access which checks the
validity of the array subscript each time an access is made.
In either case error processing can be divided into three separate activities: error
detection, error reporting and error recovery.
First consider compile-time errors: chapter 3 and section IV show that compile-
time error detection is an automatic consequence of the normal compilation
process. This implies that detection of errors cannot affect the speed of op-
eration of a compiler; therefore the (imaginary super-human) user who always
writes correct programs is not affected by the vagaries of ordinary mortals, at
least so far as speed of compilation is concerned. Once an error has been de-
tected in the source program the programmer is no longer concerned simply with
how rapidly the compiler translates but rather with how quickly the error can
be removed from the program. A small amount of computer time spent in error
reporting and recovery is well spent if it saves human time and effort. Of course
a compiler which includes a great deal of error reporting and recovery facilities
will be larger than one that contains only a small amount and so there is some
penalty in terms of space occupied, but the extra space and the few fractions of
a second of computer time spent in producing a readable error report are surely
worthwhile if they save minutes (or perhaps hours!) of human effort.
Similar considerations apply to run-time error-handling. In this case error-
detection can be expensive – the code fragments which repeatedly check for
error will inevitably slow down the execution of the object program – but the
bulk of the error processing code will be concerned with error reporting and error
recovery and so will only be activated when an error has already been detected.
It’s outside the scope of this book to debate when, if ever, the programmer
should ask the compiler to omit error checking code so as to save execution
time and object program space – at any rate the compiler should always give
the option that it can be included.
Chapter 20 discusses run-time error handling in detail. I include run-time error
checking as a task of the compiler, rather than of the operating system or
of some other separate program, because the design of object code fragments
must take into account the needs of run-time error-checking and error-reporting.
Furthermore the compiler designer must consider the production, at compile-
time, of information structures which will be used by a run-time debugger to
14 CHAPTER 1. PHASES AND PASSES
begin integer a; real b;
a := 1; b := 1.2;
a := b+1;
print(a*2)
end
Figure 1.4: Example program
provide error reports related to the original source program. The requirements
of run-time error processing can have a major influence on the detail design of
object code fragments.
Figure 1.2 doesn’t include any mention of error processing but in reality it is a
part of the task of every phase of compilation. I shall return again and again
in later chapters to the question of how the various phases may be designed to
produce readable and understandable error reports.
1.6 Two-pass Compilation
Most compilers that aren’t single-pass use only two passes rather than the four
or five implied by the structure of figure 1.2. Using two passes is a popular com-
promise between generality and the somewhat demanding space requirements of
a true multi-pass compiler. The two-pass organisation essentially follows figure
1.1 – there is an analysis pass and a translation pass. The analysis pass includes
the READ, LEXICAL ANALYSIS and SYNTAX ANALYSIS phases. In order
to combine these phases into a single pass it is conventional to make READ a
subroutine of LEXICAL ANALYSIS and LEXICAL ANALYSIS a subroutine
of SYNTAX ANALYSIS – as chapter 4 shows, this isn’t very difficult. The
translation pass usually consists of a SIMPLE TRANSLATION phase only.
The two-pass organisation seems to leave out the OBJECT DESCRIPTION
phase entirely and in a two pass compiler this phase is rather difficult to include
as a separate entity. Object description is usually carried out piecemeal during
one or other (or both) of the passes. Perhaps most convenient is to perform
all object description during the translation pass: every time the translator
encounters a section of the tree that may contain declarative information it
must be processed by the object description phase, which creates symbol table
descriptors based on any declarations contained in that section of the tree,
before it can be translated into object code. The examples in sections II and
III show algorithms in which the object description phase is a subroutine of
the translation phase, called each time the translator encounters a ‘procedure
declaration’ node or a ‘block’ node in the parse tree.
1.6. TWO-PASS COMPILATION 15
beginsymbol
integersymbol
name,
semicolonsymbol
realsymbol
name,
semicolonsymbol
name,
assignsymbol
number, 1
semicolonsymbol
name,
assignsymbol
....
a
b
(empty)
(empty)
Name Description
Figure 1.5: Part of the output from the lexical analyser
block
declarations statements
intdecl realdecl assign assign assign ProcCall
a b a 1 b 1.2 a +
b 1
print *
a 2
Figure 1.6: Output from the syntax analyser
16 CHAPTER 1. PHASES AND PASSES
assign
name plus
name 1
number
(pointer to entry
for ‘a’ in
symbol table)
(pointer to entry
for ‘b’ in
symbol table)
Figure 1.7: Data structure representation of a tree
a variable, integer, address #1
b variable, real, address #2
print
procedure, one integer argument,
address from loader
Figure 1.8: Symbol table descriptors
LOAD 1, address #2
fADDn 1, 1.0
FIX 1,
STORE 1, address #1
Figure 1.9: Possible machine (assembly) code output
1.7. AN EXAMPLE OF COMPILATION 17
1.7 An Example of Compilation
In the rest of this chapter I show, very briefly, the action of some of the phases
of compilation taking as example input the program of figure 1.4. First of all
the characters of the program are read in, then these characters are lexically
analysed into separate items. Some of the items will represent source program
identifiers and will include a pointer to the symbol table.3
Figure 1.5 shows
part of the results of lexical analysis: in most languages subdivision of the input
into items can be performed without regard to the context in which the name
occurs.
Next, the syntax analyser examines the sequence of items produced by the lexical
analyser and discovers how the items relate together to form ‘phrase’ fragments,
how the phrases inter-relate to form larger phrases and so on. The most general
description of these relationships of the fragments is a tree, as shown in figure
1.6.
Each node of the tree includes a tag or type field which identifies the kind of
source program fragment described by that node, together with a number of
pointers to nodes which describe the subphrases which make it up. Figure 1.7
shows how part of the tree shown in figure 1.6 might actually be represented as
a data structure. There are of course a number of different ways of representing
the same tree, and that shown in figure 1.7 is merely an example. There are as
many different ways of drawing the structure, and throughout most of this book
I shall show trees in the manner of figure 1.6. The only significant differences
between the two picturings is that, in the first, nodes aren’t shown as sequences
of boxes and names aren’t shown as pointers to the symbol table. Figure 1.7
is perhaps more faithful to reality, so it should be borne in mind whenever you
encounter a simplified representation like that in figure 1.6.
After syntax analysis, the object description phase takes the tree and the symbol
table entries produced by the lexical analyser. It analyses the declarative nodes
in the tree, producing descriptive information in the symbol table as shown in
figure 1.8. Standard procedures, such as ‘print’, may receive a default declara-
tion before translation: figure 1.8 shows a possible entry in the symbol table.
Note that, since the tree contains a pointer to the symbol table in each node
which contains a reference to an identifier, neither object description phase nor
translator need search the symbol table but need merely to follow the pointer
to the relevant entry.
After the object description phase has filled in the descriptors in the symbol
table, a simple translation phase can take the tree of figure 1.7 together with
the symbol table of figure 1.8 and produce an instruction sequence like that
shown in figure 1.9.4
3 The item which represents a number may also contain a pointer to a table which contains
a representation of the number. For simplicity figures 1.5 and 1.6 show the value of the
number as part of the lexical item itself.
4 See appendix B for a brief description of the assembly code instructions used in this and
18 CHAPTER 1. PHASES AND PASSES
The addresses used in the instructions need finally to be relocated by the loader.
Suppose that, when the program is loaded into store, its memory cell space
starts at address 23. Then the first line of figure 1.9 would be converted into
‘LOAD 1, 24’ and the last line into ‘STORE 1, 23’.
Optimisation of the object code in this case could produce an enormous im-
provement in its execution efficiency. The total effect of the program is to print
the number ‘4’. By looking at the way in which the values of variables are used
throughout the program and by deferring translation of assignment statements
until their result is required – in this case they are never required – an optimi-
sation phase could reduce the program just to the single statement ‘print(4)’.
Optimisation is a mighty sledgehammer designed for bigger nuts than this ex-
ample, of course, and it is always an issue whether the expense of optimisation
is worth it: in the case of figure 1.4 it would certainly cost more to optimise the
program than it would to run it!
Summary
The underlying organisation of compilers is simple and modular. This chapter
discusses how the various phases cooperate so that later chapters can concen-
trate on the separate phases in isolation.
Input and lexical analysis is discussed in chapters 4 and 8; syntax analysis in
chapters 3, 16, 17 and 18; object description in chapter 8; translation in chapters
5, 6, 7 and 9; optimisation in chapter 10; loading in chapter 4; run-time support
in chapters 11, 12, 13 and 14; run-time debugging in chapter 20.
other examples.
Chapter 2
Introduction to Translation
The most important task that a compiler performs is to translate a program
from one language into another – from source language to object language.
Simple translation is a mechanism which takes a representation of a fragment of
the source program and produces an equivalent fragment in the object language
– a code fragment which, when executed by the object machine, will perform
the operations specified by the original source fragment.
Since the object program produced by a simple translator consists of a sequence
of relatively independent object code fragments, it will be less efficient than
one produced by a mechanism which pays some attention to the context in
which each fragment must operate. Optimisation is a mechanism which exists
to cover up the mistakes of simple translation: it translates larger sections of
program than the simple translator does, in an attempt to reduce the object
code inefficiencies caused by poor interfacing of code fragments.
In order to be able to produce object code phrases the translator must have
access to a symbol table which provides a mapping from source program names
to the run-time objects which they denote. This table is built by the lexical
analyser (see chapters 4 and 8) which correlates the various occurrences of each
name throughout the program. The mapping to run-time objects is provided
by the object description phase (see chapter 8) which processes declarative in-
formation from the source program to associate each identifier in the symbol
table with a description of a run-time object.
This chapter introduces the notion of a ‘tree-walking’ translator, which I believe
is a mechanism that is not only easy to construct but which can readily and
reliably produce efficient object code fragments. Such a translator consists of a
number of mutually recursive procedures, each of which is capable of translating
one kind of source program fragment and each of which can generate a variety
of different kinds of object code fragments depending on the detailed structure
of the source fragment which is presented to it.
Because the process of translation depends on the selection at each point of one
19
20 CHAPTER 2. INTRODUCTION TO TRANSLATION
Statement:
if hour*60+minute=1050 or tired then leave(workplace)
conditional statement
[EXPRESSION]
Boolean or
[STATEMENT]
procedure call
[LEFT]
relation =
[RIGHT]
name
tired
[LEFT]
arithmetic +
[RIGHT]
number
[PROCEDURE]
name
[ARGUMENTS]
name
leave workplace
1050
[LEFT]
arithmetic *
[RIGHT]
name
minute
[LEFT]
name
hour
[RIGHT]
number
60
Figure 2.1: Tree describing a simple statement
of a number of possible code fragments translators tend to be voluminous, but
since the selection of a fragment is fairly simple and generating the instructions
is very straightforward, they tend to run very rapidly. A simple translator will
usually use less than 15% of the machine time used by all phases of the compiler
together, but will make up more than 50% of the compiler’s own source code.
2.1 Phrases and Trees
In this chapter I introduce the technical term phrase, which is used to describe
a logically complete fragment of source program. Statements, for example, are
phrases; so are expressions. Phrases are either ‘atomic’ – e.g. a name, a number
– or are made up of sub-phrases – e.g. a statement may have a sub-phrase which
is an expression, the statement itself may be a sub-phrase of a block which is
a sub-phrase of a procedure declaration, and so on. In this chapter the largest
phrase shown in the examples is an expression or a simple structured statement.
In a multi-pass compiler the unit of translation is the entire program – at least
2.1. PHRASES AND TREES 21
cond
or
=
+
*
call
numb 1050
numb 60
tired
(in symbol table)
leave
(in symbol table)
workplace
(in symbol table)
minute
(in symbol table)
hour
(in symbol table)
Figure 2.2: Data-structure representation of the tree
22 CHAPTER 2. INTRODUCTION TO TRANSLATION
at the topmost level – but a single page isn’t large enough to show the necessary
trees!
The state of the art in programming language translation is now, and will remain
for some time, that a program can only be translated if it is first analysed to
find how the phrases inter-relate: then the translator can consider each phrase
in turn. The result of the analysis shows how the program (the phrase being
translated) can be divided into sub-phrases and shows how these sub-phrases
inter-relate to make up the entire phrase. For each sub-phrase the description
shows how it is divided into sub-sub-phrases and how they are inter-related.
The description continues to show the subdivision of phrases in this way until
the atomic phrases – the items of the source text – are reached.
The most general representation of the results of such an analysis is a tree
like that in figure 2.1. The lines are branches, the place where branches start
and finish are nodes. The topmost node of the tree is its root – the tree is
conventionally drawn ‘upside down’. Nodes which don’t divide into branches
are leaves and represent the atomic items of the source program. Each phrase of
the source program is represented by a separate node of the tree. To translate a
phrase it is merely necessary to concentrate on one node and, if it has sub-nodes,
to translate its sub-nodes as well.
Figure 2.2 shows a possible data-structure representation of the tree of figure
2.1 using record-vectors and pointers. Many other representations are possible
– for example one in which each pointer is represented by a subscript of a
large integer vector. The tree structure, no matter how it is represented, gives
the translator the information it needs in order to translate the statement – it
shows which phrases are related, and how they are related. From the source
statement it isn’t immediately obvious what the object program should do.
Should it, for example, evaluate ‘hour*60’? Must it evaluate ‘minute=1050’?
What about ‘1050 or tired’? A glance at the tree gives the answers – only the
first is represented by a node because analysis has shown it to be a phrase of
the program.
In order to be able to translate a phrase of a program it is essential to know what
kind of phrase it is, what its sub-phrases are and how they are inter-related.
There can be non-tree-like representations of the necessary information, but the
tree holds and displays the information in its most accessible and useful form. In
addition it emphasises the essentially recursive nature of translation – a nature
which is somewhat hidden, but not denied, in so-called ‘linear’ representations.
2.2 Tree Walking
It’s extremely easy to translate – generate code for – a phrase if you know how
to generate code for its sub-phrases. Figures 2.3 and 2.4 show some sample
procedures which translate in this way. Each procedure translates a phrase by
generating a single instruction, or a short sequence of instructions, which links
together the code fragments that are the translation of its sub-phrases. Only
2.2. TREE WALKING 23
• Conditional statement (if-then):
1. call procedure which generates code to load value of expression part
into register 1;
2. invent a label #Lf;
3. generate JUMPFALSE 1, #Lf;
4. call procedure which generates code to carry out statement part;
5. generate #Lf:.
• Boolean or (result to be in register k):
1. : call procedure which generates code to load value of left operand
into register k;
2. call procedure which generates code to load value of right operand
into register k+1;
3. generate ORr k, k+1.
• Relation ‘=’ (result to be in register k):
1. call procedure which generates code to load value of left operand into
register k+1;
2. call procedure which generates code to load value of right operand
into register k+2;
3. generate LOADn k, TRUE
SKIPEQr k+1, k+2
LOADn k, FALSE.
• arithmetic ‘+’ (result to be in register k):
Exactly as for Boolean or, except that the final instruction is
ADDr k, k+1.
• arithmetic ‘*’ (result to be in register k):
Exactly as for Boolean or, except that the final instruction is
MULTr k, k+1.
Figure 2.3: Translation procedures for non-leaf nodes
24 CHAPTER 2. INTRODUCTION TO TRANSLATION
• name (value to be loaded into register k)
generate LOAD k, <run-time address of variable denoted by name>
• number (value to be loaded into register k)
if value is small enough then
generate LOADn k, <value of number>
else generate LOAD k, <run-time address where value will be found>
Figure 2.4: Translation procedures for leaf nodes
ADDr 2, 3
LOADn 3, 1050
LOADn 1, TRUE
SKIPEQr 2, 3
LOADn 1, FALSE
LOAD 2, tired
JUMPFALSE 1, #Lf
....
<procedure call>
#Lf:
<rest of program>
LOAD 2, hour
LOADn 3, 60
MULTr 2, 3
LOAD 3, minute
ORr 1, 2
....
....
[name]
[name]
[name]
[number]
[number]
[*]
[+]
[=]
[or]
[if]
Figure 2.5: Code produced by the translation procedures
2.3. LINEAR TREE REPRESENTATIONS 25
hour, 60, *, minute, +, 1050, =, tired, or
Figure 2.6: Linearised expression
for indivisible phrases (leaf nodes) is it necessary to know how to translate the
entire phrase. In order to generate code for a sub-phrase, all the translator
needs to do is to look at the type of phrase it is – the ‘tag’ or ‘type’ field of the
tree node – and to call the relevant translation procedure.
Figure 2.5 shows the code produced by these procedures when presented with
the tree of figure 2.1. (The figures don’t show the translation procedure which
handles the procedure call node, or the code produced – section III deals with
this topic.) Starting at the root of the tree, the translator works downwards. It’s
essentially a recursive process – one operand of a ‘+’ node may be another ‘+’
node or may contain a ‘+’ node, for example. Since the process is recursive it’s
trivially easy to translate structured statements like the conditional statement
which might at first seem the hardest to translate. Imagine how straightforward
it is to translate a PASCAL compound statement or an ALGOL 68 serial clause!
The most important thing to note about figure 2.5 is that the code would work
if it was given to the object machine to execute. Tree walking is a powerful
technique precisely because it is easy to design procedures like those in figures 2.3
and 2.4 which really will generate accurate code. The object code produced by
these procedures isn’t optimal by any means, but it’s relatively trivial to change
the procedures to improve it. Each procedure is a separate module: although
figure 2.3 doesn’t include all of the standard programming language expression
operators, it wouldn’t be necessary to make any changes to the existing set when
new node-translating procedures are added.
2.3 Linear Tree Representations
Before describing how to improve the code produced by the translation proce-
dures of figure 2.3 and 2.4 it is necessary to dispose of a red herring – ‘Reverse
Polish’ or ‘postfix string’ representation. The collection of procedures in figures
2.3 and 2.4 describe a tree walk – a process which visits the nodes of the tree in
a particular, predetermined order. If the nodes of the expression part of the tree
of figure 2.1 are written out in just the order in which the tree walker would visit
them then a translator can walk them with a non-recursive algorithm. Figure
2.6 shows the nodes of the tree from figure 2.1, written out in the order in which
they are visited by the tree walker.
Considering only the string which represents the expression part of the tree in
figure 2.1, it is possible to define a simple iterative procedure which generates
code for it: figure 2.7 shows a possible algorithm. There is a slight difficulty with
the ‘=’ node, which is in effect ‘visited’ twice in the tree walk, once to reserve
the register which will hold TRUE or FALSE and once to actually generate the
26 CHAPTER 2. INTRODUCTION TO TRANSLATION
k := 1 /* k records next free register */
until end-of-string do
switchon current-element into
case name:
Gen(LOAD, k, <address of variable>)
k := k+1 endcase
case number:
if number is small enough then
Gen(LOADn, k, <value of number>)
else
Gen(LOAD, k, <address where value will be found>)
k := k+1 endcase
case ‘+’:
Gen(ADDr, k-2, k-1); k := k-1 endcase
case ‘*’:
Gen(MULTr, k-2, k-1); k := k-1 endcase
case ‘=’:
Gen(LOADn, k, TRUE)
Gen(SKIPEQr, k-1, k-2)
Gen(LOADn, k, FALSE)
Gen(LOADr, k-2, k)
k := k-1 endcase
case ‘or’:
Gen(ORr, k-2, k-1); k := k-1 endcase
default: CompilerFail("invalid element in string")
current-element := next-element
Figure 2.7: Linear string walker
2.4. IMPROVING THE TREE WALKER 27
LOAD 1, hour
LOADn 2, 60
MULTr 1, 2
LOAD 2, minute
ADDr 1, 2
LOADn 2, 1050
LOADn 3, TRUE
SKIPEQr 1, 2
LOADn 1, FALSE
LOADr 1, 3 /* extra instruction */
LOAD 2, tired
ORr 1, 2
Figure 2.8: Code produced by the linear string walker
instructions which carry out the comparison. In the case of a simple Reverse
Polish string the operator appears only once, so that the code for the ‘=’ node
must move the computed value into its intended register – the ‘extra instruction’
shown in figure 2.8.
The string walking algorithm uses a stack of registers, so its status as ‘non-
recursive’ is in doubt anyway. Note that it is impossible to vary the way in which
the tree is walked, according to some code generation strategy, after the string
has been generated. Use of a postfix string representation of the source pro-
gram merely freezes the compiler into a particular kind of tree walk. Although
the string-walking algorithm could be improved quite easily in some ways (e.g.
avoiding ‘LOAD, ADDr’ instructions in sequence) most of the improvements which
are introduced below and discussed in detail in later chapters involve changing
the way the tree is walked and so are impossible (or at best extremely difficult)
using any linear representation.
2.4 Improving the Tree Walker
Once you have a working translator, you are in business as a compiler-writer.
Using tree walking will get you to this point faster. It will also help you to get
farther and faster along the road to better code than any ad hoc solution could.
Improving the efficiency of the code produced by a tree-walking translator means
looking at the code fragments generated by one or more of the procedures and
deciding that the procedure’s strategy was weak, or that it should look out for
that particular source construct and generate a specially selected code fragment
for it. The various possibilities are summarised in figure 2.9. It’s hard to decide
where tactics shade into strategy: figure 2.10 shows an improved arithmetic ‘+’
procedure. This procedure saves an instruction and a register when the right
operand is a leaf: I would classify this as a tactical manoeuvre. Using this
28 CHAPTER 2. INTRODUCTION TO TRANSLATION
1. Generate special code for a node with a special local property (tactics).
2. Alter the way the tree is walked when a node has a particular general
property (strategy).
3. Modify the tree before it is walked (optimise the tree).
4. Modify the code which was generated, after the tree walk has finished
(optimise the code).
Figure 2.9: Tactics and strategy
• arithmetic ‘+’ (result to be in register k)
1. generate code to calculate value of left operand in register k
2. if right operand is a name then
generate ADD k, <address of variable>
else if right operand is a number then
generate ADDn k, <value of number>
or ADD k, <place where value will be found>
else
(a) generate code to calculate value of right operand in register k+1;
(b) generate ADDr k, k+1.
Figure 2.10: Improved translation of binary operation nodes
2.4. IMPROVING THE TREE WALKER 29
LOAD 2, hour
MULTn 2, 60
ADD 2, minute
LOADn 1, TRUE
SKIPEQn 2, 1050
LOADn 1, FALSE
OR 1, tired
Figure 2.11: Code produced by improved translator
procedure the first three instructions of figure 2.5 would be compressed into
two and, if a similar improvement were made to the procedures which translate
‘=’ and or nodes, the translator would produce the code shown in figure 2.11
for the example expression. This code is a considerable improvement over that
of figure 2.5 – it uses two rather than three registers, seven rather than eleven
instructions – and it was reached by making simple and modular changes to
the procedures of the translator. Chapter 5 shows that much more can be done
about the code generated for arithmetic operator nodes, and chapter 6 shows
that quite a different approach is possible for Boolean operation nodes.
Improvements like that in figure 2.10 can also be made to the iterative string-
walking procedure. Strategic alterations are far more difficult, however, since a
linear representation doesn’t allow the translator to take account of the structure
of an expression.
Some further tactical possibilities are shown in figure 2.12. These are ‘sub-
optimisations’ because the code which is generated for a particular source frag-
ment doesn’t depend on the control context within which the object code frag-
ment will operate. True optimisations take account of this context and consider,
for example, whether there are any registers which already hold the values of
variables, whose contents can be used to avoid an unnecessary LOAD instruc-
tion. To generate this code, the procedure for assignment nodes must look for
nodes with a particular special property and generate a special code fragment
for them. The number of possible tactical improvements is enormous and the
translator should implement only those tactics which are cost-effective – i.e.
where the improvement in object program efficiency is sufficient to justify the
cost of recognising the tactical situation and also the frequency with which the
situation arises is sufficient to justify checking each possible node in the tree.
Modifying the tree and modifying the code (cases 3 and 4 in figure 2.9) are
examples of optimisation techniques. Case 3 is illustrated by ‘code motion’ out
of a loop to reduce the number of times a fixed calculation is carried out (figure
2.13) and case 4 by ‘register remembering’ to eliminate unnecessary LOAD and
STORE instructions (figure 2.14). Code modification can be done after translation
has finished or, perhaps better, as a sort of filter on the code proposed by the
translator as it generates it.
30 CHAPTER 2. INTRODUCTION TO TRANSLATION
1. Statement: a := 0 Tree: assign
a 0
Normal code: LOADn 1, 0
STORE 1, a
Optimised code: STOZ , a
2. Statement: b := b+<anything> Tree: assign
b +
b <anything>
Normal code: Optimised code:
LOAD 1, <anything>
ADD 1, b
STORE 1, b
LOAD 1, <anything>
ADDST 1, b
3. Statement: c := c+1 Tree: assign
c +
c 1
Good code: LOADn 1, 1
ADDST 1, c
Better code: INCST , c
Figure 2.12: Some simple sub-optimisations
2.4. IMPROVING THE TREE WALKER 31
Program: for i = 1 to 100 do
begin
x := 0.5 * sqrt(y+z);
a[i] := x;
b[i] := y;
....
end
Tree:
... ... for ... ...
i 1 100 compound
x:=... a[i]:=x b[i]:=y ...
program
Altered tree:
... ... for ... ...
i 1 100 compound
x:=...
a[i]:=x b[i]:=y ...
program
Figure 2.13: Moving code out of a loop
Program: a := b+1;
c := a
Original code: LOAD 1, b
ADDn 1, 1
STORE 1, a /* possibly unnecessary */
LOAD 1, a /* certainly unnecessary */
STORE 1, c /* possibly unnecessary */
Figure 2.14: Removing unnecessary instructions
32 CHAPTER 2. INTRODUCTION TO TRANSLATION
1 INTEGER + INTEGER LOAD n, a
ADD n, b
2. REAL + INTEGER LOAD n, a
LOAD n+1, b
FLOATr n+1,
fADDr n, n+1
3. REAL + COMPLEX LOAD n, a
ADD n, breal
LOAD n+1, bimag
Figure 2.15: Code for various type combinations
2.5 Using the Symbol Table Descriptors
The symbol table is built by the lexical analyser in order to correlate the vari-
ous occurrences of each particular source program name. The object description
phase inserts into the symbol table against each name a descriptor which gives
information about the run-time object which is denoted by that name, deter-
mined by the declarations in which the name appears.
The descriptor not only distinguishes between different kinds of objects – vari-
ables, arrays, procedures – but also in a compiler for an algebraic language the
descriptor will give the type of the run-time object. Figure 2.15 shows some
possible code fragments for combinations of FORTRAN’s INTEGER, REAL
and COMPLEX types in an arithmetic ‘+’ node each of whose sub-nodes is
an arithmetic variable name. As well as recording the type and kind of the
object, the descriptor must contain the (possibly relocatable) store address of
the run-time object so that the a’s and b’s in the instructions of figure 2.15 can
be replaced by the proper run-time addresses.
In languages with more complex data structures, such as COBOL, PL/1, AL-
GOL 68 or SIMULA 67, the descriptor structure will not be a simple description
of an object but rather a description of a run-time structure. In the case of
COBOL, the ADD CORRESPONDING statement provides the most obvious
example – the descriptor for HEAD-OFFICE and that for NORTH-OFFICE in
figure 2.16 must give the underlying hierarchical structure so that the transla-
tor can generate code for the necessary sequence of simple ADD statements. In
such a case, the tree obviously extends into the symbol table.
2.6 Translation Error Handling
A translation error report is generated when a source fragment appears to be
of the correct superficial form, but for some reason can’t be translated. Some
obvious causes of this are undeclared names, names whose descriptor types don’t
match the required context and so on. Figure 2.17 gives some examples. These
errors are almost all discovered by checking the tree against the contents of
2.6. TRANSLATION ERROR HANDLING 33
Statement: ADD CORRESPONDING NORTH-OFFICE TO HEAD-OFFICE
Tree: add.corr
name name
NORTH-OFFICE HEAD-OFFICE
Descriptors:
MANAGER WAGES
PLANT
NORTH-OFFICE
OFFICE
OFFICE
MANAGING-DIRECTOR SALARY
PLANT
HEAD-OFFICE
Figure 2.16: Symbol table which contains structure
34 CHAPTER 2. INTRODUCTION TO TRANSLATION
1. Operands of a node are of wrong type
Statement: if a then x := b+1
Tree: conditional
a assignment
x +
b 1
Symbol Table: a is a real variable
b is a Boolean variable
2. Name not declared - so its descriptor is empty
e.g. name 'x' not declared in example above
3. Ambiguous data name (COBOL)
Statement: MOVE PLANT TO PLANT
Symbol Table: as in figure 2.12 above.
4. Invalid combination of phrases
Statement: if u=v=0 then ....
Tree: conditional
= ....
= 0
u v
Figure 2.17: Some kinds of error detected in translation
Another Random Scribd Document
with Unrelated Content
The Lady of Lynn
CHAPTER I
MY LORD'S LEVEE
It is three years later. We are now in the year 1750.
At twelve o'clock in the morning the anteroom of the town house of
the Right Honourable the Earl of Fylingdale was tolerably filled with
a mixed company attending his levee. Some were standing at the
windows; some were sitting: a few were talking: most, however,
were unknown to each other, and if they spoke at all, it was only to
ask each other when his lordship might be expected to appear.
As is customary at a great lord's levee there were present men of all
conditions; they agreed, however, in one point, that they were all
beggars. It is the lot of the nobleman that he is chiefly courted for
the things that he can give away, and that the number of his friends
and the warmth of their friendship depend upon the influence he is
supposed to possess in the bestowal of places and appointments.
Among the suitors this morning, for instance, was a half-pay captain
who sought for a company in a newly raised regiment: he bore
himself bravely, but his face betrayed his anxiety and his necessities.
The poor man would solicit his lordship in vain, but this he did not
know, and so he would be buoyed up for a time with new hopes.
Beside him stood a lieutenant in the navy, who wanted promotion
and a ship. If good service and wounds in battle were of any avail he
should have commanded both, but it is very well known that in the
Royal Navy there are no rewards for gallantry; men grow old without
promotion: nothing helps but interest: a man may remain a
midshipman for life without interest: never has it been known that
without interest a ship has been bestowed even upon the most
deserving officer and after the most signal service. The lieutenant,
too, would be cheered by a promise, and lulled by false hopes—but
that he did not know.
One man wanted a post in the admiralty: the pay is small but the
perquisites and the pickings are large: for the same reason another
asked for a place in the customs. A young poet attended with a
subscription list and a dedication. He thought that his volume of
verse, once published, would bring him fortune, fame, and friends:
he, too, would be disappointed. The clergyman wanted another
living: one of the fat and comfortable churches in the city: a deanery
would not be amiss: he was even ready to take upon himself the
office of bishop, for which, indeed, he considered that his
qualifications admirably fitted him. Would his lordship exercise his all
powerful influence in the matter of that benefice or that promotion?
A young man, whose face betrayed the battered rake, would be
contented even with carrying the colours on the Cape Coast
regiment if nothing better could be had. Surely his lordship would
procure so small a thing as that! If nothing could be found for him
then—the common side of the King's Bench Prison and rags and
starvation until death released him. Poor wretch! He was on his way
to that refuge, but he knew it not; for my lord would promise to
procure for him what he wanted.
So they all waited, hungry and expectant, thinking how best to
frame their requests: how best to appear grateful before there was
any call for gratitude. Surely a nobleman must grow wearied with
the assurances of gratitude and promises of prayers. His experience
must teach him that gratitude is but a short-lived plant: a weed
which commonly flourishes for a brief period and produces neither
flowers nor fruit; while as for the prayers, though we may make no
doubt that the fervent prayer of the righteous availeth much, we are
nowhere assured that the prayers of the worldly and the unrighteous
are heard on behalf of another; while there is no certainty that the
promised petition will ever be offered up before the throne. Yet the
suitors, day after day, repeat the same promise, and rely on the
same belief. "Oh! my lord," they say, or sing with one accord, "your
name: your voice: your influence: it is all that I ask. My gratitude:
my life-long gratitude: my service: my prayers will all be yours."
Soon after twelve o'clock the doors of the private apartments were
thrown open and his lordship appeared, wearing the look of dignity
and proud condescension combined, which well became the star he
wore and the ancient title which he had inherited. His age was about
thirty, a time of life when there linger some remains of youth and
the serious responsibilities are yet, with some men, hardly felt. His
face was cold and proud and hard; the lips firmly set: the eyes keen
and even piercing; the features regular: his stature tall, but not
ungainly, his figure manly. It was remarkable, among those who
knew him intimately, that there was as yet no sign of luxurious living
on face and figure. He was not as yet swelled out with wine and
punch: his neck was still slender; his face pale, without any telltale
marks of wine and debauchery; so far as appearance goes he might
pass if he chose, for a person of the most rigid and even austere
virtue. This, as I have said, was considered remarkable by his
friends, most of whom were already stamped on face and feature
and figure with the outward and visible tokens of a profligate life.
For, to confess the truth at the very beginning and not to attempt
concealment, or to suffer a false belief as regards this nobleman, he
was nothing better than a cold-blooded, pitiless, selfish libertine; a
rake, and a voluptuary; one who knew and obeyed no laws save the
laws of (so-called) honour. These laws allow a man to waste his
fortune at the gaming table: to ruin confiding girls: to spend his time
with rake hell companions in drink and riot and debauchery of all
kinds. He must, however, pay his gambling debts: he must not cheat
at cards; he must be polite in speech: he must be ready to fight
whenever the occasion calls for his sword, and the quarrel seems of
sufficient importance. Lord Fylingdale, however, was not among
those who found his chief pleasure scouring the streets and in mad
riot. You shall learn, in due course, what forms of pleasure chiefly
attracted him.
I have said that his face was proud. There was not, I believe, any
man living in the whole world, who could compare with Lord
Fylingdale for pride. An overwhelming pride sat upon his brow; was
proclaimed by his eyes and was betrayed by his carriage. With such
pride did Lucifer look round upon his companions, fallen as they
were, and in the depths of hopeless ruin.
In many voyages to foreign parts I have seen something of foreign
peoples; every nation possesses its own nobility; I suppose that
king, lords and commons is the order designed for human society by
Providence. But I think that there is nowhere any pride equal to the
pride of the English aristocracy. The Spaniard, if I have observed him
aright, wraps himself in the pride of birth as with a cloak: it is often
a tattered cloak: poverty has no terrors for him so long as he has his
pride of birth. Yet he tolerates his fellow-countrymen whom he does
not despise because they lack what most he prizes. The English
nobleman, whether a peer or only a younger son, or a nephew or a
cousin, provided he is a sprig of quality, disdains and despises all
those who belong to the world of work, and have neither title, nor
pedigree, nor coat of arms. He does not see any necessity for
concealing this contempt. He lacks the courtesy which would hide it
in the presence of the man of trade or the man of a learned
profession. To be sure, the custom of the country encourages him,
because to him is given every place and every preferment. He fills
the House of Commons as well as the House of Lords: he commands
our armies, our regiments, even the companies in the regiments: he
commands our fleets and our ships: he holds all the appointments
and draws all the salaries: he makes our laws, and, as justice of the
peace, he administers them: he receives pensions, having done
nothing to deserve them; he holds sinecures which require no
duties. And the people who do the work—the merchants who bring
wealth to the country: the manufacturers; the craftsmen; the
farmers; the soldiers who fight the wars which the aristocracy
consider necessary; the sailor who carries the flag over the world: all
these are supposed to be sufficiently rewarded with a livelihood
while they maintain the nobility and their children in luxury and in
idleness and are received and treated with contempt.
I speak of what I have myself witnessed. This man's pride I have
compared with the pride of Lucifer. You shall learn while I narrate
the things which follow, that he might well be compared, as regards
his actions as well, with that proud and presumptuous spirit.
He was dressed in a manner becoming to his rank: need we dwell
upon his coat of purple velvet; his embroidered waistcoat; his white
silk stockings; his lace of ruffles and cravat; his gold buckles and his
gold clocks; his laced hat carried under his arm; his jewelled sword
hilt and the rings upon his fingers? You would think, by his dress,
that his wealth was equal to his pride, and, by his reception of the
suitors, that his power was equal to both pride and wealth together.
The levee began; one after the other stepped up to him, spoke a
few words, received a few words in reply and retired, each,
apparently, well pleased. For promises cost nothing. To the poet who
asked for a subscription and preferred a dedication, my lord
promised the former, accepted the latter, and added a few words of
praise and good wishes. But the subscription was never paid; and
the dedication was afterwards altered so far as the superscription, to
another noble patron. To the clergyman who asked for a country
living then vacant, my lord promised the most kindly consideration
and bade him write his request and send it him by letter, for better
assurance of remembrance. To the officer he promised his company
as only due to gallantry and military skill: to the place hunter he
promised a post far beyond the dreams and the hopes of the
suppliant. Nothing more came of it to either.
The company grew thin: one after the other, the suitors withdrew to
feed on promises. It is like opening your mouth to drink the wind.
But 'twas all they got.
Among those who remained to the last was a man in the dress of a
substantial shopkeeper, with a brown cloth coat and silver buttons.
He, when his opportunity arrived, advanced and bowed low to my
lord.
"Sir," said his lordship, with gracious, but cold looks, "in what way
may I be of service to you?"
"With your lordship's permission, I would seek a place in your
household—any place—scullion in the kitchen, or groom to the
stable—any place."
"Why should I give you a place? Have I room in my household for
every broken cit?"
"My lord, it is to save me from bankruptcy and the King's Bench. It is
to save my wife and children from destitution. There are already
many shopkeepers in Westminster and the city who have been
admitted servants in the households of noblemen. It is no new thing
—your lordship must have heard of the custom."
"I do not know why I should save thy family or thyself. However, this
is the affair of my steward. Go and see him. Tell him that a place in
my household will save thee from bankruptcy and prison—it may be
that a place is vacant."
The man bowed again and retired. He knew very well what was
meant. He would have to pay a round sum for the privilege. This
noble lord, like many others of his rank, took money, through his
steward, for nominal places in his household, making one citizen
yeoman of his dairy; in Leicester Fields, perhaps, where no dairy
could be placed; another steward of the granaries, having in the
town neither barns nor storehouses nor ricks: a third, clerk to the
stud book, having no race horses; and so on. Thus justice is
defeated, a man's creditors may be defied and a man may escape
payment of his just debts.
When he was gone, Lord Fylingdale looked round the room. In the
window stood, dangling a cane from his wrist, a gentleman dressed
in the highest and the latest fashion. In his left hand he held a
snuffbox adorned with the figure of a heathen goddess. To those
who know the meaning of fashion it was evident that he was in the
front rank, belonging to the few who follow or command, the
variations of the passing hour. These descend to the smallest details.
I am told that the secrets of the inner circle, the select few, who
lead the fashion, are displayed for their own gratification in the
length of the cravat, the colour of the sash, the angle of the sword,
the breadth of the ruffles, the width of the skirts, the tye of the wig.
They are also shown in the mincing voice, and the affected tone,
and the use of the latest adjectives and oaths. Yet, when one looked
more closely, it was seen that this gallant exterior arrayed an ancient
gentleman whose years were proclaimed by the sharpening of his
features, the wrinkles of his feet, the crows'-feet round his eyes, and
his bending shoulders which he continually endeavoured to set
square and upright. Hat in one hand, and snuffbox in the other, he
ambled towards his lordship on tiptoe, which happened just then to
be the fashionable gait.
"Thy servant, Sir Harry"—my lord offered him his hand with
condescension. "It warms my heart to see thee. Therefore I sent a
letter. Briefly, Sir Harry, wouldst do me a service?"
"I am always at your lordship's commands. This, I hope, I have
proved."
"Then, Sir Harry, this is the case. It is probable that for certain
private reasons, I may have to pay a visit to a country town—a town
of tarpaulins and traders, not a town of fashion"—Sir Harry
shuddered—"patience, my friend. I know not how long I shall
endure the barbaric company. But I must go—there are reasons—let
me whisper—reasons of state—important secrets which call me
there"—Sir Harry smiled and looked incredulous—"I want, on the
spot, a friend"—Sir Harry smiled again, as one who began to
understand—"a friend who would appear to be a stranger. Would
you, therefore, play the part of such a friend?"
"I will do whatever your lordship commands. Yet to leave town at
this season"—it was then the month of April—"the assembly, the
park, the card table—the society of the ladies——"
"The loss will be theirs, Sir Harry. To lose their old favourite—oh!
there will be lamentations, at the rout—— Perhaps, however, we
may find consolations."
"Impossible. There are none out of town, except at Bath or
Tunbridge——"
"The ladies of Norfolk are famous for their beauty."
"Hoydens—I know them,
"'I who erst beneath a tree
Sung, Bumpkinet, and Bowzybee,
And Blouzelind and Marian bright
In aprons blue or aprons white,'
"as Gay hath it. Hoydens, my lord, I know them. They play whist
and dance jigs."
"The Norfolk gentlemen drink hard and the wine is good."
"Nay, my lord, this is cruel. For I can drink no longer."
"I shall find other diversions for you. It is possible—I say—possible—
that the Lady Anastasia may go there as well. She will, as usual,
keep the bank if she does go."
The old beau's face cleared, whether in anticipation of Lady
Anastasia's society or her card table I know not.
"My character, Sir Harry, will be in your hands. I leave it there
confidently. For reasons—reasons of state—it should be a character
of…."
"I understand. Your lordship is a model of all the virtues——"
"So—we understand. My secretary will converse with thee further on
the point of expenditure."
Sir Harry retired, bowing and twisting his body something like an
ape.
Then a gentleman in scarlet presented himself.
"Your lordship's most obedient," he said, with scant courtesy. "I
come in obedience to your letter—for command."
"Colonel, you will hold yourself in readiness to go into the country.
There will be play—you may lose as much as you please—to Sir
Harry Malyus or to any one else whom my secretary will point out to
you. Perhaps you may have to receive a remonstrance from me. We
are strangers, remember, and I am no gambler, though I sometimes
take a card."
"I await your lordship's further commands." So he, too, retired. A
proper well-set-up figure he was, with the insolence of the trooper in
his face, and the signs of strong drink on his nose. Any one who
knew the town would set him down for a half-pay captain, a sharper,
a bully, a roysterer, one who lived by his wits, one who was skilled in
billiards and commonly lucky at any game of cards. Perhaps such a
judgment of the gallant colonel would not be far wrong.
There remained one suitor. He was a clergyman dressed in a fine silk
cassock with bands of the whitest and a noble wig of the order
Ecclesiastic. I doubt if the archbishop himself had a finer. He was in
all respects a divine of the superior kind: a dean, perhaps; an
archdeacon, perhaps; a canon, rector, vicar, chaplain, with a dozen
benefices, no doubt. His thin, slight figure carried a head too big for
his body. His face was sallow and thin, the features regular; he bore
the stamp of a scholar and had the manner of a scoffer. He spoke as
if he was in the pulpit, with a voice loud, clear and resonant, as
though the mere power of hearing that voice diffused around him
the blessings of virtue and piety and a clear conscience.
"Good, my lord," he said, "I am, as usual, a suppliant. The rectory of
St. Leonard le Size, Jewry, in the city, is now vacant. With my small
benefices in the country, it would suit me hugely. A word from your
lordship to the lord mayor—the rectory is in the gift of the
corporation—would, I am sure, suffice."
"If, my old tutor, the thing can be done by me, you may consider it
as settled. There are, however, I would have you to consider, one or
two scandals still outstanding, the memory of which may have
reached the ears of the city. These city people, for all their ignorance
of fashion, do sometimes hear of things. The little affair at Bath, for
instance——"
"The lady hath since returned to her own home. It is now quite
forgotten and blown over. My innocency is always well known to
your lordship."
"Assuredly. Has that other little business at Oxford blown over? Are
certain verses still attributed to the Reverend Benjamin Purdon?"
His reverence lightly blew upon his fingers. "That report is now
forgotten. But 'tis a censorious world. One man is hanged for looking
over a gate while another steals a pig and is applauded. As for the
author of those verses, he still remains undiscovered, while the
verses themselves—a deplorable fact—are handed about for the joy
of the undergraduates."
"Next time, then, steal the pig. Frankly, friend Purdon, thy name is
none of the sweetest, and I doubt if the bishop would consent.
Meantime, you are living, as usual, I suppose, at great expense——"
"At small expense, considering my abilities; but still at greater
expense than my slender income will allow. Am I not your lordship's
domestic chaplain? Must I not keep up the dignity due to the
position?"
"Your dignity is costly. I must get a bishopric or a deanery for you.
Meantime I have a small service to ask of you."
"Small? My lord, let it be great: it cannot be too great."
"It is that you go into the country for me."
"Not to Bath—or to Oxford?" His reverence betrayed an anxiety on
this point which was not quite in harmony with his previous
declarations.
"Not to either. To another place, where they know not thy name or
thy fame. Very good. I thought I could depend upon your loyalty. As
for arrangements and time, you will hear from my secretary." So my
lord turned on his heel and his chaplain was dismissed. He remained
for a moment, looking after his master doubtfully. The order liked
him not. He was growing old and would have chosen, had he the
power of choice, some fat city benefice with two or three country
livings thrown in. He was tired of his dependence: perhaps he was
tired of a life that ill became his profession: perhaps he could no
longer enjoy it as of old. There was, at least, no sign of repentance
as there was no touch of the spiritual life in his face, which was
stamped with the plain and visible marks of the world, the flesh and
the devil. What is that stamp? Nobody can paint it, or describe it: yet
it is understood and recognised whenever one sees it. And it stood
out legible so that all those who ran might read upon the face of this
reverend and learned divine.
When the levee was finished and everybody gone, Lord Fylingdale
sank into a chair. I know not the nature of his thoughts save that
they were not pleasant, for his face grew darker every moment.
Finally, he sprang to his feet and rang the bell. "Tell Mr. Semple that
I would speak with him," he ordered.
Mr. Semple, the same Samuel whom you have seen under a basting
from the captain, was now changed and for the better. His dress was
simple. No one could guess from his apparel the nature of his
occupation. For all professions and all crafts there is a kind of
uniform. The divine wears gown and cassock, bands and wig, which
proclaim his calling: the lawyer is also known by his gown and marks
his rank at the bar by coif and wig: the attorney puts on broadcloth
black of hue: the physician assumes black velvet, a magisterial wig,
and a gold-headed cane. The officer wears the King's scarlet; the
nobleman his star: the sprig of quality puts on fine apparel and
assumes an air and manner unknown to Cheapside and Ludgate Hill:
you may also know him by his speech. The merchant wears black
velvet with gold buttons, gold buckles, white silk stockings and a
gold-laced hat; the shopkeeper substitutes silver for gold and cloth
for velvet: the clerk has brown cloth metal buttons and worsted
stockings. As for the crafts, has not each its own jacket, sleeves,
apron, cap, and badge?
But for this man, where would we place him? What calling did he
represent? For he wore the flowered waist-coat—somewhat frayed
and stained, of a beau, and the black coat of the merchant: the
worsted stockings of the clerk and his metal buttons. Yet he was
neither gentleman, merchant, shopkeeper, clerk, nor craftsman. He
was a member of that fraternity which is no fraternity because there
is no brotherhood among them all; in which every man delights to
slander, gird at, and to depreciate his brother. In other words he
wore the dress—which is no uniform—of a poet. At this time he also
called himself secretary to his lordship having by ways known only to
himself, and by wrigglings up back stairs, and services of a kind
never proclaimed to the world, made himself useful. The position
also granted him, as it granted certain tradesmen, immunity from
arrest. He had the privilege of walking abroad through a street full of
hungering creditors, and that, not on Sundays only, like most of his
tribe, but on every day in the week.
He obeyed the summons and entered the room with a humble
cringe.
"Semple," said his lordship, crossing his legs and playing with the
tassel of his sword knot, "I have read thy letter——"
"Your lordship will impute——"
"First, what is the meaning of the preamble?"
"I have been your lordship's secretary for six months. I have
therefore perused all your lordship's letters. I have also in my zeal
for your lordship's interests—looked about me. And I discovered—
what I ventured to state in that preamble."
"Well, sir?"
"Namely, that the Fylingdale estates are gone so far as your
lordship's life is concerned—but—in a word, all is gone. And that—
your lordship will pardon the plain truth—your lordship's credit
cannot last long and that—I now touch a most delicate point to a
man of your lordship's nice sense of honour—the only resource left
is precarious."
"You mean?"
"I mean—a certain lady and a certain bank."
"How, sir? Do you dare? What has put this suspicion into your
head?"
"Nay, my lord—I have no thought but for your lordship's interests,
believe me."
"And so you tell me about the rustic heiress, and you propose a plan
——"
"I have had the temerity to do so."
"Yes. Tell me once more about this girl—and about her fortune."
"Her name is Molly Miller: she is an orphan: her guardian is an
honest sailor who has taken the greatest care of her property. She
was an heiress already when her father died. That was eighteen
years ago; she is now nineteen."
"Is she passable—to look at? A hoyden with a high colour, I
warrant."
"A cream-coloured complexion, touched with red and pink: light hair
in curls and blue eyes; the face and figure of a Venus; the sweetest
mouth in the world and the fondest manner."
"Hang me if the fellow isn't in love with her, himself! If she is all this,
man, why not apply yourself, for the post of spouse?"
"Because her guardian keeps off all would-be lovers and destines his
ward for a gentleman at least—for a nobleman, he hopes."
"He is ambitious. Now as to her fortune."
"She has a fleet of half a dozen tall vessels—nay, there are more,
but I know not how many. I was formerly clerk in a countinghouse
of the town and I learned a great deal—what each is worth and
what the freight of each voyage may produce—but not all. The
captain, her guardian, keeps things close. My lord, I can assure you,
from what I learned in that capacity and by looking into old books,
that she must be worth over a hundred thousand pounds—over a
hundred thousand pounds! My lord, there is no such heiress in the
city. In your lordship's interests I have enquired in the taverns where
the merchants' clerks congregate. They know of all the city
heiresses. The greatest, at this moment, is the only daughter of a
tallow chandler who has twenty thousand to her name. She squints."
"Why have you given me this information? The girl belongs to your
friends—are you anxious for her happiness? You know my way of
life. Would that way make her happier?"
The man made no reply.
"Come, Semple, out with it. Your reasons—gratitude—to me—or
revenge upon an enemy?"
The man coloured. He looked up: he stood upright but for a moment
only. Then his eyes dropped and his shoulders contracted.
"Gratitude, my lord, to you," he replied. "Revenge? Why what reason
should I have for revenge?"
"How should I know of any? Let it be gratitude, then."
"I have ventured to submit—not a condition—but a prayer."
"I have read the clause. I grant it. On the day after the marriage if
the plan comes to anything, I will present thee to a place where
there are no duties and many perquisites. That is understood. I
would put this promise in writing but no writing would bind me more
than my word."
"Yet I would have the promise in writing."
"You are insolent, sirrah."
"I am protecting myself. My lord, I must speak openly in this matter.
How many promises have you made this morning? How many will
you keep? I must not be pushed aside with such a promise."
Lord Fylingdale made no reply.
"I offer you a fortune of a hundred thousands pounds and more."
"I can now take this fortune without your assistance."
"With submission, my lord, you cannot. I know too much."
"What shall I write, then?"
"I am only reasonable. The girl's fortune when you have it will go
the same way as your rents and woods have gone. Provide for me,
therefore, before you begin to spend that money."
"Semple, I did not think you had so much courage. Learn that a
dozen times I have been on the point of kicking you out of the
house. Now," he rose, "give me paper and a pen—and I will write
this promise."
Semple placed a chair at the table and laid paper and pen before it.
"Let me presume so far as to dictate the promise," he said. "I
undertake and promise that on the day after my marriage with the
girl named Molly Miller, I will give Samuel Semple such a place as will
provide him for life with a salary of not less than £200 a year. So—
will your lordship sign it?"
He took up this precious paper from the table, read it, folded it and
put it in his pocket.
"What next?" asked his patron.
"I am preparing a scheme which will give a plausible excuse for your
lordship's visit to the town. I have already suggested that certain
friends should prepare the way. The lady's guardian has prejudices
in favour of morality and religion. They are, I know, beneath your
lordship's notice—yet still—it will be in fact, necessary that your
lordship's character shall be such as will commend itself to this
unfashionable old sailor."
"We will speak again upon this point. The girl you say has no lover."
"She has no lover. Your lordship's rank: your manner: your
appearance will certainly carry the day. By contrast alone with the
country bumpkins the heart of the girl will be won."
"Mr. Semple," his lordship yawned. "Do you suppose that the heart
of the girl concerns me? Go and complete your scheme—of
gratitude, not revenge."
CHAPTER II
THE LADY ANASTASIA
The Lady Anastasia was in her dressing-room in the hands of her
friseur, the French hairdresser, and her maid. She sat in a dishabille
which was a loose robe, called, I believe a nightgown, of pink silk,
trimmed with lace, which showed the greater part of a very well
shaped arm; she had one slipper off and one slipper on, which
showed a very small and well shaped foot, but no one was there to
see. Her maid was busy at the toilette table which was covered with
glass bottles containing liquids of attractive colour; silver patch
boxes; powder boxes; powder puffs; cosmetics in pots, and other
mysterious secrets into which it would be useless and fruitless to
inquire. The artist, for his part, was laboriously and conscientiously
building the edifice—object of so much ingenuity and thought—
called a "Head."
She was in the best temper imaginable. When you hear that she had
won overnight the sum of a hundred and twenty guineas you will
understand that she had exactly that number of reasons for being
satisfied with the world. Moreover, she had received from an admirer
a present in the shape of a piece of china representing a monkey,
which, she reflected with satisfaction, would awaken in the minds of
her friends the keenest feelings of envy, jealousy, hatred, longing,
and despair.
The Lady Anastasia was the young widow of an old baronet: she
was also the daughter of an earl and the sister of his successor. She
therefore enjoyed the freedom of a widow; the happiness natural to
youth; and all the privileges of rank. No woman could be happier. It
was reported that her love of the card table had greatly impaired her
income: the world said that her own private dowry was wholly gone
and a large part of her jointure. But it is a spiteful world—all that
was known for certain was that she played much and that she
played high. Perhaps Fortune, in a mood of penitence, was giving
back what she had previously taken away. The contrary is commonly
the case, viz, that Fortune, which certainly takes away with alacrity,
restores with reluctance.
Perhaps, however, the reports were not true.
She kept a small establishment in Mount Street: her people
consisted of no more than two footmen, a butler, a lady's maid, a
housekeeper, and three or four maids with two chairmen. She did
not live as a rich woman: she received, it is true, twice a week, on
Sundays and Wednesdays, but not with any expense of supper and
wine. Her friends came to play cards and she held the bank for
them. On other evenings she went out and played at the houses of
her friends.
Except for fashions and her dress—what fine woman but makes that
exception?—she had no other occupation; no other pursuit; no other
subject of conversation, than the playing of cards. She played at all
games and knew them all; she sat down with a willing mind to
Ombre, Faro, Quadrille, Basset, Loo, Cribbage, All Fours, or Beggar
my Neighbour, but mostly she preferred the game of Hazard, when
she herself kept the bank. It is a game which more than any other
allures and draws on the player so that a young man who has never
before been known to set a guinea on any card, or to play at any
game, will in a single night be filled with all the ardour and
eagerness of a practised gamester; will know the extremes of joy
and despair; and will regard the largest fortune as bestowed by
Providence for no other purpose than to prolong the excitement and
the agony of a gamester.
While the Lady Anastasia was still admiring the china vase set upon
the table, so that she might gaze upon it and so refresh her soul,
and while the friseur was still completing her head, Lord Fylingdale
was announced. The lady blushed violently: she sat up and looked
anxiously in the glass.
"Betty," she cried, "a touch of red—not much, you clumsy creature!
Will you never learn to have a lighter hand? So! that is better. I am
horribly pale. His lordship can wait in the morning room. You have
nearly finished, monsieur? Quick then! The last touches. Betty, the
flowered satin petticoat. My fan. The pearl necklace. So," she looked
again at the glass, "am I looking tolerable, Betty?"
"Your ladyship is ravishing," said Betty finishing the toilette. In truth,
it was a very pretty creature if one knew how much was real and
how much was due to art. The complexion was certainly laid on; the
hair was powdered and built up over cushions and pillows; there
were patches on the cheek: the neck was powdered; eyes naturally
very fine were set off and made more lustrous with a touch of dark
powder: the frock and petticoat and hoop were all alike removed
from nature. However, the result was a beautiful woman of fashion
who is far removed indeed from the beautiful woman as made by
the Creator. For her age the Lady Anastasia might have been seven
and twenty, or even thirty—an age when with some women, the
maturity of their beauty is even more charming than the first
sprightly loveliness of youth.
She swam out of the room with a gliding movement, then the
fashion, and entered the morning room where Lord Fylingdale
awaited her.
"Anastasia!" he said, softly, taking her hand. "It is very good of you
to see me alone. I feared you would be surrounded with courtiers
and fine ladies or with singers, musicians, hairdressers, and other
baboons. Permit me," he raised her hand to his lips. "You look divine
this morning. It is long since I have seen you look so perfectly
charming."
The lady murmured something. She was one of those women who
like above all things to hear praises of what most they prize, their
beauty, and to believe what they most desire to be the truth, the
preservation and perfecting of that beauty.
"But you came to see me alone. Was it to tell me that I look
charming? Other men tell me as much in company."
"Not altogether that, dear lady, though that is something. I come to
tell you of a change of plans."
"You have heard that the grand jury of Middlesex has presented me
by name as a corruptor of innocence, and I know not what, because
I hold my bank on Sunday nights."
"I have heard something of the matter. It is almost time, I think, to
give these presumptuous shopkeepers a lesson not to interfere with
the pursuits of persons of rank. Let them confine themselves to the
prentices who play at pitch and toss."
"Oh! what matters their presentment? I shall continue to keep the
bank on Sunday nights. Now, my dear lord, what about these plans?
What is changed?"
"We thought, you remember, about going to Tunbridge, in July."
"Well? Shall we not go there?"
"Perhaps. But there is something to be done first. Let me confide in
you——"
"My dear lord—you have never confided in anybody."
"Except in you. I think you know all my secrets if I have any. In
whom else can I confide? In the creatures who importune me for
places? In friends of the green table? In friends of the race course?
My dear Anastasia, you know, I assure you, as much about my
personal affairs as I know myself."
"If you would always speak so kindly"—her eyes became humid but
not tearful. A lady of fashion must not spoil her cheek by tears.
"Well, then, the case is this. You know of the condition of my affairs
—no one better. An opportunity presents itself to effect a great
improvement. I am invited by the highest personage to take a more
active part in the affairs of state. No one is to know this. For reasons
connected with this proposal I am to visit a certain town—a trading
town—a town of rough sailors, there to conduct certain enquiries.
There is to be a gathering at this town of the gentry and people of
the county. Would you like to go, my dear friend? It will be next
month."
"To leave town—and in May, just before the end of the season?"
"There will be opportunities, I am told, of holding a bank; and a
good many sportsmen—'tis a sporting county—may be expected to
lay their money. In a word, Anastasia, it will not be a bad exchange."
"And how can I help you? Why should I go there?"
"By letting the people—the county people, understand the many
virtues and graces which distinguish my character. No one knows me
better than yourself."
The lady smiled—"No one," she murmured.
"—Or can speak with greater authority on the subject. There will be
certain of our friends there—the parson—Sir Harry—the colonel——"
"Pah! a beggarly crew—and blown upon—they are dangerous."
"Not at this quiet and secluded town. They will be strangers to you
as well as to me. And they will be useful. After all, in such a place
you need an opening. They will lead the way."
The lady made no response.
"I may call it settled, then?" He still held her hand. "If you would
rather not go, Anastasia, I will find some one else—but I had hoped
——"
She drew away her hand. "You are right," she said, "no one knows
you so well as myself. And all I know about you is that you are
always contriving some devilry. What is it this time? But you will not
tell me. You never tell me."
"Anastasia, you do me an injustice. This is a purely political step."
"As you will. Call it what you please. I am your servant—you know
that—your handmaid—in all things—save one. Not for any other
woman, Ludovick—not for any other—unfortunate—woman will I lift
my little finger. Should you betray me in this respect——"
He laughed. "A woman? And in that company? Rest easy, dear child.
Be jealous as much as you please but not with such a cause."
He touched her cheek with his finger: he stooped and kissed her
hand and withdrew.
The Lady Anastasia stood awhile where he left her. The joy had gone
out of her heart: she trembled: she was seized with a foreboding of
evil. She threw herself upon the sofa and buried her face in her
hands, and forgetful of paste and patch and paint she suffered the
murderous tears to destroy that work of art—her finished face.
CHAPTER III
THE "SOCIETY" OF LYNN
It was about seven o'clock in the evening of early April, at the going
down of the sun that I was at last able to drop into the dingy and go
ashore. All day and all night and all the day before we had been
beating through the shallows of the Wash and the narrow channel of
the Ouse. We had laid her to her moorings off the Common Stath
and made all taut and trim: the captain had gone ashore with the
papers: the customhouse officer had been aboard: we were to begin
breaking cargo on the morrow. The ship was The Lady of Lynn, 380
tons, Robert Jaggard, master marines, being captain, and I the mate
or chief officer. There was no better skipper in the port of Lynn than
Captain Jaggard: there was no better crew than that aboard The
Lady of Lynn, not a skulker or a lubber in the whole ship's company;
and though I say it myself, I dare affirm that the mate did credit to
his ship as much as the captain and the crew. We were in the Lisbon
trade: we had therefore come home laden with casks of the rich
strong wine of the country: the Port and Lisbon Sherry and Malaga,
besides Madeira and the wine of Teneriffe and the Grand Canary.
Our people of the Marshland and the Fens and those of Lincolnshire
and Norfolk where the strong air of the east winds kill all but the
stoutest, cannot have too much of this rich wine: they will not drink
the lighter wines of Bordeaux which neither fire the blood nor mount
to the head. A prosperous voyage we had made: the Bay of Biscay
suffered us to cross with no more than half a gale: The Lady of
Lynn, in fact, was known in port to be a lucky ship—as lucky as her
owner—lucky in her voyages and lucky in her cargoes.
At the stairs of the Common Stath Yard I made fast the painter and
shipped the sculls. And there, waiting for me, was none other than
my good old friend and patron, Captain Crowle.
The captain was by this time well advanced in life, being upwards of
seventy: yet he showed little touch of time: his honest face being
still round and full; his eyes still free from lines and crows'-feet; his
cheek ruddy and freckled, as if with the salt sea breeze and the
driving spray. He was also as upright as any man of thirty and
walked with as firm a step and had no need of the stout stick which
he carried in his hand, as a weapon and a cudgel for the
unrighteous, more than a staff for the bending knees of old age.
"What cheer—ahoy?" He shouted from the quay as I dropped over
the side into the dingy. "What cheer, Jack?" he repeated when I ran
up the steps. "I've seen the skipper. Come with me to the Crown"—
but the proper place for mates was the Duke's Head. "Nay, it shall
be the Crown. A bowl of punch shall welcome back The Lady of
Lynn." He turned and looked at the ship lying in the river at her
moorings among the other craft. "She's as fine a vessel as this old
port can show—and she's named after as fine a maid. Shalt see her
to-morrow, Jack, but not to-night."
"I trust, sir, that she is well and in good spirits."
"Ay—ay. Nothing ails her—nothing ails her, Jack," he pointed with his
stick. "Look how she flourishes. There are fifteen tall ships moored
two and two off the King's Stath and half a dozen more off the
Common Stath. Count them, Jack. Six of these ships belong to the
little maid. Six of them—and two more are afloat, of which one is
homeward bound and should be in port soon if all goes well. Eight
noble ships, Jack, are hers. And the income of nigh upon eighteen
years and houses and broad lands."
"She has a prudent guardian, captain."
"May be—may be. I don't deny, Jack, but I've done the best I could.
Year after year, the money mounteth up more and more. You love
her, Jack, and therefore I tell you these things. And you can keep
counsel. I talk not in the market place. No one knows her wealth but
Welcome to our website – the perfect destination for book lovers and
knowledge seekers. We believe that every book holds a new world,
offering opportunities for learning, discovery, and personal growth.
That’s why we are dedicated to bringing you a diverse collection of
books, ranging from classic literature and specialized publications to
self-development guides and children's books.
More than just a book-buying platform, we strive to be a bridge
connecting you with timeless cultural and intellectual values. With an
elegant, user-friendly interface and a smart search system, you can
quickly find the books that best suit your interests. Additionally,
our special promotions and home delivery services help you save time
and fully enjoy the joy of reading.
Join us on a journey of knowledge exploration, passion nurturing, and
personal growth every day!
ebookbell.com

More Related Content

PDF
Chapter1
PDF
Functional programming in Python 1st Edition David Mertz
PDF
Quick Start Guide To Large Language Models Second Edition Sinan Ozdemir
PDF
Cs121 Unit Test
PDF
Parallel Programming For Multicore And Cluster Systems Thomas Rauber
PDF
Functional programming in Python 1st Edition David Mertz
PDF
ApiDesign
PDF
API design
Chapter1
Functional programming in Python 1st Edition David Mertz
Quick Start Guide To Large Language Models Second Edition Sinan Ozdemir
Cs121 Unit Test
Parallel Programming For Multicore And Cluster Systems Thomas Rauber
Functional programming in Python 1st Edition David Mertz
ApiDesign
API design

Similar to Understanding And Writing Compilers A Doityourself Guide Richard Bornat (20)

PDF
Refactoring Sql Applications Stephane Faroult Pascal Lhermite
PDF
My professional bookshelf
PDF
It is difficult
PDF
Modern TypeScript 1 / converted Edition Ben Beattie-Hood
PDF
Microservices communication styles and event bus
PDF
Web Content Management Systems Features and Best Practices 1 (Early Release) ...
PDF
Functional Thinking Paradigm Over Syntax.pdf
PDF
Web Content Management Systems Features and Best Practices 1 (Early Release) ...
PDF
Functional Programming In Scala Second Edition Meap V08 2nd All Chapters Avai...
PDF
Apress.html5.and.java script.projects.oct.2011
PDF
Flow based-1994
PDF
Publishing Technology Today
PDF
Introduction To Objectoriented Programming An 3rd Edition Timothy Budd
PDF
Modern c
PDF
Modern Compiler Design 2e.pdf
PDF
SaltStack For DevOps, Free Sample
PDF
Essential c
PDF
Essential c
PDF
Essential c notes singh projects
PDF
Essential c
Refactoring Sql Applications Stephane Faroult Pascal Lhermite
My professional bookshelf
It is difficult
Modern TypeScript 1 / converted Edition Ben Beattie-Hood
Microservices communication styles and event bus
Web Content Management Systems Features and Best Practices 1 (Early Release) ...
Functional Thinking Paradigm Over Syntax.pdf
Web Content Management Systems Features and Best Practices 1 (Early Release) ...
Functional Programming In Scala Second Edition Meap V08 2nd All Chapters Avai...
Apress.html5.and.java script.projects.oct.2011
Flow based-1994
Publishing Technology Today
Introduction To Objectoriented Programming An 3rd Edition Timothy Budd
Modern c
Modern Compiler Design 2e.pdf
SaltStack For DevOps, Free Sample
Essential c
Essential c
Essential c notes singh projects
Essential c
Ad

Recently uploaded (20)

PDF
OBE - B.A.(HON'S) IN INTERIOR ARCHITECTURE -Ar.MOHIUDDIN.pdf
PDF
A GUIDE TO GENETICS FOR UNDERGRADUATE MEDICAL STUDENTS
PDF
Weekly quiz Compilation Jan -July 25.pdf
PDF
A systematic review of self-coping strategies used by university students to ...
PPTX
Lesson notes of climatology university.
PDF
VCE English Exam - Section C Student Revision Booklet
PPTX
PPT- ENG7_QUARTER1_LESSON1_WEEK1. IMAGERY -DESCRIPTIONS pptx.pptx
PPTX
Pharmacology of Heart Failure /Pharmacotherapy of CHF
PPTX
1st Inaugural Professorial Lecture held on 19th February 2020 (Governance and...
PDF
2.FourierTransform-ShortQuestionswithAnswers.pdf
PPTX
Final Presentation General Medicine 03-08-2024.pptx
PDF
O5-L3 Freight Transport Ops (International) V1.pdf
PPTX
GDM (1) (1).pptx small presentation for students
PDF
FourierSeries-QuestionsWithAnswers(Part-A).pdf
PPTX
202450812 BayCHI UCSC-SV 20250812 v17.pptx
PPTX
Cell Types and Its function , kingdom of life
PPTX
Cell Structure & Organelles in detailed.
PDF
Chinmaya Tiranga quiz Grand Finale.pdf
PDF
The Lost Whites of Pakistan by Jahanzaib Mughal.pdf
PPTX
school management -TNTEU- B.Ed., Semester II Unit 1.pptx
OBE - B.A.(HON'S) IN INTERIOR ARCHITECTURE -Ar.MOHIUDDIN.pdf
A GUIDE TO GENETICS FOR UNDERGRADUATE MEDICAL STUDENTS
Weekly quiz Compilation Jan -July 25.pdf
A systematic review of self-coping strategies used by university students to ...
Lesson notes of climatology university.
VCE English Exam - Section C Student Revision Booklet
PPT- ENG7_QUARTER1_LESSON1_WEEK1. IMAGERY -DESCRIPTIONS pptx.pptx
Pharmacology of Heart Failure /Pharmacotherapy of CHF
1st Inaugural Professorial Lecture held on 19th February 2020 (Governance and...
2.FourierTransform-ShortQuestionswithAnswers.pdf
Final Presentation General Medicine 03-08-2024.pptx
O5-L3 Freight Transport Ops (International) V1.pdf
GDM (1) (1).pptx small presentation for students
FourierSeries-QuestionsWithAnswers(Part-A).pdf
202450812 BayCHI UCSC-SV 20250812 v17.pptx
Cell Types and Its function , kingdom of life
Cell Structure & Organelles in detailed.
Chinmaya Tiranga quiz Grand Finale.pdf
The Lost Whites of Pakistan by Jahanzaib Mughal.pdf
school management -TNTEU- B.Ed., Semester II Unit 1.pptx
Ad

Understanding And Writing Compilers A Doityourself Guide Richard Bornat

  • 1. Understanding And Writing Compilers A Doityourself Guide Richard Bornat download https://ebookbell.com/product/understanding-and-writing- compilers-a-doityourself-guide-richard-bornat-36529634 Explore and download more ebooks at ebookbell.com
  • 2. Here are some recommended products that we believe you will be interested in. You can click the link to download. Smart Thinking Skills For Critical Understanding And Writing Second Edition Reissue Matthew Allen https://ebookbell.com/product/smart-thinking-skills-for-critical- understanding-and-writing-second-edition-reissue-matthew- allen-57136102 Smart Thinking Skills For Critical Understanding And Writing 2nd Ed Matthew Allen https://ebookbell.com/product/smart-thinking-skills-for-critical- understanding-and-writing-2nd-ed-matthew-allen-4121292 Smart Thinking Skills For Critical Understanding And Writing 2nd Edition Matthew Allen https://ebookbell.com/product/smart-thinking-skills-for-critical- understanding-and-writing-2nd-edition-matthew-allen-1272680 Smart Thinking Skills For Critical Understanding And Writing Matthew Allen Allen https://ebookbell.com/product/smart-thinking-skills-for-critical- understanding-and-writing-matthew-allen-allen-27821526
  • 3. The Pester Book The Allinone Guide To Understanding And Writing Tests For Powershell Adam Bertram https://ebookbell.com/product/the-pester-book-the-allinone-guide-to- understanding-and-writing-tests-for-powershell-adam-bertram-22295790 Understanding Development And Proficiency In Writing Quantitative Corpus Linguistic Approaches Philip Durrant https://ebookbell.com/product/understanding-development-and- proficiency-in-writing-quantitative-corpus-linguistic-approaches- philip-durrant-33030350 Get Funded A Practical Guide To Understanding The Grant Application Process And Writing Winning Proposals In The Behavioral And Biomedical Fields 1st Edition Elias Phd https://ebookbell.com/product/get-funded-a-practical-guide-to- understanding-the-grant-application-process-and-writing-winning- proposals-in-the-behavioral-and-biomedical-fields-1st-edition-elias- phd-56481488 Understanding Developing And Writing Effective Ieps A Stepbystep Guide For Educators Roger Pierangelo George Giuliani https://ebookbell.com/product/understanding-developing-and-writing- effective-ieps-a-stepbystep-guide-for-educators-roger-pierangelo- george-giuliani-48922738 Understanding Chinese Multilingual Scholars Experiences Of Writing And Publishing In English A Socialcognitive Perspective Congjun Mu https://ebookbell.com/product/understanding-chinese-multilingual- scholars-experiences-of-writing-and-publishing-in-english-a- socialcognitive-perspective-congjun-mu-10788004
  • 5. Understanding and Writing Compilers A do-it-yourself guide Richard Bornat Middlesex University, London. richard@bornat.me.uk
  • 6. First published—1979. Internet edition—2007; corrected 2008. Copyright c 1979, 2007, 2008 Richard Bornat. Permission to copy without fee all or part of this material is granted provided that the copies are not made or distributed for direct commercial advantage, the copyright notice above and the title of the book appear, and notice is given that copying is by permission of Richard Bornat. Original edition published by Macmillan Press Ltd., Basingstoke, UK. Internet edition published by Richard Bornat, 28 Albany Road, LONDON N4 4RL, UK. richard@bornat.me.uk
  • 7. Preface to the online edition I wrote this book on compiling in the late 1970s. It was a success. I still meet people who learnt about compiling from it. I get three or four requests a year from people who’d like a copy. I used to tell them to use Abebooks.com, but now there aren’t any copies even there. Since I own the copyright (thanks to Macmillan), I can publish it again, for free. For a while I tried to reproduce the original book from the original nroff source, but Unix version 6 nroff is long dead, and the printer-driver hacks that made bold and underlining work are even deader. So I hacked it into L A TEX, and here it is. I fixed the errors that I knew about, changed a very few infelicities, and otherwise tried to change as little as possible. Why isn’t it in C? When I began to write this book I was heavily into BCPL. By the time it was finished I was a C programmer. The display editor ded that I developed to write the book was written in C. C is in many respects a direct descendant of BCPL. I was pressed, even before the book came out, to rewrite all the examples in C. I didn’t do that for various reasons, some good and some bad. It would be an enormous effort to do it now, because C is now so far from its origins. Old-fashioned opinions I agree with quite a lot of this book, but by no means all of it. I’ve decided to change nothing. For better or worse, here it is. i
  • 8. ii
  • 9. Preface In the past compiler writers and designers seemed to form an elite group within computing science, set apart by their esoteric knowledge and their ability to produce large, important system programs which really worked. The admiration of the computing public, whether it was once deserved or not, is no longer merited now that the principles of programming-language implementation are so well understood. Compiler-writing is no longer a mystery. This book attempts to explain and demystify the principles of compiler writing so that you can go out and build a working compiler of your own. There is enough detail in this book for you to build a compiler for quite a complicated language – certainly PASCAL, perhaps ALGOL 68 or SIMULA 67 – but it doesn’t attempt an encyclopaedic coverage of the field. It is intended more as an introduction to compiler-writing and a do-it-yourself kit for the compiler-writer, giving enough detail for you to understand the principles of the subject, than as a survey of past history or present horizons. The principles of interpretation are close enough to those of compilation for chapter 19 to give a simple introduction to interpreter writing. The method of treatment and the relative amount of attention given to various topics in this book reflects my own views about the relative importance of those topics. There is a separate section on run-time support, less attention is paid than is perhaps usual to the topic of parsing or syntax analysis and the discussion of translation is totally oriented to tree-walking. I have presented the subject in this way for both practical and educational reasons. First, the object code instruction sequences which implement run-time support are more important in practice than is usually recognised. It is differences in run-time mechanisms, as much as or more than anything else, which distinguish one language from another – say SIMULA 67 from ALGOL 68, POP-2 from ALGOL 60 – and the efficiency of run-time support code fragments is crucial to the efficiency of the object program. Second, I believe it is more important to give a practical description of syntax analysis in a book which is intended for the practical compiler-writer than to give a more formal and complete introduction to the topic. The syntax analysis mechanisms chosen for illustration in section IV] are selected for their practical relevance. Of the three mechanisms presented, the ‘one-track’ and ‘operator-precedence’ mechanisms are now rather old-fashioned but are still quite adequate to the task of parsing popular modern languages. iii
  • 10. iv Finally, my insistence on tree-walking as the best means of translation is both because it makes explanation of translation algorithms much easier and enables me to bring out the topic of ‘crucial code fragments’ which forms so much of the life of the professional compiler writer; also in my experience it is a practical way in which both novice and expert can quickly build a working translator containing the minimum number of errors. Throughout the book I have emphasised the fact that the task of compilation can be divided into separate modular sub-tasks. It is largely the identification of and emphasis on this essential modularity that has clarified the subject. Emphasis on modular design also helps me to avoid discussing every known technique for each of the tasks – if you know what the task is, one or two good ways to accomplish it and how to recognise another good way if somebody shows you one, then you are on the way to becoming a capable compiler writer. Throughout the book I have emphasised the need for the compiler to provide a service to its users. It seems to me that the demands of system or compiler efficiency are too often given precedence over the justifiable demands of the user who wants understandable error reports, accurate and reliable object code or strict adherence to an industry standard. The same goes, I believe, for the demands of small compiler size or simplicity of construction. A good compiler can be acceptably efficient and reasonably small yet still provide a good user service. Indeed I believe that the well-designed compiler will out-perform the ‘efficient’ special-purpose construction, but even if it isn’t so the compiler writer should stand by the principle that machines are provided to save human time and effort. A few seconds of machine time which saves minutes (or more often hours) of human time is machine time well spent! Host, Object and Source Language in Examples In the examples throughout the book most algorithms are written in a version of the language BCPL, details of which are briefly explained in appendix A. Some example algorithms are in PASCAL: I would have used PASCAL more extensively were it not for the fact that its lack of block structure, lack of conditional expressions and lack of a simple ‘union-type’ convention forces an obscure programming style. BCPL’s advantage is that it is untyped and that therefore the examples can show the bare bones of an algorithm without too much unnecessary hedging round of type declarations. At the same time this is a drawback: BCPL’s lack of data structure declarations makes it difficult to explain some fairly simple algorithms. Early examples give some explanation of the data structures which are manipulated by the algorithms: it is worth pointing out that in every case the values which are manipulated are pointers to record-structures rather than the structures themselves. Appendix B explains the assembly code which is used to illustrate the oper- ation of the translation algorithms in sections II and III. It is the code of a single-address, multi-register single-segment-addressing machine. Throughout
  • 11. v the book there is an emphasis on the machine-independence of compiler design and the fact that details of the object machine’s instruction set don’t affect the design of the compiler. Nevertheless it is useful, when discussing translation al- gorithms, to illustrate the code of an example object machine in order to show the advantages of good design of code fragments. With the present explosion in the use of microprocessors interest in compil- ing has re-emerged, particularly interest in compiling system-programming lan- guages. The problems of compiler-writing for small machines are mainly to do with producing compact object code: the examples presented in this book are not directly oriented towards this purpose but may be readily adapted to it. The desire to produce a very small compiler, as opposed to a small object pro- gram, should be curbed until you properly understand the principles of compiler design (when perhaps the desire will go away!) The source language which is used to illustrate syntax analysis and translation algorithms has a variable syntax and semantics, floating somewhere in the space between BCPL, ALGOL 60, ALGOL 68 and PASCAL. Some attention is given to the detailed difficulties which arise in writing a compiler for each of these languages. I didn’t wish to limit the discussion to those features displayed by any particular language, however, nor to get bogged down in the details of that language. Most examples therefore relate to the general difficulties of compiling any language which includes recursive procedures, block-structuring, dynamic arrays, pointer variables, and so on. Acknowledgements I’m enormously indebted to all the people who have argued with me about compilers and compiling over the years, lectured to me, listened to me, corrected me and generally helped me develop the ideas I present in this book. Many of the good ideas are those of my mentors. Thanks especially to Jeff Rohl for helping me in my first steps and encouraging me to develop my ideas as far as they have gone: thanks also to Bernard Sufrin for so many invaluable discussions about the best way to build and to explain compilers. Thanks to my colleagues at the University of Essex – Bruce Anderson, Mike Brady, Tony Brooker, Mike Foster, Pete Gardner, Pat Hayes, John Laski, Bob Wielinga and all the students who listened to earlier versions of this book in the form of lectures. This book was type-set on a DEC PDP-11/40 and printed using a Diablo printer. Thanks to the denizens of the Queen Mary College Computer Systems Labo- ratory for allowing me to use the machine so heavily and especially to George Coulouris, Jon Rowson, Ben Salama and Harold Thimbleby for leading me through the intricacies of the local software.
  • 12. vi
  • 13. Contents Preface to the online edition i Preface iii I Modular Organisation of Compilers 1 1 Phases and Passes 5 1.1 Tasks and Sub-tasks . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.2 Translation and Optimisation . . . . . . . . . . . . . . . . . . . . 9 1.3 Object Descriptions in the Symbol Table . . . . . . . . . . . . . . 10 1.4 Run-time Support . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.5 Source Program Errors . . . . . . . . . . . . . . . . . . . . . . . . 12 1.6 Two-pass Compilation . . . . . . . . . . . . . . . . . . . . . . . . 14 1.7 An Example of Compilation . . . . . . . . . . . . . . . . . . . . . 17 2 Introduction to Translation 19 2.1 Phrases and Trees . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.2 Tree Walking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.3 Linear Tree Representations . . . . . . . . . . . . . . . . . . . . . 25 2.4 Improving the Tree Walker . . . . . . . . . . . . . . . . . . . . . 27 2.5 Using the Symbol Table Descriptors . . . . . . . . . . . . . . . . 32 2.6 Translation Error Handling . . . . . . . . . . . . . . . . . . . . . 32 3 Introduction to Syntax Analysis 37 3.1 Language Descriptions (Grammars) . . . . . . . . . . . . . . . . . 39 3.2 Bottom-up Analysis of Expressions . . . . . . . . . . . . . . . . . 41 3.3 Top-down Analysis of Statements . . . . . . . . . . . . . . . . . . 47 vii
  • 14. viii CONTENTS 3.4 Building a Node of the Tree . . . . . . . . . . . . . . . . . . . . . 50 3.5 Syntax Error Handling . . . . . . . . . . . . . . . . . . . . . . . . 52 4 Lexical Analysis and Loading 57 4.1 Reading the Source Program . . . . . . . . . . . . . . . . . . . . 58 4.1.1 Characters to items . . . . . . . . . . . . . . . . . . . . . 59 4.1.2 Efficiency of the input phases . . . . . . . . . . . . . . . . 63 4.1.3 Representing an item . . . . . . . . . . . . . . . . . . . . . 63 4.1.4 Identifiers and keywords . . . . . . . . . . . . . . . . . . . 64 4.1.5 Detecting lexical errors . . . . . . . . . . . . . . . . . . . 65 4.1.6 Tricks of implementation . . . . . . . . . . . . . . . . . . 66 4.2 Output for a Loader . . . . . . . . . . . . . . . . . . . . . . . . . 67 4.2.1 Linking program sections . . . . . . . . . . . . . . . . . . 67 4.2.2 Relocating a program section . . . . . . . . . . . . . . . . 68 4.2.3 Fixup of label values . . . . . . . . . . . . . . . . . . . . . 69 4.2.4 Run-time debugging and the loader . . . . . . . . . . . . 71 4.2.5 Loaders and system efficiency . . . . . . . . . . . . . . . . 71 4.2.6 Error detection in the loader . . . . . . . . . . . . . . . . 72 4.3 After the Loader . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 II Translation and Crucial Code Fragments 75 5 Translating Arithmetic Expressions 81 5.1 Reverse Operations . . . . . . . . . . . . . . . . . . . . . . . . . . 84 5.2 Register Dumping . . . . . . . . . . . . . . . . . . . . . . . . . . 85 5.3 Tree Weighting . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 5.4 Avoiding Reverse Operations . . . . . . . . . . . . . . . . . . . . 92 5.5 Function Calls and Register Dumping . . . . . . . . . . . . . . . 94 5.6 Other Forms of the Tree . . . . . . . . . . . . . . . . . . . . . . . 95 5.7 Combinations of Arithmetic Types . . . . . . . . . . . . . . . . . 96 6 Translating Boolean Expressions 101 6.1 Evaluating a Relational Expression . . . . . . . . . . . . . . . . . 101 6.2 Boolean or Conditional Operators? . . . . . . . . . . . . . . . . . 103 6.3 Jumping Code for Boolean Expressions . . . . . . . . . . . . . . 104 6.3.1 Relational expressions . . . . . . . . . . . . . . . . . . . . 107 6.3.2 Binary Boolean operators . . . . . . . . . . . . . . . . . . 108
  • 15. CONTENTS ix 6.3.3 Boolean variables and constants . . . . . . . . . . . . . . 109 6.3.4 Conditional expressions . . . . . . . . . . . . . . . . . . . 109 7 Translating Statements and Declarations 113 7.1 Assignment Statement . . . . . . . . . . . . . . . . . . . . . . . . 113 7.2 ‘While’ Statement . . . . . . . . . . . . . . . . . . . . . . . . . . 116 7.3 BCPL ‘for’ Statement . . . . . . . . . . . . . . . . . . . . . . . . 118 7.4 FORTRAN ‘DO’ Statement . . . . . . . . . . . . . . . . . . . . . 122 7.5 Compound Statements . . . . . . . . . . . . . . . . . . . . . . . . 124 7.6 ALGOL-60-like Blocks . . . . . . . . . . . . . . . . . . . . . . . . 126 7.7 Procedure Declarations . . . . . . . . . . . . . . . . . . . . . . . 126 8 Creating and Using the Symbol Table 129 8.1 Table Lookup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 8.1.1 Sequential lookup . . . . . . . . . . . . . . . . . . . . . . 131 8.1.2 Binary chop . . . . . . . . . . . . . . . . . . . . . . . . . . 131 8.1.3 Name tree . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 8.2 Hash Addressing . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 8.2.1 Re-hashing after a collision . . . . . . . . . . . . . . . . . 135 8.2.2 Chaining to avoid clustering . . . . . . . . . . . . . . . . . 138 8.2.3 Keywords in the symbol table . . . . . . . . . . . . . . . . 138 8.2.4 Tables of constants . . . . . . . . . . . . . . . . . . . . . . 139 8.3 Object Descriptions in the Symbol Table . . . . . . . . . . . . . . 139 8.3.1 Assigning addresses to data objects . . . . . . . . . . . . . 140 8.3.2 Assigning addresses to labels and procedures . . . . . . . 141 8.4 Single Names with Multiple Descriptors . . . . . . . . . . . . . . 142 8.4.1 Hierarchical qualification in COBOL . . . . . . . . . . . . 142 8.4.2 Block-structuring and scope . . . . . . . . . . . . . . . . . 144 9 Accessing an Element of a Data Structure 147 9.1 Accessing an Element of a Vector . . . . . . . . . . . . . . . . . . 148 9.1.1 Fixed-size vectors . . . . . . . . . . . . . . . . . . . . . . . 150 9.1.2 Run-time checks on vector access . . . . . . . . . . . . . . 151 9.1.3 Vectors as arguments in a procedure call . . . . . . . . . . 152 9.2 Accessing an Element of an Array . . . . . . . . . . . . . . . . . 152 9.2.1 Multiplicative subscript calculation . . . . . . . . . . . . . 153 9.2.2 Non-multiplicative array address calculation . . . . . . . . 155
  • 16. x CONTENTS 9.3 Record Data Structures . . . . . . . . . . . . . . . . . . . . . . . 158 9.3.1 Record-type declarations . . . . . . . . . . . . . . . . . . . 160 9.3.2 Accessing an element of a record . . . . . . . . . . . . . . 160 9.3.3 Union types and variant records . . . . . . . . . . . . . . 163 10 Code Optimisation 165 10.0.4 Language restrictions and accurate optimisation . . . . . 167 10.0.5 Is optimisation cost-effective? . . . . . . . . . . . . . . . . 169 10.1 Net Effect and Order of Computation . . . . . . . . . . . . . . . 170 10.1.1 Basic blocks, regions and structured statements . . . . . . 172 10.2 Optimisation within a Basic Block . . . . . . . . . . . . . . . . . 174 10.2.1 Constant folding . . . . . . . . . . . . . . . . . . . . . . . 175 10.2.2 Deferred storage . . . . . . . . . . . . . . . . . . . . . . . 177 10.2.3 Global register allocation . . . . . . . . . . . . . . . . . . 177 10.2.4 Redundant code elimination . . . . . . . . . . . . . . . . . 178 10.2.5 Peephole optimisation . . . . . . . . . . . . . . . . . . . . 180 10.3 Loops and Code Motion . . . . . . . . . . . . . . . . . . . . . . . 180 10.3.1 Loop-invariant code . . . . . . . . . . . . . . . . . . . . . 181 10.3.2 Strength reduction . . . . . . . . . . . . . . . . . . . . . . 182 10.4 Hardware Design and Optimisation . . . . . . . . . . . . . . . . . 184 10.5 Language Design and Optimisation . . . . . . . . . . . . . . . . . 185 III Run-time Support 189 11 Procedure Call and Return 193 11.1 The Tasks of Procedure Call and Return Fragments . . . . . . . 193 11.2 Layout of Procedure Call and Return Fragments . . . . . . . . . 196 11.3 Recursion and the ‘Efficiency’ of FORTRAN . . . . . . . . . . . 199 11.4 Simple Stack Handling . . . . . . . . . . . . . . . . . . . . . . . . 201 11.4.1 Allocating space for vectors and arrays . . . . . . . . . . . 204 12 Arguments and Parameters 209 12.1 Different Kinds of Argument Information . . . . . . . . . . . . . 209 12.2 Passing Argument Information . . . . . . . . . . . . . . . . . . . 211 12.3 Generating Code for a Single Argument . . . . . . . . . . . . . . 215 12.4 Checking Stack Limits . . . . . . . . . . . . . . . . . . . . . . . . 217 12.5 The Evils of Run-time Argument Checking . . . . . . . . . . . . 218
  • 17. CONTENTS xi 12.5.1 Mutually recursive procedures . . . . . . . . . . . . . . . . 219 12.5.2 Separately compiled sections of program . . . . . . . . . . 221 12.5.3 Incompletely specified parameters . . . . . . . . . . . . . 222 13 Environments and Closures 225 13.1 An Explicit Representation of the Environment . . . . . . . . . . 228 13.2 The Environment Link Mechanism . . . . . . . . . . . . . . . . . 231 13.3 The Display Vector Mechanism . . . . . . . . . . . . . . . . . . . 234 13.4 ALGOL 60’s ‘call by name’ . . . . . . . . . . . . . . . . . . . . . 237 13.5 Block Structure and Data Frames . . . . . . . . . . . . . . . . . . 239 13.6 Non-local ‘goto’ Statements . . . . . . . . . . . . . . . . . . . . . 243 14 Efficiency, Heaps and Lifetimes 245 14.1 Procedure Call with PUSH and POP Instructions . . . . . . . . . 246 14.2 Addressing the Stack with a Single Register . . . . . . . . . . . . 248 14.3 Heap Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252 14.4 ALGOL 68, Lifetimes and Pointers . . . . . . . . . . . . . . . . . 258 14.4.1 Multi-word results . . . . . . . . . . . . . . . . . . . . . . 261 14.5 SIMULA 67 ‘classes’ . . . . . . . . . . . . . . . . . . . . . . . . . 262 IV Parsing Algorithms 267 14.6 The Dangers of Backtracking . . . . . . . . . . . . . . . . . . . . 270 15 Notation and Formal Language Theory 273 15.1 Languages and Sentences . . . . . . . . . . . . . . . . . . . . . . 274 15.2 Generating a Sentence . . . . . . . . . . . . . . . . . . . . . . . . 275 15.3 Grammars and Productions . . . . . . . . . . . . . . . . . . . . . 278 15.4 The Chomsky Hierarchy . . . . . . . . . . . . . . . . . . . . . . . 280 15.5 The Parsing Problem . . . . . . . . . . . . . . . . . . . . . . . . . 282 15.6 Derivations and Sentential Forms . . . . . . . . . . . . . . . . . . 283 15.7 Equivalence and Ambiguity . . . . . . . . . . . . . . . . . . . . . 284 15.7.1 When is a grammar ambiguous? . . . . . . . . . . . . . . 288 15.8 Lexical Analysis and type 3 grammars . . . . . . . . . . . . . . . 288 15.9 Warshall’s Closure Algorithm . . . . . . . . . . . . . . . . . . . . 292 15.10Context Dependency and Two-level Grammars . . . . . . . . . . 294 15.10.1Parsing with a context-dependent grammar . . . . . . . . 295
  • 18. xii CONTENTS 16 Top-down Syntax Analysis 297 16.1 Factoring to Reduce Backtracking . . . . . . . . . . . . . . . . . 299 16.2 Removing Left Recursion . . . . . . . . . . . . . . . . . . . . . . 301 16.3 One-symbol-look-ahead and One-track Analysers . . . . . . . . . 305 16.4 Context Clashes and Null Symbols . . . . . . . . . . . . . . . . . 309 16.5 A One-track Grammar is Unambiguous . . . . . . . . . . . . . . 315 16.6 Error Detection and Processing . . . . . . . . . . . . . . . . . . . 316 16.6.1 Reporting a syntax error . . . . . . . . . . . . . . . . . . . 320 16.7 Pragmatics and Tricks . . . . . . . . . . . . . . . . . . . . . . . . 320 16.8 Interpretive Top-down Analysis . . . . . . . . . . . . . . . . . . . 323 16.9 Automatic Generation of Top-down Analysers . . . . . . . . . . . 327 17 Operator-Precedence Analysis of Expressions 329 17.1 Determining the Precedence of Operators . . . . . . . . . . . . . 331 17.2 Numerical Operator Priorities . . . . . . . . . . . . . . . . . . . . 334 17.3 Reducing a Phrase-pattern to a Single Symbol . . . . . . . . . . 337 17.4 An Operator-precedence Grammar is Unambiguous . . . . . . . . 340 17.5 Input States, Error Detection and Unary Operators . . . . . . . . 340 17.5.1 Unary expression operators . . . . . . . . . . . . . . . . . 342 17.5.2 Implementing the input-state mechanism . . . . . . . . . 342 17.6 Recursive operator-precedence analysis . . . . . . . . . . . . . . . 343 18 LR(1) Syntax Analysis 347 18.0.1 Notation and etymology . . . . . . . . . . . . . . . . . . . 348 18.0.2 LR(1) languages . . . . . . . . . . . . . . . . . . . . . . . 350 18.1 Analyser States and the Stack . . . . . . . . . . . . . . . . . . . . 350 18.2 Why It Works: Non-deterministic Syntax Analysis . . . . . . . . 353 18.2.1 Efficient implementation . . . . . . . . . . . . . . . . . . . 357 18.3 Building an SLR(1) Analyser . . . . . . . . . . . . . . . . . . . . 358 18.4 Constructing an LALR(1) Analyser . . . . . . . . . . . . . . . . . 363 18.4.1 Encoding the state table . . . . . . . . . . . . . . . . . . . 366 18.4.2 It doesn’t always work! . . . . . . . . . . . . . . . . . . . 366 18.5 Error Detection and Tree-building Actions . . . . . . . . . . . . . 367 18.6 Error Recovery . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 18.7 What are the True Advantages of LR(1) Analysis? . . . . . . . . 369
  • 19. CONTENTS xiii V Interpreting and Debugging 373 19 Interpreters and Interpretation 377 19.1 Interpreting the parse tree . . . . . . . . . . . . . . . . . . . . . . 378 19.1.1 The costs of interpretation . . . . . . . . . . . . . . . . . 378 19.2 Imitative Interpretation . . . . . . . . . . . . . . . . . . . . . . . 380 19.3 Linearising or Virtual-machine Interpreters . . . . . . . . . . . . 382 19.3.1 Linearising the program . . . . . . . . . . . . . . . . . . . 382 19.3.2 Representing the environment . . . . . . . . . . . . . . . . 385 19.3.3 Setting up the environment . . . . . . . . . . . . . . . . . 387 19.4 The Design of Languages for Interpretation . . . . . . . . . . . . 388 19.4.1 ‘Deep’ and ‘shallow’ dynamic binding . . . . . . . . . . . 388 19.5 Debugging with an Interpreter . . . . . . . . . . . . . . . . . . . 389 19.5.1 Run-time editing . . . . . . . . . . . . . . . . . . . . . . . 391 19.6 Combining Interpretation and Compilation . . . . . . . . . . . . 392 20 Run-time Debugging Aids 395 20.0.1 Assembly Code Debuggers . . . . . . . . . . . . . . . . . . 396 20.0.2 Source Language Debugging . . . . . . . . . . . . . . . . . 397 20.1 Off-line debugging and the panic dump . . . . . . . . . . . . . . . 397 20.2 Interactive debugging . . . . . . . . . . . . . . . . . . . . . . . . 399 20.3 Run-time event tracing . . . . . . . . . . . . . . . . . . . . . . . . 399 20.4 Break points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400 20.5 Producing Debuggable Code . . . . . . . . . . . . . . . . . . . . . 401 20.6 Doing Without the Debugger . . . . . . . . . . . . . . . . . . . . 401 A The BCPL language 403 B Assembly code used in examples 407 Bibliography 411
  • 23. 3 Compilers, at first appearance, seem to display a bewildering variety of organisa- tion: there are single-pass compilers, multi-pass compilers, optimising compilers, load-and-go compilers, interactive compilers and so on and on. Luckily for the compiler-writer, all these apparently different forms can be seen as variations on a simple theme. All compilers perform the same collection of tasks and apparent variations come about because the tasks are put together in slightly different ways or because certain of the tasks are given more emphasis than others in some variations. Below the surface the theme is constant – all compilers per- form the same sequence of essential tasks in the same order and by and large the optional tasks which they may perform have a fixed position in the sequence as well. It’s this underlying regularity of organisation which makes it possible to describe the principles of compiler writing in a space as small as this book, because the question of the organisation of the compiler as a whole can be separated from the questions of the design of its sub-sections. Each of the sub-tasks, and the various algorithms for carrying it out, can then be studied in relative isolation. In addition the regularity makes compiler writing largely source-language inde- pendent: though to the novice it might seem that a COBOL compiler would differ enormously from a PASCAL compiler I shall show that it isn’t so. Per- haps even more surprising is that the problems of compiler writing are largely object-machine independent as well. Both source language and object machine affect only details of compiler construction, whilst the overall organisation re- mains fixed. Once you have understood the principles of compiler design, most of the details fall naturally into place. Of the chapters in this section, chapter 1 discusses the organisation of compi- lation as a sequence of phases, each carried out by a separate module of the compiler, and gives brief examples of the operation of each phase. Chapter 2 gives a more detailed introduction to the principles of translation and justifies the use of a tree-walking translation mechanism. Chapter 3 gives an introduc- tion to the principles of syntax analysis. Chapter 4 discusses lexical analysis and loading: apart from a discussion of lexical analysis grammars in chapter 15 and a discussion of symbol-table building algorithms in chapter 8, both of these rather simple topics are then ignored for the rest of the book. Later sections expand the treatment. Section II concentrates on translation and code optimisation, section III on the code which performs run-time sup- port functions such as stack handling during a procedure call, section IV deals with syntax analysis and section V treats the allied topics of interpretation (as distinct from compilation) of source programs and the run-time debugging of compiled programs. I discuss these topics in this order because I believe that it is useful to un- derstand mechanisms of translation before mechanisms of syntax analysis, even though syntax analysis occurs before translation when a program is compiled. Translation, with code optimisation, is probably the most interesting phase of compilation to the active compiler-writer. The novice needs some instruction in the mechanisms of syntax analysis – but it comes much easier if you’ve seen the
  • 24. 4 requirements of a translator first. In that way you can see why a syntax analyser is necessary and why it must produce the kind of output that it does. Also, having seen the powers and limitations of simple translation, you’re in a better position to assess the importance (or unimportance) of code optimisation.
  • 25. Chapter 1 Phases and Passes The most obvious overall task of a compiler is to read a program in one language – the ‘source’ program in the ‘source’ language – and to translate it to produce an equivalent program in another language – the ‘object’ program in the ‘object’ language. The object language is usually the machine language of a computer, or something close to it, and the source program is usually in a ‘high-level’ language such as FORTRAN, PASCAL, ALGOL 68, SIMULA 67 or BCPL, because translation from high-level language to machine language is the practical problem which compilers exist to solve. Compilers can be written to translate from any kind of programming language into any other, however, with varying degrees of efficiency of the resulting object program. Another part of the compiler’s overall task, just as important but too often neglected, is that the compiler must check that the source program makes some kind of sense and, when it doesn’t seem to make sense, must produce a descrip- tion of the problem (an error report) so that the programmer can correct the program. I’ll return to discuss the tasks of error detection and reporting later in this chapter. Before describing how the overall tasks are split up into sub-tasks it’s worth discussing the ways in which the sub-sections of a compiler can be combined to make a complete compiler. The overall activity of compilation can be divided into a sequence of phases, during each of which one of the sub-tasks is carried out. As emphasised in the introduction to this section, all compilers perform the same sub-tasks in the same sequence, and therefore all compilers consist of the same sequence of phases. Conceptually each phase transforms the pro- gram fragment-by-fragment, taking as input a fragment of some representation of the source program and producing as output a fragment of some other, trans- formed, representation. Because the transformation is fragment-by-fragment it is possible to choose how the phases are linked. If each separate fragment goes through all the phases before compilation of the next fragment starts we have a single-pass compiler, because all of the source program is compiled in a single pass over the source text. Conversely, if the entire program goes through each 5
  • 26. 6 CHAPTER 1. PHASES AND PASSES one of the phases before any of it is presented to the next phase we have a multi- pass compiler, because each phase performs a separate pass over the program. Clearly if there are N phases, you can organise the compiler into anything from 1 to N passes. Thus every compiler must be multi-phase, but it may or may not be multi-pass. Logically the phases must be joined together in the same order no matter how many passes are employed, so that it might not seem to matter whether this book concentrates on multi-pass or single-pass organisation. In practice, however, the multi-pass organisation is simpler to describe and to understand because the interface between the phases is so much more straightforward. Indeed for some languages – ALGOL 60 is a prominent example – it is difficult to construct an effective single-pass compiler at all and therefore the multi-pass organisation is also more generally useful. It is often believed that multi-pass compilation is inherently less efficient than single-pass. This is simply untrue: since in any compiler each fragment must be processed by the same sequence of phases, a single-pass compiler must perform the same amount of work as a multi-pass compiler. Suppose, however, that each pass of a multi-pass compiler were to write its output into a disc file (or any backing store file) from which the next pass had to read it in again. Such a multi- pass compiler would indeed be less efficient than a single-pass compiler which didn’t use backing store but the inefficiency would be caused by the overhead of input and output between passes. A multi-pass compiler which stores the output of each pass in the main computer memory will certainly be no slower than a single-pass compiler. On a modern computer the only disadvantage of multi-pass compilation is that it may use quite a lot of space to store the output of the various passes. Nowadays com- puter memories (for everything but the smallest micro-processors!) are large enough for this minor drawback to be overwhelmed by the enormous advan- tages of clarity of design, and consequent ease of construction, of a multi-pass compiler. For most of the rest of this book I describe a compiler organised in two passes, which is an effective compromise between economy of space and clarity of organisation. In the discussion of compilation algorithms, however, it is usually simplest to imagine that each of the phases in a compiler always performs a separate pass over the program. 1.1 Tasks and Sub-tasks Figure 1.1 shows a coarse breakdown of the overall compilation task (I deal with the parallel task of error processing separately below). A compiler must first analyse a program to see what its intended effect is. The result of this analysis will be a representation of the program which is adequate for later translation into the object language. As I show below and in chapter 2, this representation is a structure which shows how the separate fragments of program inter-relate. It splits naturally into two parts:
  • 27. 1.1. TASKS AND SUB-TASKS 7 source program analysed program object program ANALYSE TRANSLATE Figure 1.1: Coarse structure of compilation (i) the ‘parse tree’ which shows the structure of the program text – how the fragments of program join together to make up larger fragments, how those join to make still larger fragments and so on. (ii) the ‘symbol table’ which provides a correlation between all the different occurrences of each name throughout the program and hence provides a link between each name and its declaration, whether implicit or explicit, in the source text. These two information structures are the most important milestones in the compilation process. Compilation as a whole is an activity which first builds up information structures which describe how the program may be broken into frag- ments and how these fragments inter-relate, then extracts from these structures the information which is required in order to translate the program. Neither analysis nor translation is yet a simple enough sub-task to describe individually. Figure 1.2 shows a more detailed breakdown into sub-tasks. The figure shows clearly the hierarchical-sequential nature of the compiling process: each of the sub-tasks is entrusted to a particular phase and the phases don’t overlap at all.1 Capital letters (e.g. LEXICAL ANALYSIS) indicate a phase or sub-task, lower case letters (e.g. parse tree) an information structure or an intermediate program representation. The arrangement of phases in figure 1.2 is partly determined by the nature of the compiling task but is also partly conventional. In particular the LEXICAL ANALYSIS phase is present for convenience’ sake and the fact that the com- piler’s output is most commonly presented to a LOAD phase before execution is equally a matter of convenience. Consider the LEXICAL ANALYSIS phase: it has been found convenient to in- clude a phase, immediately before SYNTAX ANALYSIS, which partitions the characters of the source program into ‘items’, roughly analogous to words and punctuation marks in natural languages. This not only makes the design of the syntax analyser easier, but also it happens that almost all of those sections of the compiler whose speed of operation is important fall into the READ and LEXICAL ANALYSIS phases. Thus the task of improving the compiler’s op- erating efficiency can be reduced to the task of improving the efficiency of the relatively simple input phases and in the rest of the compiler it is possible to 1 There are some minor exceptions to this rule when compiling rather old languages such as FORTRAN. These exceptions have been eliminated in most modern languages.
  • 28. 8 CHAPTER 1. PHASES AND PASSES source program characters lexical items parse tree object code object program s y m b o l t a b l e READ LEXICAL ANALYSIS SYNTAX ANALYSIS SIMPLE TRANSLATION LOAD OBJECT DESCRIPTION OPTIMISE TREE (optional) OPTIMISE CODE (optional) Figure 1.2: Finer structure of compilation
  • 29. 1.2. TRANSLATION AND OPTIMISATION 9 ignore the demands of speed of operation in order to concentrate on clarity of design and construction. Consider next the LOAD phase shown in figure 1.2. If the source program is split into sections which are compiled separately to produce separate sections of object code, it is necessary eventually to use a loader program to combine all the separate code sections into a complete executable program. At the same time the loader can automatically incorporate sections of code from a separate code library. People find the facility to split their program into sections convenient, so the loader increases human efficiency although in terms of machine utilisation alone it can be more efficient to compile the entire program in one piece and to avoid the loading overhead entirely. At any rate the compiler may, but need not, output code in ‘loadable’ form as the compiler-writer wishes. I discuss the input and output phases (READ, LEXICAL ANALYSIS and LOAD in figure 1.2) together with the question of code output formats in chap- ter 4, so as to separate these matters from the more central and more important phases of analysis, translation and optimisation which occupy most of this book. 1.2 Translation and Optimisation Two of the phases in figure 1.2 are marked as optional – OPTIMISE TREE and OPTIMISE CODE. They are included in the figure because they are so important and so frequently included (or desired by the user if they are not included!). In order to explain the breakdown of the diagram at this point it’s necessary to distinguish between ‘simple translation’ and ‘optimisation’ in general Simple translation takes a representation of a fragment of the source program and produces an equivalent fragment of object code. It doesn’t take much account of the interaction between fragments, except insofar as it must do in order to produce correct code, but it may be quite sophisticated in the way in which it selects the particular code fragment which it generates as the translation of each source program construct. Optimisation takes a wider view of the program: it looks at a representation of a larger section of the source program and reorganises the fragments within it, or improves the way in which they interface, so as to produce a shorter object program or a faster object program than simple translation could produce. Sometimes it is possible to produce an optimised object program which is both shorter and faster than one produced by simple translation. It’s not possible to draw a hard line between the two approaches because it’s not clear when sophisticated simple translation becomes primitive optimisa- tion. Some so-called ‘optimising’ compilers would be better viewed as rather good simple translators: many straightforward compilers include some element
  • 30. 10 CHAPTER 1. PHASES AND PASSES of optimisation. True optimisations, for the purposes of this book, are those translations which exploit peculiarities of the control flow specified in the source program. Chapter 10 discusses this topic more fully. There are essentially two ways in which a compiler can optimise a program – i.e. produce a better-than-simple translation (i) It can include a phase which alters the source program algorithm in such a way that subsequent simple translation can produce the desired effect. This is shown as OPTIMISE TREE in figure 1.2: essentially it replaces the source program’s algorithm with another which has the same effect but which can be translated into more efficient code. (ii) It can include a phase which modifies the code produced by simple trans- lation in order to increase the efficiency of the object program. This is shown as OPTIMISE CODE in figure 1.2: this phase looks mainly at the ways in which the code fragments interface with each other. These two approaches can be equivalent in their effect and sometimes com- pilers employ both. The OPTIMISE TREE technique is the more machine- independent of the two and is perhaps potentially the more powerful. 1.3 Object Descriptions in the Symbol Table The names used in a program are merely used to identify run-time objects of one kind or another – variables, arrays, records, procedures, labels and so on. Apart from the needs of debugging, the source program names used are completely arbitrary and the object program operates entirely upon the run-time objects without taking any account of the names which originally introduced them. In order to keep track of the association between names and run-time objects, the compiler must keep a table (the symbol table in figure 1.2) in which it stores against each name a description of the run-time object which that name denotes. The translator uses this table to replace each occurrence of a name by a reference to the corresponding run-time object. As names are encountered in the source program they are inserted into the symbol table by the lexical analyser (more discussion of this in chapters 4 and 8) but information about the associated run-time object can only be inserted after the program has been syntactically analysed. Names are ‘declared’ ei- ther implicitly (e.g. in FORTRAN) or explicitly (most other languages!). In general the declarative information must be processed before any statement using the name can be translated. Thus correlation of names with run-time objects must precede translation, but must of course follow syntax analysis in which the declarative information is recognised. So there is only one time in the compilation process at which such correlation is possible – hence the OBJECT DESCRIPTION phase in figure 1.2 falls between syntax analysis and simple translation or optimisation.
  • 31. 1.4. RUN-TIME SUPPORT 11 program: begin integer a,b; .... procedure A(x) real x; begin real y; .... B(‘a’); .... end; procedure B(u) character u; begin string v; .... A(3.7); .... end; .... A(1.0); .... end After outer block calls A, which calls B, which calls A again: a b outer block x y A(i) u v B x y A(ii) Figure 1.3: Activation Record Structure in a simple program 1.4 Run-time Support Much of the object program consists of instructions which carry out the tasks of calculation, assignment, comparison, etc. which are specified in the source program. However there are also sections of the source program whose task is run-time support of various kinds. Two important tasks of run-time support are to implement the storage and register manipulations necessary for the execution of a procedure call2 (discussed in detail throughout section III) and the mainte- nance of ‘heap’ storage systems (touched on in chapter 14). The efficiency of the run-time support fragments in the object program ought to be a major concern of the compiler writer: users will present programs for compilation which are based on highly efficient algorithms and will be justifiably annoyed if the object program is inefficient purely because of the operation of run-time support code fragments ‘behind the scenes’. The most important task of the run-time support fragments is to maintain the Activation Record Structure. When a procedure call is executed a procedure 2 Throughout this book I use the word ‘procedure’ to include the word ‘function’ – a function is merely a procedure which computes and returns a result. So far as run-time support is concerned, functions and procedures are almost identical.
  • 32. 12 CHAPTER 1. PHASES AND PASSES activation is created, which exists until the corresponding procedure return is executed. In non-recursive languages such as FORTRAN only one activation at most can ever exist for each procedure but in languages which permit recursion there may be several activations in existence, each in a different stage of exe- cution of a single procedure body and each with its private storage and private copy of the arguments with which it was called. Each of these activations can be described by a data structure – in a recursive programming language the data structure is an ‘activation record’, ‘data frame’ or ‘stack frame’ stored on a stack – and the linkages between these data structures define the Activation Record Structure. Figure 1.3 shows a simple example of the structure which can result. The maintenance of the Activation Record Structure in an implementation of a recursive programming language is an important ‘overhead’ cost in the execution of the object program. By prohibiting recursion, languages such as FORTRAN reduce the overhead cost but the discussion in section III shows how careful design of code fragments can minimise or even eliminate the efficiency gap be- tween recursive and non-recursive languages. Even recursive languages must restrict the manipulation of some values in the object program in order to allow the Activation Record Structure to be kept in a stack: chapter 14 discusses the way in which the necessary restrictions can be applied in an implementation of ALGOL 68 and touches on the implementation of SIMULA 67, which can avoid the imposition of such restrictions because it doesn’t use a data-frame stack. 1.5 Source Program Errors “Any fool can write a compiler for correct source programs” (J.S. Rohl 1972) People write programs and people make mistakes. They often need help in order to correct their mistakes and sometimes a compiler can provide it. Compiler designers too often seem to place ‘helping people’ rather low down on their scale of priorities, well below ‘compiler efficiency’, ‘object program efficiency’ and ‘source language facilities’. My opinion is that this order of priorities is wrong-headed but I hope to demonstrate in this book that it is unnecessary as well. There is no conflict in practice between, on the one hand, the goal of providing help with errors in source programs and, on the other hand, the goal of providing a compiler which is efficient and which produces efficient object programs. A compiler has two basic tasks to perform in connection with source programs which contain errors: (i) Compile-time error processing: The compiler must somehow provide information to a programmer who has submitted a program which cannot be compiled. The information must, as far as possible, give an explanation of why the program cannot
  • 33. 1.5. SOURCE PROGRAM ERRORS 13 be compiled and if possible make suggestions as to how the error might be corrected. (ii) Error-checking code: Many programs, though syntactically correct, specify impossible sequences of operations when they are executed. Some object program errors (e.g. arithmetic overflow) may be detected by the object machine’s hardware but probably many others will not be. In order to detect run-time errors which are not covered by hardware checks the compiler must produce ob- ject code fragments which check whether a run-time error has occurred each time they are run. An example is the array-bound check: it is com- mon for a compiler to generate code for an array access which checks the validity of the array subscript each time an access is made. In either case error processing can be divided into three separate activities: error detection, error reporting and error recovery. First consider compile-time errors: chapter 3 and section IV show that compile- time error detection is an automatic consequence of the normal compilation process. This implies that detection of errors cannot affect the speed of op- eration of a compiler; therefore the (imaginary super-human) user who always writes correct programs is not affected by the vagaries of ordinary mortals, at least so far as speed of compilation is concerned. Once an error has been de- tected in the source program the programmer is no longer concerned simply with how rapidly the compiler translates but rather with how quickly the error can be removed from the program. A small amount of computer time spent in error reporting and recovery is well spent if it saves human time and effort. Of course a compiler which includes a great deal of error reporting and recovery facilities will be larger than one that contains only a small amount and so there is some penalty in terms of space occupied, but the extra space and the few fractions of a second of computer time spent in producing a readable error report are surely worthwhile if they save minutes (or perhaps hours!) of human effort. Similar considerations apply to run-time error-handling. In this case error- detection can be expensive – the code fragments which repeatedly check for error will inevitably slow down the execution of the object program – but the bulk of the error processing code will be concerned with error reporting and error recovery and so will only be activated when an error has already been detected. It’s outside the scope of this book to debate when, if ever, the programmer should ask the compiler to omit error checking code so as to save execution time and object program space – at any rate the compiler should always give the option that it can be included. Chapter 20 discusses run-time error handling in detail. I include run-time error checking as a task of the compiler, rather than of the operating system or of some other separate program, because the design of object code fragments must take into account the needs of run-time error-checking and error-reporting. Furthermore the compiler designer must consider the production, at compile- time, of information structures which will be used by a run-time debugger to
  • 34. 14 CHAPTER 1. PHASES AND PASSES begin integer a; real b; a := 1; b := 1.2; a := b+1; print(a*2) end Figure 1.4: Example program provide error reports related to the original source program. The requirements of run-time error processing can have a major influence on the detail design of object code fragments. Figure 1.2 doesn’t include any mention of error processing but in reality it is a part of the task of every phase of compilation. I shall return again and again in later chapters to the question of how the various phases may be designed to produce readable and understandable error reports. 1.6 Two-pass Compilation Most compilers that aren’t single-pass use only two passes rather than the four or five implied by the structure of figure 1.2. Using two passes is a popular com- promise between generality and the somewhat demanding space requirements of a true multi-pass compiler. The two-pass organisation essentially follows figure 1.1 – there is an analysis pass and a translation pass. The analysis pass includes the READ, LEXICAL ANALYSIS and SYNTAX ANALYSIS phases. In order to combine these phases into a single pass it is conventional to make READ a subroutine of LEXICAL ANALYSIS and LEXICAL ANALYSIS a subroutine of SYNTAX ANALYSIS – as chapter 4 shows, this isn’t very difficult. The translation pass usually consists of a SIMPLE TRANSLATION phase only. The two-pass organisation seems to leave out the OBJECT DESCRIPTION phase entirely and in a two pass compiler this phase is rather difficult to include as a separate entity. Object description is usually carried out piecemeal during one or other (or both) of the passes. Perhaps most convenient is to perform all object description during the translation pass: every time the translator encounters a section of the tree that may contain declarative information it must be processed by the object description phase, which creates symbol table descriptors based on any declarations contained in that section of the tree, before it can be translated into object code. The examples in sections II and III show algorithms in which the object description phase is a subroutine of the translation phase, called each time the translator encounters a ‘procedure declaration’ node or a ‘block’ node in the parse tree.
  • 35. 1.6. TWO-PASS COMPILATION 15 beginsymbol integersymbol name, semicolonsymbol realsymbol name, semicolonsymbol name, assignsymbol number, 1 semicolonsymbol name, assignsymbol .... a b (empty) (empty) Name Description Figure 1.5: Part of the output from the lexical analyser block declarations statements intdecl realdecl assign assign assign ProcCall a b a 1 b 1.2 a + b 1 print * a 2 Figure 1.6: Output from the syntax analyser
  • 36. 16 CHAPTER 1. PHASES AND PASSES assign name plus name 1 number (pointer to entry for ‘a’ in symbol table) (pointer to entry for ‘b’ in symbol table) Figure 1.7: Data structure representation of a tree a variable, integer, address #1 b variable, real, address #2 print procedure, one integer argument, address from loader Figure 1.8: Symbol table descriptors LOAD 1, address #2 fADDn 1, 1.0 FIX 1, STORE 1, address #1 Figure 1.9: Possible machine (assembly) code output
  • 37. 1.7. AN EXAMPLE OF COMPILATION 17 1.7 An Example of Compilation In the rest of this chapter I show, very briefly, the action of some of the phases of compilation taking as example input the program of figure 1.4. First of all the characters of the program are read in, then these characters are lexically analysed into separate items. Some of the items will represent source program identifiers and will include a pointer to the symbol table.3 Figure 1.5 shows part of the results of lexical analysis: in most languages subdivision of the input into items can be performed without regard to the context in which the name occurs. Next, the syntax analyser examines the sequence of items produced by the lexical analyser and discovers how the items relate together to form ‘phrase’ fragments, how the phrases inter-relate to form larger phrases and so on. The most general description of these relationships of the fragments is a tree, as shown in figure 1.6. Each node of the tree includes a tag or type field which identifies the kind of source program fragment described by that node, together with a number of pointers to nodes which describe the subphrases which make it up. Figure 1.7 shows how part of the tree shown in figure 1.6 might actually be represented as a data structure. There are of course a number of different ways of representing the same tree, and that shown in figure 1.7 is merely an example. There are as many different ways of drawing the structure, and throughout most of this book I shall show trees in the manner of figure 1.6. The only significant differences between the two picturings is that, in the first, nodes aren’t shown as sequences of boxes and names aren’t shown as pointers to the symbol table. Figure 1.7 is perhaps more faithful to reality, so it should be borne in mind whenever you encounter a simplified representation like that in figure 1.6. After syntax analysis, the object description phase takes the tree and the symbol table entries produced by the lexical analyser. It analyses the declarative nodes in the tree, producing descriptive information in the symbol table as shown in figure 1.8. Standard procedures, such as ‘print’, may receive a default declara- tion before translation: figure 1.8 shows a possible entry in the symbol table. Note that, since the tree contains a pointer to the symbol table in each node which contains a reference to an identifier, neither object description phase nor translator need search the symbol table but need merely to follow the pointer to the relevant entry. After the object description phase has filled in the descriptors in the symbol table, a simple translation phase can take the tree of figure 1.7 together with the symbol table of figure 1.8 and produce an instruction sequence like that shown in figure 1.9.4 3 The item which represents a number may also contain a pointer to a table which contains a representation of the number. For simplicity figures 1.5 and 1.6 show the value of the number as part of the lexical item itself. 4 See appendix B for a brief description of the assembly code instructions used in this and
  • 38. 18 CHAPTER 1. PHASES AND PASSES The addresses used in the instructions need finally to be relocated by the loader. Suppose that, when the program is loaded into store, its memory cell space starts at address 23. Then the first line of figure 1.9 would be converted into ‘LOAD 1, 24’ and the last line into ‘STORE 1, 23’. Optimisation of the object code in this case could produce an enormous im- provement in its execution efficiency. The total effect of the program is to print the number ‘4’. By looking at the way in which the values of variables are used throughout the program and by deferring translation of assignment statements until their result is required – in this case they are never required – an optimi- sation phase could reduce the program just to the single statement ‘print(4)’. Optimisation is a mighty sledgehammer designed for bigger nuts than this ex- ample, of course, and it is always an issue whether the expense of optimisation is worth it: in the case of figure 1.4 it would certainly cost more to optimise the program than it would to run it! Summary The underlying organisation of compilers is simple and modular. This chapter discusses how the various phases cooperate so that later chapters can concen- trate on the separate phases in isolation. Input and lexical analysis is discussed in chapters 4 and 8; syntax analysis in chapters 3, 16, 17 and 18; object description in chapter 8; translation in chapters 5, 6, 7 and 9; optimisation in chapter 10; loading in chapter 4; run-time support in chapters 11, 12, 13 and 14; run-time debugging in chapter 20. other examples.
  • 39. Chapter 2 Introduction to Translation The most important task that a compiler performs is to translate a program from one language into another – from source language to object language. Simple translation is a mechanism which takes a representation of a fragment of the source program and produces an equivalent fragment in the object language – a code fragment which, when executed by the object machine, will perform the operations specified by the original source fragment. Since the object program produced by a simple translator consists of a sequence of relatively independent object code fragments, it will be less efficient than one produced by a mechanism which pays some attention to the context in which each fragment must operate. Optimisation is a mechanism which exists to cover up the mistakes of simple translation: it translates larger sections of program than the simple translator does, in an attempt to reduce the object code inefficiencies caused by poor interfacing of code fragments. In order to be able to produce object code phrases the translator must have access to a symbol table which provides a mapping from source program names to the run-time objects which they denote. This table is built by the lexical analyser (see chapters 4 and 8) which correlates the various occurrences of each name throughout the program. The mapping to run-time objects is provided by the object description phase (see chapter 8) which processes declarative in- formation from the source program to associate each identifier in the symbol table with a description of a run-time object. This chapter introduces the notion of a ‘tree-walking’ translator, which I believe is a mechanism that is not only easy to construct but which can readily and reliably produce efficient object code fragments. Such a translator consists of a number of mutually recursive procedures, each of which is capable of translating one kind of source program fragment and each of which can generate a variety of different kinds of object code fragments depending on the detailed structure of the source fragment which is presented to it. Because the process of translation depends on the selection at each point of one 19
  • 40. 20 CHAPTER 2. INTRODUCTION TO TRANSLATION Statement: if hour*60+minute=1050 or tired then leave(workplace) conditional statement [EXPRESSION] Boolean or [STATEMENT] procedure call [LEFT] relation = [RIGHT] name tired [LEFT] arithmetic + [RIGHT] number [PROCEDURE] name [ARGUMENTS] name leave workplace 1050 [LEFT] arithmetic * [RIGHT] name minute [LEFT] name hour [RIGHT] number 60 Figure 2.1: Tree describing a simple statement of a number of possible code fragments translators tend to be voluminous, but since the selection of a fragment is fairly simple and generating the instructions is very straightforward, they tend to run very rapidly. A simple translator will usually use less than 15% of the machine time used by all phases of the compiler together, but will make up more than 50% of the compiler’s own source code. 2.1 Phrases and Trees In this chapter I introduce the technical term phrase, which is used to describe a logically complete fragment of source program. Statements, for example, are phrases; so are expressions. Phrases are either ‘atomic’ – e.g. a name, a number – or are made up of sub-phrases – e.g. a statement may have a sub-phrase which is an expression, the statement itself may be a sub-phrase of a block which is a sub-phrase of a procedure declaration, and so on. In this chapter the largest phrase shown in the examples is an expression or a simple structured statement. In a multi-pass compiler the unit of translation is the entire program – at least
  • 41. 2.1. PHRASES AND TREES 21 cond or = + * call numb 1050 numb 60 tired (in symbol table) leave (in symbol table) workplace (in symbol table) minute (in symbol table) hour (in symbol table) Figure 2.2: Data-structure representation of the tree
  • 42. 22 CHAPTER 2. INTRODUCTION TO TRANSLATION at the topmost level – but a single page isn’t large enough to show the necessary trees! The state of the art in programming language translation is now, and will remain for some time, that a program can only be translated if it is first analysed to find how the phrases inter-relate: then the translator can consider each phrase in turn. The result of the analysis shows how the program (the phrase being translated) can be divided into sub-phrases and shows how these sub-phrases inter-relate to make up the entire phrase. For each sub-phrase the description shows how it is divided into sub-sub-phrases and how they are inter-related. The description continues to show the subdivision of phrases in this way until the atomic phrases – the items of the source text – are reached. The most general representation of the results of such an analysis is a tree like that in figure 2.1. The lines are branches, the place where branches start and finish are nodes. The topmost node of the tree is its root – the tree is conventionally drawn ‘upside down’. Nodes which don’t divide into branches are leaves and represent the atomic items of the source program. Each phrase of the source program is represented by a separate node of the tree. To translate a phrase it is merely necessary to concentrate on one node and, if it has sub-nodes, to translate its sub-nodes as well. Figure 2.2 shows a possible data-structure representation of the tree of figure 2.1 using record-vectors and pointers. Many other representations are possible – for example one in which each pointer is represented by a subscript of a large integer vector. The tree structure, no matter how it is represented, gives the translator the information it needs in order to translate the statement – it shows which phrases are related, and how they are related. From the source statement it isn’t immediately obvious what the object program should do. Should it, for example, evaluate ‘hour*60’? Must it evaluate ‘minute=1050’? What about ‘1050 or tired’? A glance at the tree gives the answers – only the first is represented by a node because analysis has shown it to be a phrase of the program. In order to be able to translate a phrase of a program it is essential to know what kind of phrase it is, what its sub-phrases are and how they are inter-related. There can be non-tree-like representations of the necessary information, but the tree holds and displays the information in its most accessible and useful form. In addition it emphasises the essentially recursive nature of translation – a nature which is somewhat hidden, but not denied, in so-called ‘linear’ representations. 2.2 Tree Walking It’s extremely easy to translate – generate code for – a phrase if you know how to generate code for its sub-phrases. Figures 2.3 and 2.4 show some sample procedures which translate in this way. Each procedure translates a phrase by generating a single instruction, or a short sequence of instructions, which links together the code fragments that are the translation of its sub-phrases. Only
  • 43. 2.2. TREE WALKING 23 • Conditional statement (if-then): 1. call procedure which generates code to load value of expression part into register 1; 2. invent a label #Lf; 3. generate JUMPFALSE 1, #Lf; 4. call procedure which generates code to carry out statement part; 5. generate #Lf:. • Boolean or (result to be in register k): 1. : call procedure which generates code to load value of left operand into register k; 2. call procedure which generates code to load value of right operand into register k+1; 3. generate ORr k, k+1. • Relation ‘=’ (result to be in register k): 1. call procedure which generates code to load value of left operand into register k+1; 2. call procedure which generates code to load value of right operand into register k+2; 3. generate LOADn k, TRUE SKIPEQr k+1, k+2 LOADn k, FALSE. • arithmetic ‘+’ (result to be in register k): Exactly as for Boolean or, except that the final instruction is ADDr k, k+1. • arithmetic ‘*’ (result to be in register k): Exactly as for Boolean or, except that the final instruction is MULTr k, k+1. Figure 2.3: Translation procedures for non-leaf nodes
  • 44. 24 CHAPTER 2. INTRODUCTION TO TRANSLATION • name (value to be loaded into register k) generate LOAD k, <run-time address of variable denoted by name> • number (value to be loaded into register k) if value is small enough then generate LOADn k, <value of number> else generate LOAD k, <run-time address where value will be found> Figure 2.4: Translation procedures for leaf nodes ADDr 2, 3 LOADn 3, 1050 LOADn 1, TRUE SKIPEQr 2, 3 LOADn 1, FALSE LOAD 2, tired JUMPFALSE 1, #Lf .... <procedure call> #Lf: <rest of program> LOAD 2, hour LOADn 3, 60 MULTr 2, 3 LOAD 3, minute ORr 1, 2 .... .... [name] [name] [name] [number] [number] [*] [+] [=] [or] [if] Figure 2.5: Code produced by the translation procedures
  • 45. 2.3. LINEAR TREE REPRESENTATIONS 25 hour, 60, *, minute, +, 1050, =, tired, or Figure 2.6: Linearised expression for indivisible phrases (leaf nodes) is it necessary to know how to translate the entire phrase. In order to generate code for a sub-phrase, all the translator needs to do is to look at the type of phrase it is – the ‘tag’ or ‘type’ field of the tree node – and to call the relevant translation procedure. Figure 2.5 shows the code produced by these procedures when presented with the tree of figure 2.1. (The figures don’t show the translation procedure which handles the procedure call node, or the code produced – section III deals with this topic.) Starting at the root of the tree, the translator works downwards. It’s essentially a recursive process – one operand of a ‘+’ node may be another ‘+’ node or may contain a ‘+’ node, for example. Since the process is recursive it’s trivially easy to translate structured statements like the conditional statement which might at first seem the hardest to translate. Imagine how straightforward it is to translate a PASCAL compound statement or an ALGOL 68 serial clause! The most important thing to note about figure 2.5 is that the code would work if it was given to the object machine to execute. Tree walking is a powerful technique precisely because it is easy to design procedures like those in figures 2.3 and 2.4 which really will generate accurate code. The object code produced by these procedures isn’t optimal by any means, but it’s relatively trivial to change the procedures to improve it. Each procedure is a separate module: although figure 2.3 doesn’t include all of the standard programming language expression operators, it wouldn’t be necessary to make any changes to the existing set when new node-translating procedures are added. 2.3 Linear Tree Representations Before describing how to improve the code produced by the translation proce- dures of figure 2.3 and 2.4 it is necessary to dispose of a red herring – ‘Reverse Polish’ or ‘postfix string’ representation. The collection of procedures in figures 2.3 and 2.4 describe a tree walk – a process which visits the nodes of the tree in a particular, predetermined order. If the nodes of the expression part of the tree of figure 2.1 are written out in just the order in which the tree walker would visit them then a translator can walk them with a non-recursive algorithm. Figure 2.6 shows the nodes of the tree from figure 2.1, written out in the order in which they are visited by the tree walker. Considering only the string which represents the expression part of the tree in figure 2.1, it is possible to define a simple iterative procedure which generates code for it: figure 2.7 shows a possible algorithm. There is a slight difficulty with the ‘=’ node, which is in effect ‘visited’ twice in the tree walk, once to reserve the register which will hold TRUE or FALSE and once to actually generate the
  • 46. 26 CHAPTER 2. INTRODUCTION TO TRANSLATION k := 1 /* k records next free register */ until end-of-string do switchon current-element into case name: Gen(LOAD, k, <address of variable>) k := k+1 endcase case number: if number is small enough then Gen(LOADn, k, <value of number>) else Gen(LOAD, k, <address where value will be found>) k := k+1 endcase case ‘+’: Gen(ADDr, k-2, k-1); k := k-1 endcase case ‘*’: Gen(MULTr, k-2, k-1); k := k-1 endcase case ‘=’: Gen(LOADn, k, TRUE) Gen(SKIPEQr, k-1, k-2) Gen(LOADn, k, FALSE) Gen(LOADr, k-2, k) k := k-1 endcase case ‘or’: Gen(ORr, k-2, k-1); k := k-1 endcase default: CompilerFail("invalid element in string") current-element := next-element Figure 2.7: Linear string walker
  • 47. 2.4. IMPROVING THE TREE WALKER 27 LOAD 1, hour LOADn 2, 60 MULTr 1, 2 LOAD 2, minute ADDr 1, 2 LOADn 2, 1050 LOADn 3, TRUE SKIPEQr 1, 2 LOADn 1, FALSE LOADr 1, 3 /* extra instruction */ LOAD 2, tired ORr 1, 2 Figure 2.8: Code produced by the linear string walker instructions which carry out the comparison. In the case of a simple Reverse Polish string the operator appears only once, so that the code for the ‘=’ node must move the computed value into its intended register – the ‘extra instruction’ shown in figure 2.8. The string walking algorithm uses a stack of registers, so its status as ‘non- recursive’ is in doubt anyway. Note that it is impossible to vary the way in which the tree is walked, according to some code generation strategy, after the string has been generated. Use of a postfix string representation of the source pro- gram merely freezes the compiler into a particular kind of tree walk. Although the string-walking algorithm could be improved quite easily in some ways (e.g. avoiding ‘LOAD, ADDr’ instructions in sequence) most of the improvements which are introduced below and discussed in detail in later chapters involve changing the way the tree is walked and so are impossible (or at best extremely difficult) using any linear representation. 2.4 Improving the Tree Walker Once you have a working translator, you are in business as a compiler-writer. Using tree walking will get you to this point faster. It will also help you to get farther and faster along the road to better code than any ad hoc solution could. Improving the efficiency of the code produced by a tree-walking translator means looking at the code fragments generated by one or more of the procedures and deciding that the procedure’s strategy was weak, or that it should look out for that particular source construct and generate a specially selected code fragment for it. The various possibilities are summarised in figure 2.9. It’s hard to decide where tactics shade into strategy: figure 2.10 shows an improved arithmetic ‘+’ procedure. This procedure saves an instruction and a register when the right operand is a leaf: I would classify this as a tactical manoeuvre. Using this
  • 48. 28 CHAPTER 2. INTRODUCTION TO TRANSLATION 1. Generate special code for a node with a special local property (tactics). 2. Alter the way the tree is walked when a node has a particular general property (strategy). 3. Modify the tree before it is walked (optimise the tree). 4. Modify the code which was generated, after the tree walk has finished (optimise the code). Figure 2.9: Tactics and strategy • arithmetic ‘+’ (result to be in register k) 1. generate code to calculate value of left operand in register k 2. if right operand is a name then generate ADD k, <address of variable> else if right operand is a number then generate ADDn k, <value of number> or ADD k, <place where value will be found> else (a) generate code to calculate value of right operand in register k+1; (b) generate ADDr k, k+1. Figure 2.10: Improved translation of binary operation nodes
  • 49. 2.4. IMPROVING THE TREE WALKER 29 LOAD 2, hour MULTn 2, 60 ADD 2, minute LOADn 1, TRUE SKIPEQn 2, 1050 LOADn 1, FALSE OR 1, tired Figure 2.11: Code produced by improved translator procedure the first three instructions of figure 2.5 would be compressed into two and, if a similar improvement were made to the procedures which translate ‘=’ and or nodes, the translator would produce the code shown in figure 2.11 for the example expression. This code is a considerable improvement over that of figure 2.5 – it uses two rather than three registers, seven rather than eleven instructions – and it was reached by making simple and modular changes to the procedures of the translator. Chapter 5 shows that much more can be done about the code generated for arithmetic operator nodes, and chapter 6 shows that quite a different approach is possible for Boolean operation nodes. Improvements like that in figure 2.10 can also be made to the iterative string- walking procedure. Strategic alterations are far more difficult, however, since a linear representation doesn’t allow the translator to take account of the structure of an expression. Some further tactical possibilities are shown in figure 2.12. These are ‘sub- optimisations’ because the code which is generated for a particular source frag- ment doesn’t depend on the control context within which the object code frag- ment will operate. True optimisations take account of this context and consider, for example, whether there are any registers which already hold the values of variables, whose contents can be used to avoid an unnecessary LOAD instruc- tion. To generate this code, the procedure for assignment nodes must look for nodes with a particular special property and generate a special code fragment for them. The number of possible tactical improvements is enormous and the translator should implement only those tactics which are cost-effective – i.e. where the improvement in object program efficiency is sufficient to justify the cost of recognising the tactical situation and also the frequency with which the situation arises is sufficient to justify checking each possible node in the tree. Modifying the tree and modifying the code (cases 3 and 4 in figure 2.9) are examples of optimisation techniques. Case 3 is illustrated by ‘code motion’ out of a loop to reduce the number of times a fixed calculation is carried out (figure 2.13) and case 4 by ‘register remembering’ to eliminate unnecessary LOAD and STORE instructions (figure 2.14). Code modification can be done after translation has finished or, perhaps better, as a sort of filter on the code proposed by the translator as it generates it.
  • 50. 30 CHAPTER 2. INTRODUCTION TO TRANSLATION 1. Statement: a := 0 Tree: assign a 0 Normal code: LOADn 1, 0 STORE 1, a Optimised code: STOZ , a 2. Statement: b := b+<anything> Tree: assign b + b <anything> Normal code: Optimised code: LOAD 1, <anything> ADD 1, b STORE 1, b LOAD 1, <anything> ADDST 1, b 3. Statement: c := c+1 Tree: assign c + c 1 Good code: LOADn 1, 1 ADDST 1, c Better code: INCST , c Figure 2.12: Some simple sub-optimisations
  • 51. 2.4. IMPROVING THE TREE WALKER 31 Program: for i = 1 to 100 do begin x := 0.5 * sqrt(y+z); a[i] := x; b[i] := y; .... end Tree: ... ... for ... ... i 1 100 compound x:=... a[i]:=x b[i]:=y ... program Altered tree: ... ... for ... ... i 1 100 compound x:=... a[i]:=x b[i]:=y ... program Figure 2.13: Moving code out of a loop Program: a := b+1; c := a Original code: LOAD 1, b ADDn 1, 1 STORE 1, a /* possibly unnecessary */ LOAD 1, a /* certainly unnecessary */ STORE 1, c /* possibly unnecessary */ Figure 2.14: Removing unnecessary instructions
  • 52. 32 CHAPTER 2. INTRODUCTION TO TRANSLATION 1 INTEGER + INTEGER LOAD n, a ADD n, b 2. REAL + INTEGER LOAD n, a LOAD n+1, b FLOATr n+1, fADDr n, n+1 3. REAL + COMPLEX LOAD n, a ADD n, breal LOAD n+1, bimag Figure 2.15: Code for various type combinations 2.5 Using the Symbol Table Descriptors The symbol table is built by the lexical analyser in order to correlate the vari- ous occurrences of each particular source program name. The object description phase inserts into the symbol table against each name a descriptor which gives information about the run-time object which is denoted by that name, deter- mined by the declarations in which the name appears. The descriptor not only distinguishes between different kinds of objects – vari- ables, arrays, procedures – but also in a compiler for an algebraic language the descriptor will give the type of the run-time object. Figure 2.15 shows some possible code fragments for combinations of FORTRAN’s INTEGER, REAL and COMPLEX types in an arithmetic ‘+’ node each of whose sub-nodes is an arithmetic variable name. As well as recording the type and kind of the object, the descriptor must contain the (possibly relocatable) store address of the run-time object so that the a’s and b’s in the instructions of figure 2.15 can be replaced by the proper run-time addresses. In languages with more complex data structures, such as COBOL, PL/1, AL- GOL 68 or SIMULA 67, the descriptor structure will not be a simple description of an object but rather a description of a run-time structure. In the case of COBOL, the ADD CORRESPONDING statement provides the most obvious example – the descriptor for HEAD-OFFICE and that for NORTH-OFFICE in figure 2.16 must give the underlying hierarchical structure so that the transla- tor can generate code for the necessary sequence of simple ADD statements. In such a case, the tree obviously extends into the symbol table. 2.6 Translation Error Handling A translation error report is generated when a source fragment appears to be of the correct superficial form, but for some reason can’t be translated. Some obvious causes of this are undeclared names, names whose descriptor types don’t match the required context and so on. Figure 2.17 gives some examples. These errors are almost all discovered by checking the tree against the contents of
  • 53. 2.6. TRANSLATION ERROR HANDLING 33 Statement: ADD CORRESPONDING NORTH-OFFICE TO HEAD-OFFICE Tree: add.corr name name NORTH-OFFICE HEAD-OFFICE Descriptors: MANAGER WAGES PLANT NORTH-OFFICE OFFICE OFFICE MANAGING-DIRECTOR SALARY PLANT HEAD-OFFICE Figure 2.16: Symbol table which contains structure
  • 54. 34 CHAPTER 2. INTRODUCTION TO TRANSLATION 1. Operands of a node are of wrong type Statement: if a then x := b+1 Tree: conditional a assignment x + b 1 Symbol Table: a is a real variable b is a Boolean variable 2. Name not declared - so its descriptor is empty e.g. name 'x' not declared in example above 3. Ambiguous data name (COBOL) Statement: MOVE PLANT TO PLANT Symbol Table: as in figure 2.12 above. 4. Invalid combination of phrases Statement: if u=v=0 then .... Tree: conditional = .... = 0 u v Figure 2.17: Some kinds of error detected in translation
  • 55. Another Random Scribd Document with Unrelated Content
  • 56. The Lady of Lynn
  • 57. CHAPTER I MY LORD'S LEVEE It is three years later. We are now in the year 1750. At twelve o'clock in the morning the anteroom of the town house of the Right Honourable the Earl of Fylingdale was tolerably filled with a mixed company attending his levee. Some were standing at the windows; some were sitting: a few were talking: most, however, were unknown to each other, and if they spoke at all, it was only to ask each other when his lordship might be expected to appear. As is customary at a great lord's levee there were present men of all conditions; they agreed, however, in one point, that they were all beggars. It is the lot of the nobleman that he is chiefly courted for the things that he can give away, and that the number of his friends and the warmth of their friendship depend upon the influence he is supposed to possess in the bestowal of places and appointments. Among the suitors this morning, for instance, was a half-pay captain who sought for a company in a newly raised regiment: he bore himself bravely, but his face betrayed his anxiety and his necessities. The poor man would solicit his lordship in vain, but this he did not know, and so he would be buoyed up for a time with new hopes. Beside him stood a lieutenant in the navy, who wanted promotion and a ship. If good service and wounds in battle were of any avail he should have commanded both, but it is very well known that in the Royal Navy there are no rewards for gallantry; men grow old without promotion: nothing helps but interest: a man may remain a midshipman for life without interest: never has it been known that without interest a ship has been bestowed even upon the most deserving officer and after the most signal service. The lieutenant,
  • 58. too, would be cheered by a promise, and lulled by false hopes—but that he did not know. One man wanted a post in the admiralty: the pay is small but the perquisites and the pickings are large: for the same reason another asked for a place in the customs. A young poet attended with a subscription list and a dedication. He thought that his volume of verse, once published, would bring him fortune, fame, and friends: he, too, would be disappointed. The clergyman wanted another living: one of the fat and comfortable churches in the city: a deanery would not be amiss: he was even ready to take upon himself the office of bishop, for which, indeed, he considered that his qualifications admirably fitted him. Would his lordship exercise his all powerful influence in the matter of that benefice or that promotion? A young man, whose face betrayed the battered rake, would be contented even with carrying the colours on the Cape Coast regiment if nothing better could be had. Surely his lordship would procure so small a thing as that! If nothing could be found for him then—the common side of the King's Bench Prison and rags and starvation until death released him. Poor wretch! He was on his way to that refuge, but he knew it not; for my lord would promise to procure for him what he wanted. So they all waited, hungry and expectant, thinking how best to frame their requests: how best to appear grateful before there was any call for gratitude. Surely a nobleman must grow wearied with the assurances of gratitude and promises of prayers. His experience must teach him that gratitude is but a short-lived plant: a weed which commonly flourishes for a brief period and produces neither flowers nor fruit; while as for the prayers, though we may make no doubt that the fervent prayer of the righteous availeth much, we are nowhere assured that the prayers of the worldly and the unrighteous are heard on behalf of another; while there is no certainty that the promised petition will ever be offered up before the throne. Yet the
  • 59. suitors, day after day, repeat the same promise, and rely on the same belief. "Oh! my lord," they say, or sing with one accord, "your name: your voice: your influence: it is all that I ask. My gratitude: my life-long gratitude: my service: my prayers will all be yours." Soon after twelve o'clock the doors of the private apartments were thrown open and his lordship appeared, wearing the look of dignity and proud condescension combined, which well became the star he wore and the ancient title which he had inherited. His age was about thirty, a time of life when there linger some remains of youth and the serious responsibilities are yet, with some men, hardly felt. His face was cold and proud and hard; the lips firmly set: the eyes keen and even piercing; the features regular: his stature tall, but not ungainly, his figure manly. It was remarkable, among those who knew him intimately, that there was as yet no sign of luxurious living on face and figure. He was not as yet swelled out with wine and punch: his neck was still slender; his face pale, without any telltale marks of wine and debauchery; so far as appearance goes he might pass if he chose, for a person of the most rigid and even austere virtue. This, as I have said, was considered remarkable by his friends, most of whom were already stamped on face and feature and figure with the outward and visible tokens of a profligate life. For, to confess the truth at the very beginning and not to attempt concealment, or to suffer a false belief as regards this nobleman, he was nothing better than a cold-blooded, pitiless, selfish libertine; a rake, and a voluptuary; one who knew and obeyed no laws save the laws of (so-called) honour. These laws allow a man to waste his fortune at the gaming table: to ruin confiding girls: to spend his time with rake hell companions in drink and riot and debauchery of all kinds. He must, however, pay his gambling debts: he must not cheat at cards; he must be polite in speech: he must be ready to fight whenever the occasion calls for his sword, and the quarrel seems of sufficient importance. Lord Fylingdale, however, was not among those who found his chief pleasure scouring the streets and in mad
  • 60. riot. You shall learn, in due course, what forms of pleasure chiefly attracted him. I have said that his face was proud. There was not, I believe, any man living in the whole world, who could compare with Lord Fylingdale for pride. An overwhelming pride sat upon his brow; was proclaimed by his eyes and was betrayed by his carriage. With such pride did Lucifer look round upon his companions, fallen as they were, and in the depths of hopeless ruin. In many voyages to foreign parts I have seen something of foreign peoples; every nation possesses its own nobility; I suppose that king, lords and commons is the order designed for human society by Providence. But I think that there is nowhere any pride equal to the pride of the English aristocracy. The Spaniard, if I have observed him aright, wraps himself in the pride of birth as with a cloak: it is often a tattered cloak: poverty has no terrors for him so long as he has his pride of birth. Yet he tolerates his fellow-countrymen whom he does not despise because they lack what most he prizes. The English nobleman, whether a peer or only a younger son, or a nephew or a cousin, provided he is a sprig of quality, disdains and despises all those who belong to the world of work, and have neither title, nor pedigree, nor coat of arms. He does not see any necessity for concealing this contempt. He lacks the courtesy which would hide it in the presence of the man of trade or the man of a learned profession. To be sure, the custom of the country encourages him, because to him is given every place and every preferment. He fills the House of Commons as well as the House of Lords: he commands our armies, our regiments, even the companies in the regiments: he commands our fleets and our ships: he holds all the appointments and draws all the salaries: he makes our laws, and, as justice of the peace, he administers them: he receives pensions, having done nothing to deserve them; he holds sinecures which require no duties. And the people who do the work—the merchants who bring wealth to the country: the manufacturers; the craftsmen; the
  • 61. farmers; the soldiers who fight the wars which the aristocracy consider necessary; the sailor who carries the flag over the world: all these are supposed to be sufficiently rewarded with a livelihood while they maintain the nobility and their children in luxury and in idleness and are received and treated with contempt. I speak of what I have myself witnessed. This man's pride I have compared with the pride of Lucifer. You shall learn while I narrate the things which follow, that he might well be compared, as regards his actions as well, with that proud and presumptuous spirit. He was dressed in a manner becoming to his rank: need we dwell upon his coat of purple velvet; his embroidered waistcoat; his white silk stockings; his lace of ruffles and cravat; his gold buckles and his gold clocks; his laced hat carried under his arm; his jewelled sword hilt and the rings upon his fingers? You would think, by his dress, that his wealth was equal to his pride, and, by his reception of the suitors, that his power was equal to both pride and wealth together. The levee began; one after the other stepped up to him, spoke a few words, received a few words in reply and retired, each, apparently, well pleased. For promises cost nothing. To the poet who asked for a subscription and preferred a dedication, my lord promised the former, accepted the latter, and added a few words of praise and good wishes. But the subscription was never paid; and the dedication was afterwards altered so far as the superscription, to another noble patron. To the clergyman who asked for a country living then vacant, my lord promised the most kindly consideration and bade him write his request and send it him by letter, for better assurance of remembrance. To the officer he promised his company as only due to gallantry and military skill: to the place hunter he promised a post far beyond the dreams and the hopes of the suppliant. Nothing more came of it to either. The company grew thin: one after the other, the suitors withdrew to feed on promises. It is like opening your mouth to drink the wind.
  • 62. But 'twas all they got. Among those who remained to the last was a man in the dress of a substantial shopkeeper, with a brown cloth coat and silver buttons. He, when his opportunity arrived, advanced and bowed low to my lord. "Sir," said his lordship, with gracious, but cold looks, "in what way may I be of service to you?" "With your lordship's permission, I would seek a place in your household—any place—scullion in the kitchen, or groom to the stable—any place." "Why should I give you a place? Have I room in my household for every broken cit?" "My lord, it is to save me from bankruptcy and the King's Bench. It is to save my wife and children from destitution. There are already many shopkeepers in Westminster and the city who have been admitted servants in the households of noblemen. It is no new thing —your lordship must have heard of the custom." "I do not know why I should save thy family or thyself. However, this is the affair of my steward. Go and see him. Tell him that a place in my household will save thee from bankruptcy and prison—it may be that a place is vacant." The man bowed again and retired. He knew very well what was meant. He would have to pay a round sum for the privilege. This noble lord, like many others of his rank, took money, through his steward, for nominal places in his household, making one citizen yeoman of his dairy; in Leicester Fields, perhaps, where no dairy could be placed; another steward of the granaries, having in the town neither barns nor storehouses nor ricks: a third, clerk to the stud book, having no race horses; and so on. Thus justice is
  • 63. defeated, a man's creditors may be defied and a man may escape payment of his just debts. When he was gone, Lord Fylingdale looked round the room. In the window stood, dangling a cane from his wrist, a gentleman dressed in the highest and the latest fashion. In his left hand he held a snuffbox adorned with the figure of a heathen goddess. To those who know the meaning of fashion it was evident that he was in the front rank, belonging to the few who follow or command, the variations of the passing hour. These descend to the smallest details. I am told that the secrets of the inner circle, the select few, who lead the fashion, are displayed for their own gratification in the length of the cravat, the colour of the sash, the angle of the sword, the breadth of the ruffles, the width of the skirts, the tye of the wig. They are also shown in the mincing voice, and the affected tone, and the use of the latest adjectives and oaths. Yet, when one looked more closely, it was seen that this gallant exterior arrayed an ancient gentleman whose years were proclaimed by the sharpening of his features, the wrinkles of his feet, the crows'-feet round his eyes, and his bending shoulders which he continually endeavoured to set square and upright. Hat in one hand, and snuffbox in the other, he ambled towards his lordship on tiptoe, which happened just then to be the fashionable gait. "Thy servant, Sir Harry"—my lord offered him his hand with condescension. "It warms my heart to see thee. Therefore I sent a letter. Briefly, Sir Harry, wouldst do me a service?" "I am always at your lordship's commands. This, I hope, I have proved." "Then, Sir Harry, this is the case. It is probable that for certain private reasons, I may have to pay a visit to a country town—a town of tarpaulins and traders, not a town of fashion"—Sir Harry shuddered—"patience, my friend. I know not how long I shall endure the barbaric company. But I must go—there are reasons—let
  • 64. me whisper—reasons of state—important secrets which call me there"—Sir Harry smiled and looked incredulous—"I want, on the spot, a friend"—Sir Harry smiled again, as one who began to understand—"a friend who would appear to be a stranger. Would you, therefore, play the part of such a friend?" "I will do whatever your lordship commands. Yet to leave town at this season"—it was then the month of April—"the assembly, the park, the card table—the society of the ladies——" "The loss will be theirs, Sir Harry. To lose their old favourite—oh! there will be lamentations, at the rout—— Perhaps, however, we may find consolations." "Impossible. There are none out of town, except at Bath or Tunbridge——" "The ladies of Norfolk are famous for their beauty." "Hoydens—I know them, "'I who erst beneath a tree Sung, Bumpkinet, and Bowzybee, And Blouzelind and Marian bright In aprons blue or aprons white,' "as Gay hath it. Hoydens, my lord, I know them. They play whist and dance jigs." "The Norfolk gentlemen drink hard and the wine is good." "Nay, my lord, this is cruel. For I can drink no longer." "I shall find other diversions for you. It is possible—I say—possible— that the Lady Anastasia may go there as well. She will, as usual, keep the bank if she does go."
  • 65. The old beau's face cleared, whether in anticipation of Lady Anastasia's society or her card table I know not. "My character, Sir Harry, will be in your hands. I leave it there confidently. For reasons—reasons of state—it should be a character of…." "I understand. Your lordship is a model of all the virtues——" "So—we understand. My secretary will converse with thee further on the point of expenditure." Sir Harry retired, bowing and twisting his body something like an ape. Then a gentleman in scarlet presented himself. "Your lordship's most obedient," he said, with scant courtesy. "I come in obedience to your letter—for command." "Colonel, you will hold yourself in readiness to go into the country. There will be play—you may lose as much as you please—to Sir Harry Malyus or to any one else whom my secretary will point out to you. Perhaps you may have to receive a remonstrance from me. We are strangers, remember, and I am no gambler, though I sometimes take a card." "I await your lordship's further commands." So he, too, retired. A proper well-set-up figure he was, with the insolence of the trooper in his face, and the signs of strong drink on his nose. Any one who knew the town would set him down for a half-pay captain, a sharper, a bully, a roysterer, one who lived by his wits, one who was skilled in billiards and commonly lucky at any game of cards. Perhaps such a judgment of the gallant colonel would not be far wrong. There remained one suitor. He was a clergyman dressed in a fine silk cassock with bands of the whitest and a noble wig of the order
  • 66. Ecclesiastic. I doubt if the archbishop himself had a finer. He was in all respects a divine of the superior kind: a dean, perhaps; an archdeacon, perhaps; a canon, rector, vicar, chaplain, with a dozen benefices, no doubt. His thin, slight figure carried a head too big for his body. His face was sallow and thin, the features regular; he bore the stamp of a scholar and had the manner of a scoffer. He spoke as if he was in the pulpit, with a voice loud, clear and resonant, as though the mere power of hearing that voice diffused around him the blessings of virtue and piety and a clear conscience. "Good, my lord," he said, "I am, as usual, a suppliant. The rectory of St. Leonard le Size, Jewry, in the city, is now vacant. With my small benefices in the country, it would suit me hugely. A word from your lordship to the lord mayor—the rectory is in the gift of the corporation—would, I am sure, suffice." "If, my old tutor, the thing can be done by me, you may consider it as settled. There are, however, I would have you to consider, one or two scandals still outstanding, the memory of which may have reached the ears of the city. These city people, for all their ignorance of fashion, do sometimes hear of things. The little affair at Bath, for instance——" "The lady hath since returned to her own home. It is now quite forgotten and blown over. My innocency is always well known to your lordship." "Assuredly. Has that other little business at Oxford blown over? Are certain verses still attributed to the Reverend Benjamin Purdon?" His reverence lightly blew upon his fingers. "That report is now forgotten. But 'tis a censorious world. One man is hanged for looking over a gate while another steals a pig and is applauded. As for the author of those verses, he still remains undiscovered, while the verses themselves—a deplorable fact—are handed about for the joy of the undergraduates."
  • 67. "Next time, then, steal the pig. Frankly, friend Purdon, thy name is none of the sweetest, and I doubt if the bishop would consent. Meantime, you are living, as usual, I suppose, at great expense——" "At small expense, considering my abilities; but still at greater expense than my slender income will allow. Am I not your lordship's domestic chaplain? Must I not keep up the dignity due to the position?" "Your dignity is costly. I must get a bishopric or a deanery for you. Meantime I have a small service to ask of you." "Small? My lord, let it be great: it cannot be too great." "It is that you go into the country for me." "Not to Bath—or to Oxford?" His reverence betrayed an anxiety on this point which was not quite in harmony with his previous declarations. "Not to either. To another place, where they know not thy name or thy fame. Very good. I thought I could depend upon your loyalty. As for arrangements and time, you will hear from my secretary." So my lord turned on his heel and his chaplain was dismissed. He remained for a moment, looking after his master doubtfully. The order liked him not. He was growing old and would have chosen, had he the power of choice, some fat city benefice with two or three country livings thrown in. He was tired of his dependence: perhaps he was tired of a life that ill became his profession: perhaps he could no longer enjoy it as of old. There was, at least, no sign of repentance as there was no touch of the spiritual life in his face, which was stamped with the plain and visible marks of the world, the flesh and the devil. What is that stamp? Nobody can paint it, or describe it: yet it is understood and recognised whenever one sees it. And it stood out legible so that all those who ran might read upon the face of this reverend and learned divine.
  • 68. When the levee was finished and everybody gone, Lord Fylingdale sank into a chair. I know not the nature of his thoughts save that they were not pleasant, for his face grew darker every moment. Finally, he sprang to his feet and rang the bell. "Tell Mr. Semple that I would speak with him," he ordered. Mr. Semple, the same Samuel whom you have seen under a basting from the captain, was now changed and for the better. His dress was simple. No one could guess from his apparel the nature of his occupation. For all professions and all crafts there is a kind of uniform. The divine wears gown and cassock, bands and wig, which proclaim his calling: the lawyer is also known by his gown and marks his rank at the bar by coif and wig: the attorney puts on broadcloth black of hue: the physician assumes black velvet, a magisterial wig, and a gold-headed cane. The officer wears the King's scarlet; the nobleman his star: the sprig of quality puts on fine apparel and assumes an air and manner unknown to Cheapside and Ludgate Hill: you may also know him by his speech. The merchant wears black velvet with gold buttons, gold buckles, white silk stockings and a gold-laced hat; the shopkeeper substitutes silver for gold and cloth for velvet: the clerk has brown cloth metal buttons and worsted stockings. As for the crafts, has not each its own jacket, sleeves, apron, cap, and badge? But for this man, where would we place him? What calling did he represent? For he wore the flowered waist-coat—somewhat frayed and stained, of a beau, and the black coat of the merchant: the worsted stockings of the clerk and his metal buttons. Yet he was neither gentleman, merchant, shopkeeper, clerk, nor craftsman. He was a member of that fraternity which is no fraternity because there is no brotherhood among them all; in which every man delights to slander, gird at, and to depreciate his brother. In other words he wore the dress—which is no uniform—of a poet. At this time he also called himself secretary to his lordship having by ways known only to himself, and by wrigglings up back stairs, and services of a kind
  • 69. never proclaimed to the world, made himself useful. The position also granted him, as it granted certain tradesmen, immunity from arrest. He had the privilege of walking abroad through a street full of hungering creditors, and that, not on Sundays only, like most of his tribe, but on every day in the week. He obeyed the summons and entered the room with a humble cringe. "Semple," said his lordship, crossing his legs and playing with the tassel of his sword knot, "I have read thy letter——" "Your lordship will impute——" "First, what is the meaning of the preamble?" "I have been your lordship's secretary for six months. I have therefore perused all your lordship's letters. I have also in my zeal for your lordship's interests—looked about me. And I discovered— what I ventured to state in that preamble." "Well, sir?" "Namely, that the Fylingdale estates are gone so far as your lordship's life is concerned—but—in a word, all is gone. And that— your lordship will pardon the plain truth—your lordship's credit cannot last long and that—I now touch a most delicate point to a man of your lordship's nice sense of honour—the only resource left is precarious." "You mean?" "I mean—a certain lady and a certain bank." "How, sir? Do you dare? What has put this suspicion into your head?"
  • 70. "Nay, my lord—I have no thought but for your lordship's interests, believe me." "And so you tell me about the rustic heiress, and you propose a plan ——" "I have had the temerity to do so." "Yes. Tell me once more about this girl—and about her fortune." "Her name is Molly Miller: she is an orphan: her guardian is an honest sailor who has taken the greatest care of her property. She was an heiress already when her father died. That was eighteen years ago; she is now nineteen." "Is she passable—to look at? A hoyden with a high colour, I warrant." "A cream-coloured complexion, touched with red and pink: light hair in curls and blue eyes; the face and figure of a Venus; the sweetest mouth in the world and the fondest manner." "Hang me if the fellow isn't in love with her, himself! If she is all this, man, why not apply yourself, for the post of spouse?" "Because her guardian keeps off all would-be lovers and destines his ward for a gentleman at least—for a nobleman, he hopes." "He is ambitious. Now as to her fortune." "She has a fleet of half a dozen tall vessels—nay, there are more, but I know not how many. I was formerly clerk in a countinghouse of the town and I learned a great deal—what each is worth and what the freight of each voyage may produce—but not all. The captain, her guardian, keeps things close. My lord, I can assure you, from what I learned in that capacity and by looking into old books, that she must be worth over a hundred thousand pounds—over a hundred thousand pounds! My lord, there is no such heiress in the
  • 71. city. In your lordship's interests I have enquired in the taverns where the merchants' clerks congregate. They know of all the city heiresses. The greatest, at this moment, is the only daughter of a tallow chandler who has twenty thousand to her name. She squints." "Why have you given me this information? The girl belongs to your friends—are you anxious for her happiness? You know my way of life. Would that way make her happier?" The man made no reply. "Come, Semple, out with it. Your reasons—gratitude—to me—or revenge upon an enemy?" The man coloured. He looked up: he stood upright but for a moment only. Then his eyes dropped and his shoulders contracted. "Gratitude, my lord, to you," he replied. "Revenge? Why what reason should I have for revenge?" "How should I know of any? Let it be gratitude, then." "I have ventured to submit—not a condition—but a prayer." "I have read the clause. I grant it. On the day after the marriage if the plan comes to anything, I will present thee to a place where there are no duties and many perquisites. That is understood. I would put this promise in writing but no writing would bind me more than my word." "Yet I would have the promise in writing." "You are insolent, sirrah." "I am protecting myself. My lord, I must speak openly in this matter. How many promises have you made this morning? How many will you keep? I must not be pushed aside with such a promise."
  • 72. Lord Fylingdale made no reply. "I offer you a fortune of a hundred thousands pounds and more." "I can now take this fortune without your assistance." "With submission, my lord, you cannot. I know too much." "What shall I write, then?" "I am only reasonable. The girl's fortune when you have it will go the same way as your rents and woods have gone. Provide for me, therefore, before you begin to spend that money." "Semple, I did not think you had so much courage. Learn that a dozen times I have been on the point of kicking you out of the house. Now," he rose, "give me paper and a pen—and I will write this promise." Semple placed a chair at the table and laid paper and pen before it. "Let me presume so far as to dictate the promise," he said. "I undertake and promise that on the day after my marriage with the girl named Molly Miller, I will give Samuel Semple such a place as will provide him for life with a salary of not less than £200 a year. So— will your lordship sign it?" He took up this precious paper from the table, read it, folded it and put it in his pocket. "What next?" asked his patron. "I am preparing a scheme which will give a plausible excuse for your lordship's visit to the town. I have already suggested that certain friends should prepare the way. The lady's guardian has prejudices in favour of morality and religion. They are, I know, beneath your lordship's notice—yet still—it will be in fact, necessary that your lordship's character shall be such as will commend itself to this unfashionable old sailor."
  • 73. "We will speak again upon this point. The girl you say has no lover." "She has no lover. Your lordship's rank: your manner: your appearance will certainly carry the day. By contrast alone with the country bumpkins the heart of the girl will be won." "Mr. Semple," his lordship yawned. "Do you suppose that the heart of the girl concerns me? Go and complete your scheme—of gratitude, not revenge."
  • 74. CHAPTER II THE LADY ANASTASIA The Lady Anastasia was in her dressing-room in the hands of her friseur, the French hairdresser, and her maid. She sat in a dishabille which was a loose robe, called, I believe a nightgown, of pink silk, trimmed with lace, which showed the greater part of a very well shaped arm; she had one slipper off and one slipper on, which showed a very small and well shaped foot, but no one was there to see. Her maid was busy at the toilette table which was covered with glass bottles containing liquids of attractive colour; silver patch boxes; powder boxes; powder puffs; cosmetics in pots, and other mysterious secrets into which it would be useless and fruitless to inquire. The artist, for his part, was laboriously and conscientiously building the edifice—object of so much ingenuity and thought— called a "Head." She was in the best temper imaginable. When you hear that she had won overnight the sum of a hundred and twenty guineas you will understand that she had exactly that number of reasons for being satisfied with the world. Moreover, she had received from an admirer a present in the shape of a piece of china representing a monkey, which, she reflected with satisfaction, would awaken in the minds of her friends the keenest feelings of envy, jealousy, hatred, longing, and despair. The Lady Anastasia was the young widow of an old baronet: she was also the daughter of an earl and the sister of his successor. She therefore enjoyed the freedom of a widow; the happiness natural to youth; and all the privileges of rank. No woman could be happier. It was reported that her love of the card table had greatly impaired her income: the world said that her own private dowry was wholly gone
  • 75. and a large part of her jointure. But it is a spiteful world—all that was known for certain was that she played much and that she played high. Perhaps Fortune, in a mood of penitence, was giving back what she had previously taken away. The contrary is commonly the case, viz, that Fortune, which certainly takes away with alacrity, restores with reluctance. Perhaps, however, the reports were not true. She kept a small establishment in Mount Street: her people consisted of no more than two footmen, a butler, a lady's maid, a housekeeper, and three or four maids with two chairmen. She did not live as a rich woman: she received, it is true, twice a week, on Sundays and Wednesdays, but not with any expense of supper and wine. Her friends came to play cards and she held the bank for them. On other evenings she went out and played at the houses of her friends. Except for fashions and her dress—what fine woman but makes that exception?—she had no other occupation; no other pursuit; no other subject of conversation, than the playing of cards. She played at all games and knew them all; she sat down with a willing mind to Ombre, Faro, Quadrille, Basset, Loo, Cribbage, All Fours, or Beggar my Neighbour, but mostly she preferred the game of Hazard, when she herself kept the bank. It is a game which more than any other allures and draws on the player so that a young man who has never before been known to set a guinea on any card, or to play at any game, will in a single night be filled with all the ardour and eagerness of a practised gamester; will know the extremes of joy and despair; and will regard the largest fortune as bestowed by Providence for no other purpose than to prolong the excitement and the agony of a gamester. While the Lady Anastasia was still admiring the china vase set upon the table, so that she might gaze upon it and so refresh her soul, and while the friseur was still completing her head, Lord Fylingdale
  • 76. was announced. The lady blushed violently: she sat up and looked anxiously in the glass. "Betty," she cried, "a touch of red—not much, you clumsy creature! Will you never learn to have a lighter hand? So! that is better. I am horribly pale. His lordship can wait in the morning room. You have nearly finished, monsieur? Quick then! The last touches. Betty, the flowered satin petticoat. My fan. The pearl necklace. So," she looked again at the glass, "am I looking tolerable, Betty?" "Your ladyship is ravishing," said Betty finishing the toilette. In truth, it was a very pretty creature if one knew how much was real and how much was due to art. The complexion was certainly laid on; the hair was powdered and built up over cushions and pillows; there were patches on the cheek: the neck was powdered; eyes naturally very fine were set off and made more lustrous with a touch of dark powder: the frock and petticoat and hoop were all alike removed from nature. However, the result was a beautiful woman of fashion who is far removed indeed from the beautiful woman as made by the Creator. For her age the Lady Anastasia might have been seven and twenty, or even thirty—an age when with some women, the maturity of their beauty is even more charming than the first sprightly loveliness of youth. She swam out of the room with a gliding movement, then the fashion, and entered the morning room where Lord Fylingdale awaited her. "Anastasia!" he said, softly, taking her hand. "It is very good of you to see me alone. I feared you would be surrounded with courtiers and fine ladies or with singers, musicians, hairdressers, and other baboons. Permit me," he raised her hand to his lips. "You look divine this morning. It is long since I have seen you look so perfectly charming."
  • 77. The lady murmured something. She was one of those women who like above all things to hear praises of what most they prize, their beauty, and to believe what they most desire to be the truth, the preservation and perfecting of that beauty. "But you came to see me alone. Was it to tell me that I look charming? Other men tell me as much in company." "Not altogether that, dear lady, though that is something. I come to tell you of a change of plans." "You have heard that the grand jury of Middlesex has presented me by name as a corruptor of innocence, and I know not what, because I hold my bank on Sunday nights." "I have heard something of the matter. It is almost time, I think, to give these presumptuous shopkeepers a lesson not to interfere with the pursuits of persons of rank. Let them confine themselves to the prentices who play at pitch and toss." "Oh! what matters their presentment? I shall continue to keep the bank on Sunday nights. Now, my dear lord, what about these plans? What is changed?" "We thought, you remember, about going to Tunbridge, in July." "Well? Shall we not go there?" "Perhaps. But there is something to be done first. Let me confide in you——" "My dear lord—you have never confided in anybody." "Except in you. I think you know all my secrets if I have any. In whom else can I confide? In the creatures who importune me for places? In friends of the green table? In friends of the race course? My dear Anastasia, you know, I assure you, as much about my personal affairs as I know myself."
  • 78. "If you would always speak so kindly"—her eyes became humid but not tearful. A lady of fashion must not spoil her cheek by tears. "Well, then, the case is this. You know of the condition of my affairs —no one better. An opportunity presents itself to effect a great improvement. I am invited by the highest personage to take a more active part in the affairs of state. No one is to know this. For reasons connected with this proposal I am to visit a certain town—a trading town—a town of rough sailors, there to conduct certain enquiries. There is to be a gathering at this town of the gentry and people of the county. Would you like to go, my dear friend? It will be next month." "To leave town—and in May, just before the end of the season?" "There will be opportunities, I am told, of holding a bank; and a good many sportsmen—'tis a sporting county—may be expected to lay their money. In a word, Anastasia, it will not be a bad exchange." "And how can I help you? Why should I go there?" "By letting the people—the county people, understand the many virtues and graces which distinguish my character. No one knows me better than yourself." The lady smiled—"No one," she murmured. "—Or can speak with greater authority on the subject. There will be certain of our friends there—the parson—Sir Harry—the colonel——" "Pah! a beggarly crew—and blown upon—they are dangerous." "Not at this quiet and secluded town. They will be strangers to you as well as to me. And they will be useful. After all, in such a place you need an opening. They will lead the way." The lady made no response.
  • 79. "I may call it settled, then?" He still held her hand. "If you would rather not go, Anastasia, I will find some one else—but I had hoped ——" She drew away her hand. "You are right," she said, "no one knows you so well as myself. And all I know about you is that you are always contriving some devilry. What is it this time? But you will not tell me. You never tell me." "Anastasia, you do me an injustice. This is a purely political step." "As you will. Call it what you please. I am your servant—you know that—your handmaid—in all things—save one. Not for any other woman, Ludovick—not for any other—unfortunate—woman will I lift my little finger. Should you betray me in this respect——" He laughed. "A woman? And in that company? Rest easy, dear child. Be jealous as much as you please but not with such a cause." He touched her cheek with his finger: he stooped and kissed her hand and withdrew. The Lady Anastasia stood awhile where he left her. The joy had gone out of her heart: she trembled: she was seized with a foreboding of evil. She threw herself upon the sofa and buried her face in her hands, and forgetful of paste and patch and paint she suffered the murderous tears to destroy that work of art—her finished face.
  • 80. CHAPTER III THE "SOCIETY" OF LYNN It was about seven o'clock in the evening of early April, at the going down of the sun that I was at last able to drop into the dingy and go ashore. All day and all night and all the day before we had been beating through the shallows of the Wash and the narrow channel of the Ouse. We had laid her to her moorings off the Common Stath and made all taut and trim: the captain had gone ashore with the papers: the customhouse officer had been aboard: we were to begin breaking cargo on the morrow. The ship was The Lady of Lynn, 380 tons, Robert Jaggard, master marines, being captain, and I the mate or chief officer. There was no better skipper in the port of Lynn than Captain Jaggard: there was no better crew than that aboard The Lady of Lynn, not a skulker or a lubber in the whole ship's company; and though I say it myself, I dare affirm that the mate did credit to his ship as much as the captain and the crew. We were in the Lisbon trade: we had therefore come home laden with casks of the rich strong wine of the country: the Port and Lisbon Sherry and Malaga, besides Madeira and the wine of Teneriffe and the Grand Canary. Our people of the Marshland and the Fens and those of Lincolnshire and Norfolk where the strong air of the east winds kill all but the stoutest, cannot have too much of this rich wine: they will not drink the lighter wines of Bordeaux which neither fire the blood nor mount to the head. A prosperous voyage we had made: the Bay of Biscay suffered us to cross with no more than half a gale: The Lady of Lynn, in fact, was known in port to be a lucky ship—as lucky as her owner—lucky in her voyages and lucky in her cargoes. At the stairs of the Common Stath Yard I made fast the painter and shipped the sculls. And there, waiting for me, was none other than my good old friend and patron, Captain Crowle.
  • 81. The captain was by this time well advanced in life, being upwards of seventy: yet he showed little touch of time: his honest face being still round and full; his eyes still free from lines and crows'-feet; his cheek ruddy and freckled, as if with the salt sea breeze and the driving spray. He was also as upright as any man of thirty and walked with as firm a step and had no need of the stout stick which he carried in his hand, as a weapon and a cudgel for the unrighteous, more than a staff for the bending knees of old age. "What cheer—ahoy?" He shouted from the quay as I dropped over the side into the dingy. "What cheer, Jack?" he repeated when I ran up the steps. "I've seen the skipper. Come with me to the Crown"— but the proper place for mates was the Duke's Head. "Nay, it shall be the Crown. A bowl of punch shall welcome back The Lady of Lynn." He turned and looked at the ship lying in the river at her moorings among the other craft. "She's as fine a vessel as this old port can show—and she's named after as fine a maid. Shalt see her to-morrow, Jack, but not to-night." "I trust, sir, that she is well and in good spirits." "Ay—ay. Nothing ails her—nothing ails her, Jack," he pointed with his stick. "Look how she flourishes. There are fifteen tall ships moored two and two off the King's Stath and half a dozen more off the Common Stath. Count them, Jack. Six of these ships belong to the little maid. Six of them—and two more are afloat, of which one is homeward bound and should be in port soon if all goes well. Eight noble ships, Jack, are hers. And the income of nigh upon eighteen years and houses and broad lands." "She has a prudent guardian, captain." "May be—may be. I don't deny, Jack, but I've done the best I could. Year after year, the money mounteth up more and more. You love her, Jack, and therefore I tell you these things. And you can keep counsel. I talk not in the market place. No one knows her wealth but
  • 82. Welcome to our website – the perfect destination for book lovers and knowledge seekers. We believe that every book holds a new world, offering opportunities for learning, discovery, and personal growth. That’s why we are dedicated to bringing you a diverse collection of books, ranging from classic literature and specialized publications to self-development guides and children's books. More than just a book-buying platform, we strive to be a bridge connecting you with timeless cultural and intellectual values. With an elegant, user-friendly interface and a smart search system, you can quickly find the books that best suit your interests. Additionally, our special promotions and home delivery services help you save time and fully enjoy the joy of reading. Join us on a journey of knowledge exploration, passion nurturing, and personal growth every day! ebookbell.com