.\" Define Superscript Macro
.de SUP
\v'-.4m'\s-3\\$1\s+3\v'.4m'
..
.\" Define Subscript Macro
.de SUB
\*<\s-3\\$1\s+3\*>
..
.\" Define Horizontal Line Macro
.nr VS 15p
.VS
.de HL
.sp 0.5v
\D'l \\n[.ll]u 0'
.sp 0.5v
..
.TL
Users\(aq Reference to B
.AU
Ken Thompson
.AI
Bell Telephone Laboratories
.AB
.LP
B is a computer language intended for recursive, primarily non-numeric
applications typified by system programming.
B has a small, unrestrictive syntax that is easy to compile.
Because of the unusual freedom of expression and a rich set of
operators, B programs are often quite compact.
.br
.br
This manual contains a concise definition of the language, sample
programs, and instructions for using the PDP-11 version of B.
.AE
.LP
.br
.br
.HL
.PP
[ \f[I]
On January 7, 1972 Ken Thompson published the original manual for
the B language on the PDP-11.
In July of 1997, Dennis Ritchie created an HMTL rendition of that document
after scanning, OCR, and editing it, and published it on his Bell Labs website.
Nokia, as of this writing, still maintains that website
\f[CR]https://www.nokia.com/bell-labs/about/dennis-m-ritchie/kbman.html\fP
.PP
\f[I]This modern \f[CR]groff(1)\fP rendition was created in April of 2026 by
Clem Cole, by using Unix \f[CR]pandoc\fP(1) tool to convert from Dennis's HTML to
the \f[CR]-ms\fP macro packaged as supplied by the newest tool suite.
There was some minor editing, including adding some of Ken's section numbers,
which are not in Dennis's rendition.   
Note to the careful reader, Ken never created a section 11.0,
as he skips from section 10.0 to 12.0.
Since Dennis did not repair that error in the original document, I did not either.
All of the versions have been placed under \f[CR]Documents\fP
directory of the Unix Historical Society (TUHS), in PDF form, as
well HTML files as appropriate.
and source to this document.
The scan of the original that Dennis started with is called \f[CR]kbman.pdf\fP.
An image of the original document is available in PDF: just under 1MB.
.PP
\f[I]Also in that directory is CSTR #8, which is a report by
Steve Johnson and Brian Kernighan describing the B implementation
on Honeywell equipment.
Dennis created two PDFs from this document, one is called: \f[CR]btut.pdf\fP \(em
.UL "A Tutorial Introduction to the Language B" ,
and the other \f[CR]bref.pdf\fP \(em 
.UL "User's Reference to B on MH-TSS"
.PP
\f[I]The language being described was really the same, but it\(aqs
interesting to look at the differences in description and environment.
To describe the BNF syntax, Ken used a variant that depends on super-
and subscripts to say \(dqhow many\(dq, which in the original were
stacked above each other when they occurred simultaneously.
This doesn\(aqt look too awful in the HTML rendering, but was probably
better in the original.
.PP
\f[I]The other thing that is observable is the degree to which the GCOS
version had to struggle to work in that environment.
Its library description is full of odd concepts
like \(dqthe AFT\(dq the *SRC file, and other peculiarities. ]
.LP
.HL
.SH 4
1.0 Introduction
.LP
.br
.br
B is a computer language directly descendant from BCPL [1,2].
B 1s running at Murray Hill on the DEC PDP-11 computer under the UNIX-11
time sharing system [3,4].
B is good for recursive, non-numeric, machine independent applications,
such as system and language work.
.br
.br
B, compared to BCPL, is syntactically rich in expressions and
syntactically poor in statements.
A look at the examples in section 9 of this document will give a flavor
of the language.
.br
.br
B was designed and implemented by D. M. Ritchie and the author.
.SH 4
2.0 Syntax
.LP
.br
.br
The syntactic notation in this manual is basically BNF with the
following exceptions:
.IP "\f[B]1.\f[R]" 3
The metacharacters \f[CR]<\f[R] and \f[CR]>\f[R] are removed.
Literals are underlined [in typewriter font here -- DMR] to
differentiate them from syntactic variables.
.RS 3
.RE
.IP "\f[B]2.\f[R]" 3
The metacharacter \f[CR]|\f[R] is removed.
Each syntactic alternative is placed on a separate line.
.RS 3
.RE
.IP "\f[B]3.\f[R]" 3
The metacharacters \f[CR]{\f[R] and \f[CR]}\f[R] denote syntactic
grouping.
.RS 3
.RE
.IP "\f[B]4.\f[R]" 3
A syntactic group followed by numerical sub- and superscripts denote
repetition of the group as follows:
.RS 3
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    {..}\*<m\*>    m,m+1,...
.sp 0.5v
    {..}\*<m\*>\s-3\h'-\w'm'u'\s+3\*{n\*}    m,m+1,...,n
\f[]
.fi
.RS 3
.RE
.RE
.SH 4
2.1 Canonical Syntax
.LP
.br
.br
The syntax given in this section defines all the legal constructions in
B without specifying the association rules.
These are given later along with the semantic description of each
construction.
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
program ::=
    {definition}\*<0\*>

definition ::=
.sp 0.5v
    name {[\h'-\w'['u'\l'\w'['u\(ul' {constant}\*<0\*>\s-3\h'-\w'0'u'\s+3\*{1\*} ]\h'-\w']'u'\l'\w']'u\(ul'}\*<0\*>\s-3\h'-\w'0'u'\s+3\*{1\*} {ival {,\h'-\w','u'\l'\w'['u\(ul' ival}\*<0\*>}\*<0\*>\s-3\h'-\w'0'u'\s+3\*{1\*} ;\h'-\w';'u'\l'\w';'u\(ul'
.sp 0.5v
    name (\h'-\w'('u'\l'\w'('u\(ul' {name {,\h'-\w','u'\l'\w','u\(ul' name}\*<0\*>}\*<0\*>\s-3\h'-\w'0'u'\s+3\*{1\*} )\h'-\w')'u'\l'\w')'u\(ul' statement

ival ::=
.sp 0.5v
    constant
.sp 0.5v
    name
.bp
statement ::=
.sp 0.5v
    auto\h'-\w'auto'u'\l'\w'auto'u\(ul' name {constant}\*<0\*>\s-3\h'-\w'0'u'\s+3\*{1\*} {,\h'-\w','u'\l'\w','u\(ul' name {constant}\*<0\*>\s-3\h'-\w'0'u'\s+3\*{1\*}}\*<0\*> ;\h'-\w';'u'\l'\w';'u\(ul'  statement
.sp 0.5v
    extrn\h'-\w'extrn'u'\l'\w'extrn'u\(ul' name {,\h'-\w','u'\l'\w','u\(ul' name}\*<0\*> ;\h'-\w';'u'\l'\w';'u\(ul' statement
.sp 0.5v
    name :\h'-\w':'u'\l'\w':'u\(ul' statement
.sp 0.5v
    case\h'-\w'case'u'\l'\w'case'u\(ul' constant :\h'-\w':'u'\l'\w':'u\(ul' statement
.sp 0.5v
    {\h'-\w'{'u'\l'\w'{'u\(ul' {statement}\*<0\*> }\h'-\w'}'u'\l'\w'}'u\(ul'
.sp 0.5v
    if\h'-\w'if'u'\l'\w'if'u\(ul' (\h'-\w'('u'\l'\w'('u\(ul' rvalue )\h'-\w')'u'\l'\w')'u\(ul' statement {else\h'-\w'else'u'\l'\w'else'u\(ul' statement}\*<0\*>\s-3\h'-\w'0'u'\s+3\*{1\*}
.sp 0.5v
    while\h'-\w'while'u'\l'\w'while'u\(ul' (\h'-\w'('u'\l'\w'('u\(ul' rvalue )\h'-\w')'u'\l'\w')'u\(ul' statement
.sp 0.5v
    switch\h'-\w'switch'u'\l'\w'switch'u\(ul' rvalue statement
.sp 0.5v
    goto\h'-\w'goto'u'\l'\w'goto'u\(ul' rvalue ;\h'-\w';'u'\l'\w';'u\(ul'
.sp 0.5v
    return\h'-\w'return'u'\l'\w'return'u\(ul' {(\h'-\w'('u'\l'\w'('u\(ul' rvalue )\h'-\w')'u'\l'\w')'u\(ul'}\*<0\*>\s-3\h'-\w'0'u'\s+3\*{1\*} ;\h'-\w';'u'\l'\w';'u\(ul'
.sp 0.5v
    {rvalue}\*<0\*>\s-3\h'-\w'0'u'\s+3\*{1\*} ;\h'-\w';'u'\l'\w';'u\(ul'

rvalue ::=
.sp 0.5v
    (\h'-\w'('u'\l'\w'('u\(ul' rvalue )\h'-\w')'u'\l'\w')'u\(ul'
.sp 0.5v
    lvalue
.sp 0.5v
    constant
.sp 0.5v
    lvalue assign rvalue
.sp 0.5v
    inc-dec lvalue
.sp 0.5v
    lvalue inc-dec
.sp 0.5v
    unary rvalue
.sp 0.5v
    &\h'-\w'&'u'\l'\w'&'u\(ul' lvalue
.sp 0.5v
    rvalue binary rvalue
.sp 0.5v
    rvalue ?\h'-\w'?'u'\l'\w'?'u\(ul' rvalue :\h'-\w':'u'\l'\w':'u\(ul' rvalue
.sp 0.5v
    rvalue (\h'-\w'('u'\l'\w'('u\(ul'{rvalue {,\h'-\w','u'\l'\w','u\(ul' rvalue}\*<0\*> }\*<0\*>\s-3\h'-\w'0'u'\s+3\*{1\*} )\h'-\w')'u'\l'\w')'u\(ul'

assign ::=
.sp 0.5v
    =\h'-\w'='u'\l'\w'='u\(ul' {binary}\*<0\*>\s-3\h'-\w'0'u'\s+3\*{1\*}
.bp
inc-dec ::=
.sp 0.5v
    ++\h'-\w'++'u'\l'\w'++'u\(ul'
.sp 0.5v
    --\h'-\w'--'u'\l'\w'--'u\(ul'
    
unary ::=
.sp 0.5v
    -\h'-\w'-'u'\l'\w'-'u\(ul'
.sp 0.5v
    !\h'-\w'!'u'\l'\w'!'u\(ul'

binary ::=
.sp 0.5v
    |\h'-\w'|'u'\l'\w'|'u\(ul'
.sp 0.5v
    &\h'-\w'&'u'\l'\w'&'u\(ul'
.sp 0.5v
    ==\h'-\w'=='u'\l'\w'=='u\(ul'
.sp 0.5v
    !=\h'-\w'!='u'\l'\w'!='u\(ul'
.sp 0.5v
    <\h'-\w'<'u'\l'\w'<'u\(ul'
.sp 0.5v
    <=\h'-\w'<='u'\l'\w'<='u\(ul'
.sp 0.5v
    >\h'-\w'>'u'\l'\w'>'u\(ul'
.sp 0.5v
    >=\h'-\w'>='u'\l'\w'>='u\(ul'
.sp 0.5v
    <<\h'-\w'<<'u'\l'\w'<<'u\(ul'
.sp 0.5v
    >>\h'-\w'>>'u'\l'\w'>>'u\(ul'
.sp 0.5v
    -\h'-\w'-'u'\l'\w'-'u\(ul'
.sp 0.5v
    +\h'-\w'+'u'\l'\w'+'u\(ul'
.sp 0.5v
    %\h'-\w'%'u'\l'\w'%'u\(ul'
.sp 0.5v
    *\h'-\w'*'u'\l'\w'*'u\(ul'
.sp 0.5v
    /\h'-\w'/'u'\l'\w'/'u\(ul'

lvalue ::=
.sp 0.5v
    name
.sp 0.5v
    *\h'-\w'*'u'\l'\w'*'u\(ul' rvalue
.sp 0.5v
    rvalue [\h'-\w'['u'\l'\w'['u\(ul' rvalue ]\h'-\w']'u'\l'\w']'u\(ul'
.bp
constant ::=
.sp 0.5v
    {digit}\*<1\*>
.sp 0.5v
    \(aq\h'-\w'\(aq'u'\l'\w'\(aq'u\(ul' {char}\*<1\*>\s-3\h'-\w'1'u'\s+3\*{2\*} \(aq\h'-\w'\(aq'u'\l'\w'\(aq'u\(ul'
.sp 0.5v
    \(dq\h'-\w'\(dq'u'\l'\w'\(dq'u\(ul' {char}\*<0\*> \(dq\h'-\w'\(dq'u'\l'\w'\(dq'u\(ul'

name ::=
.sp 0.5v
    alpha {alpha-digit}\*<0\*>\s-3\h'-\w'0'u'\s+3\*{7\*}

alpha-digit ::=
.sp 0.5v
    alpha
.sp 0.5v
    digit
.sp 0.5v
\f[]
.fi
.RS 3
.RE
.SH 4
2.2 Comments and Character Sets
.LP
.br
.br
Comments are delimited as in PL/I by /* and */.
.br
.br
In general, B requires tokens to be separated by blanks, comments or
newlines, however the compiler infers separators surrounding any of the
characters \f[CR](){}[],;?:\f[R] or surrounding any maximal sequence of
the characters \f[CR]+-*/<>&|!\f[R].
.br
.br
The character set used in B is ANSCII.
.br
.br
The syntactic variable \(aqalpha\(aq is not defined.
It represents the characters \f[CR]A\f[R] to \f[CR]Z\f[R], \f[CR]a\f[R]
to \f[CR]z\f[R], \f[CR]_\f[R], and backspace.
.br
.br
The syntactic variable \(aqdigit\(aq is not defined.
It represents the characters \f[CR]0\f[R], \f[CR]1\f[R], \f[CR]2\f[R],
\&...
\f[CR]9\f[R].
.br
.br
The syntactic variable \(aqchar\(aq is not defined.
It is essentially any character in the set plus the escape character
\f[CR]*\f[R] followed by another character to represent characters not
easily represented in the set.
The following escape sequences are currently defined:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    *0  null
    *e  end-of-file
    *(  {
    *)  }
    *t  tab
    **  *
    *\(aq  \(aq
    *\(dq  \(dq
    *n  new line
\f[]
.fi
.RS 3
.RE
.LP
All keywords in the language are only recognized in lower case.
Keywords are reserved.
.SH 4
3.0 Rvalues and Lvalues
.LP
.br
.br
An rvalue is a binary bit pattern of a fixed length.
On the PDP-11 it is 16 bits.
Objects are rvalues of different kinds such as integers, labels, vectors
and functions.
The actual kind of object represented is called the type of the rvalue.
.br
.br
A B expression can be evaluated to yield an rvalue, but its type is
undefined until the rvalue is used in some context.
It is then assumed to represent an object of the required type.
For example, in the following function call
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    (b?f:g[i])(1,x>1)
\f[]
.fi
.RS 3
.RE
.LP
The expression \f[CR](b?f:g[i])\f[] is evaluated to yield an rvalue which is
interpreted to be of type function.
Whether \f[CR]f\f[] and \f[CR]g[i]\f[] are in fact functions is not checked.
Similarly, \f[CR]b\f[] is assumed to be of type truth value, \f[CR]x\f[]
to be type integer \fIetc\fR.
.br
.br
There is no check to insure that here are no type mismatches.
Similarly, there are no wanted, or unwanted, type conversions.
.br
.br
An lvalue is a bit pattern representing a storage location containing an
rvalue.
An lvalue is a type in B. The unary operator * can be used to interpret
an rvalue as an lvalue.
Thus
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    *X
\f[]
.fi
.RS 3
.RE
.LP
evaluates the expression \f[CR]x\fP to yield an rvalue, which is then interpreted
as an lvalue.
If it is then used in an rvalue context, the application of \f[CR]*\f[R]
yields the rvalue therein stored.
The operator \f[CR]*\f[R] can be thought of as indirection.
.br
.br
The unary operator \f[CR]&\f[R] can be used to interpret an lvalue as an
rvalue.
Thus
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    &x
\f[]
.fi
.RS 3
.RE
.LP
evaluates the expression \f[CR]x\fP as an lvalue.
The application of \f[CR]&\f[R] then yields the lvalue as an rvalue.
The operator \f[CR]&\f[R] can therefore be thought of as the address
function.
.br
.br
The names lvalue and rvalue come from the assignment statement which
requires an lvalue on the left and an rvalue on the right.
.SH 4
4.0 Expression Evaluation
.LP
.br
.br
Binding of expressions (lvalues and rvalues) is in the same order as the
sub-sections of this section except as noted.
Thus expressions referred to as operands of \f[CR]+\f[R] (section 4.4)
are expressions defined in sections 4.1 to 4.3.
The binding of operators at the same level (left to right, right to
left) is specified in each sub-section.
.SH 4
4.1 Primary Expressions
.IP "\f[B]1.\f[R]" 3
A name is an lvalue of one of three storage classes (automatic, external
and internal).
.RS 3
.RE
.IP "\f[B]2.\f[R]" 3
A decimal constant is an rvalue.
It consists of a digit between \f[CR]1\fP and \f[CR]9\fP followed by any
number of digits between \f[CR]O\fP and \f[CR]9\fP.
The value of the constant should not exceed the maximum value that can
be stored in an object.
.RS 3
.RE
.IP "\f[B]4.\f[R]" 3
An octal constant is the same as a decimal constant except that it
begins with a zero.
It is then interpreted in base 8.
Note that \f[CR]09\fP (base 8) is legal and equal to \f[CR]011\fP.
A character constant is represented by \f[CR]\(aq\f[R] followed by one
or two characters (possibly escaped) followed by another \f[CR]\(aq\f[R].
It has an rvalue equal to the value of the characters packed and right
adjusted.
.RS 3
.RE
.IP "\f[B]5.\f[R]" 3
A string is any number of characters between \f[CR]\(dq\f[R] characters.
The characters are packed into adjacent objects (lvalues sequential) and
terminated with the character \(aq\f[CR]*e\fP\(aq.
The rvalue of the string is the lvalue of the object containing the
first character.
See section 8.0 for library functions used to manipulate strings in a
machine independent fashion.
.RS 3
.RE
.IP "\f[B]6.\f[R]" 3
Any expression in () parentheses is a primary expression.
Parentheses are used to alter order of binding.
.RS 3
.RE
.IP "\f[B]7.\f[R]" 3
A vector is a primary expression followed by any expression in \f[CR][]\fP
brackets.
The two expressions are evaluated to rvalues, added and the result is
used as an lvalue.
The primary expression can be thought of as a pointer to the base of a
vector, while the bracketed expression can be thought of as the offset
in the vector.
Since \f[CR]E1[E2\fP] is identical to \f[CR]*(E1+E2)\fP,
and addition is commutative, the
base of the vector and the offset in the vector can swap positions.
.RS 3
.RE
.IP "\f[B]8.\f[R]" 3
A function is a primary expression followed by any number of expressions
in \f[CR]()\f[R] parentheses separated by commas.
The expressions In parentheses are evaluated (in an unspecified order)
to rvalues and assigned to the function\(aqs parameters.
The primary expression is evaluated to an rvalue (assumed to be type
function).
The function is then called.
Each call is recursive at little cost in time or space.
.RS 3
.RE
.IP "\f[B]\f[R]" 3
Primary expressions are bound left to right.
.RS 3
.RE
.SH 4
4.2 Unary Operators
.LP
.br
.br
.IP "\f[B]1.\f[R]" 3
The rvalue (or indirection) prefix unary operator \f[CR]*\f[R] is
described in section 3.0.
Its operand is evaluated to rvalue, and then used as an lvalue.
In this manner, address arithmetic may be performed.
.RS 3
.RE
.IP "\f[B]2.\f[R]" 3
The lvalue (or address) prefix unary operator \f[CR]&\fP is also described in
section 3.0.
Note that \f[CR]&*x\fP is identically \f[CR]x\fP,
but \f[CR]*&x\fP is only \f[CR]x\fP if \f[CR]x\fP is an lvalue.
.RS 3
.RE
.IP "\f[B]3.\f[R]" 3
The operand of the negate prefix unary operator \f[CR]-\fP is interpreted as an
integer rvalue.
The result is an rvalue with opposite sign.
.RS 3
.RE
.IP "\f[B]4.\f[R]" 3
The \f[CR]NOT\fP prefix unary operator \f[CR]!\fP
takes an integer rvalue operand.
The result is zero if the operand is non-zero.
The result is one if the operand is zero.
.RS 3
.RE
.IP "\f[B]5.\f[R]" 3
The increment \f[CR]++\f[R] and decrement \f[CR]--\f[R] unary operators
may be used either in prefix or postfix form.
Either form requires an lvalue operand.
The rvalue stored in the lvalue is either incremented or decremented by
one.
The result is the rvalue either before or after the operation depending
on postfix or prefix notation respectively.
Thus if \f[CR]x\fP currently contains the rvalue \f[CR]5\fP,
then \f[CR]++x\fP and \f[CR]x++\fP both change \f[CR]x\fP to \f[CR]6\fP.
The value of \f[CR]++x\fP is \f[CR]6\fP while \f[CR]x++\fP is \f[CR]5\fP.
.RS 3
.RE
.IP "\f[B]\f[R]" 3
Similarly, \f[CR]--x\fP and \f[CR]x--\fP store \f[CR]4\fP in \f[CR]x\fP.
The former has rvalue result \f[CR]4\fP, the latter \f[CR]5\fP.
.RS 3
.RE
.LP
.br
.br
Unary operators are bound right to left.
Thus \f[CR]-!x+\fP+ is bound \f[CR]-(!(x++))\fP.
.SH 4
4.3 Multiplicative Operators
.LP
.br
.br
The multiplicative binary operators \f[CR]*\f[R], \f[CR]/\f[R], and
\f[CR]%\f[R], expect rvalue integer operands.
The result is also an integer.
.IP "\f[B]1.\f[R]" 3
The operator * denotes multiplication.
.RS 3
.RE
.IP "\f[B]2.\f[R]" 3
The operator \f[CR]/\f[R] denotes division.
The result is correct if the first operand is divisible by the second.
If both operands are positive, the result is truncated toward zero.
Otherwise the rounding is undefined, but never greater than one.
.RS 3
.RE
.IP "\f[B]3.\f[R]" 3
The operator \f[CR]%\f[R] denotes modulo.
If both operands are positive, the result is correct.
It is undefined otherwise.
.RS 3
.RE
.LP
.br
.br
The multiplicative operators bind left to right.
.SH 4
4.4 Additive Operators
.LP
.br
.br
The binary operators \f[CR]+\f[R] and \f[CR]-\f[R] are add and subtract.
The additive operators bind left to right.
.SH 4
4.5 Shift Operators
.LP
.br
.br
The binary operators \f[CR]<<\f[R] and \f[CR]>>\f[R] are left and right
shift respectively.
The left rvalue operand is taken as a bit pattern.
The right operand is taken as an integer shift count.
The result is the bit pattern shifted by the shift count.
Vacated bits are filled with zeros.
The result is undefined if the shift count negative or larger than an
object length.
The shift operators bind left to right.
.SH 4
4.6 Relational Operators
.LP
.br
.br
The relational operators \f[CR]<\f[R] (less than), \f[CR]<=\f[R] (less
than or equal to), \f[CR]>\f[R] (greater than), and \f[CR]>=\f[R] (
greater than or equal to) take integer rvalue operands.
The result is one if the operands are in the given relation to one
another.
The result is zero otherwise.
.SH 4
4.7 Equality Operators
.LP
.br
.br
The equality operators \f[CR]==\f[R] (equal to) and \f[CR]!=\f[R] (not
equal to) perform similarly to the relation operators.
.SH 4
4.8 \f[CR]AND\fP operator
.LP
.br
.br
The \f[CR]AND\fP operator \f[CR]&\f[R] takes operands as bit patterns.
The result is the bit pattern that is the bit-wise \f[CR]AND\fP of the operands.
The operator binds and evaluates left to right.
.SH 4
4.9 The \f[CR]OR\fP operator
.LP
.br
.br
The \f[CR]OR\fP operator \f[CR]|\fP performs exactly as \f[CR]AND\fP,
but the result is the bit-wise inclusive \f[CR]OR\fP of the operands.
The \f[CR]OR\fP operator also binds and evaluates left to right.
.SH 4
4.10 Conditional Expression
.LP
.br
.br
Three rvalue expressions separated by \f[CR]?\f[R] and \f[CR]:\f[R] form
a conditional expression.
The first expression (to the left of the \f[CR]?\fP)
is evaluated.
If the result is non-zero, the second expression is evaluated and the
third ignored.
If the value is zero, the second expression is ignored and the third is
evaluated.
The result is either the evaluation of the second or third expression.
.br
.br
Binding is right to left.
Thus \f[CR]a?b:c?d:e \f[] is \f[CR]a?b:(c?d:e)\f[].
.SH 4
4.11 Assignment Operators
.LP
.br
.br
There are 16 assignment operators in B. All have the form
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    lvalue op rvalue
\f[]
.fi
.RS 3
.RE
.LP
The assignment operator \f[CR]=\fP merely evaluates the rvalue and stores the
result in the lvalue.
The assignment operators \f[CR]=|\f[R], \f[CR]=&\f[R], \f[CR]===\f[R],
\f[CR]=|=\f[R], \f[CR]=<\f[R], \f[CR]=<=\f[R], \f[CR]=>\f[R],
\f[CR]=>=\f[R], \f[CR]=<<\f[R], \f[CR]=>>\f[R], \f[CR]=+\f[R],
\f[CR]=-\f[R], \f[CR]=%\f[R], \f[CR]=*\f[R], and \f[CR]=/\f[R] perform a
binary operation (see sections 4.3 to 4.9) between the rvalue stored in
the assignment\(aqs lvalue and the assignment\(aqs rvalue.
The result is then stored in the lvalue.
The expression \f[CR]x=*10\fP is identical to \f[CR]x=x*10\fP.
Note that this is not \f[CR]x= *10\fP.
The result of an assignment is the rvalue.
Assignments bind right to left, thus \f[CR]x=y=0\fP assigns
zero to \f[CR]y\fP, then \f[CR]x\fP, and returns the rvalue zero.
.SH 4
5.0 Statements
.LP
.br
.br
Statements define program execution.
Each statement is executed by the computer in sequence.
There are, of course, statements to conditionally or unconditionally
alter normal sequencing.
.SH 4
5.1 Compound Statement
.LP
.br
.br
A sequence of statements in \f[CR]{}\f[R] braces is syntactically a
single statement.
This mechanism is provided so that where a single statement is expected,
any number of statements can be placed.
.SH 4
5.2 Conditional Statement
.LP
.br
.br
A conditional statement has two forms.
The first:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    if\h'-\w'if'u'\l'\w'if'u\(ul'(rvalue) statement\*<1\*>
\f[]
.fi
.RS 3
.RE
.LP
evaluates the rvalue and executes statement\*<1\*>, if the rvalue is
non-zero.
If the rvalue is zero, statement\*<1\*> is skipped.
The second form:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    if\h'-\w'if'u'\l'\w'if'u\(ul'(rvalue) statement\*<1\*> else\h'-\w'else'u'\l'\w'else'u\(ul' statement\*<2\*>
\f[]
.fi
.RS 3
.RE
.LP
is defined as follows in terms of the first form:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    if\h'-\w'if'u'\l'\w'if'u\(ul'(x=(rvalue)) statement\*<1\*> if\h'-\w'if'u'\l'\w'if'u\(ul'(!x) statement\*<2\*>
\f[]
.fi
.RS 3
.RE
.LP
Thus, only one of the two statements is executed, depending on the value
of rvalue.
In the above example, \f[CR]x\fP is not a real variable, but just a demonstration
aid.
.SH 4
5.3 While Statement
.LP
.br
.br
The while statement has the form:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    while\h'-\w'while'u'\l'\w'while'u\(ul'(rvalue) statement
\f[]
.fi
.RS 3
.RE
.LP
The execution is described in terms of the conditional and goto
statements as follows:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    x: if\h'-\w'if'u'\l'\w'if'u\(ul'(rvalue) { statement goto\h'-\w'goto'u'\l'\w'goto'u\(ul' x; ]
\f[]
.fi
.RS 3
.RE
.LP
Thus the statement is executed repeatedly while the rvalue is non-zero.
Again, \f[CR]x\fP is a demonstration aid.
.SH 4
5.4 Switch Statement
.LP
.br
.br
The switch statement is the most complicated statement in B. The switch
has the form:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    switch\h'-\w'switch'u'\l'\w'switch'u\(ul' rvalue statement\*<1\*>
\f[]
.fi
.RS 3
.RE
.LP
Virtually always, statement\*<1\*> above is a compound statement.
Each statement in statement\*<1\*> may be preceded by a case as follows:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    case\h'-\w'case'u'\l'\w'case'u\(ul' constant:\h'-\w':'u'\l'\w':'u\(ul'
\f[]
.fi
.RS 3
.RE
.LP
During execution, the rvalue is evaluated and compared to each case
constant in undefined order.
If a case constant is equal to the evaluated rvalue, control is passed
to the statement following the case.
If the rvalue matches none of the cases, statement\*<1\*> is skipped.
.SH 4
5.5 Goto Statement
.LP
.br
.br
The goto statement is as follows:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    goto\h'-\w'goto'u'\l'\w'goto'u\(ul' rvalue ;\h'-\w';'u'\l'\w';'u\(ul'
\f[]
.fi
.RS 3
.RE
.LP
The rvalue is expected to be of type label.
Control is then passed to the corresponding label.
Goto\(aqs cannot be executed to labels outside the currently executing
function level.
.SH 4
5.6 Return Statement
.LP
.br
.br
The return statement is used in a function to return control to the
caller of a function.
The first form simply returns control.
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    return\h'-\w'return'u'\l'\w'return'u\(ul' ;\h'-\w';'u'\l'\w';'u\(ul'
\f[]
.fi
.RS 3
.RE
.LP
The second form returns an rvalue for the execution of the function.
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    return\h'-\w'return'u'\l'\w'return'u\(ul' (\h'-\w'('u'\l'\w'('u\(ul' rvalue )\h'-\w')'u'\l'\w')'u\(ul' ;\h'-\w';'u'\l'\w';'u\(ul'
\f[]
.fi
.RS 3
.RE
.LP
The caller of the function need not use the returned rvalue.
.SH 4
5.7 Rvalue Statement
.LP
.br
.br
Any rvalue followed by a semicolon is a statement.
The two most common rvalue statements are assignment and function call.
.SH 4
5.8 Null Statement
.LP
.br
.br
A semicolon is a null statement causing no execution.
It is used mainly to carry a label after the last executable statement
in a compound statement.
It sometimes has use to supply a null body to a while statement.
.SH 4
6.0 Declarations
.LP
.br
.br
Declarations in B specify storage class of variables.
Such declarations also, in some circumstances, specify initialization.
.br
.br
There are three storage classes in B. Automatic storage is allocated for
each function invocation.
External storage is allocated before execution and is available to any
and all functions.
Internal storage is local to a function and is available only to that
function, but is available to all invocations of that function.
.SH 4
6.1 External Declaration
.LP
.br
.br
The external declaration has the form:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    extrn\h'-\w'extrn'u'\l'\w'extrn'u\(ul' name\*<1\*> ,\h'-\w','u'\l'\w','u\(ul' name\*<2\*> ... ;\h'-\w';'u'\l'\w';'u\(ul'
\f[]
.fi
.RS 3
.RE
.LP
The external declaration specifies that each of the named variables is
of the external storage class.
The declaration must occur before the first use of each of the
variables.
Each of the variables must also be externally defined.
.SH 4
6.2 Automatic Declaration
.LP
.br
.br
The automatic declaration also constitutes a definition:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    auto\h'-\w'auto'u'\l'\w'auto'u\(ul' name\*<1\*> {constant}\*<0\*>}\*<0\*>\s-3\h'-\w'0'u'\s+3\*{1\*} ,\h'-\w','u'\l'\w','u\(ul' name\*<2\*> {constant]\*<0\*>}\*<0\*>\s-3\h'-\w'0'u'\s+3\*{1\*} ... ;\h'-\w';'u'\l'\w';'u\(ul'
\f[]
.fi
.RS 3
.RE
.LP
In absence of the constant, the automatic declaration defines the
variable to be of class automatic.
At the same time, storage is allocated for the variable.
When an automatic declaration is followed by a constant, the automatic
variable is also initialized to the base of an automatic vector of the
size of the constant.
The actual subscripts used to reference the vector range from zero to
the value of the constant less one.
.SH 4
6.3 Internal Declaration
.LP
.br
.br
The first reference to a variable not declared as external or automatic
constitutes an internal declaration, All internal variables not defined
as labels are flagged as undefined within a function.
Labels are defined and initialized as follows:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    name :\h'-\w':'u'\l'\w':'u\(ul'
\f[]
.fi
.RS 3
.RE
.SH 4
7.0 External Definitions
.LP
.br
.br
A complete B program consists of a series of external definitions.
Execution is started by the hidden sequence
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    main(); exit();
\f[]
.fi
.RS 3
.RE
.LP
Thus, it is expected that one of the external definitions is a function
definition of main.
(Exit is a predefined library function.
See section 8.0)
.SH 4
7.1 Simple Definition
.LP
.br
.br
The simple external definition allocates an external object and
optionally initializes it:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    name {ival ,\h'-\w','u'\l'\w','u\(ul' ival ...}\*<0\*> ;\h'-\w''u'\l'\w''u\(ul'
\f[]
.fi
.RS 3
.RE
.LP
If the external object is defined with no initialization, it is
initialized with zero.
A single initialization with a constant initializes the external with
the value of the constant.
Initialization with a name initializes the external to the address of
that name.
Many such initializations may be accessed as a vector based at &name.
.SH 4
7.2 Vector Definitions
.LP
.br
.br
An external vector definition has the following form:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    name [\h'-\w'['u'\l'\w'['u\(ul' {constant}\*<0\*>}\*<0\*>\s-3\h'-\w'0'u'\s+3\*{1\*} ]\h'-\w']'u'\l'\w']'u\(ul' {ival ,\h'-\w','u'\l'\w','u\(ul' ival ...}\*<0\*> ;\h'-\w';'u'\l'\w';'u\(ul'
\f[]
.fi
.RS 3
.RE
.LP
The name is initialized with the lvalue of the base of an external
vector.
If the vector size is missing, zero is assumed.
In either case, the vector is initialized with the list of initial
values.
Each initial value is either a constant or a name.
A constant initial value initializes the vector element to the value of
the constant.
The name initializes the element to the address of the name.
The actual size is the maximum of the given size and the number of
initial values.
.SH 4
7.3 Function Definitions
.LP
.br
.br
Function definitions have the following form:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    name (\h'-\w'('u'\l'\w'('u\(ul' arguments )\h'-\w')'u'\l'\w')'u\(ul' statement
\f[]
.fi
.RS 3
.RE
.LP
The name is initialized to the rvalue of the function.
The arguments consist of a list of names separated by commas.
Each name defines an automatic lvalue that is assigned the rvalue of the
corresponding function call actual parameters.
The statement (often compound) defines the execution of the function
when invoked.
.SH 4
8.0 Library Functions
.LP
.br
.br
There is a library of B functions maintained in the file /f[CR]/etc/libb.a\fP.
The following is a list of those functions currently in the library.
See section II of [4] for complete descriptions of the functions marked
with an *.
.IP "\f[B]c = char(string, i);\f[R]" 3
.br
The \f[CR]i\fP-th character of the \f[CR]string\fP is returned.
.RS 3
.RE
.IP "\f[B]error = chdir(string) ;\f[R]" 3
.br
The path name represented by the \f[CR]string\fP becomes the current directory.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]error = chmod(string, mode);\f[R]" 3
.br
The file specified by the \f[CR]string\fP has its \f[CR]mode\fP changed to the mode
argument.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]error = chown(string, owner);\f[R]" 3
.br
The file specified by the \f[CR]string\fP has
its owner changed to the \f[CR]owner\fP argument.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]error = close(file) ;\f[R]" 3
.br
The open file specified by the file argument is closed.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]file = creat(string, mode);\f[R]" 3
.br
The file specified by the \f[CR]string\fP is either truncated or
created in the \f[CR]mode\fP specified depending on its prior existence.
In both cases, the file is opened for writing and a file descriptor is
returned.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]ctime(time, date);\f[R]" 3
.br
The system time (60-ths of a second) represented in the two-word
vector \f[CR]time\fP is converted to a 16-character \f[CR]date\fP
in the 8-word vector date.
The converted date has the following format: \(dq\f[CR]Mmm dd hh:mm:ss\fP\(dq.
.RS 3
.RE
.IP "\f[B]execl(string, arg0, arg1, ..., 0);\f[R]" 3
.br
The current process is replaced by the execution of the file specified
by \f[CR]string\fP.
The \f[CR]arg\fP-i strings are passed as arguments.
A return indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]execv(string, argv, count);\f[R]" 3
.br
The current process is replaced by the execution of the file specified
by \f[CR]string\fP.
The vector of \f[CR]argv\fP strings of length \f[CR]count\fP are passed as arguments.
A return indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]exit( ) ;\f[R]" 3
.br
The current process is terminated.
(*)
.RS 3
.RE
.IP "\f[B]error = fork( ) ;\f[R]" 3
.br
The current process splits into two.
The child process is returned a zero.
The parent process is returned the process ID of the child.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]error = fstat(file, status);\f[R]" 3
.br
The \f[CR]i-node\fP of the open file designated by file is put in the 20-word
vector status.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]char = getchar( ) ;\f[R]" 3
The next character form the standard input file is returned.
The character \(aq*e\(aq is returned for an \f[CR]end-of-file\fP.
.RS 3
.RE
.IP "\f[B]id = getuid();\f[R]" 3
.br
The user-ID of the current process is returned.
(*)
.RS 3
.RE
.IP "\f[B]error = gtty(file, ttystat);\f[R]" 3
.br
The teletype modes of the open file designated by file is returned in
the 3-word vector ttstat.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]lchar(string, i, char);\f[R]" 3
.br
The character char is stored in the \f[CR]i\fP-th character of the \f[CR]string\fP.
.RS 3
.RE
.IP "\f[B]error = link(string1, string2);\f[R]" 3
.br
The pathname specified by \f[CR]string2\fP is created such that it is a link to
the existing file specified by \f[CR]string1\fP.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]error = mkdir(string, mode);\f[R]" 3
.br
The directory specified by the \f[CR]string\fP is made to exist with the
specified access mode.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]file = open(string, mode);\f[R]" 3
.br
The file specified by the \f[CR]string\fP is opened for reading if mode is zero,
for writing if mode is not zero.
The open file designator is returned.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]printf(format, argl, ...);\f[R]" 3
.br
See section 9.3 below.
.RS 3
.RE
.IP "\f[B]printn(number, base);\f[R]" 3
.br
See section 9.1 below.
.RS 3
.RE
.IP "\f[B]putchar(char) ;\f[R]" 3
.br
The character char is written on the standard output file.
.RS 3
.RE
.IP "\f[B]nread = read(file, buffer, count);\f[R]" 3
.br
The \f[CR]count\fP number bytes are read into the vector \f[CR]buffer\fP
from the open file designated by \f[CR]file\fP.
The actual number of bytes read are returned.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]error = seek(file, offset, pointer);\f[R]" 3
.br
The I/O pointer on the open file designated by \f[CR]file\fP is set to the value
of the designated \f[CR]pointer\fP plus the \f[CR]offset\fP.
A \f[CR]pointer\fP of zero designates the beginning of the file.
A \f[CR]pointer\fP of one designates the current I/O pointer.
A \f[CR]pointer\fP of two designates the end of the file.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]error = setuid(id);\f[R]" 3
.br
The user-ID of the current process is set to \f[CR]id\fP.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]error = stat(string, status);\f[R]" 3
.br
The \f[CR]i-node\fP of the file specified by the \f[CR]string\fP
is put in the 20-word vector \f[CR]status\fP.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]error = stty(file, ttystat);\f[R]" 3
.br
The teletype modes of the open file designated by \f[CR]file\fP
is set from the 3-word vector \f[CR]ttystat\fP.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]time(timev);\f[R]" 3
.br
The current system time is returned in the 2-word vector \f[CR]timev\fP.
(*)
.RS 3
.RE
.IP "\f[B]error = unlink(string);\f[R]" 3
.br
The link specified by the \f[CR]string\fP is removed.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]error = wait( );\f[R]" 3
.br
The current process is suspended until one of its child processes
terminates.
At that time, the child\(aqs process-ID is returned.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.IP "\f[B]nwrite = write(file, buffer, count);\f[R]" 3
.br
The \f[CR]count\fP number of bytes are written out of the
vector \f[CR]buffer\fP on the open file designated by \f[CR]file\fP.
The actual number of bytes written are returned.
A negative number returned indicates an \f[CR]error\fP.
(*)
.RS 3
.RE
.LP
.br
.br
Besides the functions available from the library, there is a predefined
external vector named argv included with every program.
The size of \f[CR]argv\fP is \f[CR]argv[0]+1\fP.
The elements \f[CR]argv[1]...argv[argv[0]]\fP are the parameter strings as passed
by the system in the execution of the current process.
(See shell in II of [4])
.SH 4
9.0 Examples
.LP
.br
.br
The examples appear exactly as given to B.
.SH 4
9.1 Print a Non-Negative Number
.LP
.br
.br
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
/* The following function will print a non-negative number, n, to
  the base b, where 2<=b<=10,  This routine uses the fact that
  in the ANSCII character set, the digits O to 9 have sequential
  code values.  */

printn(n,b) {
    extrn putchar;
    auto a;

    if(a=n/b) /* assignment, not test for equality */
        printn(a, b); /* recursive */
    putchar(n%b + \(aq0\(aq);
}
\f[]
.fi
.RS 3
.RE
.SH 4
9.2 Calculate the Constant \fIe\fP-2
.LP
.br
.br
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
/* The following program will calculate the constant e-2 to about
   4000 decimal digits, and print it 50 characters to the line in
   groups of 5 characters.  The method is simple output conversion
   of the expansion
     1/2! + 1/3! + ... = .111....
   where the bases of the digits are 2, 3, 4, . . . */

main() {
    extrn putchar, n, v;
    auto i, c, col, a;

    i = col = 0;
    while(i<n)
        v[i++] = 1;
    while(col<2*n) {
        a = n+1 ;
        c = i = 0;
        while (i<n) {
            c =+ v[i] *10;
            v[i++]  = c%a;
            c =/ a--;
        }

        putchar(c+\(aq0\(aq);
        if(!(++col%5))
            putchar(col%50?\(aq \(aq: \(aq*n\(aq);
    }
    putchar(\(aq*n*n\(aq);
}

v[2000];
n 2000;
\f[]
.fi
.RS 3
.RE
.SH 4
9.3 The \f[CR]printf\f[R] Formatting, Conversion and Print Routine
.LP
.br
.br
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
/* The following function is a general formatting, printing, and
   conversion subroutine.  The first argument is a format string.
   Character sequences of the form \(aq%x\(aq are interpreted and cause
   conversion of type \(aqx\(aq of the next argument, other character
   sequences are printed verbatim.   Thus

    printf(\(dqdelta is %d*n\(dq, delta);

    will convert the variable delta to decimal (%d) and print the
    string with the converted form of delta in place of %d.   The
    conversions %d-decimal, %o-octal, *s-string and %c-character
    are allowed.

    This program calls upon the function \(aqprintn\(aq. (see section
    9.1) */

printf(fmt, x1,x2,x3,x4,x5,x6,x7,x8,x9) {
    extrn printn, char, putchar;
    auto adx, x, c, i, j;

    i= 0;   /* fmt index */
    adx = &x1;  /* argument pointer */
loop :
    while((c=char(fmt,i++) ) != \(aq%\(aq) {
        if(c == \(aq*e\(aq)
            return;
        putchar(c);
    }
    x = *adx++;
    switch c = char(fmt,i++) {

    case \(aqd\(aq: /* decimal */
    case \(aqo\(aq: /* octal */
        if(x < O) {
            x = -x ;
            putchar(\(aq-\(aq);
        }
        printn(x, c==\(aqo\(aq?8:1O);
        goto loop;

    case \(aqc\(aq : /* char */
        putchar(x);
        goto loop;

    case \(aqs\(aq: /* string */
        while(c=char(x, j++)) != \(aq*e\(aq)
            putchar(c);
        goto loop;
    }
    putchar(\(aq%\(aq) ;
    i--;
    adx--;
    goto loop;
}
\f[]
.fi
.RS 3
.RE
.SH 4
10.0 Usage
.LP
.br
.br
Currently on UNIX, there is no B command.
The B compiler phases must be executed piecemeal.
The first phase turns a B source program into an intermediate language.
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
/etc/bc source interm
\f[]
.fi
.RS 3
.RE
.LP
The next phase turns the intermediate language into assembler source, at
which time the intermediate language can be removed.
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
/etc/ba interm asource
rm interm
\f[]
.fi
.RS 3
.RE
.LP
The next phase assembles the assembler source into the object file
a.out.
After this the a.out file can be renamed and the assembler source file
can be removed.
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
as asource
mv a.out object
rm asource
\f[]
.fi
.RS 3
.RE
.LP
The last phase loads the various object files with the necessary
libraries in the desired order.
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
ld object /etc/brt1 -lb /etc/bilib /etc/brt2
\f[]
.fi
.RS 3
.RE
.LP
Now a.out contains the completely bound and loaded program and can be
executed.
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
a.out
\f[]
.fi
.RS 3
.RE
.LP
A canned sequence of shell commands exists invoked as follows:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
sh /usr/b/rc x
\f[]
.fi
.RS 3
.RE
.LP
It will compile, convert, assemble and load the file \f[CR]x.b\fP into the
executable file \f[CR]a.out\fP.
.SH 4
12.0 Implementation and Debugging
.LP
.br
.br
A B program is implemented as a reverse Polish threaded code
interpreter: The object code consists of a series of addresses of
interpreter subroutines.
Machine register \f[CR]3\fP is dedicated as the interpreter program counter.
Machine register \f[CR]4\fP is dedicated as the interpreter display pointer.
The display pointer points to the base of the current stack frame.
The first word of each stack frame is a pointer to the previous stack
frame (prior display pointer.)
The second word in each frame is is the saved interpreter program
counter (return point of the call creating the frame.)
Automatic variables start at the third word of each frame.
Machine register \f[CR]5\fP is dedicated as the interpreter stack pointer.
The machine stack pointer plays no role in the interpretation.
An example source code segment, object code and interpreter subroutines
follow:
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
    automatic = external + 100.;
    va; 4       / lvalue of first automatic on stack
    x; .external    / rvalue of external on stack
    c; 100.     / rvalue of constant on stack
    b12     / binary operator #12 (+)
    b1      / binary operator #1 (=)
    ...
va:
    mov (r3)+,r0
    add r4,rO       / dp+offset of automatic
    asr rO      / lvalues are word addresses
    mov r0,(r5)+
    jmp    *(r3)+       / linkage between subroutines
x:
    mov *(r3)+,(r5)+
    jmp *(r3)+
c:
    mov (r3)+,(r5)+
    jmp *(r3)+

b12:
    add -(r5),-2(r5)
    jmp *(r3)+

b1:
    mov -(r5),r0    / rvalue
    mov -(r5),r1    / lvalue
    asl r1      / now byte address
    mov r0,(r1)     / actual assignment
    mov r0,(r5)+    / = returns an lvalue
    jmp *(r3)+
\f[]
.fi
.RS 3
.RE
.LP
The above code as compared to the obvious 3 instruction directly
executed equivalent gives the approximate 5:1 speed and 2:1 space
penalties one pays in using B.
.br
.br
The salient features for debugging are then:
.IP "\f[B]1.\f[R]" 3
Machine \f[CR]r4\fP is the display pointer and can be used to trace function
calls and determine automatic variable values at each call.
.RS 3
.RE
.IP "\f[B]2.\f[R]" 3
Machine \f[CR]r3\fP is the current program counter and can be used to determine
the current point of execution.
All externals are globals with their variable names prefixed by
\(aq.\(aq.
Thus the debugger [4] can be used directly to give values of external
variables.
.RS 3
.RE
.IP "\f[B]4.\f[R]" 3
All data lvalues are word addresses and therefore not directly
examinable by the debugger.
(See \(aq request in I of [4]) [I don\(aqt really know what this might
refer to.
It is probably some command in the very early Unix debugger -- DMR]
.RS 3
.RE
.SH 4
13.0 Nasties
.LP
.br
.br
This section describes the \(aqglitches\(aq found in all languages, but
rarely reported.
.IP "\f[B]1.\f[R]" 3
The compiler makes sense of certain expressions with operators in
ambiguous cases (\fIe.g.\fP \f[CR]a+++b\fP) but
not others even in unambiguous cases (\fIe.g.\fP \f[CR]a+++++b\fP).
.RS 3
.RE
.IP "\f[B]2.\f[R]" 3
The B assembler \f[CR]/etc/ba\fP does not correctly handle all possible
combinations of intermediate language.
The symptom is undefined symbols in the assembly of
the output from \f[CR]/etc/ba\fP.
This is rare.
.RS 3
.RE
.IP "\f[B]3.\f[R]" 3
The B interpreter \f[CR]/etc/bilib\fP is really a library of threaded code
segment.
The following code segments have not yet been written:
.RS 3
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
b103    =&
b104    ===
b105    =!=
b106    =<=
b107    =<
b110    =>=
b111    =>
b120    =/
\f[]
.fi
.RS 3
.RE
.RE
.IP "\f[B]4.\f[R]" 3
Initialization of external variables with addresses of other externals
is not possible due to a loader deficiency.
.RS 3
.RE
.IP "\f[B]5.\f[R]" 3
Since externals are implemented as globals with names
preceded by \(aq\f[CR].\fP\(aq, the external
names \(aq\f[CR]byte\fP\(aq, \(aq\f[CR]endif\fP\(aq, \(aq\f[CR]even\fP\(aq
and \(aq\f[CR]globl\fP\(aq conflict with assembler pseudooperations and should be
avoided.
.RS 3
.RE
.SH 4
14.0 Diagnostics
.LP
.br
.br
Diagnostics consist of two letters, an optional name, and a source line
number.
Due to the free format of the source, the number might be high.
The following is a list of the diagnostics.
.IP "\f[B]\f[R]" 3
.IP
.nf
\f[CR]
error   name    meaninq

$)  --  {} imbalance
()  --  () imbalance
*/  --  /* */ imbalance
[]  --  [] imbalance
>c  --  case table overflow (fatal)
>e  --  expression stack overflow (fatal)
>i  --  label table overflow (fatal)
>s  --  symbol table overflow (fatal)
ex  --  expression syntax
lv  --  rvalue where lvalue expected
rd  name    name redeclaration
sx  keyword statement syntax
un  name    undefined name
xx  --  external syntax
\f[]
.fi
.RS 3
.RE
.LP
[ signature line ]
.SH 4
References
.LP
.br
.br
.IP "\f[B]1.\f[R]" 3
Richards, M.
.UL "The BCPL Reference Manual."
Multics repository M0099.
.RS 3
.RE
.IP "\f[B]2.\f[R]" 3
Canaday, R.H.
and Ritchie, D.M.
.UL "Bell Laboratories BCPL."
MM 69-1371/1373-7/12.
.RS 3
.RE
.IP "\f[B]3.\f[R]" 3
Ritchie, D.M.
.UL "The UNIX Time Sharing System."
MM 71-1273-4.
.RS 3
.RE
.IP "\f[B]4.\f[R]" 3
Thompson, K. and Ritchie, D.M.
.UL "UNIX Programmer\(aqs Manual.  Available by arrangement."
.RS 3
.RE
.LP
.br
.br
[\f[I]Two of these references\(emthe one to Richards\(aqs early BCPL
manual, and to the first edition of the UNIX Programmer\(aqs manual, are
available under my main home page.
The ultimate content of \(dqThe UNIX Time Sharing System evolved into
the C.ACM paper, there (in further revised form) into the BSTJ paper
available there as well.\f[R]]
.br
.br
Copyright \(co 1996 Lucent Technologies Inc.
All rights reserved.
