Recent from talks
Nothing was collected or created yet.
| BCPL | |
|---|---|
| Paradigm | procedural, imperative, structured |
| Designed by | Martin Richards |
| First appeared | 1967[1] |
| Typing discipline | typeless (everything is a word) |
| Influenced by | |
| CPL | |
| Influenced | |
| B, C, Go[2] | |
BCPL (Basic Combined Programming Language) is a procedural, imperative, and structured programming language. Originally intended for writing compilers for other languages, BCPL is no longer in common use. However, its influence is still felt because a stripped down and syntactically changed version of BCPL, called B, was the language on which the C programming language was based. BCPL introduced several features of many modern programming languages, including using curly braces to delimit code blocks.[3] BCPL was first implemented by Martin Richards of the University of Cambridge in 1967.[1]
Design
[edit]This section needs additional citations for verification. (January 2017) |
BCPL was designed so that small and simple compilers could be written for it; reputedly some compilers could be run in 16 kilobytes. Furthermore, the original compiler, itself written in BCPL, was easily portable. BCPL was thus a popular choice for bootstrapping a system.[citation needed] A major reason for the compiler's portability lay in its structure. It was split into two parts: the front end parsed the source and generated O-code, an intermediate language. The back end took the O-code and translated it into the machine code for the target machine. Only 1⁄5 of the compiler's code needed to be rewritten to support a new machine, a task that usually took between 2 and 5 person-months. This approach became common practice later (e.g. Pascal, Java).
The language is unusual in having only one data type: a word, a fixed number of bits, usually chosen to align with the same platform architecture's machine word and of adequate capacity to represent any valid storage address. For many machines of the time, this data type was a 16-bit word. This choice later proved to be a significant problem when BCPL was used on machines in which the smallest addressable item was not a word but a byte or on machines with larger word sizes such as 32-bit or 64-bit.[citation needed]
The interpretation of any value was determined by the operators used to process the values. (For example, + added two values together, treating them as integers; ! indirected through a value, effectively treating it as a pointer.) In order for this to work, the implementation provided no type checking.
The mismatch between BCPL's word orientation and byte-oriented hardware was addressed in several ways. One was by providing standard library routines for packing and unpacking words into byte strings. Later, two language features were added: the bit-field selection operator and the infix byte indirection operator (denoted by %).[4]
BCPL handles bindings spanning separate compilation units in a unique way. There are no user-declarable global variables; instead, there is a global vector, similar to "blank common" in Fortran. All data shared between different compilation units comprises scalars and pointers to vectors stored in a pre-arranged place in the global vector. Thus, the header files (files included during compilation using the "GET" directive) become the primary means of synchronizing global data between compilation units, containing "GLOBAL" directives that present lists of symbolic names, each paired with a number that associates the name with the corresponding numerically addressed word in the global vector. As well as variables, the global vector contains bindings for external procedures. This makes dynamic loading of compilation units very simple to achieve. Instead of relying on the link loader of the underlying implementation, effectively, BCPL gives the programmer control of the linking process.[citation needed]
The global vector also made it very simple to replace or augment standard library routines. A program could save the pointer from the global vector to the original routine and replace it with a pointer to an alternative version. The alternative might call the original as part of its processing. This could be used as a quick ad hoc debugging aid.[citation needed]
BCPL was the first brace programming language and the braces survived the syntactical changes and have become a common means of denoting program source code statements. In practice, on limited keyboards of the day, source programs often used the sequences $( and $) or [ and ] in place of the symbols { and }. The single-line // comments of BCPL, which were not adopted by C, reappeared in C++ and later in C99.
The book BCPL: The language and its compiler describes the philosophy of BCPL as follows:
The philosophy of BCPL is not one of the tyrant who thinks he knows best and lays down the law on what is and what is not allowed; rather, BCPL acts more as a servant offering his services to the best of his ability without complaint, even when confronted with apparent nonsense. The programmer is always assumed to know what he is doing and is not hemmed in by petty restrictions.[5]
History
[edit]BCPL was first implemented by Martin Richards of the University of Cambridge in 1967.[1] BCPL was a response to difficulties with its predecessor, Cambridge Programming Language, later renamed Combined Programming Language (CPL), which was designed during the early 1960s. Richards created BCPL by "removing those features of the full language which make compilation difficult". The first compiler implementation, for the IBM 7094 under Compatible Time-Sharing System, was written while Richards was visiting Project MAC at the Massachusetts Institute of Technology in the spring of 1967. The language was first described in a paper presented to the 1969 Spring Joint Computer Conference.[citation needed]
BCPL has been rumored to have originally stood for "Bootstrap Cambridge Programming Language", but CPL was never created since development stopped at BCPL, and the acronym was later reinterpreted for the BCPL book.[clarification needed][citation needed]
BCPL is the language in which the original "Hello, World!" program was written.[6] The first MUD was also written in BCPL (MUD1).
Several operating systems were written partially or wholly in BCPL (for example, TRIPOS and the earliest versions of AmigaDOS). BCPL was also the initial language used in the Xerox PARC Alto project. Among other projects, the Bravo document preparation system was written in BCPL.
An early compiler, bootstrapped in 1969, by starting with a paper tape of the O-code of Richards's Atlas 2 compiler, targeted the ICT 1900 series. The two machines had different word-lengths (48 vs 24 bits), different character encodings, and different packed string representations—and the successful bootstrapping increased confidence in the practicality of the method.
By late 1970, implementations existed for the Honeywell 635 and Honeywell 645, IBM 360, PDP-10, TX-2, CDC 6400, UNIVAC 1108, PDP-9, KDF 9 and Atlas 2. In 1974 a dialect of BCPL was implemented at BBN without using the intermediate O-code. The initial implementation was a cross-compiler hosted on BBN's TENEX PDP-10s, and directly targeted the PDP-11s used in BBN's implementation of the second generation IMPs used in the ARPANET.[citation needed]
There was also a version produced for the BBC Micro in the mid-1980s, by Richards Computer Products, a company started by John Richards, the brother of Martin Richards.[7] The BBC Domesday Project made use of the language. Versions of BCPL for the Amstrad CPC and Amstrad PCW computers were also released in 1986 by UK software house Arnor Ltd. MacBCPL was released for the Apple Macintosh in 1985 by Topexpress Ltd, of Kensington, England.
Both the design and philosophy of BCPL strongly influenced B, which in turn influenced C.[8] Programmers at the time debated whether an eventual successor to C would be called "D", the next letter in the alphabet, or "P", the next letter in the parent language name. The language most accepted as being C's successor is C++ (with ++ being C's increment operator),[9] although meanwhile, a D programming language also exists.
In 1979, implementations of BCPL existed for at least 25 architectures; the language gradually fell out of favour as C became popular on non-Unix systems.
Martin Richards maintains a modern version of BCPL on his website, last updated in 2023.[10] This can be set up to run on various systems including Linux, FreeBSD, and Mac OS X. The latest distribution includes graphics and sound libraries, and there is a comprehensive manual. He continues to program in it, including for his research on musical automated score following.
A common informal MIME type for BCPL is text/x-bcpl.
Examples
[edit]Hello world
[edit]Richards and Whitby-Strevens[11] provide an example of the "Hello, World!" program for BCPL using a standard system header, 'LIBHDR':
GET "LIBHDR"
LET START() BE WRITES("Hello, World")
Further examples
[edit]This section possibly contains original research. (August 2019) |
If these programs are run using Richards' current version of Cintsys (December 2018), LIBHDR, START and WRITEF must be changed to lower case to avoid errors.
Print factorials:
GET "LIBHDR"
LET START() = VALOF $(
FOR I = 1 TO 5 DO
WRITEF("%N! = %I4*N", I, FACT(I))
RESULTIS 0
$)
AND FACT(N) = N = 0 -> 1, N * FACT(N - 1)
Count solutions to the N queens problem:
GET "LIBHDR"
GLOBAL $(
COUNT: 200
ALL: 201
$)
LET TRY(LD, ROW, RD) BE
TEST ROW = ALL THEN
COUNT := COUNT + 1
ELSE $(
LET POSS = ALL & ~(LD | ROW | RD)
UNTIL POSS = 0 DO $(
LET P = POSS & -POSS
POSS := POSS - P
TRY(LD + P << 1, ROW + P, RD + P >> 1)
$)
$)
LET START() = VALOF $(
ALL := 1
FOR I = 1 TO 12 DO $(
COUNT := 0
TRY(0, 0, 0)
WRITEF("%I2-QUEENS PROBLEM HAS %I5 SOLUTIONS*N", I, COUNT)
ALL := 2 * ALL + 1
$)
RESULTIS 0
$)
References
[edit]- ^ a b c "Martin Richards (2003 Computer Pioneer Award)". IEEE Computer Society. Archived from the original on 24 November 2017. Retrieved 24 November 2017.
- ^ Pike, Rob (24 April 2014). "Hello Gophers". Retrieved 11 March 2016.
- ^ https://www.cl.cam.ac.uk/~mr10/bcplman.pdf The BCPL Cintsys and Cintpos User Guide, 2.1.4 Section brackets
- ^ "Clive Feather on CPL and BCPL". www.lysator.liu.se. Retrieved 1 March 2024.
- ^ Richards, Martin; Whitby-Strevens, Colin (1980). BCPL: The Language and its Compiler. Cambridge University Press. p. 5. ISBN 978-0521785433.
- ^ BCPL, Jargon File
- ^ "Reuters technical development: Glossary - THE BARON". www.thebaron.info.
- ^ Kernighan, Brian W.; Dennis M. Ritchie (1978). The C Programming Language. Bell Telephone Laboratories. p. 2. ISBN 0-13-110163-3.
- ^ History of C++ Retrieved 12 December 2017
- ^ Martin Richards. "BCPL/README (BCPL Cintcode distribution)".
- ^ Richards, Martin; Whitby-Strevens, Colin (1980). BCPL: The Language and its Compiler. Cambridge University Press. p. 8. ISBN 978-0521785433.
Further reading
[edit]- Martin Richards, The BCPL Reference Manual Archived 11 June 2015 at the Wayback Machine (Memorandum M-352, Project MAC, Cambridge, MA, USA, July, 1967)
- Martin Richards, BCPL - a tool for compiler writing and systems programming (Proceedings of the Spring Joint Computer Conference, Vol 34, pp 557–566, 1969)
- Martin Richards, Arthur Evans, Robert F. Mabee, The BCPL Reference Manual (MAC TR-141, Project MAC, Cambridge, MA, USA, 1974)
- Martin Richards, Colin Whitby-Strevens, BCPL, the language and its compiler (Cambridge University Press, 1980) ISBN 0-521-28681-6
External links
[edit]- Martin Richards' BCPL distribution
- Martin Richards' BCPL Reference Manual, 1967 Archived 11 June 2015 at the Wayback Machine by Dennis M. Ritchie
- BCPL entry in the Jargon File
- Nordier & Associates' x86 port
- ArnorBCPL manual (1986, Amstrad PCW/CPC)
- How BCPL evolved from CPL, Martin Richards [1]
- Ritchie's The Development of the C Language has commentary about BCPL's influence on C
- The BCPL Cintsys and Cintpos User Guide
- BCPL Reference Manual, 1975 Xerox Palo Alto Research Center
History
Origins and Development
BCPL was developed by Martin Richards in 1967 while visiting MIT as a simplified subset of the Cambridge Programming Language (CPL), stripping away much of CPL's complexity to create a more manageable tool for programming tasks.[1] This evolution stemmed directly from Richards' work on implementing CPL, where he identified the need for a language that could be compiled more efficiently without sacrificing essential expressiveness.[1] The primary motivations for BCPL's creation were the limitations of CPL, particularly its intricate type system and mode matching features, which resulted in excessively slow compilation times on hardware like the IBM 7094.[1] Richards aimed to produce a language emphasizing simplicity, portability across machines, and efficiency for systems programming applications, such as compiler development and operating system components.[1] Influenced by his doctoral dissertation on CPL subsets, Richards designed BCPL with a word-addressable memory model assuming 16- or 24-bit words, ensuring uniform handling of values to enhance machine independence.[1] Key milestones in BCPL's early development include its first implementation in 1967 on the IBM 7094 under the CTSS operating system, at MIT's Project MAC, which validated the language's core concepts in a practical setting.[1] By 1969, the introduction of O-code—an abstract machine code representing operations on an idealized stack machine—further advanced portability by allowing the compiler's translation phase to generate machine-independent intermediate code, which could then be optimized and targeted to specific architectures with relative ease.[6]Adoption and Decline
BCPL saw significant adoption in the late 1970s and early 1980s for systems programming and specialized applications, leveraging its portability through O-code to facilitate implementations across diverse hardware. One of the earliest notable uses was the development of the TRIPOS operating system in 1976 at the University of Cambridge Computer Laboratory, where most of the system, including its filing system, command language, and interprocess communication primitives, was written in BCPL to provide a friendly interactive multiprocessing environment for single users on mini-computers. TRIPOS later influenced AmigaDOS, with its core components ported and adapted in BCPL for the Amiga 1000 in 1985, forming the basis of the original Amiga operating system's command-line interface and file handling. Another key application was the implementation of MUD1 in 1978 by Roy Trubshaw and Richard Bartle at the University of Essex, marking BCPL's role in pioneering networked multi-user environments as the first virtual world game supporting remote connections over ARPANET precursors. BCPL was also employed in writing compilers for other languages, fulfilling its original design intent, such as in the development of tools for B and early systems programming tasks. In the 1980s, the Cintsys interpreter extended BCPL's reach to the BBC Micro, enabling systems-level programming and standalone code generation on this educational microcomputer platform. Ports of BCPL expanded its footprint to various hardware architectures, underscoring its utility in early computing ecosystems. The initial implementation targeted the PDP-7 minicomputer in the late 1960s, with a hand-mapped compiler serving as a foundation for subsequent Unix precursors. By the early 1970s, BCPL had been ported to the IBM System/360 mainframe, as well as to Multics (on Honeywell 645) and GECOS (on Honeywell systems), for large-scale systems development.[7] A dedicated BCPL compiler was developed for the Xerox Alto in 1975, integrating with the Alto's innovative graphical interface and contributing to software for this pioneering personal workstation, which operated in a networked research setting at Xerox PARC. These ports, along with AmigaDOS integration, highlighted BCPL's involvement in early networked systems, where its structured approach aided in building interconnected applications like multi-user simulations. Despite these successes, BCPL's popularity waned in the late 1970s due to the rise of C, which was developed at Bell Labs to enhance Unix portability and efficiency on byte-addressable hardware. BCPL's word-oriented design, while portable via O-code, lacked direct hardware-specific optimizations, leading to inefficiencies on emerging byte-oriented machines like the PDP-11; adaptations in the 1970s, such as library routines for byte manipulation, addressed this but could not compete with C's typed structure and closer alignment with Unix's assembly roots. By the late 1970s, with the publication of K&R C and Unix's widespread adoption, BCPL was largely supplanted for new projects, though it persisted for legacy maintenance in specialized domains. The last major integrations occurred in the 1980s, including Cintsys on the BBC Micro around 1981 and AmigaDOS in 1985, after which its use diminished as C and derivatives dominated systems programming.Design and Features
Core Principles
BCPL's core principles emphasize simplicity, portability, and flexibility, making it particularly suited for systems programming and compiler development. At its foundation, BCPL is a typeless language where all data is treated as machine words, consisting of binary bit patterns without compile-time type checking. Instead, the language relies on runtime conventions to interpret these bit patterns as abstract objects, such as integers or strings, allowing programmers to manipulate data at a low level while avoiding the overhead of type systems. This typeless approach, which views all values as uniform-sized entities on the stack, enables efficient code generation and reduces complexity in implementation.[6][1] A key architectural choice for achieving portability is the use of O-code, an intermediate abstract machine language that simulates a stack-based virtual machine. O-code consists of a small number of basic instructions that perform transformations on an imaginary stack, decoupling the source code from the target hardware and allowing the compiler to generate machine-independent output. This design facilitates quick compilation and easy adaptation to different architectures, as the O-code can be translated into native code for various machines without altering the high-level semantics. By prioritizing a minimal instruction set, BCPL avoids hardware-specific dependencies, aligning with its goal of serving as a versatile tool for systems programming.[6][1] Data sharing in BCPL is managed through a global vector, which provides a single namespace for all variables and functions, eliminating the need for complex scoping mechanisms. This global vector acts as a shared memory area where separately compiled modules can access common data, promoting modularity while keeping the language's structure straightforward. The memory model is word-addressable, with storage cells numbered consecutively such that adjacent cells differ by one, enabling direct pointer arithmetic where incrementing a pointer accesses the next memory location. Dynamic allocation is handled via built-in functions like$alloc (or getvec) for requesting memory and $free for releasing it, tying the extent of dynamically allocated variables to their scope and supporting flexible runtime memory management without built-in garbage collection. These principles stem from efforts to simplify the overly complex Combined Programming Language (CPL), resulting in a leaner design focused on practical utility.[6][1]
Syntax and Semantics
BCPL employs curly braces{} to delimit blocks, which group declarations and sequences of statements for structured programming. Statements within blocks or at the top level are separated by semicolons ;, though the language's preprocessor often inserts them automatically between commands on separate lines to simplify writing. Expressions form the core of computations and control, constructed from primaries such as names, constants, and applications, combined via operators; notably, the @ operator generates an Lvalue (address) for indirection, while ! retrieves an Rvalue or invokes routines as in f!(e1, e2) for calling a routine f with arguments.[8][2]
Control flow in BCPL relies on conditional and repetitive structures defined through keyword-based syntax. The if construct evaluates an expression e and executes a command c if e is true (non-zero), as in if e do c, with an optional else clause for the alternative branch: if e do c1 else c2. Repetition uses while e do c to execute c repeatedly while e remains true, until e do c for execution until e becomes true, and a for loop for indexed iteration like for i = e1 to e2 do c, incrementing i from e1 to e2. Although BCPL supports labels followed by colons and unconditional jumps via goto l where l is a label's value, such unstructured control is discouraged in favor of the provided loops to promote readability and maintainability. Nested function definitions enable modular code, with inner routines accessing outer scope variables under dynamic scoping rules, where bindings are resolved at runtime based on the call stack rather than purely lexical position.[2][9]
Semantically, BCPL evaluates expressions strictly from left to right, ensuring predictable order for operator applications within a sequence, though short-circuiting applies in logical contexts to avoid unnecessary computations once the outcome is determined. The typeless nature allows flexible interpretation of words (machine words) across operations, with results always yielding a word value. For hardware compatibility, the language incorporates bit-field and byte-level manipulations, such as left and right shifts (lshift, rshift) for bit patterns and library routines like getbyte and putbyte for byte access within words. Vectors, essential for arrays and dynamic storage, are allocated using vec in declarations, such as let v = vec n to create a block of n+1 words indexed from 0, accessed via indirection like !(v + i).[8][2]
The operator set emphasizes simplicity and uniformity, treating all operands as words. Arithmetic operators include addition (+), subtraction (-), multiplication (*), integer division (/), and remainder (rem), performing standard integer operations with overflow undefined. Relational operators compare word values: greater than (>), less than (<), and equality (=), yielding true (1) or false (0); additional relations like greater or equal (>=) and not equal (~=) follow similar binary patterns. Logical and bitwise operators handle boolean-like decisions and bit manipulation: & and | for conjunction and disjunction with short-circuit evaluation in boolean contexts, and ~ for negation, all producing word results of 0 or 1. Conditional expressions further extend manipulation capabilities.[2][9]
Error handling in BCPL lacks built-in exceptions or runtime checks, placing responsibility on the programmer to validate conditions explicitly through if-statements or result inspections before operations; undefined behaviors, such as division by zero or invalid indirections, lead to machine-dependent outcomes without language-level recovery.[8][2]
Implementations
Historical Implementations
The first implementation of BCPL was a compiler developed by Martin Richards in 1967 for the IBM 7094 mainframe running under MIT's Compatible Time-Sharing System (CTSS).[3] This initial compiler, written in approximately 1000 lines of Doug Ross's AED-0 macro language, targeted the 36-bit word architecture of the 7094, where 6-bit characters were packed into words to accommodate the machine's limited character set.[3] By 1968, the compiler had been rewritten in BCPL itself at the University of Cambridge, achieving self-hosting status and demonstrating the language's suitability for compiler bootstrapping on early systems.[3] Key historical implementations in the 1970s included the Tripos BCPL system, developed at Cambridge for the PDP-11 minicomputer family under operating systems like DOS-11.[3] This version supported the Tripos operating system kernel, written entirely in BCPL, and was ported to PDP-11/40 and larger models, enabling efficient systems programming on 16-bit hardware.[10] Another notable tool was the 1983 BCPL compiler for the BBC Micro, released by Acornsoft and implemented by Chris Jobson and John Richards, which provided a full BCPL environment on the 6502-based microcomputer for educational and hobbyist use.[11] Portability was enhanced through O-code, an intermediate abstract machine code that facilitated interpreters on diverse hardware, including the English Electric KDF9 at Oxford's Programming Research Group.[3] These O-code interpreters, operational by 1967-1968, allowed BCPL programs to run via translation to O-code followed by machine-specific interpretation, supporting early multi-machine deployments without full recompilation.[3] In the 1970s, porting efforts focused on adapting BCPL to byte-addressable architectures, such as the PDP-11 and BBN's TENEX on PDP-10, where bit-pattern indirection operators (like the % for byte pointers) were refined to handle variable-length strings and efficient memory access.[8] Martin Richards' reference implementation served as the basis for these adaptations, emphasizing typeless word operations while introducing byte-level indirection for compatibility with emerging minicomputers.[12] BCPL's design also enabled cross-compilers for early microcomputers, with Richards' framework used to target systems like the Zilog Z80 in the late 1970s, often via PDP-11 hosts at Cambridge and the University of Kent.[3] These cross-compilation tools generated native code for resource-constrained 8-bit machines, underscoring BCPL's role in bootstrapping software for the microprocessor era.[3]Modern Implementations
Martin Richards has continued to maintain and update BCPL implementations into the 21st century, with the latest distribution of the Cintcode system as of March 2022.[12] This version includes the C-hosted compilerbcplc, which supports extensions such as cross-referencing, and interpretive systems compatible with x86 and ARM architectures.[12] The distribution provides both 32-bit BCPL and 64-bit BCPL64 variants, enabling execution on contemporary hardware including Linux and Windows platforms.[12]
Open-source efforts have preserved and extended BCPL through various GitHub repositories focused on O-code emulators and ports. For instance, the 8l/bcpl repository hosts an upgraded compiler supporting t32 and t64 options for generating Cintcode on 32-bit and 64-bit systems.[13] Similarly, SergeGris/BCPL-compiler is an x86 port of the classic Tripos BCPL compiler from around 1980, with runtime support.[14] Ports to JavaScript, such as the michael-nestler/bcpl-interpreter project, facilitate web-based demos and interactive experimentation with the language.[15]
Recent developments emphasize BCPL's role in educational and historical contexts, with implementations integrated into emulators for vintage systems like the Xerox Alto and PDP-10 via SIMH.[16] Discussions around historical accuracy have appeared in retro computing communities, highlighting faithful recreations of original BCPL environments for teaching purposes.[17]
BCPL remains available for download from the University of Cambridge website, where the full distributions (e.g., bcpl.tgz for 32-bit and bcpl64.tgz for 64-bit) support compatibility with modern 64-bit systems through extended word sizes and updated environment variables like BCPLROOT.[12] These resources are provided free for private and academic use, ensuring ongoing accessibility for researchers and enthusiasts.[12]
Examples
Hello World Program
The simplest BCPL program, known as the "Hello, World!" example, demonstrates basic output and program structure using the language's core features. Originating in documentation from 1972, this program exemplifies BCPL's design for conciseness and portability, requiring fewer than 10 lines to produce the greeting on standard output. A representative implementation, compatible with standard BCPL systems like the Cintcode virtual machine, is as follows:GET "libhdr"
LET start() BE VALOF
$(
selectoutput(stdoutput)
putf("Hello, World!*n")
RESULTIS 0
$)
GET "libhdr"
LET start() BE VALOF
$(
selectoutput(stdoutput)
putf("Hello, World!*n")
RESULTIS 0
$)
GET "libhdr", which provides essential functions and globals such as stdoutput, a predefined vector representing the standard output stream. The entry point is the start() routine, enclosed in a VALOF block to enable returning a value; alternative implementations may use main() instead. The selectoutput(stdoutput) call directs output to the standard stream, ensuring portability across systems where the default stream might differ. The putf function then performs formatted output, where the string literal "Hello, World!" is implicitly a character vector (equivalent to a $vec construction for dynamic allocation, such as $vec(14, 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0)), and *n specifies a newline. Finally, RESULTIS 0 terminates the program with a zero exit status, signaling successful completion.[18]
The $( ... $) delimits the sequence of expressions within the VALOF block of the start() routine, which serves as the program's entry point. In this minimal example, there are no additional global declarations beyond the start() routine itself. Output semantics rely on the current stream's configuration, as detailed in BCPL's syntax rules. This structure ensures the program runs identically on diverse hardware via BCPL's interpretive or compiled implementations, from historical systems like the PDP-7 to modern emulators.[18]
Advanced Examples
To illustrate BCPL's capabilities in handling recursive computations, one common example is calculating the factorial of a number, which leverages the language's support for nested routine definitions and conditional expressions. The following code computes and prints factorials from 0 to 16, using recursion to define the factorial function.[19]GET "libhdr"
LET start() = VALOF
{ FOR i = 0 TO 16 DO writef("%n! = %n*n", i, factorial(i))
RESULTIS 0
}
AND factorial(n) = n=0 -> 1, n*factorial(n-1)
GET "libhdr"
LET start() = VALOF
{ FOR i = 0 TO 16 DO writef("%n! = %n*n", i, factorial(i))
RESULTIS 0
}
AND factorial(n) = n=0 -> 1, n*factorial(n-1)
GET "libhdr", which provides functions like writef for formatted output. The start() routine serves as the entry point, using a FOR loop to iterate over values of i from 0 to 16 and invoke the recursive factorial function for each. The AND keyword defines the additional factorial routine nested within the scope, a feature allowing multiple routines to share the same block for modularity without global pollution. The recursion is expressed concisely via the conditional operator ->, which evaluates the left expression if the condition holds (here, base case n=0 returns 1), otherwise the right (recursive call n*factorial(n-1)). This demonstrates BCPL's efficient handling of nested routines, where the recursive depth is limited only by stack space, and integer arithmetic operates on machine words, potentially wrapping around on overflow for large n beyond the word size (typically 32 or 64 bits, resulting in modulo 2^w behavior).[9]
For more complex control flow involving backtracking, the N-Queens problem showcases BCPL's prowess in recursive search with bit manipulation for efficiency. The task is to place N queens on an N×N chessboard such that no two attack each other, counting solutions for boards up to 16×16. The bit-oriented approach uses masks to track conflicts on rows, left diagonals, and right diagonals.[20]
GET "libhdr"
GLOBAL { count:ug; all }
LET try(ld, row, rd) BE TEST row=all
THEN count := count + 1
ELSE { LET poss = all & ~(ld | row | rd)
WHILE poss DO
{ LET p = poss & -poss
poss := poss - p
try((ld + p) << 1, row + p, (rd + p) >> 1)
}
}
LET start() = VALOF
{ all := 1
FOR i = 1 TO 16 DO
{ count := 0
try(0, 0, 0)
writef("Number of solutions to %i2-queens is %i7*n", i, count)
all := (all << 1) | 1
}
RESULTIS 0
}
GET "libhdr"
GLOBAL { count:ug; all }
LET try(ld, row, rd) BE TEST row=all
THEN count := count + 1
ELSE { LET poss = all & ~(ld | row | rd)
WHILE poss DO
{ LET p = poss & -poss
poss := poss - p
try((ld + p) << 1, row + p, (rd + p) >> 1)
}
}
LET start() = VALOF
{ all := 1
FOR i = 1 TO 16 DO
{ count := 0
try(0, 0, 0)
writef("Number of solutions to %i2-queens is %i7*n", i, count)
all := (all << 1) | 1
}
RESULTIS 0
}
GLOBAL declaration reserves space for count (unsigned global for solution tally) and all (bitmask for available positions). The recursive try routine takes bitmasks ld (left diagonal), row (current row), and rd (right diagonal). It first tests if the row mask equals all, indicating a full placement, and increments count if so. Otherwise, it computes possible positions poss by bitwise ANDing all with the negation of occupied bits (~ (ld | row | rd)), excluding conflicts. A WHILE loop iterates over set bits in poss using the isolate-lowest-bit idiom (p = poss & -poss), subtracts it to clear, and recurses with updated masks: left diagonal shifted left (<< 1), row ORed with p, and right diagonal shifted right (>> 1). This exploits BCPL's word operations for compact, efficient conflict checking without arrays, as bitwise AND, OR, and shifts operate directly on machine words for speed. The start() routine initializes all as a mask of N ones (updated via left shift and OR 1 in the loop), resets count, calls try, and outputs results using writef. For N=8, it correctly yields 92 solutions, highlighting backtracking's depth-first exploration. Note the absence of explicit labels here, as BCPL favors structured loops like WHILE over GOTO, though labels can be used for jumps when needed (e.g., L: ... GOTO L). Integer overflows in mask computations wrap modulo the word size, ensuring consistent bit patterns without runtime checks.[9]
Simple list processing in BCPL often uses vectors for dynamic arrays, demonstrating allocation and manipulation of contiguous memory blocks. Consider an example that dynamically allocates a vector, populates it with sequential integers, and computes their sum, illustrating basic data processing. This leverages BCPL's vector syntax for runtime sizing and element access.[9]
GET "libhdr"
LET start() = VALOF
{ LET len = 10
LET v = VEC len
LET sum = 0
FOR i = 0 TO len-1 DO v!i := i + 1
FOR i = 0 TO len-1 DO sum := sum + v!i
writef("Sum of vector = %n*n", sum)
RESULTIS 0
}
GET "libhdr"
LET start() = VALOF
{ LET len = 10
LET v = VEC len
LET sum = 0
FOR i = 0 TO len-1 DO v!i := i + 1
FOR i = 0 TO len-1 DO sum := sum + v!i
writef("Sum of vector = %n*n", sum)
RESULTIS 0
}
LET v = VEC len dynamically allocates a vector of length len (reserving len + 1 words, indexed 0 to len), using runtime evaluation for flexible sizing without static declarations. The first FOR loop fills the vector via v!i := i + 1, where ! dereferences the address offset i for assignment, treating the vector as a pointer to words. The second loop accumulates the sum using integer addition on word values. Dynamic allocation occurs at block entry, with automatic deallocation on exit, preventing leaks in recursive or nested contexts. Word operations like addition are efficient, performing modulo arithmetic on overflow (e.g., if sum exceeds the word maximum, it wraps), which suits low-level programming but requires care for large aggregates. This vector-based approach simulates list processing, extendable to linked structures by storing pointers in vector cells for traversal.[9]
