Previous Table of Contents Next


2.2.2. Origins: The Languages

BCPL was designed by Martin Richards in the mid–1960s while he was visiting MIT. It was used during the early 1970s for several interesting projects, among them the OS6 operating system at Oxford (Stoy & Strachey, 1972) and parts of the seminal Alto work at Xerox PARC (Thacker, McCreight, Lampson, Sproull, & Boggs, 1979). We became familiar with it because the MIT CTSS system (Corbato, Merwin-Dagget, & Daley, 1962) on which Richards worked was used for Multics development. The original BCPL compiler was transported both to Multics and to the GE–635 GECOS system by Rudd Canaday and others at Bell Labs (Canaday & Ritchie, 1969); during the final throes of Multics’s life at Bell Labs and immediately after, it was the language of choice among the group of people who would later become involved with UNIX.

BCPL, B, and C all fit firmly in the traditional procedural family typified by Fortran and Algol 60. They are particularly oriented toward system programming, are small and compactly described, and are amenable to translation by simple compilers. They are close to the machine in that the abstractions they introduce are readily grounded in the concrete data types and operations supplied by conventional computers, and they rely on library routines for input/output and other interactions with an operating system. With less success, they also use library procedures to specify interesting control constructs such as co-routines and procedure closures. At the same time, their abstractions lie at a sufficiently high level that, with care, portability between machines can be achieved.

BCPL, B, and C differ syntactically in many details, but broadly, they are similar. Programs consist of a sequence of global declarations and function (procedure) declarations. Procedures can be nested in BCPL but may not refer to nonstatic objects defined in containing procedures. B and C avoid this restriction by imposing a more severe one: no nested procedures at all. Each of the languages (except for earliest versions of B) recognizes separate compilation and provides a means for including text from named files.

Several syntactic and lexical mechanisms of BCPL are more elegant and regular than those of B and C. For example, BCPL’s procedure and data declarations have a more uniform structure, and it supplies a more complete set of looping constructs. Although BCPL programs are notionally supplied from an undelimited stream of characters, clever rules allow most semicolons to be elided after statements that end on a line boundary. B and C omit this convenience and end most statements with semicolons. In spite of the differences, most of the statements and operators of BCPL map directly into corresponding B and C.

Some of the structural differences between BCPL and B stemmed from limitations on intermediate memory. For example, BCPL declarations may take the form

   let P1 be command
   and P2 be command
   and P3 be command

where the program text represented by command contains whole procedures. The subdeclarations are connected and occur simultaneously, so the name P3 is known inside procedure P1. Similarly, BCPL can package a group of declarations and statements into an expression that yields a value, for example

   E1 := valof $( declarations ; commands ; resultis E2 $) + 1

The BCPL compiler readily handled such constructs by storing and analyzing a parsed representation of the entire program in memory before producing output. Storage limitations on the B compiler demanded a one–pass technique in which output was generated as soon as possible, and the syntactic redesign that made this possible was carried forward into C.

Certain less pleasant aspects of BCPL owed to its own technological problems and were consciously avoided in the design of B. For example, BCPL uses a global vector mechanism for communicating between separately compiled programs. In this scheme, the programmer explicitly associates the name of each externally visible procedure and data object with a numeric offset in the global vector; the linkage is accomplished in the compiled code by using these numeric offsets. B evaded this inconvenience initially by insisting that the entire program be presented all at once to the compiler. Later implementations of B, and all those of C, use a conventional linker to resolve external names occurring in files compiled separately, instead of placing the burden of assigning offsets on the programmer.

Other fiddles in the transition from BCPL to B were introduced as a matter of taste, and some remain controversial (for example, the decision to use the single character = for assignment instead of :=). Similarly, B uses /* */ to enclose comments, whereas BCPL uses // to ignore text up to the end of the line. The legacy of PL/I is evident here. (C++ has resurrected the BCPL comment convention.) Fortran influenced the syntax of declarations: B declarations begin with a specifier such as auto or static, followed by a list of names, and C not only followed this style but also ornamented it by placing its type keywords at the start of declarations.

Not every difference between the BCPL language documented in Richards and Whitbey-Strevens’s book (1979) and B was deliberate; we started from an earlier version of BCPL (Richards, 1967). For example, the endcase that escapes from a BCPL switchon statement was not present in the language when we learned it in the 1960s, so the overloading of the break keyword to escape from the B and C switch statement owes to divergent evolution rather than conscious change.


Previous Table of Contents Next