100% found this document useful (1 vote)
45 views

Introduction To Compilers and Language Design 2nd Edition Douglas Thain 2024 Scribd Download

Design

Uploaded by

gjocinajmun
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
45 views

Introduction To Compilers and Language Design 2nd Edition Douglas Thain 2024 Scribd Download

Design

Uploaded by

gjocinajmun
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 79

Full download ebooks at ebookmeta.

com

Introduction to Compilers and Language Design 2nd


Edition Douglas Thain

https://ebookmeta.com/product/introduction-to-compilers-and-
language-design-2nd-edition-douglas-thain/

OR CLICK BUTTON

DOWLOAD NOW

Download more ebook from https://ebookmeta.com


More products digital (pdf, epub, mobi) instant
download maybe you interests ...

An Introduction to Design Science 2nd Edition Paul


Johannesson

https://ebookmeta.com/product/an-introduction-to-design-
science-2nd-edition-paul-johannesson/

An Introduction to Emergency Exercise Design and


Evaluation 2nd Edition Robert Mccreight

https://ebookmeta.com/product/an-introduction-to-emergency-
exercise-design-and-evaluation-2nd-edition-robert-mccreight/

How to Design Programs An Introduction to Programming


and Computing 2nd Edition Matthias Felleisen

https://ebookmeta.com/product/how-to-design-programs-an-
introduction-to-programming-and-computing-2nd-edition-matthias-
felleisen/

Chemical Engineering Design and Analysis: An


Introduction 2nd Edition Duncan

https://ebookmeta.com/product/chemical-engineering-design-and-
analysis-an-introduction-2nd-edition-duncan/
Design and Analysis of Experiments Douglas C.
Montgomery

https://ebookmeta.com/product/design-and-analysis-of-experiments-
douglas-c-montgomery/

Scenography Expanded An Introduction to Contemporary


Performance Design 2nd Edition Joslin Mckinney (Editor)

https://ebookmeta.com/product/scenography-expanded-an-
introduction-to-contemporary-performance-design-2nd-edition-
joslin-mckinney-editor/

Language History Language Change and Language


Relationship An Introduction to Historical and
Comparative Linguistics Hans Henrich Hock Brian D
Joseph
https://ebookmeta.com/product/language-history-language-change-
and-language-relationship-an-introduction-to-historical-and-
comparative-linguistics-hans-henrich-hock-brian-d-joseph/

Language Assessment: Principles and Classroom Practice


Second Edition H . Douglas Brown

https://ebookmeta.com/product/language-assessment-principles-and-
classroom-practice-second-edition-h-douglas-brown/

Speech and Language Processing An Introduction to


Natural Language Processing Computational Linguistics
and Speech Recognition 3rd Edition Dan Jurafsky

https://ebookmeta.com/product/speech-and-language-processing-an-
introduction-to-natural-language-processing-computational-
linguistics-and-speech-recognition-3rd-edition-dan-jurafsky/
Introduction to Compilers
and Language Design
Second Edition

Prof. Douglas Thain


University of Notre Dame
Introduction to Compilers and Language Design
Copyright © 2020 Douglas Thain.
Paperback ISBN: 979-8-655-18026-0
Second edition.

Anyone is free to download and print the PDF edition of this book for per-
sonal use. Commercial distribution, printing, or reproduction without the
author’s consent is expressly prohibited. All other rights are reserved.

You can find the latest version of the PDF edition, and purchase inexpen-
sive hardcover copies at http://compilerbook.org

Revision Date: January 15, 2021


iii

For Lisa, William, Zachary, Emily, and Alia.

iii
iv

iv
v

Contributions

I am grateful to the following people for their contributions to this book:


Andrew Litteken drafted the chapter on ARM assembly; Kevin Latimer
drew the RegEx to NFA and the LR example figures; Benjamin Gunning
fixed an error in LL(1) parse table construction; Tim Shaffer completed the
detailed LR(1) example.
And the following people corrected typos:
Sakib Haque (27), John Westhoff (26), Emily Strout (26), Gonzalo Martinez
(25), Daniel Kerrigan (24), Brian DuSell (23), Ryan Mackey (20), TJ Dasso
(18), Nedim Mininovic (15), Noah Yoshida (14), Joseph Kimlinger (12),
Nolan McShea (11), Jongsuh Lee (11), Kyle Weingartner (10), Andrew Lit-
teken (9), Thomas Cane (9), Samuel Battalio (9), Stéphane Massou (8), Luis
Prieb (7), William Diederich (7), Jonathan Xu (6), Gavin Inglis (6), Kath-
leen Capella (6), Edward Atkinson (6), Tanner Juedeman (5), John Johnson
(4), Luke Siela (4), Francis Schickel (4), Eamon Marmion (3), Molly Zachlin
(3), David Chiang (3), Jacob Mazur (3), Spencer King (2), Yaoxian Qu (2),
Maria Aranguren (2), Patrick Lacher (2), Connor Higgins (2), Tango Gu (2),
Andrew Syrmakesis (2), Horst von Brand (2), John Fox (2), Jamie Zhang
(2), Benjamin Gunning (1) Charles Osborne (1), William Theisen (1), Jes-
sica Cioffi (1), Ben Tovar (1), Ryan Michalec (1), Patrick Flynn (1), Clint
Jeffery (1), Ralph Siemsen (1), John Quinn (1), Paul Brunts (1), Luke Wurl
(1), Bruce Mardle (1), Dane Williams (1), Thomas Fisher (1), Alan Johnson
(1), Jacob Harris (1), Jeff Clinton (1)
Please send any comments or corrections via email to Prof. Douglas
Thain (dthain@nd.edu).

v
vi

vi
CONTENTS vii

Contents

1 Introduction 1
1.1 What is a compiler? . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Why should you study compilers? . . . . . . . . . . . . . . . 2
1.3 What’s the best way to learn about compilers? . . . . . . . . 2
1.4 What language should I use? . . . . . . . . . . . . . . . . . . 2
1.5 How is this book different from others? . . . . . . . . . . . . 3
1.6 What other books should I read? . . . . . . . . . . . . . . . . 4

2 A Quick Tour 5
2.1 The Compiler Toolchain . . . . . . . . . . . . . . . . . . . . . 5
2.2 Stages Within a Compiler . . . . . . . . . . . . . . . . . . . . 6
2.3 Example Compilation . . . . . . . . . . . . . . . . . . . . . . . 7
2.4 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

3 Scanning 11
3.1 Kinds of Tokens . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.2 A Hand-Made Scanner . . . . . . . . . . . . . . . . . . . . . . 12
3.3 Regular Expressions . . . . . . . . . . . . . . . . . . . . . . . 13
3.4 Finite Automata . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.4.1 Deterministic Finite Automata . . . . . . . . . . . . . 16
3.4.2 Nondeterministic Finite Automata . . . . . . . . . . . 17
3.5 Conversion Algorithms . . . . . . . . . . . . . . . . . . . . . . 19
3.5.1 Converting REs to NFAs . . . . . . . . . . . . . . . . . 19
3.5.2 Converting NFAs to DFAs . . . . . . . . . . . . . . . . 22
3.5.3 Minimizing DFAs . . . . . . . . . . . . . . . . . . . . . 24
3.6 Limits of Finite Automata . . . . . . . . . . . . . . . . . . . . 26
3.7 Using a Scanner Generator . . . . . . . . . . . . . . . . . . . . 26
3.8 Practical Considerations . . . . . . . . . . . . . . . . . . . . . 28
3.9 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.10 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . 33

4 Parsing 35
4.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.2 Context Free Grammars . . . . . . . . . . . . . . . . . . . . . 36

vii
viii CONTENTS

4.2.1 Deriving Sentences . . . . . . . . . . . . . . . . . . . . 37


4.2.2 Ambiguous Grammars . . . . . . . . . . . . . . . . . . 38
4.3 LL Grammars . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.3.1 Eliminating Left Recursion . . . . . . . . . . . . . . . 41
4.3.2 Eliminating Common Left Prefixes . . . . . . . . . . . 42
4.3.3 First and Follow Sets . . . . . . . . . . . . . . . . . . . 43
4.3.4 Recursive Descent Parsing . . . . . . . . . . . . . . . . 45
4.3.5 Table Driven Parsing . . . . . . . . . . . . . . . . . . . 47
4.4 LR Grammars . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
4.4.1 Shift-Reduce Parsing . . . . . . . . . . . . . . . . . . . 50
4.4.2 The LR(0) Automaton . . . . . . . . . . . . . . . . . . 51
4.4.3 SLR Parsing . . . . . . . . . . . . . . . . . . . . . . . . 55
4.4.4 LR(1) Parsing . . . . . . . . . . . . . . . . . . . . . . . 59
4.4.5 LALR Parsing . . . . . . . . . . . . . . . . . . . . . . . 62
4.5 Grammar Classes Revisited . . . . . . . . . . . . . . . . . . . 62
4.6 The Chomsky Hierarchy . . . . . . . . . . . . . . . . . . . . . 63
4.7 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.8 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . 67

5 Parsing in Practice 69
5.1 The Bison Parser Generator . . . . . . . . . . . . . . . . . . . 70
5.2 Expression Validator . . . . . . . . . . . . . . . . . . . . . . . 73
5.3 Expression Interpreter . . . . . . . . . . . . . . . . . . . . . . 74
5.4 Expression Trees . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.5 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
5.6 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . 83

6 The Abstract Syntax Tree 85


6.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
6.2 Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
6.3 Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
6.4 Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
6.5 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
6.6 Putting it All Together . . . . . . . . . . . . . . . . . . . . . . 95
6.7 Building the AST . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.8 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

7 Semantic Analysis 99
7.1 Overview of Type Systems . . . . . . . . . . . . . . . . . . . . 100
7.2 Designing a Type System . . . . . . . . . . . . . . . . . . . . . 103
7.3 The B-Minor Type System . . . . . . . . . . . . . . . . . . . . 106
7.4 The Symbol Table . . . . . . . . . . . . . . . . . . . . . . . . . 107
7.5 Name Resolution . . . . . . . . . . . . . . . . . . . . . . . . . 111
7.6 Implementing Type Checking . . . . . . . . . . . . . . . . . . 113
7.7 Error Messages . . . . . . . . . . . . . . . . . . . . . . . . . . 117

viii
CONTENTS ix

7.8 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118


7.9 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . 118

8 Intermediate Representations 119


8.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
8.2 Abstract Syntax Tree . . . . . . . . . . . . . . . . . . . . . . . 119
8.3 Directed Acyclic Graph . . . . . . . . . . . . . . . . . . . . . . 120
8.4 Control Flow Graph . . . . . . . . . . . . . . . . . . . . . . . . 125
8.5 Static Single Assignment Form . . . . . . . . . . . . . . . . . 127
8.6 Linear IR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
8.7 Stack Machine IR . . . . . . . . . . . . . . . . . . . . . . . . . 129
8.8 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
8.8.1 GIMPLE - GNU Simple Representation . . . . . . . . 130
8.8.2 LLVM - Low Level Virtual Machine . . . . . . . . . . 131
8.8.3 JVM - Java Virtual Machine . . . . . . . . . . . . . . . 132
8.9 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
8.10 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . 134

9 Memory Organization 135


9.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
9.2 Logical Segmentation . . . . . . . . . . . . . . . . . . . . . . . 135
9.3 Heap Management . . . . . . . . . . . . . . . . . . . . . . . . 138
9.4 Stack Management . . . . . . . . . . . . . . . . . . . . . . . . 140
9.4.1 Stack Calling Convention . . . . . . . . . . . . . . . . 141
9.4.2 Register Calling Convention . . . . . . . . . . . . . . 142
9.5 Locating Data . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
9.6 Program Loading . . . . . . . . . . . . . . . . . . . . . . . . . 146
9.7 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . 148

10 Assembly Language 149


10.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
10.2 Open Source Assembler Tools . . . . . . . . . . . . . . . . . . 150
10.3 X86 Assembly Language . . . . . . . . . . . . . . . . . . . . . 152
10.3.1 Registers and Data Types . . . . . . . . . . . . . . . . 152
10.3.2 Addressing Modes . . . . . . . . . . . . . . . . . . . . 154
10.3.3 Basic Arithmetic . . . . . . . . . . . . . . . . . . . . . 156
10.3.4 Comparisons and Jumps . . . . . . . . . . . . . . . . . 158
10.3.5 The Stack . . . . . . . . . . . . . . . . . . . . . . . . . 159
10.3.6 Calling a Function . . . . . . . . . . . . . . . . . . . . 160
10.3.7 Defining a Leaf Function . . . . . . . . . . . . . . . . . 162
10.3.8 Defining a Complex Function . . . . . . . . . . . . . . 163
10.4 ARM Assembly . . . . . . . . . . . . . . . . . . . . . . . . . . 167
10.4.1 Registers and Data Types . . . . . . . . . . . . . . . . 167
10.4.2 Addressing Modes . . . . . . . . . . . . . . . . . . . . 168
10.4.3 Basic Arithmetic . . . . . . . . . . . . . . . . . . . . . 170

ix
x CONTENTS

10.4.4 Comparisons and Branches . . . . . . . . . . . . . . . 171


10.4.5 The Stack . . . . . . . . . . . . . . . . . . . . . . . . . 173
10.4.6 Calling a Function . . . . . . . . . . . . . . . . . . . . 174
10.4.7 Defining a Leaf Function . . . . . . . . . . . . . . . . . 175
10.4.8 Defining a Complex Function . . . . . . . . . . . . . . 176
10.4.9 64-bit Differences . . . . . . . . . . . . . . . . . . . . . 179
10.5 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . 180

11 Code Generation 181


11.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
11.2 Supporting Functions . . . . . . . . . . . . . . . . . . . . . . . 181
11.3 Generating Expressions . . . . . . . . . . . . . . . . . . . . . 183
11.4 Generating Statements . . . . . . . . . . . . . . . . . . . . . . 188
11.5 Conditional Expressions . . . . . . . . . . . . . . . . . . . . . 192
11.6 Generating Declarations . . . . . . . . . . . . . . . . . . . . . 193
11.7 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194

12 Optimization 195
12.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
12.2 Optimization in Perspective . . . . . . . . . . . . . . . . . . . 196
12.3 High Level Optimizations . . . . . . . . . . . . . . . . . . . . 197
12.3.1 Constant Folding . . . . . . . . . . . . . . . . . . . . . 197
12.3.2 Strength Reduction . . . . . . . . . . . . . . . . . . . . 199
12.3.3 Loop Unrolling . . . . . . . . . . . . . . . . . . . . . . 199
12.3.4 Code Hoisting . . . . . . . . . . . . . . . . . . . . . . . 200
12.3.5 Function Inlining . . . . . . . . . . . . . . . . . . . . . 201
12.3.6 Dead Code Detection and Elimination . . . . . . . . . 202
12.4 Low-Level Optimizations . . . . . . . . . . . . . . . . . . . . 204
12.4.1 Peephole Optimizations . . . . . . . . . . . . . . . . . 204
12.4.2 Instruction Selection . . . . . . . . . . . . . . . . . . . 204
12.5 Register Allocation . . . . . . . . . . . . . . . . . . . . . . . . 207
12.5.1 Safety of Register Allocation . . . . . . . . . . . . . . 208
12.5.2 Priority of Register Allocation . . . . . . . . . . . . . . 208
12.5.3 Conflicts Between Variables . . . . . . . . . . . . . . . 209
12.5.4 Global Register Allocation . . . . . . . . . . . . . . . . 210
12.6 Optimization Pitfalls . . . . . . . . . . . . . . . . . . . . . . . 211
12.7 Optimization Interactions . . . . . . . . . . . . . . . . . . . . 212
12.8 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
12.9 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . 215

A Sample Course Project 217


A.1 Scanner Assignment . . . . . . . . . . . . . . . . . . . . . . . 217
A.2 Parser Assignment . . . . . . . . . . . . . . . . . . . . . . . . 217
A.3 Pretty-Printer Assignment . . . . . . . . . . . . . . . . . . . . 218
A.4 Typechecker Assignment . . . . . . . . . . . . . . . . . . . . . 218

x
CONTENTS xi

A.5 Optional: Intermediate Representation . . . . . . . . . . . . . 218


A.6 Code Generator Assignment . . . . . . . . . . . . . . . . . . . 218
A.7 Optional: Extend the Language . . . . . . . . . . . . . . . . . 219

B The B-Minor Language 221


B.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
B.2 Tokens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
B.3 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
B.4 Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
B.5 Declarations and Statements . . . . . . . . . . . . . . . . . . . 224
B.6 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
B.7 Optional Elements . . . . . . . . . . . . . . . . . . . . . . . . 225

C Coding Conventions 227

Index 229

xi
xii CONTENTS

xii
LIST OF FIGURES xiii

List of Figures

2.1 A Typical Compiler Toolchain . . . . . . . . . . . . . . . . . . 5


2.2 The Stages of a Unix Compiler . . . . . . . . . . . . . . . . . 6
2.3 Example AST . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.4 Example Intermediate Representation . . . . . . . . . . . . . 9
2.5 Example Assembly Code . . . . . . . . . . . . . . . . . . . . . 10

3.1 A Simple Hand Made Scanner . . . . . . . . . . . . . . . . . . 12


3.2 Relationship Between REs, NFAs, and DFAs . . . . . . . . . 19
3.3 Subset Construction Algorithm . . . . . . . . . . . . . . . . . 22
3.4 Converting an NFA to a DFA via Subset Construction . . . . 23
3.5 Hopcroft’s DFA Minimization Algorithm . . . . . . . . . . . 24
3.6 Structure of a Flex File . . . . . . . . . . . . . . . . . . . . . . 27
3.7 Example Flex Specification . . . . . . . . . . . . . . . . . . . . 29
3.8 Example Main Program . . . . . . . . . . . . . . . . . . . . . 29
3.9 Example Token Enumeration . . . . . . . . . . . . . . . . . . 30
3.10 Build Procedure for a Flex Program . . . . . . . . . . . . . . . 30

4.1 Two Derivations of the Same Sentence . . . . . . . . . . . . . 38


4.2 A Recursive-Descent Parser . . . . . . . . . . . . . . . . . . . 46
4.3 LR(0) Automaton for Grammar G10 . . . . . . . . . . . . . . 53
4.4 SLR Parse Table for Grammar G10 . . . . . . . . . . . . . . . 56
4.5 Part of LR(0) Automaton for Grammar G11 . . . . . . . . . . 58
4.6 LR(1) Automaton for Grammar G10 . . . . . . . . . . . . . . 61
4.7 The Chomsky Hierarchy . . . . . . . . . . . . . . . . . . . . . 64

5.1 Bison Specification for Expression Validator . . . . . . . . . . 71


5.2 Main Program for Expression Validator . . . . . . . . . . . . 72
5.3 Build Procedure for Bison and Flex Together . . . . . . . . . 72
5.4 Bison Specification for an Interpreter . . . . . . . . . . . . . . 75
5.5 AST for Expression Interpreter . . . . . . . . . . . . . . . . . 76
5.6 Building an AST for the Expression Grammar . . . . . . . . . 78
5.7 Evaluating Expressions . . . . . . . . . . . . . . . . . . . . . . 80
5.8 Printing and Evaluating Expressions . . . . . . . . . . . . . . 81

7.1 The Symbol Structure . . . . . . . . . . . . . . . . . . . . . . . 107

xiii
xiv LIST OF FIGURES

7.2 A Nested Symbol Table . . . . . . . . . . . . . . . . . . . . . . 109


7.3 Symbol Table API . . . . . . . . . . . . . . . . . . . . . . . . . 110
7.4 Name Resolution for Declarations . . . . . . . . . . . . . . . 112
7.5 Name Resolution for Expressions . . . . . . . . . . . . . . . . 112

8.1 Sample DAG Data Structure Definition . . . . . . . . . . . . 120


8.2 Example of Constant Folding . . . . . . . . . . . . . . . . . . 125
8.3 Example Control Flow Graph . . . . . . . . . . . . . . . . . . 126

9.1 Flat Memory Model . . . . . . . . . . . . . . . . . . . . . . . . 135


9.2 Logical Segments . . . . . . . . . . . . . . . . . . . . . . . . . 136
9.3 Multiprogrammed Memory Layout . . . . . . . . . . . . . . 137

10.1 X86 Register Structure . . . . . . . . . . . . . . . . . . . . . . 153


10.2 X86 Register Structure . . . . . . . . . . . . . . . . . . . . . . 154
10.3 Summary of System V ABI Calling Convention . . . . . . . . 160
10.4 System V ABI Register Assignments . . . . . . . . . . . . . . 162
10.5 Example X86-64 Stack Layout . . . . . . . . . . . . . . . . . . 164
10.6 Complete X86 Example . . . . . . . . . . . . . . . . . . . . . . 166
10.7 ARM Addressing Modes . . . . . . . . . . . . . . . . . . . . . 169
10.8 ARM Branch Instructions . . . . . . . . . . . . . . . . . . . . 172
10.9 Summary of ARM Calling Convention . . . . . . . . . . . . . 174
10.10ARM Register Assignments . . . . . . . . . . . . . . . . . . . 175
10.11Example ARM Stack Frame . . . . . . . . . . . . . . . . . . . 177
10.12Complete ARM Example . . . . . . . . . . . . . . . . . . . . . 178

11.1 Code Generation Functions . . . . . . . . . . . . . . . . . . . 182


11.2 Example of Generating X86 Code from a DAG . . . . . . . . 184
11.3 Expression Generation Skeleton . . . . . . . . . . . . . . . . . 186
11.4 Generating Code for a Function Call . . . . . . . . . . . . . . 187
11.5 Statement Generator Skeleton . . . . . . . . . . . . . . . . . . 188

12.1 Timing a Fast Operation . . . . . . . . . . . . . . . . . . . . . 197


12.2 Constant Folding Pseudo-Code . . . . . . . . . . . . . . . . . 198
12.3 Example X86 Instruction Templates . . . . . . . . . . . . . . . 206
12.4 Example of Tree Rewriting . . . . . . . . . . . . . . . . . . . . 207
12.5 Live Ranges and Register Conflict Graph . . . . . . . . . . . 210
12.6 Example of Global Register Allocation . . . . . . . . . . . . . 211

xiv
1

Chapter 1 – Introduction

1.1 What is a compiler?

A compiler translates a program in a source language to a program in


a target language. The most well known form of a compiler is one that
translates a high level language like C into the native assembly language
of a machine so that it can be executed. And of course there are compilers
for other languages like C++, Java, C#, and Rust, and many others.
The same techniques used in a traditional compiler are also used in
any kind of program that processes a language. For example, a typeset-
ting program like TEX translates a manuscript into a Postscript document.
A graph-layout program like Dot consumes a list of nodes and edges and
arranges them on a screen. A web browser translates an HTML document
into an interactive graphical display. To write programs like these, you
need to understand and use the same techniques as in traditional compil-
ers.
Compilers exist not only to translate programs, but also to improve them.
A compiler assists a programmer by finding errors in a program at compile
time, so that the user does not have to encounter them at runtime. Usually,
a more strict language results in more compile-time errors. This makes the
programmer’s job harder, but makes it more likely that the program is
correct. For example, the Ada language is infamous among programmers
as challenging to write without compile-time errors, but once working, is
trusted to run safety-critical systems such as the Boeing 777 aircraft.
A compiler is distinct from an interpreter, which reads in a program
and then executes it directly, without emitting a translation. This is also
sometimes known as a virtual machine. Languages like Python and Ruby
are typically executed by an interpreter that reads the source code directly.
Compilers and interpreters are closely related, and it is sometimes pos-
sible to exchange one for the other. For example, Java compilers translate
Java source code into Java bytecode, which is an abstract form of assem-
bly language. Some implementations of the Java Virtual Machine work as
interpreters that execute one instruction at a time. Others work by trans-
lating the bytecode into local machine code, and then running the machine
code directly. This is known as just in time compiling or JIT.

1
2 CHAPTER 1. INTRODUCTION

1.2 Why should you study compilers?

You will be a better programmer. A great craftsman must understand his


or her tools, and a programmer is no different. By understanding more
deeply how a compiler translates your program into machine language,
you will become more skilled at writing effective code and debugging it
when things go wrong.
You can create tools for debugging and translating. If you can write a parser
for a given language, then you can write all manner of supporting tools
that help you (and others) debug your own programs. An integrated de-
velopment environment like Eclipse incorporates parsers for languages
like Java, so that it can highlight syntax, find errors without compiling,
and connect code to documentation as you write.
You can create new languages. A surprising number of problems are
made easier by expressing them compactly in a custom language. (These
are sometimes known as domain specific languages or simply little lan-
guages.) By learning the techniques of compilers, you will be able to im-
plement little languages and avoid some pitfalls of language design.
You can contribute to existing compilers. While it’s unlikely that you will
write the next great C compiler (since we already have several), language
and compiler development does not stand still. Standards development
results in new language features; optimization research creates new ways
of improving programs; new microprocessors are created; new operating
systems are developed; and so on. All of these developments require the
continuous improvement of existing compilers.
You will have fun while solving challenging problems. Isn’t that enough?

1.3 What’s the best way to learn about compilers?

The best way to learn about compilers is to write your own compiler from
beginning to end. While that may sound daunting at first, you will find
that this complex task can be broken down into several stages of moder-
ate complexity. The typical undergraduate computer science student can
write a complete compiler for a simple language in a semester, broken
down into four or five independent stages.

1.4 What language should I use?

Without question, you should use the C programming language and the
X86 assembly language, of course!
Ok, maybe the answer isn’t quite that simple. There is an ever-increasing
number of programming languages that all have different strengths and
weaknesses. Java is simple, consistent, and portable, albeit not high per-
formance. Python is easy to learn and has great library support, but is
weakly typed. Rust offers exceptional static type-safety, but is not (yet)

2
1.5. HOW IS THIS BOOK DIFFERENT FROM OTHERS? 3

widely used. It is quite possible to write a compiler in nearly any lan-


guage, and you could use this book as a guide to do so.
However, we really think that you should learn C, write a compiler in
C, and use it to compile a C-like language which produces assembly for a
widely-used processor, like X86 or ARM. Why? Because it is important for
you to learn the ins and outs of technologies that are in wide use, and not
just those that are abstractly beautiful.
C is the most widely-used portable language for low-level coding (com-
pilers, and libraries, and kernels) and it is also small enough that one
can learn how to compile every aspect of C in a single semester. True, C
presents some challenges related to type safety and pointer use, but these
are manageable for a project the size of a compiler. There are other lan-
guages with different virtues, but none as simple and as widely used as C.
Once you write a C compiler, then you are free to design your own (better)
language.
Likewise, the X86 has been the most widely-deployed computer archi-
tecture in desktops, servers, and laptops for several decades. While it is
considerably more complex than other architectures like MIPS or SPARC
or ARM, one can quickly learn the essential subset of instructions nec-
essary to build a compiler. Of course, ARM is quickly catching up as a
popular architecture in the mobile, embedded, and low power space, so
we have included a section on that as well.
That said, the principles presented in this book are widely applicable.
If you are using this as part of a class, your instructor may very well choose
a different compilation language and different target assembly, and that’s
fine too.

1.5 How is this book different from others?

Most books on compilers are very heavy on the abstract theory of scan-
ners, parsers, type systems, and register allocation, and rather light on
how the design of a language affects the compiler and the runtime. Most
are designed for use by a graduate survey of optimization techniques.
This book takes a broader approach by giving a lighter dose of opti-
mization, and introducing more material on the process of engineering a
compiler, the tradeoffs in language design, and considerations for inter-
pretation and translation.
You will also notice that this book doesn’t contain a whole bunch of
fiddly paper-and-pencil assignments to test your knowledge of compiler
algorithms. (Ok, there are a few of those in Chapters 3 and 4.) If you want
to test your knowledge, then write some working code. To that end, the
exercises at the end of each chapter ask you to take the ideas in the chapter,
and either explore some existing compilers, or write parts of your own. If
you do all of them in order, you will end up with a working compiler,
summarized in the final appendix.

3
4 CHAPTER 1. INTRODUCTION

1.6 What other books should I read?

For general reference on compilers, I suggest the following books:

• Charles N. Fischer, Ron K. Cytron, and Richard J. LeBlanc Jr, “Craft-


ing a Compiler”, Pearson, 2009.
This is an excellent undergraduate textbook which focuses on object-oriented soft-
ware engineering techniques for constructing a compiler, with a focus on generating
output for the Java Virtual Machine.

• Christopher Fraser and David Hanson, “A Retargetable C Com-


piler: Design and Implementation”, Benjamin/Cummings, 1995.
Also known as the “LCC book”, this book focuses entirely on explaining the C imple-
mentation of a C compiler by taking the unusual approach of embedding the literal
code into the textbook, so that code and explanation are intertwined.

• Alfred V. Aho, Monica S. Lam, Ravi Sethi, and Jeffrey D. Ullman,


“Compilers: Principles, Techniques, and Tools”, Addison Wesley,
2006. Affectionately known as the “dragon book”, this is a comprehensive treat-
ment of the theory of compilers from scanning through type theory and optimization
at an advanced graduate level.

Ok, what are you waiting for? Let’s get to work.

4
5

Chapter 2 – A Quick Tour

2.1 The Compiler Toolchain

A compiler is one component in a toolchain of programs used to create


executables from source code. Typically, when you invoke a single com-
mand to compile a program, a whole sequence of programs are invoked in
the background. Figure 2.1 shows the programs typically used in a Unix
system for compiling C source code to assembly code.

Headers
(stdio.h)

Source Preprocessor Preprocessed Compiler Assembly Assembler


(prog.c) (cpp) Source (cc1) (prog.s) (as)

Object Code
(prog.o)

Dynamic Static
Running Executable
Linker Linker
Process (prog)
(ld.so) (ld)

Libraries
Dynamic Libraries (libc.a)
(libc.so)

Figure 2.1: A Typical Compiler Toolchain

• The preprocessor prepares the source code for the compiler proper.
In the C and C++ languages, this means consuming all directives that
start with the # symbol. For example, an #include directive causes
the preprocessor to open the named file and insert its contents into
the source code. A #define directive causes the preprocessor to
substitute a value wherever a macro name is encountered. (Not all
languages rely on a preprocessor.)

• The compiler proper consumes the clean output of the preproces-


sor. It scans and parses the source code, performs typechecking and

5
6 CHAPTER 2. A QUICK TOUR

other semantic routines, optimizes the code, and then produces as-
sembly language as the output. This part of the toolchain is the main
focus of this book.

• The assembler consumes the assembly code and produces object


code. Object code is “almost executable” in that it contains raw ma-
chine language instructions in the form needed by the CPU. How-
ever, object code does not know the final memory addresses in which
it will be loaded, and so it contains gaps that must be filled in by the
linker.

• The linker consumes one or more object files and library files and
combines them into a complete, executable program. It selects the
final memory locations where each piece of code and data will be
loaded, and then “links” them together by writing in the missing ad-
dress information. For example, an object file that calls the printf
function does not initially know the address of the function. An
empty (zero) address will be left where the address must be used.
Once the linker selects the memory location of printf, it must go
back and write in the address at every place where printf is called.

In Unix-like operating systems, the preprocessor, compiler, assembler,


and linker are historically named cpp, cc1, as, and ld respectively. The
user-visible program cc simply invokes each element of the toolchain in
order to produce the final executable.

2.2 Stages Within a Compiler

In this book, our focus will be primarily on the compiler proper, which is
the most interesting component in the toolchain. The compiler itself can
be divided into several stages:

Abstract
Character Semantic Intermediate Code Assembly
Scanner Tokens Parser Syntax
Stream Routines Representation Generator Code
Tree

Optimizers

Figure 2.2: The Stages of a Unix Compiler

• The scanner consumes the plain text of a program, and groups to-
gether individual characters to form complete tokens. This is much
like grouping characters into words in a natural language.

6
2.3. EXAMPLE COMPILATION 7

• The parser consumes tokens and groups them together into com-
plete statements and expressions, much like words are grouped into
sentences in a natural language. The parser is guided by a grammar
which states the formal rules of composition in a given language.
The output of the parser is an abstract syntax tree (AST) that cap-
tures the grammatical structures of the program. The AST also re-
members where in the source file each construct appeared, so it is
able to generate targeted error messages, if needed.
• The semantic routines traverse the AST and derive additional mean-
ing (semantics) about the program from the rules of the language
and the relationship between elements of the program. For exam-
ple, we might determine that x + 10 is a float expression by ob-
serving the type of x from an earlier declaration, then applying the
language rule that addition between int and float values yields
a float. After the semantic routines, the AST is often converted into
an intermediate representation (IR) which is a simplified form of
assembly code suitable for detailed analysis. There are many forms
of IR which we will discuss in Chapter 8.
• One or more optimizers can be applied to the intermediate represen-
tation, in order to make the program smaller, faster, or more efficient.
Typically, each optimizer reads the program in IR format, and then
emits the same IR format, so that each optimizer can be applied in-
dependently, in arbitrary order.
• Finally, a code generator consumes the optimized IR and transforms
it into a concrete assembly language program. Typically, a code gen-
erator must perform register allocation to effectively manage the
limited number of hardware registers, and instruction selection and
sequencing to order assembly instructions in the most efficient form.

2.3 Example Compilation

Suppose we wish to compile this fragment of code into assembly:

height = (width+56) * factor(foo);

The first stage of the compiler (the scanner) will read in the text of
the source code character by character, identify the boundaries between
symbols, and emit a series of tokens. Each token is a small data structure
that describes the nature and contents of each symbol:

id:height = ( id:width + int:56 ) * id:factor ( id:foo ) ;

At this stage, the purpose of each token is not yet clear. For example,
factor and foo are simply known to be identifiers, even though one is

7
8 CHAPTER 2. A QUICK TOUR

the name of a function, and the other is the name of a variable. Likewise,
we do not yet know the type of width, so the + could potentially rep-
resent integer addition, floating point addition, string concatenation, or
something else entirely.
The next step is to determine whether this sequence of tokens forms
a valid program. The parser does this by looking for patterns that match
the grammar of a language. Suppose that our compiler understands a
language with the following grammar:

Grammar G1
1. expr → expr + expr
2. expr → expr * expr
3. expr → expr = expr
4. expr → id ( expr )
5. expr → ( expr )
6. expr → id
7. expr → int

Each line of the grammar is called a rule, and explains how various
parts of the language are constructed. Rules 1-3 indicate that an expression
can be formed by joining two expressions with operators. Rule 4 describes
a function call. Rule 5 describes the use of parentheses. Finally, rules 6 and
7 indicate that identifiers and integers are atomic expressions. 1
The parser looks for sequences of tokens that can be replaced by the
left side of a rule in our grammar. Each time a rule is applied, the parser
creates a node in a tree, and connects the sub-expressions into the abstract
syntax tree (AST). The AST shows the structural relationships between
each symbol: addition is performed on width and 56, while a function
call is applied to factor and foo.
With this data structure in place, we are now prepared to analyze the
meaning of the program. The semantic routines traverse the AST and de-
rive additional meaning by relating parts of the program to each other, and
to the definition of the programming language. An important component
of this process is typechecking, in which the type of each expression is
determined, and checked for consistency with the rest of the program. To
keep things simple here, we will assume that all of our variables are plain
integers.
To generate linear intermediate code, we perform a post-order traver-
sal of the AST and generate an IR instruction for each node in the tree. A
typical IR looks like an abstract assembly language, with load/store in-
structions, arithmetic operations, and an infinite number of registers. For
example, this is a possible IR representation of our example program:
1 The careful reader will note that this example grammar has ambiguities. We will discuss

that in some detail in Chapter 4.

8
2.3. EXAMPLE COMPILATION 9

ASSIGN

ID
MUL
height

ADD CALL

ID INT ID ID
width 56 factor foo

Figure 2.3: Example AST

LOAD $56 -> r1


LOAD width -> r2
IADD r1, r2 -> r3
ARG foo
CALL factor -> r4
IMUL r3, r4 -> r5
STOR r5 -> height
Figure 2.4: Example Intermediate Representation

The intermediate representation is where most forms of optimization


occur. Dead code is removed, common operations are combined, and code
is generally simplified to consume fewer resources and run more quickly.
Finally, the intermediate code must be converted to the desired assem-
bly code. Figure 2.5 shows X86 assembly code that is one possible trans-
lation of the IR given above. Note that the assembly instructions do not
necessarily correspond one-to-one with IR instructions.
A well-engineered compiler is highly modular, so that common code
elements can be shared and combined as needed. To support multiple lan-
guages, a compiler can provide distinct scanners and parsers, each emit-
ting the same intermediate representation. Different optimization tech-
niques can be implemented as independent modules (each reading and

9
10 CHAPTER 2. A QUICK TOUR

MOVQ width, %rax # load width into rax


ADDQ $56, %rax # add 56 to rax
MOVQ %rax, -8(%rbp) # save sum in temporary
MOVQ foo, %edi # load foo into arg 0 register
CALL factor # invoke factor, result in rax
MOVQ -8(%rbp), %rbx # load sum into rbx
IMULQ %rbx # multiply rbx by rax
MOVQ %rax, height # store result into height
Figure 2.5: Example Assembly Code

writing the same IR) so that they can be enabled and disabled indepen-
dently. A retargetable compiler contains multiple code generators, so that
the same IR can be emitted for a variety of microprocessors.

2.4 Exercises

1. Determine how to invoke the preprocessor, compiler, assembler, and


linker manually in your local computing environment. Compile a
small complete program that computes a simple expression, and ex-
amine the output at each stage. Are you able to follow the flow of
the program in each form?

2. Determine how to change the optimization level for your local com-
piler. Find a non-trivial source program and compile it at multiple
levels of optimization. How does the compile time, program size,
and run time vary with optimization levels?

3. Search the internet for the formal grammars for three languages that
you are familiar with, such as C++, Ruby, and Rust. Compare them
side by side. Which language is inherently more complex? Do they
share any common structures?

10
11

Chapter 3 – Scanning

3.1 Kinds of Tokens

Scanning is the process of identifying tokens from the raw text source code
of a program. At first glance, scanning might seem trivial – after all, iden-
tifying words in a natural language is as simple as looking for spaces be-
tween letters. However, identifying tokens in source code requires the
language designer to clarify many fine details, so that it is clear what is
permitted and what is not.
Most languages will have tokens in these categories:
• Keywords are words in the language structure itself, like while or
class or true. Keywords must be chosen carefully to reflect the
natural structure of the language, without interfering with the likely
names of variables and other identifiers.
• Identifiers are the names of variables, functions, classes, and other
code elements chosen by the programmer. Typically, identifiers are
arbitrary sequences of letters and possibly numbers. Some languages
require identifiers to be marked with a sentinel (like the dollar sign
in Perl) to clearly distinguish identifiers from keywords.
• Numbers could be formatted as integers, or floating point values, or
fractions, or in alternate bases such as binary, octal or hexadecimal.
Each format should be clearly distinguished, so that the programmer
does not confuse one with the other.
• Strings are literal character sequences that must be clearly distin-
guished from keywords or identifiers. Strings are typically quoted
with single or double quotes, but also must have some facility for
containing quotations, newlines, and unprintable characters.
• Comments and whitespace are used to format a program to make it
visually clear, and in some cases (like Python) are significant to the
structure of a program.
When designing a new language, or designing a compiler for an exist-
ing language, the first job is to state precisely what characters are permit-
ted in each type of token. Initially, this could be done informally by stating,

11
12 CHAPTER 3. SCANNING

token_t scan_token( FILE *fp ) {


int c = fgetc(fp);
if(c==’*’) {
return TOKEN_MULTIPLY;
} else if(c==’!’) {
char d = fgetc(fp);
if(d==’=’) {
return TOKEN_NOT_EQUAL;
} else {
ungetc(d,fp);
return TOKEN_NOT;
}
} else if(isalpha(c)) {
do {
char d = fgetc(fp);
} while(isalnum(d));
ungetc(d,fp);
return TOKEN_IDENTIFIER;
} else if ( . . . ) {
. . .
}
}

Figure 3.1: A Simple Hand Made Scanner

for example, “An identifier consists of a letter followed by any number of letters
and numerals.”, and then assigning a symbolic constant (TOKEN IDENTIFIER)
for that kind of token. As we will see, an informal approach is often am-
biguous, and a more rigorous approach is needed.

3.2 A Hand-Made Scanner

Figure 3.1 shows how one might write a scanner by hand, using simple
coding techniques. To keep things simple, we only consider just a few
tokens: * for multiplication, ! for logical-not, != for not-equal, and se-
quences of letters and numbers for identifiers.
The basic approach is to read one character at a time from the input
stream (fgetc(fp)) and then classify it. Some single-character tokens are
easy: if the scanner reads a * character, it immediately returns
TOKEN MULTIPLY, and the same would be true for addition, subtraction,
and so forth.
However, some characters are part of multiple tokens. If the scanner
encounters !, that could represent a logical-not operation by itself, or it
could be the first character in the != sequence representing not-equal-to.

12
3.3. REGULAR EXPRESSIONS 13

Upon reading !, the scanner must immediately read the next character. If
the next character is =, then it has matched the sequence != and returns
TOKEN NOT EQUAL.
But, if the character following ! is something else, then the non-matching
character needs to be put back on the input stream using ungetc, because
it is not part of the current token. The scanner returns TOKEN NOT and will
consume the put-back character on the next call to scan token.
In a similar way, once a letter has been identified by isalpha(c), then
the scanner keeps reading letters or numbers, until a non-matching char-
acter is found. The non-matching character is put back, and the scanner
returns TOKEN IDENTIFIER.
(We will see this pattern come up in every stage of the compiler: an
unexpected item doesn’t match the current objective, so it must be put
back for later. This is known more generally as backtracking.)
As you can see, a hand-made scanner is rather verbose. As more to-
ken types are added, the code can become quite convoluted, particularly
if tokens share common sequences of characters. It can also be difficult
for a developer to be certain that the scanner code corresponds to the de-
sired definition of each token, which can result in unexpected behavior on
complex inputs. That said, for a small language with a limited number of
tokens, a hand-made scanner can be an appropriate solution.
For a complex language with a large number of tokens, we need a more
formalized approach to defining and scanning tokens. A formal approach
will allow us to have a greater confidence that token definitions do not
conflict and the scanner is implemented correctly. Further, a formal ap-
proach will allow us to make the scanner compact and high performance
– surprisingly, the scanner itself can be the performance bottleneck in a
compiler, since every single character must be individually considered.
The formal tools of regular expressions and finite automata allow us
to state very precisely what may appear in a given token type. Then, auto-
mated tools can process these definitions, find errors or ambiguities, and
produce compact, high performance code.

3.3 Regular Expressions

Regular expressions (REs) are a language for expressing patterns. They


were first described in the 1950s by Stephen Kleene [4] as an element of
his foundational work in automata theory and computability. Today, REs
are found in slightly different forms in programming languages (Perl),
standard libraries (PCRE), text editors (vi), command-line tools (grep),
and many other places. We can use regular expressions as a compact
and formal way of specifying the tokens accepted by the scanner of a
compiler, and then automatically translate those expressions into working
code. While easily explained, REs can be a bit tricky to use, and require
some practice in order to achieve the desired results.

13
14 CHAPTER 3. SCANNING

Let us define regular expressions precisely:

A regular expression s is a string which denotes L(s), a set of strings


drawn from an alphabet Σ. L(s) is known as the “language of s.”
L(s) is defined inductively with the following base cases:

• If a ∈ Σ then a is a regular expression and L(a) = {a}.


•  is a regular expression and L() contains only the empty string.

Then, for any regular expressions s and t:

1. s|t is a RE such that L(s|t) = L(s) ∪ L(t).


2. st is a RE such that L(st) contains all strings formed by the
concatenation of a string in L(s) followed by a string in L(t).
3. s∗ is a RE such that L(s∗ ) = L(s) concatenated zero or more times.

Rule #3 is known as the Kleene closure and has the highest precedence.
Rule #2 is known as concatenation. Rule #1 has the lowest precedence and
is known as alternation. Parentheses can be added to adjust the order of
operations in the usual way.
Here are a few examples using just the basic rules. (Note that a finite
RE can indicate an infinite set.)
Regular Expression s Language L(s)
hello { hello }
d(o|i)g { dog,dig }
moo* { mo,moo,mooo,... }
(moo)* { ,moo,moomoo,moomoomoo,... }
a(b|a)*a { aa,aaa,aba,aaaa,aaba,abaa,... }
The syntax described so far is entirely sufficient to write any regular
expression. But, it is also handy to have a few helper operations built on
top of the basic syntax:

s? indicates that s is optional.


s? can be written as (s|)

s+ indicates that s is repeated one or more times.


s+ can be written as ss*

[a-z] indicates any character in that range.


[a-z] can be written as (a|b|...|z)

[ˆx] indicates any character except one.


[ˆx] can be written as Σ - x

14
3.4. FINITE AUTOMATA 15

Regular expressions also obey several algebraic properties, which make


it possible to re-arrange them as needed for efficiency or clarity:

Associativity: a|(b|c) = (a|b)|c


Commutativity: a|b = b|a
Distribution: a(b|c) = ab|ac
Idempotency: a** = a*

Using regular expressions, we can precisely state what is permitted in


a given token. Suppose we have a hypothetical programming language
with the following informal definitions and regular expressions. For each
token type, we show examples of strings that match (and do not match)
the regular expression.
Informal definition: An identifier is a sequence of capital letters and num-
bers, but a number must not come first.
Regular expression: [A-Z]+([A-Z]|[0-9])*
Matches strings: PRINT
MODE5
Does not match: hello
4YOU
Informal definition: A number is a sequence of digits with an optional dec-
imal point. For clarity, the decimal point must have
digits on both left and right sides.
Regular expression: [0-9]+(.[0-9]+)?
Matches strings: 123
3.14
Does not match: .15
30.
Informal definition: A comment is any text (except a right angle bracket)
surrounded by angle brackets.
Regular expression: <[ˆ>]*>
Matches strings: <tricky part>
<<<<look left>
Does not match: <this is an <illegal> comment>

3.4 Finite Automata

A finite automaton (FA) is an abstract machine that can be used to repre-


sent certain forms of computation. Graphically, an FA consists of a number
of states (represented by numbered circles) and a number of edges (repre-
sented by labeled arrows) between those states. Each edge is labeled with
one or more symbols drawn from an alphabet Σ.
The machine begins in a start state S0 . For each input symbol presented
to the FA, it moves to the state indicated by the edge with the same label

15
16 CHAPTER 3. SCANNING

as the input symbol. Some states of the FA are known as accepting states
and are indicated by a double circle. If the FA is in an accepting state after
all input is consumed, then we say that the FA accepts the input. We say
that the FA rejects the input string if it ends in a non-accepting state, or if
there is no edge corresponding to the current input symbol.
Every RE can be written as an FA, and vice versa. For a simple regular
expression, one can construct an FA by hand. For example, here is an FA
for the keyword for:

f o r
0 1 2 3

Here is an FA for identifiers of the form [a-z][a-z0-9]+

a-z
0-9

a-z
a-z 0-9
0 1 2

And here is an FA for numbers of the form ([1-9][0-9]*)|0

0-9

0-9
1-9 1 2

0
0
3

3.4.1 Deterministic Finite Automata


Each of these three examples is a deterministic finite automaton (DFA).
A DFA is a special case of an FA where every state has no more than one
outgoing edge for a given symbol. Put another way, a DFA has no am-
biguity: for every combination of state and input symbol, there is exactly
one choice of what to do next.
Because of this property, a DFA is very easy to implement in software
or hardware. One integer (c) is needed to keep track of the current state.

16
3.4. FINITE AUTOMATA 17

The transitions between states are represented by a matrix (M [s, i]) which
encodes the next state, given the current state and input symbol. (If the
transition is not allowed, we mark it with E to indicate an error.) For each
symbol, we compute c = M [s, i] until all the input is consumed, or an error
state is reached.

3.4.2 Nondeterministic Finite Automata

The alternative to a DFA is a nondeterministic finite automaton (NFA).


An NFA is a perfectly valid FA, but it has an ambiguity that makes it some-
what more difficult to work with.
Consider the regular expression [a-z]*ing, which represents all lower-
case words ending in the suffix ing. It can be represented with the follow-
ing automaton:

[a-z]

i n g
0 1 2 3

Now consider how this automaton would consume the word sing. It
could proceed in two different ways. One would be to move to state 0 on
s, state 1 on i, state 2 on n, and state 3 on g. But the other, equally valid
way would be to stay in state 0 the whole time, matching each letter to the
[a-z] transition. Both ways obey the transition rules, but one results in
acceptance, while the other results in rejection.
The problem here is that state 0 allows for two different transitions on
the symbol i. One is to stay in state 0 matching [a-z] and the other is to
move to state 1 matching i.
Moreover, there is no simple rule by which we can pick one path or
another. If the input is sing, the right solution is to proceed immediately
from state zero to state one on i. But if the input is singing, then we
should stay in state zero for the first ing and proceed to state one for the
second ing .
An NFA can also have an  (epsilon) transition, which represents the
empty string. This transition can be taken without consuming any input
symbols at all. For example, we could represent the regular expression
a*(ab|ac) with this NFA:

17
18 CHAPTER 3. SCANNING

a
a b 3
ε 1 2

0 ε
4 a c
5 6

This particular NFA presents a variety of ambiguous choices. From


state zero, it could consume a and stay in state zero. Or, it could take an 
to state one or state four, and then consume an a either way.
There are two common ways to interpret this ambiguity:
• The crystal ball interpretation suggests that the NFA somehow “knows”
what the best choice is, by some means external to the NFA itself. In
the example above, the NFA would choose whether to proceed to
state zero, one, or four before consuming the first character, and it
would always make the right choice. Needless to say, this isn’t pos-
sible in a real implementation.
• The many-worlds interpretation suggests that the NFA exists in all
allowable states simultaneously. When the input is complete, if any
of those states are accepting states, then the NFA has accepted the
input. This interpretation is more useful for constructing a working
NFA, or converting it to a DFA.
Let us use the many-worlds interpretation on the example above. Sup-
pose that the input string is aaac. Initially the NFA is in state zero. With-
out consuming any input, it could take an epsilon transition to states one
or four. So, we can consider its initial state to be all of those states si-
multaneously. Continuing on, the NFA would traverse these states until
accepting the complete string aaac:

States Action
0, 1, 4 consume a
0, 1, 2, 4, 5 consume a
0, 1, 2, 4, 5 consume a
0, 1, 2, 4, 5 consume c
6 accept
In principle, one can implement an NFA in software or hardware by
simply keeping track of all of the possible states. But this is inefficient.
In the worst case, we would need to evaluate all states for all characters
on each input transition. A better approach is to convert the NFA into an
equivalent DFA, as we show below.

18
3.5. CONVERSION ALGORITHMS 19

3.5 Conversion Algorithms

Regular expressions and finite automata are all equally powerful. For ev-
ery RE, there is an FA, and vice versa. However, a DFA is by far the most
straightforward of the three to implement in software. In this section, we
will show how to convert an RE into an NFA, then an NFA into a DFA,
and then to optimize the size of the DFA.

Thompson's Subset Transition


Nondeterministic Deterministic
Regular Construction Construction Matrix
Finite Finite Code
Expression
Automaton Automaton

Figure 3.2: Relationship Between REs, NFAs, and DFAs

3.5.1 Converting REs to NFAs


To convert a regular expression to a nondeterministic finite automaton, we
can follow an algorithm given first by McNaughton and Yamada [5], and
then by Ken Thompson [6].
We follow the same inductive definition of regular expression as given
earlier. First, we define automata corresponding to the base cases of REs:

The NFA for any character a is: The NFA for an  transition is:

a ε

Now, suppose that we have already constructed NFAs for the regular
expressions A and B, indicated below by rectangles. Both A and B have
a single start state (on the left) and accepting state (on the right). If we
write the concatenation of A and B as AB, then the corresponding NFA is
simply A and B connected by an  transition. The start state of A becomes
the start state of the combination, and the accepting state of B becomes the
accepting state of the combination:

The NFA for the concatenation AB is:

A ε B

19
20 CHAPTER 3. SCANNING

In a similar fashion, the alternation of A and B written as A|B can be ex-


pressed as two automata joined by common starting and accepting nodes,
all connected by  transitions:

The NFA for the alternation A|B is:

A
ε ε

ε ε
B

Finally, the Kleene closure A* is constructed by taking the automaton


for A, adding starting and accepting nodes, then adding  transitions to
allow zero or more repetitions:

The NFA for the Kleene closure A* is:

ε ε

ε
ε

Example. Let’s consider the process for an example regular expression


a(cat|cow)*. First, we start with the innermost expression cat and as-
semble it into three transitions resulting in an accepting state. Then, do the
same thing for cow, yielding these two FAs:

c o w

c a t

The alternation of the two expressions cat|cow is accomplished by


adding a new starting and accepting node, with epsilon transitions. (The
boxes are not part of the graph, but simply highlight the previous graph
components carried forward.)

20
3.5. CONVERSION ALGORITHMS 21

c a t
ε ε

ε ε
c o w

Then, the Kleene closure (cat|cow)* is accomplished by adding an-


other starting and accepting state around the previous FA, with epsilon
transitions between:

c o w
ε ε

ε ε ε ε
c a t

Finally, the concatenation of a(cat|cow)* is achieved by adding a


single state at the beginning for a:

c w
o
ε ε
ε
a ε ε ε ε
c a t

You can easily see that the NFA resulting from the construction algo-
rithm, while correct, is quite complex and contains a large number of ep-
silon transitions. An NFA representing the tokens for a complete language
could end up having thousands of states, which would be very impractical
to implement. Instead, we can convert this NFA into an equivalent DFA.

21
22 CHAPTER 3. SCANNING

3.5.2 Converting NFAs to DFAs


We can convert any NFA into an equivalent DFA using the technique of
subset construction. The basic idea is to create a DFA such that each state
in the DFA corresponds to multiple states in the NFA, according to the
“many-worlds” interpretation.
Suppose that we begin with an NFA consisting of states N and start
state N0 . We wish to construct an equivalent DFA consisting of states D
and start state D0 . Each D state will correspond to multiple N states. First,
we define a helper function known as the epsilon closure:

Epsilon closure.
−closure(n) is the set of NFA states reachable from NFA state n by zero
or more  transitions.
Now we define the subset construction algorithm. First, we create a
start state D0 corresponding to the −closure(N0 ). Then, for each outgo-
ing character c from the states in D0 , we create a new state containing the
epsilon closure of the states reachable by c. More precisely:

Subset Construction Algorithm.


Given an NFA with states N and start state N0 , create an equivalent DFA
with states D and start state D0 .
Let D0 = −closure(N0 ).
Add D0 to a list.
While items remain on the list:
Let d be the next DFA state removed from the list.
For each character c in Σ:
Let T contain all NFA states Nk such that:
c
Nj ∈ d and Nj → − Nk
Create new DFA state Di = −closure(T)
If Di is not already in the list, add it to the end.

Figure 3.3: Subset Construction Algorithm

22
3.5. CONVERSION ALGORITHMS 23

c a t
ε N8 N9 N10 N11 ε
a ε ε ε
N0 N1 N2 N3 N12 N13
ε ε
c o w
N4 N5 N6 N7

D3:
N6
w
D4:
o N7, N12, N13,
N2, N3, N4, N8
c
D1:
D0: a N1, N2, N3,
c D2:
N0
N4, N8, N13
N5, N9 c
D6:
a N11, N12, N13,
N2,N3, N4, N8
t
D5:
N10

Figure 3.4: Converting an NFA to a DFA via Subset Construction

Example. Let’s work out the algorithm on the NFA in Figure 3.4. This
is the same NFA corresponding to the RE a(cat|cow)* with each of the
states numbered for clarity.

1. Compute D0 which is −closure(N0 ). N0 has no  transitions, so


D0 = {N0 }. Add D0 to the work list.
2. Remove D0 from the work list. The character a is an outgoing tran-
sition from N0 to N1 . −closure(N1 ) = {N1 , N2 , N3 , N4 , N8 , N13 } so
add all of those to new state D1 and add D1 to the work list.
c c
3. Remove D1 from the work list. We can see that N4 → − N5 and N8 → −
N9 , so we create a new state D2 = {N5 , N9 } and add it to the work
list.
4. Remove D2 from the work list. Both a and o are possible transitions
o a
because of N5 − → N6 and N9 −
→ N10 . So, create a new state D3 for the
o transition to N6 and new state D5 for the a transition to N10 . Add
both D3 and D5 to the work list.
w
5. Remove D3 from the work list. The only possible transition is N6 −

N7 so create a new state D4 containing the −closure(N7 ) and add it
to the work list.
t
6. Remove D5 from the work list. The only possible transition is N10 →

N11 so create a new state D6 containing −closure(N11 ) and add it to
the work list.

23
24 CHAPTER 3. SCANNING

7. Remove D4 from the work list, and observe that the only outgoing
transition c leads to states N5 and N9 which already exist as state D2 ,
c
so simply add a transition D4 → − D2 .
c
8. Remove D6 from the work list and, in a similar way, add D6 →
− D2 .
9. The work list is empty, so we are done.

3.5.3 Minimizing DFAs


The subset construction algorithm will definitely generate a valid DFA,
but the DFA may possibly be very large (especially if we began with a
complex NFA generated from an RE.) A large DFA will have a large tran-
sition matrix that will consume a lot of memory. If it doesn’t fit in L1 cache,
the scanner could run very slowly. To address this problem, we can apply
Hopcroft’s algorithm to shrink a DFA into a smaller (but equivalent) DFA.
The general approach of the algorithm is to optimistically group to-
gether all possibly-equivalent states S into super-states T . Initially, we
place all non-accepting S states into super-state T0 and accepting states
into super-state T1 . Then, we examine the outgoing edges in each state
s ∈ Ti . If, a given character c has edges that begin in Ti and end in dif-
ferent super-states, then we consider the super-state to be inconsistent with
respect to c. (Consider an impermissible transition as if it were a transi-
tion to TE , a super-state for errors.) The super-state must then be split into
multiple states that are consistent with respect to c. Repeat this process for
all super-states and all characters c ∈ Σ until no more splits are required.

DFA Minimization Algorithm.


Given a DFA with states S, create an equivalent DFA with
an equal or fewer number of states T .
First partition S into T such that:
T0 = non-accepting states of S.
T1 = accepting states of S.
Repeat:
∀Ti ∈ T :
∀c ∈ Σ:
c
if Ti →
− { more than one T state },
then split Ti into multiple T states
such that c has the same action in each.
Until no more states are split.

Figure 3.5: Hopcroft’s DFA Minimization Algorithm

24
3.5. CONVERSION ALGORITHMS 25

Example. Suppose we have the following non-optimized DFA and


wish to reduce it to a smaller DFA:

3
b
b
1 a
a
b
b
a 4 5
2
a
a

We begin by grouping all of non-accepting states 1, 2, 3, 4 into one


super-state and the accepting state 5 into another super-state, like this:

b
1,2,3,4 a 5
b

Now, we ask whether this graph is consistent with respect to all possi-
ble inputs, by referring back to the original DFA. For example, we observe
that, if we are in super-state (1,2,3,4) then an input of a always goes to
state 2, which keeps us within the super-state. So, this DFA is consistent
with respect to a. However, from super-state (1,2,3,4) an input of b can
either stay within the super-state or go to super-state (5). So, the DFA is
inconsistent with respect to b.
To fix this, we try splitting out one of the inconsistent states (4) into a
new super-state, taking the transitions with it:

b b
4 5
1,2,3
a
a,b

Again, we examine each super-state for consistency with respect to


each input character. Again, we observe that super-state 1,2,3 is consis-
tent with respect to a, but not consistent with respect to b because it can
either lead to state 3 or state 4. We attempt to fix this by splitting out state
2 into its own super-state, yielding this DFA.

25
26 CHAPTER 3. SCANNING

b a

b
b 4 5
a
1,3 2 a
a
b

Again, we examine each super-state and observe that each possible in-
put is consistent with respect to the super-state, and therefore we have the
minimal DFA.

3.6 Limits of Finite Automata

Regular expressions and finite automata are powerful and effective at rec-
ognizing simple patterns in individual words or tokens, but they are not
sufficient to analyze all of the structures in a problem. For example, could
you use a finite automaton to match an arbitrary number of nested paren-
theses?
It’s not hard to write out an FA that could match, say, up to three pairs
of nested parentheses, like this:

0
( 1
( 2
( 3
) ) )

But the key word is arbitrary! To match any number of parentheses


would require an infinite automaton, which is obviously impractical. Even
if we were to apply some practical upper limit (say, 100 pairs) the automa-
ton would still be impractically large when combined with all the other
elements of a language that must be supported.
For example, a language like Python permits the nesting of parentheses
() for precedence, curly brackets {} to represent dictionaries, and square
brackets [] to represent lists. An automaton to match up to 100 nested
pairs of each in arbitrary order would have 1,000,000 states!
So, we limit ourselves to using regular expressions and finite automata
for the narrow purpose of identifying the words and symbols within a
problem. To understand the higher level structure of a program, we will
instead use parsing techniques introduced in Chapter 4.

3.7 Using a Scanner Generator

Because a regular expression precisely describes all the allowable forms


of a token, we can use a program to automatically transform a set of reg-

26
3.7. USING A SCANNER GENERATOR 27

%{
(C Preamble Code)
%}
(Character Classes)
%%
(Regular Expression Rules)
%%
(Additional Code)

Figure 3.6: Structure of a Flex File

ular expressions into code for a scanner. Such a program is known as a


scanner generator. The program Lex, developed at AT&T, was one of the
earliest examples of a scanner generator. Vern Paxson translated Lex into
the C language to create Flex, which is distributed under the Berkeley li-
cense and is widely used in Unix-like operating systems today to generate
scanners implemented in C or C++.
To use Flex, we write a specification of the scanner that is a mixture of
regular expressions, fragments of C code, and some specialized directives.
The Flex program itself consumes the specification and produces regular
C code that can then be compiled in the normal way.
Figure 3.6 gives the overall structure of a Flex file. The first section con-
sists of arbitrary C code that will be placed at the beginning of scanner.c,
like include files, type definitions, and similar things. Typically, this is
used to include a file that contains the symbolic constants for tokens.
The second section declares character classes, which are symbolic short-
hands for commonly used regular expressions. For example, you might
declare DIGIT [0-9]. This class can be referred to later as {DIGIT}.
The third section is the most important part. It states a regular expres-
sion for each type of token that you wish to match, followed by a fragment
of C code that will be executed whenever the expression is matched. In the
simplest case, this code returns the type of the token, but it can also be used
to extract token values, display errors, or anything else appropriate.
The fourth section is arbitrary C code that will go at the end of the
scanner, typically for additional helper functions. A peculiar requirement
of Flex is that we must define a function yywrap() which returns 1 to
indicate that the input is complete at the end of the file. If we wanted to
continue scanning in another file, then yywrap() would open the next file
and return 0.
The regular expression language accepted by Flex is very similar to
that of formal regular expressions discussed above. The main difference is
that characters that have special meaning with a regular expression (like
parentheses, square brackets, and asterisks) must be escaped with a back-
slash or surrounded with double quotes. Also, a period (.) can be used to

27
28 CHAPTER 3. SCANNING

match any character at all, which is helpful for catching error conditions.
Figure 3.7 shows a simple but complete example to get you started.
This specification describes just a few tokens: a single character addition
(which must be escaped with a backslash), the while keyword, an iden-
tifier consisting of one or more letters, and a number consisting of one or
more digits. As is typical in a scanner, any other type of character is an
error, and returns an explicit token type for that purpose.
Flex generates the scanner code, but not a complete program, so you
must write a main function to go with it. Figure 3.8 shows a simple driver
program that uses this scanner. First, the main program must declare as
extern the symbols it expects to use in the generated scanner code: yyin
is the file from which text will be read, yylex is the function that imple-
ments the scanner, and the array yytext contains the actual text of each
token discovered. Finally, we must have a consistent definition of the to-
ken types across the parts of the program, so into token.h we put an
enumeration describing the new type token t. This file is included in
both scanner.flex and main.c.
Figure 3.10 shows how all the pieces come together. scanner.flex is
converted into scanner.c by invoking flex -o scanner.c
scanner.flex. Then, both main.c and scanner.c are compiled to
produce object files, which are linked together to produce the complete
program.

3.8 Practical Considerations

Handling keywords. In many languages, keywords (such as while or


if) would otherwise match the definitions of identifiers, unless specially
handled. There are several solutions to this problem. One is to enter a
regular expression for every single keyword into the Flex specification.
(These must precede the definition of identifiers, since Flex will accept the
first expression that matches.) Another is to maintain a single regular ex-
pression that matches all identifiers and keywords. The action associated
with that rule can compare the token text with a separate list of keywords
and return the appropriate type. Yet another approach is to treat all key-
words and identifiers as a single token type, and allow the problem to be
sorted out by the parser. (This is necessary in languages like PL/1, where
identifiers can have the same names as keywords, and are distinguished
by context.)
Tracking source locations. In later stages of the compiler, it is useful
for the parser or typechecker to know exactly what line and column num-
ber a token was located at, usually to print out a helpful error message.
(“Undefined symbol spider at line 153.”) This is easily done by having
the scanner match newline characters, and increase the line count (but not
return a token) each time one is found.
Cleaning tokens. Strings, characters, and similar token types need to

28
3.8. PRACTICAL CONSIDERATIONS 29

Contents of File: scanner.flex

%{
#include "token.h"
%}
DIGIT [0-9]
LETTER [a-zA-Z]
%%
(" "|\t|\n) /* skip whitespace */
\+ { return TOKEN_ADD; }
while { return TOKEN_WHILE; }
{LETTER}+ { return TOKEN_IDENT; }
{DIGIT}+ { return TOKEN_NUMBER; }
. { return TOKEN_ERROR; }
%%
int yywrap() { return 1; }

Figure 3.7: Example Flex Specification

Contents of File: main.c

#include "token.h"
#include <stdio.h>

extern FILE *yyin;


extern int yylex();
extern char *yytext;

int main() {
yyin = fopen("program.c","r");
if(!yyin) {
printf("could not open program.c!\n");
return 1;
}

while(1) {
token_t t = yylex();
if(t==TOKEN_EOF) break;
printf("token: %d text: %s\n",t,yytext);
}
}

Figure 3.8: Example Main Program

29
30 CHAPTER 3. SCANNING

Contents of File: token.h

typedef enum {
TOKEN_EOF=0,
TOKEN_WHILE,
TOKEN_ADD,
TOKEN_IDENT,
TOKEN_NUMBER,
TOKEN_ERROR
} token_t;

Figure 3.9: Example Token Enumeration

token.h

scanner.flex Flex scanner.c Compiler scanner.o

Linker scanner.exe

main.c main.o
Compiler

Figure 3.10: Build Procedure for a Flex Program

be cleaned up after they are matched. For example, "hello\n" needs to


have its quotes removed and the backslash-n sequence converted to a lit-
eral newline character. Internally, the compiler only cares about the actual
contents of the string. Typically, this is accomplished by writing a function
string clean in the postamble of the Flex specification. The function is
invoked by the matching rule before returning the desired token type.
Constraining tokens. Although regular expressions can match tokens
of arbitrary length, it does not follow that a compiler must be prepared to
accept them. There would be little point to accepting a 1000-letter iden-
tifier, or an integer larger than the machine’s word size. The typical ap-
proach is to set the maximum token length (YYLMAX in flex) to a very large
value, then examine the token to see if it exceeds a logical limit in the ac-
tion that matches the token. This allows you to emit an error message that
describes the offending token as needed.
Error Handling. The easiest approach to handling errors or invalid
input is simply to print a message and exit the program. However, this
is unhelpful to users of your compiler – if there are multiple errors, it’s
(usually) better to see them all at once. A good approach is to match the

30
3.9. EXERCISES 31

minimum amount of invalid text (using the dot rule) and return an explicit
token type indicating an error. The code that invokes the scanner can then
emit a suitable message, and then ask for the next token.

3.9 Exercises

1. Write regular expressions for the following entities. You may find it
necessary to justify what is and is not allowed within each expres-
sion:

(a) English days of the week: Monday, Tuesday, ...


(b) All integers where every three digits are separated by commas
for clarity, such as:
78
1,092
692,098,000
(c) Internet email addresses like:
"John Doe" <john.doe@gmail.com>
(d) HTTP Uniform Resource Locators (URLs)
as described by RFC-1738.

2. Write a regular expression for a string containing any number of X


and single pairs of < > and { } which may be nested but not inter-
leaved. For example these strings are allowed:
XXX<XX{X}XXX>X
X{X}X<X>X{X}X<X>X
But these are not allowed:
XXX<X<XX>>XX
XX<XX{XX>XX}XX

3. Test the regular expressions you wrote in the previous two problems
by translating them into your favorite programming language that
has native support for regular expressions. (Perl and Python are two
good choices.) Evaluate the correctness of your program by writing
test cases that should (and should not) match.

4. Convert these REs into NFAs using Thompson’s construction:

(a) for | [a-z]+ | [xb]?[0-9]+


(b) a ( bc*d | ed ) d*
(c) ( a*b | b*a | ba )*

5. Convert the NFAs in the previous problem into DFAs using the sub-
set construction method.

31
Other documents randomly have
different content
d'étoiles. Dans le nord vacillent des fusées d'aurore boréale toujours
changeantes et mobiles, jamais en repos, absolument comme l'âme
humaine. Et, sans y prendre garde, mes pensées reviennent toujours
à mes chers adorés… Je songe au retour; notre tâche est
maintenant accomplie, le Fram remonte à toute vitesse le fjord. La
terre aimée de la patrie nous sourit dans un gai soleil, et… les
souffrances poignantes, les longues angoisses sont oubliées dans un
moment d'inexprimable joie. Oh! non, c'est trop pénible! A grands
pas je me promène pour chasser cette hantise déprimante.
De plus en plus décourageant le résultat des observations. Nous
sommes aujourd'hui par 77°43′ et 138°8′ de Long. Est. Jamais
encore nous n'avions rétrogradé aussi loin. Depuis le 29 septembre
nous avons été repoussés de 83 milles vers le sud. Toute la théorie
dont la vérité me paraissait indiscutable, s'écroule comme un
château de cartes détruit par la plus légère brise. Imaginez les plus
ingénieuses hypothèses, bientôt les faits les auront réduits à néant.
Suis-je véritablement sincère en écrivant ces tristes réflexions? Oui,
sur le moment, car elles sont le résultat de l'amertume de mon
découragement. Après tout, si nous sommes dans une mauvaise
voie, à quoi cela aboutira-t-il? A la déception d'espérances humaines,
tout simplement. Et si nous périssons dans cette entreprise, quelle
influence cela aura-t-il sur les cycles infinis de l'éternité?
9 novembre.—Pris dans la journée une série de températures et
d'échantillons d'eau de 10 en 10 mètres, depuis la surface jusqu'au
fond, situé à une profondeur de 53 mètres. Partout la mer a une
température uniforme de −1°,5, la même température que j'ai
observée à une latitude plus méridionale. Il n'y a donc ici que de
l'eau originaire du bassin polaire. La salure est très faible. L'apport
des fleuves sibériens fait sentir son influence jusqu'ici.
11 novembre.—La «jeune glace» autour du navire atteint une
épaisseur de 0m,39. Dure à la surface, elle devient en dessous
poreuse et friable. Cette couche date de quinze jours. Dès la
première nuit, elle a atteint une épaisseur de 0m,078; les deux nuits
suivantes, elle a seulement augmenté de 0,052, et, pendant les
douze nuits suivantes, de 0,26. L'accroissement d'une couche de la
glace se ralentit donc à mesure que son épaisseur augmente, et
cesse même complètement lorsqu'elle a atteint une certaine hauteur.
19 novembre.—Toujours la même vie monotone. Depuis une
semaine, vent du sud; aujourd'hui, par exception, brise légère de
nord-nord-ouest. La banquise reste calme, hermétiquement fermée
autour du navire. Depuis la dernière pression violente, le Fram a
certainement sous sa quille une épaisseur de glace de 3 à 7
mètres [12] . A notre grande joie, l'observation d'hier constate un gain
de 44 milles vers le nord depuis le 8. Nous avons également fait un
pas considérable vers l'est. Que seulement la dérive nous porte dans
cette direction!
[ Plus tard nous creusâmes la glace jusqu'à une
12] profondeur de 10 mètres sans réussir à atteindre
l'eau.

Le Fram constitue au milieu de la banquise un abri chaud et


confortable. Même par un froid de 30° le poêle n'est pas allumé. Une
lampe suffit à rendre la température très agréable dans le carré. Mes
compagnons, du reste, ne s'aperçoivent pas du froid. Alors que le
thermomètre marque 30° sous zéro, Bentzen va en chemise lire sur
le pont les thermomètres. Presque nulle part trace d'humidité;
partout excellente ventilation, grâce à la manche à air qui répand
dans tout le navire des flots d'air froid et vivifiant.
27 novembre.—La température de l'air se maintient sans grande
variation entre −25° et −30°. Dans la cale du navire elle descend à
−11°.
A différentes reprises, les rayons de l'aurore boréale me semblent
prendre une orientation parallèle à la direction du vent. Dans la
matinée du 23, ce phénomène se montrant dans le sud-est,
j'annonce à mes compagnons que la brise qui, en ce moment,
souffle du nord-est, descendra au sud-est; quelques heures plus tard
cette prédiction se réalise.
Ce matin, à neuf heures, une forte pression; dans la soirée, la
glace gémit bruyamment aux environs. Le Fram ne se trouve plus,
semble-t-il, au centre des convulsions. Probablement le dernier
assaut violent a comprimé autour de nous toute la glace en une
masse très résistante que le froid a solidifiée, tandis que, plus loin, la
banquise, moins compacte, peut s'ouvrir et par suite être soumise à
des pressions.
3 décembre.—Dérive au nord-est, terriblement lente. Depuis le
28 novembre, nous avons avancé seulement de cinq milles.
5 décembre.—35°,7, la plus basse température éprouvée
jusqu'ici. Nous sommes par 78°50′, à 6 milles plus au nord que le 2;
la vitesse de dérive serait de 2 milles par jour.—Dans l'après-midi,
magnifique aurore boréale; de l'est à l'ouest, le ciel est illuminé par
une arcade flamboyante. Un peu plus tard, le temps devient couvert;
une seule étoile est visible, l'étoile du foyer. Comme je l'aime, ce
petit point lumineux! Chaque fois que je monte sur le pont, je la
cherche, cette étoile, et toujours elle est là brillante dans son
impassibilité radieuse. Elle me semble notre protectrice.
8 décembre.—De 7 à 8 heures du matin, encore une pression.
L'après-midi je dessinais dans le carré, lorsque subitement un choc
violent, suivi d'un craquement formidable, se fait entendre juste au-
dessus de ma tête, comme si de gros blocs de glace tombaient de la
mâture sur le pont. En un clin d'œil, tous les hommes sont debout;
les paresseux qui faisaient la sieste à ce moment passent en hâte un
vêtement et accourent dans le carré. Kvik, effrayé par la violence de
la détonation, a même quitté ses quartiers d'hiver. Qu'est-ce qui a
bien pu se passer? Impossible de découvrir la cause de ce fracas
épouvantable. La glace est en mouvement et paraît en train de
s'écarter du navire. Ce bruit a été probablement causé par une
pression inopinée qui a déterminé le décollement de la glace sur
toute la longueur du bâtiment. On n'entend aucun craquement dans
les œuvres du navire; le Fram n'a donc pas éprouvé d'avarie.
Dehors, il fait très froid, le mieux est de rentrer.
A six heures du soir, nouvelle pression d'une durée de vingt
minutes. La banquise grince et détone à l'arrière; dans le carré, le
bruit est tel que toute conversation devient impossible à moins de
hurler à tue-tête. Pendant ce sabbat, l'orgue fait entendre des
phrases de la mélodie de Kjerulf: «Le chant des rossignols
m'empêche de dormir.»
10 décembre.—Aujourd'hui, grand événement dans la vie
monotone du bord: apparition d'un journal, le Framsjaa, la Vigie du
Fram; directeur, notre excellent docteur. Le premier numéro, lu le soir
à haute voix dans le carré, excite une gaieté générale. Dans notre
situation, l'entrain est un remède préventif contre la maladie; par
son amusante initiative, Blessing contribue ainsi à fortifier notre
excellent état sanitaire.
13 décembre.—Depuis hier soir, sans une minute de repos, les
chiens aboient furieusement. A plusieurs reprises, les hommes de
garde ont cherché et exploré les environs; en dépit de leurs
recherches, impossible de découvrir la cause de cet émoi. Ce matin,
on constate la disparition de trois chiens. Après le déjeuner, Mogstad
et Peter vont examiner la neige autour du navire, espérant découvrir
les pistes des fugitifs. «Vous feriez bien de prendre un fusil,» leur dit
Jacobsen. «Oh! non, nous n'en avons pas besoin,» réplique Peter. En
bas de l'échelle, il y a pourtant des traces d'ours et de sang. Nos
deux gaillards ne s'acheminent pas moins sur la banquise, armés
seulement d'une lanterne et escortés par toute la meute. A quelques
centaines de pas du navire, surgit tout à coup de l'obscurité un ours
énorme. A cette vue, nos hommes prennent aussitôt leur galop vers
le bord. Mogstad, chaussé de légers mocassins, s'esquive
rapidement, mais Peter, empêtré dans ses lourdes bottes à semelle
en bois, n'avance que très lentement. Notre homme a beau faire
diligence, jamais il n'aperçoit le navire. Dans la confusion de la
retraite, le malheureux s'est trompé de route! Heureusement l'ours
ne le suit plus; le voilà donc tranquille, lorsque, à deux pas de là, le
pauvre Peter glisse et roule au milieu des hummocks. Enfin, il arrive
sur la glace plate qui entoure le navire; encore quelques pas, et il
sera en sûreté quand soudain quelque chose bouge tout près de lui.
Un chien, suppose-t-il; avant qu'il ait eu le temps d'élucider la
question, l'ours arrive sur lui et le mord au côté. Notre homme
empoigne alors sa lanterne et en assène un coup si violent sur le
museau de l'animal que le verre se brise bruyamment en mille
morceaux. La bête effrayée recule, et, profitant de son effarement,
l'ami Pierre a le temps de grimper lestement à bord. A la nouvelle de
cette attaque, nous sautons sur nos fusils; quelques minutes après,
l'assaillant tombait mort.
Après cet incident, nous partons à la recherche des bêtes
disparues, et découvrons bientôt leurs cadavres éventrés. Sans
éveiller notre attention, l'ours a pu grimper à bord par l'échelle,
enlever les chiens à sa portée et redescendre ensuite aussi
tranquillement qu'il était venu.
Kvik met au monde treize enfants, un précieux renfort pour la
meute réduite maintenant à un effectif de vingt-six bêtes. Elle ne
peut en nourrir que huit, il faut donc nous décider à noyer les
autres.
Position d'hier: 79°8′ Lat. N. Un gain de 8 milles en trois jours!

LE PIÈGE A OURS DE SVERDRUP

Depuis le début de notre dérive, pas une chute de neige ne s'est


produite. Noël approche pourtant, et il n'y a pas de vrai Noël sans
d'épais flocons. Oh! la belle chose que la neige silencieuse,
adoucissant de sa nappe virginale tous les contours brusques. Cette
banquise de glace vive est comme une vie sans amour; rien ne
l'adoucit. L'amour, c'est la neige de la vie. Il ferme les blessures
reçues dans le combat de l'existence et resplendit plus pure que la
neige. Qu'est-ce qu'une vie sans amour? Elle est pareille à ce champ
de glace, une chose froide et rugueuse errant à la dérive des vents,
sans rien pour couvrir les gouffres qui la déchirent, pour amortir le
choc des collisions et pour arrondir les angles saillants de ses blocs
brisés. Oui, une telle vie est semblable à cette glace flottante nue et
pleine d'aspérités.
21 décembre.—Le temps passe avec une rapidité extraordinaire.
Voici déjà le jour le plus court de l'année, si je puis m'exprimer ainsi,
puisque nous n'avons plus de jour. Maintenant nous irons vers le
retour du soleil et vers l'été. Aujourd'hui sondage; à 2,100 mètres,
pas de fond! Qui aurait pu s'attendre à trouver ici une pareille
profondeur?
22 décembre.—Dans la nuit nouvelle visite d'ours. L'animal se
dirige d'abord vers le navire, puis, apercevant le piège dressé par
Sverdrup et Lars, s'achemine immédiatement vers l'instrument. A
cette vue, le cœur bat à notre capitaine; d'une minute à l'autre il
s'attend à entendre le bruit produit par le déclenchement de
l'appareil. Mais maître Martin est très prudent; il examine
soigneusement la machine, et, se levant sur les pattes de derrière,
s'appuie juste à côté de la trappe pour contempler un instant le
délicieux morceau de graisse qui constitue l'appât; après un moment
d'hésitation, il redescend à terre. Évidemment cette grande chose
plantée là, au milieu de la glace, ne lui dit rien qui vaille. Il flaire le
support, tourne tout autour, et, après avoir de nouveau contemplé le
piège, s'en va en hochant la tête. Il semble dire: «Ces mauvais gars
ont fort bien arrangé la chose à mon intention, mais je ne suis pas si
bête pour m'y laisser prendre.» Décidément, malgré toute
l'ingéniosité de Sverdrup, le fusil est encore plus sûr. Arrivé à
soixante pas du navire, l'ours, reçu par une salve nourrie, tombe
mort. Une seule balle l'avait frappée; comme d'habitude en pareil
cas, chacun des quatre tireurs s'attribua l'honneur du coup.
CARICATURES EXTRAITES DU «FRAMSJAA»

Promenade en temps de paix avec les chaussures patentées


de Sverdrup.

Les compagnons du Fram sur le sentier de la guerre:


différence entre la chaussure Sverdrup et le mocassin lapon.

Les compagnons du Fram sont encore sur le sentier de la


guerre.

24 décembre.—Un radieux clair de lune illumine la silencieuse


nuit arctique… A l'approche du grand jour de la Noël, notre petit
monde est de plus en plus gai. Chacun songe évidemment aux
absents, mais personne ne laisse deviner ses soucis.—Le carré et les
cabines sont brillamment illuminés et le menu du repas
particulièrement soigné. Faire bombance, c'est pour nous la seule
manière de fêter les solennités. Le dîner est excellent et le souper
non moins exquis. Après cela on sert les gâteaux traditionnels,
auxquels Juell travaille depuis des semaines. Le «clou» de la fête est
l'arrivée de deux boîtes contenant les cadeaux de Noël, présents de
la mère et de la fiancée de Hansen. C'est avec une véritable joie
d'enfant que chacun reçoit son petit souvenir: une pipe, un couteau
ou une autre bagatelle de ce genre. Il semble que ces caisses soient
un message de tous les chers absents. Après cela, une série de
toasts et de discours, puis lecture d'un nouveau numéro du Framsjaa
accompagné d'un supplément illustré dû au crayon du célèbre artiste
polaire Huttetu. Les gravures reproduites à la page précédente,
représentant les aventures de Peter avec son ours, donnent une idée
de ce talent jusqu'ici méconnu.
25 décembre.—Là-bas, au pays, très certainement ils songent
aujourd'hui à nous et s'attristent à la pensée des souffrances que
nous devons endurer, supposent-ils, au milieu du grand désert glacé
de l'Océan Arctique. Que ne peuvent-ils nous voir gais et bien
portants! A coup sûr notre vie n'est pas plus pénible que la leur.
Jamais je n'ai mené une existence aussi douce et jamais je n'ai
autant redouté l'embonpoint. Voyez, par exemple, le menu du dîner.
Pas moins de cinq plats. Une soupe à l'oxtail, un pudding de poisson,
un rôti de renne avec des petits pois, des pommes de terre, de la
confiture d'airelle, de la confiture de baies de marais [13] avec de la
crème et des galettes. Tout le monde fait si bien honneur au repas
que personne n'a faim au souper. Dans la soirée on sert le café avec
accompagnement d'ananas, de macarons, de gâteaux au gingembre
et de mendiants. Pour vous donner une idée de notre ordinaire,
n'oublions pas le déjeuner composé de café, de pain frais, de
langue, de corned beef, de fromage et de marmelade. A l'exception
des gâteaux, notre menu quotidien n'est pas différent. Avec cela,
nous habitons une bonne et solide maison, bien éclairée par de
grandes lampes à pétrole ou par l'électricité; nous avons toute
espèce de jeux pour nous distraire et toute une bibliothèque pour
nous instruire. Que peut-on demander de plus?
[ Baie de marais ou ronce faux mûrier (Rubus
13] Chamæmorus).

26 décembre.—Aujourd'hui et hier −38°, la plus basse


température observée depuis le commencement de l'hivernage.
Dans la journée en me promenant sur la banquise, j'arrive sur le
bord d'un grand lac, couvert de «jeune glace» coupée par une large
crevasse. Les rayons de la lune jouent sur la surface noire de l'eau et
cette vue me rappelle soudain les scènes du pays des fjords. A perte
de vue, du haut d'un monticule de glace, la nappe d'eau bleue
s'étend dans la direction du nord.
LES RAYONS DE LA LUNE JOUENT SUR LA SURFACE DE L'EAU

28 décembre.—En avant du Fram, dans une direction


perpendiculaire à celle de son gisement, s'est ouvert un chenal; la
glace formée à sa surface la nuit dernière porte des traces de
pression. Nous ne prêtons pas la moindre attention à tous ces
mouvements de la banquise qui ont causé tant d'émois à nos
prédécesseurs. Aucun préparatif n'a été fait à bord en vue d'un
accident. Nous n'avons sur le pont ni vivres, ni tente, ni équipement
prêts à être débarqués. Et ce n'est pas par négligence; mais nous
n'avons pas lieu de craindre les convulsions de la glace. Nous avons
pu apprécier la résistance de notre bâtiment, et notre confiance en
lui est absolue. Contre sa coque inébranlable, les blocs les plus durs
viennent s'aplatir et perdre leur force d'impulsion.
De l'avis de tous les explorateurs, la longue nuit de l'hiver
arctique exercerait l'influence la plus pernicieuse sur l'organisme et
déterminerait fatalement l'éclosion du scorbut parmi les équipages.
Un marin anglais avec lequel je m'entretins de cette question avant
mon départ fut particulièrement pessimiste. «Non, jamais, assurait-il,
une expédition polaire ne pourrait échapper au scorbut; c'était là un
mal inévitable; tous les chefs de mission qui prétendaient en avoir
été indemnes, avaient simplement donné un autre nom à la terrible
maladie.» Maintenant, je suis en mesure de réfuter cette opinion par
notre expérience. La nuit polaire n'a eu aucune influence débilitante
ou déprimante sur moi; tout au contraire, pendant cet hivernage, j'ai
l'impression de rajeunir. Cette vie régulière me convient
parfaitement; jamais je ne me souviens avoir été en meilleure santé.
Bien plus, je recommanderai les régions arctiques comme un
excellent sanatorium pour les personnes affaiblies ou atteintes
d'affections nerveuses.
J'en viens même à avoir honte de nous; ces terribles souffrances
de la longue nuit de l'hiver polaire, décrites en termes si dramatiques
par nos prédécesseurs, nous n'en éprouvons aucune. Elles sont
pourtant bien nécessaires pour donner de l'intérêt à une relation
d'expédition arctique! Si cela continue ainsi, qu'aurons-nous à
raconter au retour? Tous mes compagnons sont également gros et
gras; aucun d'eux n'a la mine pâle et les joues caves traditionnelles
des hiverneurs polaires, et chez eux pas trace d'abattement. Écoutez
seulement dans le carré l'animation des conversations et les éclats
de rire. Cet excellent état sanitaire et moral, nous le devons à la
qualité et à la variété de notre ordinaire, à la bonne ventilation du
navire, à nos fréquentes promenades en plein air, à l'absence de tout
surmenage physique, enfin aux quotidiennes distractions que nous
apportent la lecture et les jeux. Notre système de vie en commun,
sans aucune inégalité de traitement pour les divers membres de
l'expédition, a également exercé la plus heureuse influence.

LA LECTURE DE L'ANÉMOMÈTRE

Plusieurs de mes camarades se plaignent d'insomnie. Le manque


de sommeil est aussi, dit-on, une conséquence inévitable de
l'obscurité de l'hiver arctique. Pour mon compte je n'en ai jamais
souffert; je ne fais pas, il est vrai, la sieste dans l'après-midi, comme
la plupart de mes compagnons. Après avoir dormi plusieurs heures
dans la journée, mes camarades ne pouvaient s'attendre à ronfler
ensuite toute la nuit. L'homme ne peut toujours dormir, disait
justement Sverdrup.
31 décembre.—Voici le dernier jour de l'année. Une longue
année, qui nous a apporté et beaucoup de bien, et beaucoup de
mal! Elle a débuté par le bien, par la naissance de la petite Liv, un
bonheur si étrange que d'abord j'y pouvais à peine croire. Ensuite
est venue l'heure triste du départ. Nulle année n'a apporté une peine
plus lourde que celle-là. Depuis, ma vie n'est qu'une longue attente.
Comme l'a dit le poète: «Veux-tu ignorer les peines et les soucis,
n'aime jamais.»
L'attente! il y a des maux pires!
Vieille année, tu m'as apporté la déception; tu ne m'as pas
conduit aussi loin vers le nord que je l'avais espéré. Après tout, tu
aurais pu me traiter encore plus mal. Mes calculs ne se sont-ils pas
réalisés en partie? Le Fram n'a-t-il pas été poussé dans la direction
désirée? Une seule chose me contrarie; la multiplicité des zigzags de
la dérive.
Une nuit magnifique termine l'année. Au-dessus de la grande
étendue blanche, le ciel d'une incomparable pureté n'est qu'un
scintillement d'étoiles, illuminé par le flamboiement silencieux de
l'aurore boréale, et sur ce fond de paillettes brillantes, le Fram
détache en vigueur sa masse noire argentée de givre.
Tout naturellement, grande réjouissance dans la soirée. A minuit,
j'adresse à mes compagnons une courte allocution de circonstance,
les remerciant de leur bon esprit de camaraderie et de leur
confiance. Ensuite chants et lecture de poésies.
3 janvier 1894.—La température varie entre −39° et −40°!!! Par
un pareil froid, la lecture des instruments de météorologie n'est pas
précisément agréable, surtout celle des thermomètres à maximum et
à minimum placés dans le «nid de corbeau». Plus pénibles encore
sont les observations astronomiques exécutées tous les deux jours.
Pour manier les petites vis très délicates des instruments,
naturellement Hansen et son aide doivent être dégantés, d'où de
fréquentes congélations aux mains. Souvent le froid est tellement
pénétrant que les observateurs doivent interrompre leur travail pour
battre la semelle et pour se frapper les bras. Et cependant, jamais ils
ne veulent avouer leurs souffrances. «Hansen, il ne fait pas chaud
là-haut, lui demandons-nous, lorsqu'il rentre au carré.—Ma foi non,
cependant la température est, je vous assure, très supportable.—
Soit, mais vous avez les pieds gelés.—Non, en vérité, je ne puis le
dire, j'ai seulement un peu froid aux doigts.» En effet… deux de ses
doigts sont «mordus», et il s'obstine à refuser les gants en peau de
loup que je lui offre. Aujourd'hui, le temps est trop doux pour une
telle précaution, affirme-t-il.
Un jour, par 40° sous zéro, Hansen monta sur le pont en chemise
et en caleçon pour une lecture d'instrument. Et des explorateurs ont
affirmé l'impossibilité d'exécuter des observations par de pareilles
températures!
4 janvier.—L'aube me semble plus claire, mais, peut-être est-ce
par un effet de mon imagination? Je suis de très belle humeur, bien
que nous dérivions encore vers le sud. Après tout, qu'importe? Peut-
être dans cette direction notre expédition ne sera-t-elle pas moins
fructueuse pour la science? En attendant, je connais maintenant la
nature du bassin polaire. La mer profonde à travers laquelle nous
dérivons est un prolongement des grandes fosses atlantiques. Mes
prévisions se trouveraient vérifiées complètement, si seulement nous
avions un vent favorable. Bien d'autres, avant nous, n'ont-ils pas
attendu une bonne brise! Au fond, ce désir d'atteindre le pôle est
une suggestion du démon de la vanité.
La vanité? n'est-ce pas une maladie d'enfant qui devient plus
aiguë avec les années et qui pourtant devrait disparaître?
Tous mes calculs, à l'exception d'un seul, se sont trouvés justes.
Nous avons suivi notre route le long de la côte de Sibérie, en dépit
de toutes les prédictions défavorables; nous sommes parvenus au
nord plus loin que je n'avais osé l'espérer et juste à la longitude que
je souhaitais atteindre; comme je le désirais, nous avons été pris
dans les glaces, et le Fram a supporté sans la moindre avarie toutes
les pressions, alors que les explorateurs les plus expérimentés
avaient affirmé sa perte certaine. Enfin, notre hivernage sur cette
banquise en dérive est bien moins pénible que celui des précédentes
expéditions. Notre vie ressemble à celle que nous mènerions en
Norvège. Tous réunis dans une même pièce, nous formons comme
un petit coin de la patrie.
Le seul point sur lequel mes calculs se sont trouvés en défaut
est, je ne puis le cacher, d'une très haute importance. Le plus grand
fond rencontré par la Jeannette n'était que de 164 mètres; je croyais
donc l'Océan polaire peu profond et supposais par suite l'action des
courants très sensible dans cette mer et l'apport des fleuves
sibériens capable de repousser la banquise très loin vers le nord.
Aussi, grand a été mon étonnement de trouver dans cet Océan des
abîmes atteignant 1,800 mètres au moins et peut-être même le
double. Au milieu d'une pareille masse d'eau un courant, s'il existe,
doit être très faible. Mon seul espoir maintenant est dans les vents.
Christophe Colomb a découvert l'Amérique par suite d'un faux calcul,
dont il n'était pas d'ailleurs responsable. Seul, le ciel sait où nous
conduira mon erreur. Mais, en vérité, je le dis: le bois flotté de
provenance sibérienne qui se rencontre sur les côtes du Grönland ne
peut mentir; nous devons donc suivre le même chemin que lui.
8 janvier.—La petite Liv a aujourd'hui un an. A la maison c'est
grande fête. Que ne donnerai-je pour te voir aujourd'hui, cher petit
être? Tu m'as sans doute oublié depuis longtemps et tu ne sais plus
ce que c'est qu'un père? Tu le sauras un jour de nouveau.
Dans l'après-midi, Vénus apparaît pour la première fois au-dessus
de l'horizon. Entourée d'une auréole rouge, elle éclaire le grand
désert glacé comme un phare puissant… C'est l'étoile de Liv, comme
Jupiter est l'étoile du foyer. Un pareil jour ne peut nous apporter que
joie et bonheur. En effet, nous dérivons vers le nord; nous sommes
certainement au delà du 79°.
15 janvier.—Un bon pas vers le nord. Hier nous étions par 79°19′
et 137°31′ Long. E.—Dans la journée je fais une longue excursion à
pied. La glace est unie, excellente pour le traînage; à mesure que
j'avance, elle devient de plus en plus plane. Plus j'examine cette
banquise et plus mûrit dans ma tête un projet auquel j'ai depuis
longtemps déjà souvent réfléchi. Sur une telle glace il serait possible
d'atteindre le pôle avec des traîneaux et des chiens, en laissant le
navire poursuivre sa route vers la Terre François-Joseph, le Spitzberg
ou le Grönland. Ce serait une entreprise facile pour deux hommes…
En tous cas, il serait prématuré de partir au printemps prochain. Je
dois d'abord connaître les résultats de la dérive pendant l'été. En
second lieu est-il juste d'abandonner les autres? Si je réussissais à
revenir en Norvège et que mes compagnons périssent avec le Fram!
Mais, d'autre part, n'est-ce pas pour explorer le bassin polaire que
l'expédition est partie, et n'est-ce pas dans ce but que le peuple
norvégien a libéralement donné son argent? Mon devoir est de faire
tous les efforts possibles pour arriver au but… Pour le moment il faut
attendre les événements.
Jeudi 18 janvier.—Vent de S.-S.-E., de S.-E., et d'E.-S.-E., Vitesse
de 5 à 6 mètres par seconde. Ces grandes brises déterminent
presque toujours une hausse du thermomètre; aujourd'hui il monte
à −25°. Moins violents, les vents du sud produisent un
refroidissement de l'air, tandis que ceux de la partie nord, lorsqu'ils
sont faibles, amènent une élévation de température. Payer attribue
l'échauffement des couches d'air observées, par les brises fraîches, à
leur passage au-dessus de nappes d'eau libre. Cette explication ne
me semble pas exacte, surtout dans cette région où il existe peu ou
point d'ouvertures dans la banquise. A mon avis, cette hausse de
température serait déterminée par l'arrivée, à la surface de la terre,
de nappes d'air provenant des hautes régions de l'atmosphère. L'air
des régions supérieures doit, en effet, avoir une température plus
élevée que celle des nappes ambiantes à notre globe, refroidies par
la radiation des neiges et des glaces. En second lieu, en descendant,
l'air subit un échauffement en raison de l'augmentation de pression
qu'il éprouve.
23 janvier.—Ce matin, lorsque je monte sur le pont, Caïaphas
aboie furieusement dans la direction de l'est. Il doit y avoir quelque
animal de ce côté. Muni seulement d'un revolver, je pars à la
découverte, accompagné de Sverdrup. Aussitôt le chien file devant
nous, toujours en donnant de la voix. J'examine soigneusement les
environs; impossible de rien distinguer. Caïaphas aboie toujours et
pointe les oreilles. D'une seconde à l'autre je m'attends à voir surgir
un ours. Nous voici sur le bord de l'ouverture voisine du navire;
notre chien avance lentement et avec précaution, puis s'arrête en
grognant sourdement. Évidemment nous approchons du gibier. Je
grimpe sur un hummock, et devant moi j'aperçois quelque chose de
sombre qui semble remuer. «Un chien noir, dis-je à Sverdrup.—Mais
non, répond-il, c'est un ours.» Ce que j'ai pris tout d'abord pour un
chien est seulement la tête de la bête; sa démarche est bien celle de
l'ours, mais cet ours blanc est terriblement noir. Je m'avance vers lui,
le revolver à la main, prêt à lui envoyer mes six balles dans le
museau, lorsque je vois l'animal se lever, et du coup je reconnais un
morse. L'énorme bête se jette aussitôt à l'eau et plonge, puis après
être revenue à la surface et s'être ébrouée, reste à nous regarder.
Inutile d'envoyer des balles de revolver à un pareil monstre; autant
essayer de prendre une oie sauvage en lui déposant le fameux grain
de sel sur la queue. Quel dommage que nous n'ayons pas un
harpon! Nous revenons en toute hâte à bord chercher les armes
nécessaires; le temps de les préparer, le gibier a disparu. Jamais
auparavant, que je sache, on n'avait rencontré un morse sur la
banquise en pleine mer.
Bonne dérive vers le nord. 79°41′ Lat. N. 135°29′ Long. Est.
25 janvier.—En me promenant j'atteins la fin de l'ouverture située
à l'est du Fram; sa longueur n'est pas moindre de 11 kilomètres. Au
retour de cette excursion, la banquise commence à s'agiter. La jeune
glace qui couvre le chenal se brise sous mes pas et s'amoncelle en
deux hautes murailles avec des bruits étranges. Tantôt on croit
entendre un gémissement de chien, tantôt un fracas de puissante
chute d'eau. A différentes reprises le passage m'est fermé, soit par
la brusque ouverture d'une nappe d'eau, soit par le soulèvement
d'un monticule de blocs. La partie de la banquise où est enfermé le
Fram, située au sud de nous, paraît être poussée vers l'est, à moins
que ce ne soit la portion du pack sur laquelle nous nous trouvons qui
dérive dans l'ouest.
27 janvier.—Le jour augmente sensiblement. A midi on peut lire
les caractères d'un journal. Le soir, pendant deux heures, très
violentes pressions. Les glaces craquent et se brisent dans des
heurts terribles, et leurs débris s'empilent en hautes murailles le long
des rives du lac. On entend venir le grondement… il approche de
plus en plus… le navire éprouve des chocs violents; il semble qu'il
soit soulevé par des vagues de glace arrivant par l'arrière. Les
hummocks à tribord grincent, le bruit devient assourdissant. Une
accalmie se produit et je regagne le carré. A peine me suis-je remis
au travail que les pressions reprennent de plus en plus violentes.
A bâbord le vieil hummock est lentement soulevé, tandis que se
déchire la grande flaque située dans son voisinage. Le fracas et la
violence des chocs augmentent de minute en minute; le navire
frémit, et cela dure ainsi jusqu'à dix heures et demie. A minuit moins
un quart, nouvelle attaque de la glace, plus faible; puis, tout rentre
dans le calme. L'assaut a été particulièrement violent à l'arrière. Un
monticule formé de blocs empilés dépasse six mètres [14] ; des
glaçons épais de trois mètres environ ont été brisés et entassés les
uns au-dessus des autres.
[ Il reçut le nom de Grand Hummock et suivit le
14] Fram pendant toute sa dérive.

La lune est à son dernier quartier; la production de cette forte


pression à cette époque ne concorde donc pas avec nos
observations antérieures. Peut-être est-elle due au voisinage d'une
terre.
30 janvier.—Depuis avant-hier, calme plat, néanmoins légère
dérive au sud-est. Lorsque le vent a, pendant quelque temps, soufflé
d'un point du compas, la banquise éprouve une compression dans
cette direction, puis, dès que la brise tombe, subit une détente et
s'étend en sens contraire. A cette réaction sont dus, je crois, le recul
d'un mille constaté depuis le 27, et l'attaque de ce jour-là. Depuis
cette date, la glace est calme. Les pressions se produisent
probablement lors du changement de direction de la dérive.
2 février.—82°10′ Lat. N. et 132°10′ Long. Est. En l'honneur du
passage du 80°, grande fête à bord.
6 février.—Le thermomètre oscille entre −47° et −48°. Dans le
salon il s'élève à +22°. Lorsque l'on sort, la différence de
température est donc de 69 à 70°. Néanmoins, fût-on même
légèrement vêtu et tête nue, on n'éprouve pas une impression de
froid.
L'air est calme et remarquablement clair. L'horizon, dans le sud,
resplendit d'une lueur jaune très intense passant au vert et au bleu.
Le ciel d'Italie n'est pas d'un bleu plus intense. Cette puissante
coloration se produit toujours par les grands froids. Le lendemain, le
thermomètre descend à −49°,6.
Depuis le mois dernier, tous les membres de notre petite colonie
ont augmenté de poids; pour quelques-uns, l'accroissement atteint
deux kilogrammes. Sverdrup, Blessing et Juell tiennent le «record»
avec 86kg,2.
15 février.—Longue excursion en traîneau. Sur la glace unie,
quatre chiens peuvent traîner deux hommes. J'étudie l'importante
question de la marche sur la banquise en vue des plans d'avenir.
Combien exagérées sont les craintes qu'inspirent les basses
températures arctiques! Certainement il ne fait pas chaud par −40°
et −42°; mais un tel froid ne cause aucune souffrance. Hier, dans
une promenade sur les ski, j'étais vêtu d'une chemise ordinaire et de
deux blouses en peau; aux jambes, caleçon, pantalon, jambières en
drap, et je suais à grosses gouttes.
Aujourd'hui, pour la promenade en traîneau, je porte une
chemise de flanelle, un gilet, un jersey en laine, une veste en
vadmel et une blouse en peau de phoque. Avec cet accoutrement, la
température me semble très agréable; comme hier, je transpire à
plusieurs reprises. Sur la figure, je porte un masque de flanelle, mais
cet appareil me tient beaucoup trop chaud; je ne le mets que
lorsqu'une brise très fraîche me souffle dans le nez.
16 février.—Après une dérive dans le sud, les jours précédents,
nous voici de nouveau au nord, au 80°1′; pourtant, depuis le 12, le
vent a toujours soufflé du nord.

IMAGE RÉFRACTÉE DU SOLEIL

A midi, grand émoi! Après une absence de cent douze jours, le


soleil, ou du moins son image réfractée, apparaît à l'horizon. Un long
trait de feu brille d'abord, puis deux autres superposés et séparés
par un intervalle sombre. Du haut de la mâture j'aperçois quatre,
puis cinq raies horizontales, toutes d'égale longueur. L'ensemble
forme un soleil rectangulaire, d'un rouge pâle, traversé de bandes
horizontales sombres. A midi, d'après une observation, l'astre se
trouvait encore à 2°22′ au-dessous de l'horizon. Le 20 février
seulement, le soleil devait se trouver au-dessus de l'horizon. Cet
événement fut, bien entendu, l'occasion d'une fête.
22 février.—Depuis trois jours, vent de sud; cependant nous ne
sommes qu'au 80°11′. En septembre, nous étions par 79°; depuis,
nous n'avons guère gagné plus d'un degré. A cette vitesse, il nous
faudra encore quarante-cinq mois pour atteindre le Pôle, quatre-
vingts ou cent mois pour regagner, de l'autre côté, le 80° de Lat.,
ensuite un ou deux mois pour revenir en Norvège. En admettant que
la dérive se poursuive toujours dans les mêmes conditions de
vitesse, nous ne reviendrons que dans huit ans!!!
Avant mon départ, lorsque je plantais de petits arbustes et de
jeunes arbres dans le jardin pour les générations futures, Brogger
écrivait avec juste raison: «Personne ne peut savoir la longueur de
leur ombre lorsqu'il sera de retour.» Ils sont maintenant sous la
neige; mais au printemps ils recommenceront à bourgeonner et à
grandir. Combien de fois avant mon retour? Pourvu que leurs ombres
ne soient pas trop longues!

STRATIFICATION DE LA GLACE

Cette inactivité est absolument énervante; j'éprouve un


impérieux besoin d'exercice violent. Qu'un ouragan n'arrive-t-il et ne
secoue-t-il cette banquise en hautes vagues! Qu'au moins nous
puissions lutter et faire quelque chose! Cette inaction est bien la vie
la plus misérable. Pour se laisser ainsi conduire vers le but par les
forces aveugles de la nature sans jamais pouvoir intervenir, il faut à
coup sûr dix fois plus d'énergie que pour le combat.

STRATIFICATION DE LA GLACE
Le 19, forages dans la glace. A bâbord, son épaisseur est de
1m,875 et à l'avant de 2m,08; elle n'est donc pas très grande, si l'on
songe qu'elle est «vieille» d'un mois, et que pendant ce mois la
température est descendue à −50°. La plaque sur laquelle se trouve
installé le piège à ours atteint une profondeur de 3m,45; de plus,
quelques glaçons adhèrent à sa face immergée. Elle présente une
sorte de stratification rappelant celle d'un glacier, rendue apparente
par des dépôts de matières noires colorées d'organismes rougeâtres,
qui se trouvent à la surface de chaque couche. En différents
endroits, les strates sont plissées et même brisées comme dans une
coupe géologique; plissements et fractures proviennent évidemment
des pressions exercées latéralement dans les chocs des glaçons.
Cette disposition était particulièrement frappante près d'un grand
toross formé par la dernière convulsion de la banquise. (Voir les
figures précédentes.) La plaque, épaisse de plus de 3 mètres, avait
été plissée sans se briser, notamment au pied du monticule
amoncelé à sa surface. Sous le poids de cette surcharge, la surface
du glaçon était, en certains endroits, descendue jusqu'au niveau de
la mer, tandis qu'ailleurs, pressé par des blocs qui avaient été
poussés sous elle, cette flaque s'élevait à 0m,50 au-dessus de l'eau.
En dépit du froid, cette glace est donc très plastique. A cette
époque, la température de la banquise, à une très petite profondeur,
devait varier de −30° à −20°.
4 mars.—Toujours les mêmes alternatives de progrès et de recul.
Le 24 février, après vingt-quatre heures seulement de vent de sud,
nous sommes repoussés au 79°54′; nous dérivons ensuite dans l'est,
puis au nord-est. Le 27, nous atteignons le 80° 10′; maintenant nous
sommes de nouveau repoussés par un vent de sud-est.
Hier et aujourd'hui, le thermomètre descend à −37° et à −38°.
Actuellement, le vent du nord détermine un abaissement de
température, et celui du sud une hausse du thermomètre. Au
commencement de l'hiver, c'était le contraire.
12 mars.—Toujours en dérive vers le sud. Je commence à être
découragé. N'en ai-je pas le droit? L'une après l'autre, toutes mes
espérances s'évanouissent. Et pendant ce temps, indifférente à tous
nos sentiments, la nature poursuit impassible son cycle.
Temps très froid; le 8 au soir, le thermomètre descend à −48°,5,
le 11 à −50°, et dans la soirée à −51°,2. Néanmoins, chaque jour
nous faisons des excursions. Quoique nous ne soyons pas plus
couverts que d'habitude [15] , nous ne sommes nullement
incommodés par cette basse température. Tout au contraire, elle
nous semble très agréable. Nous nous sentons seulement froid au
ventre et aux jambes; mais il suffit de battre la semelle pour se
réchauffer. Très certainement on pourrait supporter une température
encore plus basse, de 10°, 20° et même 30°. Les sensations
éprouvent des modifications très curieuses. En Norvège, j'ose à
peine mettre le nez dehors par une température de −20°, alors
même que l'air est calme; ici, par un froid de −50° et avec du vent,
je n'hésite pas à sortir.
[ Les uns étaient vêtus d'une chemise et d'une
15] peau de loup, les autres d'une jaquette de laine et
d'une blouse légère en peau de phoque.

13 mars.—Nouvelle visite d'un morse. Les chiens l'aperçoivent du


pont du navire, à une distance d'au moins 1,000 mètres, bien qu'il
ne fasse pas très clair. Ces animaux ont une vue extraordinairement
perçante.
16 mars.—Essai de marche à la voile avec les traîneaux.
L'expérience réussit parfaitement. Une légère brise suffit à pousser
rapidement les véhicules.
21 mars.—Enfin! Vent de sud-est et dérive vers le nord.
L'équinoxe de printemps est passé, et nous sommes à la même
latitude qu'en automne. Où serons-nous en septembre prochain? Si
nous nous trouvons plus au sud, la victoire sera incertaine; si, au
contraire, nous avons avancé vers le nord, la bataille est gagnée;
mais cela sera peut-être long. Je place maintenant mes espérances
dans l'été. La large étendue d'eau libre, qui, en septembre,
s'étendait jusqu'au 70°, n'avait certainement pas été produite par la
fusion de la banquise, et avait été formée par les vents et les
courants. Pour qu'elle se reforme l'été prochain, la glace devra donc
être repoussée vers le nord, et, par suite, nous entraînera dans la
direction dérivée.
26 mars.—Le 23, nous sommes de nouveau au 80°. En quatre
jours, nous avons regagné le terrain perdu en trois semaines. Le
thermomètre moral remonte; cette hausse est de courte durée; le
26, la dérive s'arrête.
Le soleil monte et illumine de sa joyeuse clarté le grand désert
glacé. Le printemps arrive, mais il n'apporte guère la joie. Il est triste
et froid. Sept ans d'une pareille vie, mettons même quatre, après
une telle épreuve, dans quel état moral serons-nous? Et elle? Je
n'ose y penser.
Cette inaction et cette monotonie brisent tous les ressorts de
l'homme. Pas la moindre lutte! Tout est calme et mort, enseveli sous
une carapace de glace! Cela fait passer des frissons jusque dans
l'âme. Que ne donnerai-je pas pour batailler au jour contre les
éléments, pour être seulement exposé à un danger quelconque?… Il
faut s'armer de patience et attendre le résultat de la lente dérive.
Suit-elle une mauvaise direction, je romprai alors tous les ponts et
nous partirons vers le nord à pied à travers la banquise; j'y suis bien
résolu. Il n'y a point d'autre parti à prendre. Ce sera une entreprise
bien téméraire, la lutte pour la vie ou pour la mort. Je n'ai pas à
choisir. Il est indigne d'un homme d'assumer une tâche, puis de
l'abandonner une fois qu'elle est commencée. Une seule direction
nous est ouverte; celle du nord. En avant [16] !
[ Le navire du Dr Nansen portait le nom d'En Avant
16] (en norvégien Fram).

Mes yeux s'arrêtent sur le tableau d'Eilif Pettersen suspendu dans


le carré: Une forêt de sapins en Norvège; et j'ai l'impression de me
retrouver au milieu de ces bois aimés. Solennelles forêts, vous avez
été les confidentes de mon enfance. Au milieu de vous, j'ai appris à
sentir les grandes impressions de la nature, sa sauvage majesté et
sa mélancolie. Pour la vie vous avez donné à mon âme une
impression indélébile… Seul, au milieu des grands bois, assis devant
un feu, sur les bords d'une mare solitaire, sous le ciel étoilé,
combien j'étais heureux dans cette magnifique harmonie de la
nature!
A bord, tout le monde est très affairé. On coupe des voiles pour
les canots, pour les traîneaux, pour le moulin; on forge des
couteaux, des épieux pour les ours; on fabrique des chaussures à
semelles de bois et des clous. Le docteur, toujours en vacances faute
de malades, s'établit relieur, tandis qu'avec l'aide d'Amundsen je
refais les cartons de musique usés par l'humidité. Je les découpe
dans des feuilles de zinc; l'essai donne d'excellents résultats, et
maintenant, en avant la manivelle! «Des flots d'harmonie sacrée et
profane» remplissent le navire; les valses ont surtout du succès.
Cette musique entraînante donne comme un regain de vie aux
habitants du Fram.

OBSERVATION D'UNE ÉCLIPSE DE SOLEIL


6 avril.—Aujourd'hui, grand événement. Une éclipse de soleil doit
se produire. D'après les calculs de Hansen, elle aura lieu à midi
cinquante-six minutes. Il s'agit de prendre une bonne observation
afin de contrôler la marche de nos chronomètres. A l'avance, la
grande lunette et le théodolite sont disposés sur la glace, et,
pendant deux heures Hansen, Johansen et moi, nous nous relayons
de cinq en cinq minutes aux instruments. Enfin, le moment décisif
approche. Hansen, installé à la grande lunette, surveille le soleil,
tandis que Johansen observe le chronomètre. Une ombre paraît sur
le bord de l'astre. Top! crie notre astronome, Top! répond Johansen.
Le chronomètre marque exactement 12 h. 56′7″,5; seulement sept
secondes cinq dixièmes plus tard que Hansen ne l'avait calculé, un
résultat excellent qui prouve la marche régulière de nos instruments.
7 avril.—Dans la matinée, je suis tout à coup tiré de ma rêverie
par un bruit de pas précipités sur la dunette. Évidemment des
hommes courent; un ours s'est sans doute montré aux alentours.
N'entendant le bruit d'aucune décharge, je retombe dans mes
pensées, lorsque j'entends tout à coup la voix de Johansen. Mogstad
et lui ont tué deux ours, du moins ils le croient, et reviennent
chercher des cartouches. Tout le monde monte alors sur le pont.
Immédiatement je m'habille, chausse mes ski, et bientôt rencontre
la bande des chasseurs revenant bredouille. Les ours, soi-disant
morts sur le coup, se sont relevés et sont loin. Néanmoins, je me
mets à leur poursuite. La dimension des pistes indique le passage
d'une ourse et d'un ourson. La mère a dû être gravement blessée;
les empreintes laissées sur la neige indiquent qu'elle est tombée à
plusieurs reprises. Il sera donc possible de la rejoindre; dans cet
espoir, je continue ma poursuite. Sur ces entrefaites survient un
épais brouillard. Le Fram est depuis longtemps hors de vue; je n'en
marche pas moins pendant quelque temps encore. Enfin je m'arrête,
je me sens une faim terrible. Dans ma hâte, je n'ai pas déjeuné et
seulement, à cinq heures et demie du soir, je rentre à bord. Pendant
mon absence, quelques hommes, partis à ma rencontre avec un
traîneau pour rapporter mon gibier, ont aperçu deux autres ours.
Johansen leur a immédiatement donné la chasse, sans plus de
résultat que moi. Quatre ours en un jour, après être resté trois mois
sans en voir un seul. Cela signifie quelque chose. Peut-être
approchons-nous d'une terre. Nous sommes aujourd'hui par 80°15′;
jamais nous n'avons atteint une aussi haute latitude.
30 avril.—Nous atteignons le 80°44′30″ et le vent souffle
toujours du sud et du sud-est. Un temps clair et rayonnant de
printemps, bien que le thermomètre affirme le contraire. On a
commencé la toilette du navire. La neige et la glace qui recouvraient
le pont et les murailles du Fram ont été enlevées, et le gréement
nettoyé; maintenant la mâture dresse sa silhouette noire sur le ciel
bleu.
Nous nous chauffons au soleil, suivant des yeux les brumes
blanches qui flottent dans l'air diaphane; dans ce repos nous
songeons au printemps de Norvège, à l'éclosion des bourgeons et
des fleurs. Ici rien de pareil. Dans toutes les directions, la grande
blancheur déserte pèse comme un poids de mort sur la mer animée.
CHAPITRE III
LE PRINTEMPS ET L'ÉTÉ AU MILIEU
DE LA BANQUISE

Enfin, elle est arrivée, cette saison qu'en Norvège nous appelons
le printemps, la saison de la joie et de la vie, le réveil de la nature
après le long assoupissement hivernal. Ici elle n'a apporté aucun
changement. C'est toujours la même plaine de glace.
Suivant que la dérive nous porte dans le nord ou dans le sud,
nous sommes pleins d'espoir ou découragés, et, comme toujours
dans ces alternatives, je fais des plans d'avenir. Un jour, il me semble
que mon plan se réalisera. Le 17 avril, comme nous sommes
poussés dans le nord, je suis persuadé de l'existence d'un courant à
travers le bassin polaire. Vingt-quatre heures du vent du nord nous
ont fait gagner 9 milles. Nous en avons fini, sans doute, avec cette
énervante dérive vers le sud. La présence de couches d'eau ayant
une température relativement élevée en est à mes yeux un indice
favorable.
Pendant le printemps, nos progrès furent plus satisfaisants que
durant l'hiver, comme le montre la carte des pages 56–57. Le 1er
mai, nous étions presque au 81°, et le 18 juin nous touchions au
83°, puis en juillet et en août nous revînmes en arrière. Le 1er
septembre nous avions rétrogradé au 81°14′. Somme toute, c'était
toujours le même genre de locomotion; le Fram avançait à la façon
d'un crabe. Chaque fois qu'il avait fait un pas vers le nord, il reculait
ensuite. C'était, comme le disait l'un de nous, politicien ardent, une
lutte constante entre la droite et la gauche, entre les progressistes et
les réactionnaires. Toujours après une période de vent progressiste
et de dérive encourageante vers le nord, l'extrême droite l'emportait
de nouveau; le navire restait alors immobile, ou même était ramené
en arrière, au grand désespoir d'Amundsen.
Pendant toute la dérive, l'avant du Fram fut tourné vers le sud,
généralement vers le S. ¼ S.-O., et le navire ne dévia que très peu
de cette direction. Il marchait vers le nord, qui était son but, le nez
toujours dirigé vers le sud. Il se refusait, semble-t-il, à augmenter la
distance entre lui et le monde habité, paraissant soupirer après les
rivages méridionaux, tandis qu'une puissance invisible l'entraînait
vers l'inconnu.
Pendant le printemps, en vue de préparer mon excursion
projetée vers le nord, j'étudiais les conditions de viabilité de la
banquise dans des excursions journalières, soit sur les ski, soit en
traîneau.
En avril, la glace devint très praticable pour les chiens. Sous
l'action des rayons solaires, les monticules produits par la pression
avaient été en partie nivelés, et les crevasses s'étaient fermées.
Pendant des milles, on pouvait cheminer sans rencontrer de grands
obstacles. En mai, la situation devint moins bonne par suite de
l'ouverture de nombreux canaux dans toutes les directions, autant
de larges fossés qui, à chaque instant, arrêtaient la marche. Dans les
premiers jours du mois, les froids étant encore très vifs, ces nappes
d'eau étaient rapidement recouvertes par une couche cristalline,
suffisamment épaisse pour résister au poids d'une caravane; plus
tard, par suite de l'élévation de la température, la formation de la
glace devint beaucoup plus lente et même s'arrêta complètement. A
la fin de mai et au commencement de juin, on n'aurait pu avancer
que très lentement à travers le réseau inextricable de canaux et de
lacs qui, à cette époque, morcelaient la banquise.

You might also like