@SubSection
@Tag { objects }
@Title { Basic structural operators }
@Begin
@PP
A programming language may be considered complete when it attains the
power of a Turing machine, but no such criterion seems relevant to
document formatting. Instead, as the language develops and new
applications are attempted, deficiencies are exposed and the operator set is
revised to overcome them.
@PP
Lout has a repertoire of 23 primitive operators
(Figure {@NumberOf primitives}),
@Figure
@Caption { The 23 primitive operators of Lout, in order of
increasing precedence. }
@Tag { primitives }
@Tab
vmargin { 0.5vx }
@Fmta { @Col @I A ! @Col B }
{
@Rowa
A { object {@Code "/"}gap object }
B { Vertical concatenation with mark alignment }
@Rowa
A { object {@Code "//"}gap object }
B { Vertical concatenation with left justification }
@Rowa
A { object {@Code "|"}gap object }
B { Horizontal concatenation with mark alignment }
@Rowa
A { object {@Code "||"}gap object }
B { Horizontal concatenation with top-justification }
@Rowa
A { object {@Code "&"}gap object }
B { Horizontal concatenation within paragraphs }
@Rowa
A { {@Code "@OneCol"} object }
B { Hide all but one column mark of @I object }
@Rowa
A { {@Code "@OneRow"} object }
B { Hide all but one row mark of @I object }
@Rowa
A { font @Code "@Font" object }
B { Render @I object in nominated font }
@Rowa
A { breakstyle @Code "@Break" object}
B { Break paragraphs of @I object in nominated style }
@Rowa
A { spacestyle @Code "@Space" object }
B { Render spaces between words in nominated style }
@Rowa
A { length {@Code "@Wide"} object }
B { Render @I object to width @I length }
@Rowa
A { length {@Code "@High"} object }
B { Render @I object to height @I length }
@Rowa
A { {@Code "@HExpand"} object}
B { Expand horizontal gaps to fill available space }
@Rowa
A { {@Code "@VExpand"} object}
B { Expand vertical gaps to fill available space }
@Rowa
A { {@Code "@HScale"} object }
B { Horizontal geometrical scaling to fill available space }
@Rowa
A { {@Code "@VScale"} object }
B { Vertical geometrical scaling to fill available space }
@Rowa
A { angle {@Code "@Rotate"} object }
B { Rotate @I object by @I angle }
@Rowa
A { PostScript {@Code "@Graphic"} object }
B { Escape to graphics language }
@Rowa
A { @Code "@Next" object }
B { Add 1 to an object denoting a number }
@Rowa
A { object @Code "@Case" alternatives }
B { Select from a set of alternative objects }
@Rowa
A { identifier @Code "&&" object }
B { Cross reference }
@Rowa
A { cross-reference @Code "@Open" object }
B { Retrieve value from cross reference }
@Rowa
A { cross-reference @Code "@Tagged" object}
B { Attach cross referencing tag to object }
}
which has proven adequate for a wide variety of features, including equations,
tables, and page layout, and so seems to be reasonably complete in this
pragmatic sense. In this section we introduce the eight concatenation and
mark-hiding operators. To them falls the basic task of assembling complex
objects from simple ones, and they were the first
to be designed and implemented.
@PP
Many of the operators of Eqn can be viewed as building small tables. A
built-up fraction, for example, has one column and three rows
(numerator, line, and denominator). Numerous investigations of this
kind convinced the author that operators capable of assembling the rows
and columns of tables would suffice for building all kinds of objects.
@PP
The simplest objects are empty objects and literal words like
{@Code metempsychosis}, which have one column mark and one row mark:
@ID {
@ShowMarks metempsychosis
}
To place two arbitrary objects side by side, we use the infix
operator {@Code "|"}, denoting horizontal concatenation. For
example,
@ID {
@Code "USA |0.2i Australia"
}
produces the object
@ID {
@ShowMarks USA |0.2i @ShowMarks Australia
}
The row marks are merged into one, fixing the vertical position of
the objects relative to each other; their horizontal separation is
determined by the @I gap attached to the operator, in this case 0.2
inches. We think of the gap as part of the operator, although
strictly it is a third parameter. It may be omitted, defaulting to
{@Code "0i"}.
@PP
@I {Vertical concatenation} & , denoted by the infix operator {@Code "/"},
is the same apart from the change of direction:
@ID {
@Code "Australia /0.1i USA"
}
produces the object
@ID {
@ShowMarks Australia /0.1i
@ShowMarks USA
}
with column marks merged and a 0.1 inch gap.
@PP
Consider now what happens when horizontal and vertical are combined:
@ID @OneRow @Code {
|1m "{" USA |1m "|0.2i" |1m Australia "}"
/1vx "/0.1i" | "{" Washington | "|" | Canberra "}"
}
The two parameters of @Code "/" now have two column marks each, and
they will be merged with the corresponding marks in the other
parameter, yielding the object
@ID @OneRow {
@BackEnd @Case {
PostScript @Yield {
@ShowMarks USA &
{ 0 ymark moveto xsize 10 pt add ymark lineto [ 3 pt ] 0 setdash stroke }
@Graphic {1c @Wide }
|0.2i @ShowMarks Australia
/0.1i @ShowMarks Washington | @ShowMarks Canberra
}
PDF @Yield {
@ShowMarks USA &
{ [ __mul(3, __pt) ] 0 d 0 __ymark m __add(__xsize, __mul(10, __pt)) __ymark l S }
@Graphic {1c @Wide }
|0.2i @ShowMarks Australia
/0.1i @ShowMarks Washington | @ShowMarks Canberra
}
}
}
The @Code "0.2i" gap separates columns, not individual items in
columns, so a gap attached to the second @Code "|" would serve no
purpose; any such gap is ignored. If the number of marks to be merged
differs, empty columns are added at the right to equalize the number. The
four marks protruding from the result are all available for merging
with neighbouring marks by other concatenation operators. The precedence
of @Code "|" is higher than the precedence of {@Code "/"}, so the braces
could be omitted.
@PP
When lines of text are concatenated, it is conventional to measure
their separation from baseline to baseline (mark to mark in Lout),
rather than from edge to edge as above. This idea of different
reference points for measurement evolved over the years into a
system of six @I {gap modes} (Figure {@NumberOf gapmodes}), expressed
by appending a letter to the length. For example, @Code "|0.2i" is
an abbreviation for {@Code "|0.2ie"}, meaning 0.2 inches measured
from edge to edge; @Code "|0.3ix"
produces a 0.3 inch gap measured from mark to mark and widened if
necessary to prevent overstriking; and @Code "|2.5it" places its right
parameter 2.5 inches from the current left margin, irrespective of
the position of the left parameter. There is also a choice of
eleven units of measurement (inches, centimetres, multiples of the
current font size, etc.), the most interesting being
the @Code r unit: one @Code r is the column width minus the width of
the following object, so that @Code "|1rt" produces sufficient space
to right justify the following object, and @Code "|0.5rt" to center
it. These features implement spacings needed in practice rather
than suggested by theory. They work with all five concatenation
operators, horizontal and vertical.
@Figure
@Tag { gapmodes }
@Caption { The six gap modes (@I length is any length). Hyphenation
mode has an extra property not shown here. }
@Fig {
{ /2.5vx Edge-to-edge |0.3i {@Code "|"} &1p {@I length} &1p {@Code e}
/4.2vx Hyphenation |0.3i {@Code "|"} &1p {@I length} &1p {@Code h}
/4.2vx Overstrike |0.3i {@Code "|"} &1p {@I length} &1p {@Code o}
/4.2vx Mark-to-mark |0.3i {@Code "|"} &1p {@I length} &1p {@Code x}
/4.2vx Kerning |0.3i {@Code "|"} &1p {@I length} &1p {@Code k}
/4.2vx Tabulation |0.3i {@Code "|"} &1p {@I length} &1p {@Code t}
}
||0.5i
@Box margin { 0c } 6c @Wide 14.5c @High 9p @Font
{
@OneRow {
@At { 1c @Wide 0.5c @High } @Put { @LBox 0.2co }
@At { 4c @Wide 0.5c @High } @Put { @LBox 0.5co }
@At { 2.2c @Wide 1.4c @High } @Put { @DoubleArrow 1.8c }
@At { 2.2c @Wide 1.6c @High } @Put { 1.8c @Wide { &0.5rt @I length } }
}
//4vx
@OneRow {
@At { 1c @Wide 0.5c @High } @Put { @LBox 0.2co }
@At { 4c @Wide 0.5c @High } @Put { @LBox 0.5co }
@At { 2.2c @Wide 1.4c @High } @Put { @DoubleArrow 1.8c }
@At { 2.2c @Wide 1.6c @High } @Put { 1.8c @Wide { &0.5rt @I length } }
}
//4vx
@OneRow {
@At { 1c @Wide 0.5c @High } @Put { @LBox 0.2co }
@At { 4c @Wide 0.5c @High } @Put { @LBox 0.5co }
@At { 1.2c @Wide 1.5c @High } @Put { @DoubleArrow 3.3c }
@At { 1.2c @Wide 1.7c @High } @Put { 3.3c @Wide { &0.5rt @I length } }
}
//4vx
@OneRow {
@At { 1c @Wide 0.5c @High } @Put { @LBox 0.2co }
@At { 4c @Wide 0.5c @High } @Put { @LBox 0.5co }
@At { 1.2c @Wide 1.5c @High } @Put { @DoubleArrow 3.3c }
@At { 1.2c @Wide 1.7c @High }
@Put 3.3c @Wide { |0.5rt @Eq { max(length, a+b) } }
@At { 1.2c @Wide 0.4c @High } @Put { @DoubleArrow 1.0c }
@At { 1.2c @Wide 0.2c @High } @Put { 1.0c @Wide { &0.5rt @I a } }
@At { 4c @Wide 0.4c @High } @Put { @DoubleArrow 0.5c }
@At { 4c @Wide 0.2c @High } @Put { 0.5c @Wide { &0.5rt @I b } }
}
//4.5vx
@OneRow {
@At { 1c @Wide 0.5c @High } @Put { @LBox 0.2co }
@At { 4c @Wide 0.5c @High } @Put { @LBox 0.5co }
@At { 1.2c @Wide 1.5c @High } @Put { @DoubleArrow 3.3c }
@At { 1.2c @Wide 1.7c @High }
@Put { 3.3c @Wide { |0.5rt @Eq { max(length, a, b) } } }
@At { 1.2c @Wide 0.4c @High } @Put { @DoubleArrow 1.0c }
@At { 1.2c @Wide 0.2c @High } @Put { 1.0c @Wide { &0.5rt @I a } }
@At { 4c @Wide 0.4c @High } @Put { @DoubleArrow 0.5c }
@At { 4c @Wide 0.2c @High } @Put { 0.5c @Wide { &0.5rt @I b } }
}
//4vx
@OneRow {
@At { 1c @Wide 0.5c @High } @Put { @LBox 0.2co }
@At { 4c @Wide 0.5c @High } @Put { @LBox 0.5co }
@At { 0.0c @Wide 1.6c @High } @Put { @DoubleArrow 4.0c }
@At { 2.8c @Wide 1.8c @High } @Put { @I length }
}
//5vx
@DoubleArrow 6c
//0.1c |0.5rt @I { current bound }
}
}
@PP
When we construct a built-up fraction, the result has three row marks, but
only the second should be visible outside the object:
@ID @Eq { @ShowMarks { X over Y } }
This is a common problem, and accordingly a @Code "@OneRow" operator was
introduced for hiding all but one of the row marks of its
parameter. Normally, the first mark is the survivor, but a later mark can
be chosen by prefixing @Code "^" to the preceding concatenation operator:
@ID @Code "@OneRow { X ^/2p @HLine /2p Y }"
has the desired result, where {@Code "2p"} is two points and @Code "@HLine"
is an easy combination of Lout's graphics operators. A similar operator,
{@Code "@OneCol"}, hides column marks.
@PP
A variant of @Code "/" called @Code "//" is provided which performs
vertical concatenation but ignores all column marks and simply
left-justifies its two parameters:
@ID @OneRow @Code {
"Heading //0.1i"
"A |0.2i B /0.1i"
"C | D"
}
has result
@ID { Heading //0.1i A |0.2i B /0.1i C | D }
showing that spanning columns in tables motivate the inclusion of this
operator. There is an analogous @Code "||" operator. The author
would have preferred to leave out these operators, since they
complicate the implementation, and it is interesting to examine the
prospects of doing so.
@PP
The @Code "//" operator is formally redundant, because in general
the expression @Code "x // y" can be replaced by
@ID @OneRow @Code {
"@OneCol { | x } /"
"@OneCol { | y }"
}
for any objects {@Code x} and {@Code y}. By concatenating an empty
object at the left of @Code x and hiding all but that empty object's
column mark, we effectively shift {@Code x}'s column mark to its left
edge. The same goes for {@Code y}, so the @Code "/" operator has just
one column mark to merge, at the extreme left, and its effect is
indistinguishable from {@Code "//"}.
@PP
Unfortunately, if @Code y consists of two rows separated by {@Code "/"},
as in the example above, both rows must be placed inside the
{@Code "@OneCol"}, and the table cannot be entered in the simple
row-by-row manner that non-expert users naturally expect. Another
advantage of @Code "//" is that its left parameter can be printed
before its right parameter is known; this is important when the left
parameter is an entire page.
@PP
The fifth and final concatenation operator, {@Code "&"}, is an explicit
version of the horizontal concatenation operator interpolated when
objects are separated by white space. It is formally identical to
@Code "|" except for taking higher precedence and being subject to
replacement by @Code "//1vx" during paragraph breaking
(Section {@NumberOf style}).
@End @SubSection