1
0
mirror of synced 2026-02-27 00:39:58 +00:00
Files

2152 lines
57 KiB
Plaintext

.if n .po 10
.lg 0
.ta 6m, +6m, +6m, +6m, +6m, +6m
.ta 6m, +6m, +6m, +6m, +6m, +6m
.if n .po 10
. Paragraph
.de PA
.sp
.ti +5
.ne 2
..
. New page
.de np
'fi
'in 0
'sp 2
'tl ''- % -''
'sp 2
'tl '------'------'------'
'sp 2
'tl '\\*(sn \\*(st''\\fBQed\\fP Tutorial'
'sp 2
'if \\n(be .nf
'in
..
. Section start
.de SE
.ne 5
.sp
.ul
\\$1 \\$2
.ds sn "\\$1
.ds st "\\$2
.PA
..
. Begin example
.de BE
.sp
.nf
.in 5
.nr be 1
..
. End example
.de EE
.sp
.fi
.in 0
.nr be 0
..
. Start of text proper
.sp 10
.ce 25
.ps 16
Programming in \fBQed\fP: A Tutorial
.ps 10
.sp 5
Robert Pike
.ce 0
.wh -5 np
.sp 5
.SE I Introduction
\fBQed\fP is a programmable text editor, intended for use primarily
by programmers.
For the average user, \fBQed\fP's power will likely be unnecessary,
even troublesome, and its use is discouraged.
For a ``knowledgeable'' user who is willing to learn how to use it
properly, however, \fBQed\fP is a powerful tool, both when used
as an editor or as a (rather idiosyncratic and low-level) programming
language.
.PA
This document will depend quite heavily on the University of Toronto
\fBUNIX\fP manual section for \fBEd\fP, ed(I).
\fBEd\fP at U. of T. is a heavily modified,
although functionally almost identical,
version of the original version 6 \fBUNIX\fP editor.
The features of U. of T. \fBEd\fP not found in regular version
6 \fBEd\fP include: the `*' address separator,
two character error messages (a query followed by a character
which indicates the error, e.g. `?s' for a failed substitution),
a simple undo `u' command,
and a join `j' command of somewhat greater generality
than the \fBPWB\fP version.
There is an ``add-on'' document for U. of T. \fBEd\fP
which describes the enhancements and modifications, at user level,
made at U. of T.
This document assumes the reader to be familiar with
all these features,
and to be quite familiar with \fBEd\fP's capabilities in general
and particular.
.PA
Before beginning
the description of \fBQed\fP,
some warning should be given.
\fBUNIX\fP \fBEd\fP is closely based on a version of \fBQed\fP,
running under the \fBGCOS\fP operating system,
which was written by Dennis Ritchie and Ken Thompson.
When Dennis Ritchie wrote \fBEd\fP,
he removed many of the features,
including most of the programming capabilities,
but left in most of the text editing power.
Although the \fBQed\fP described here is significantly
more complex and powerful than \fBEd\fP
(and quite unrelated to the \fBGCOS\fP version),
its increase in power is not proportionate to its
increase in complexity.
In short, \fBEd\fP is a very powerful editor, and for general
editing jobs quite sufficient.
\fBQed\fP simplifies some more complicated tasks,
and its multifile capability and programmability makes many things possible which
cannot be done in \fBEd\fP,
but to use it well requires a fairly thorough understanding
of its operation, which is fairly intricate.
.PA
\fBQed\fP has several drawbacks that should be admitted early.
\fBQed\fP programs can be difficult to read, even if done
carefully, since it operates at about the level of
a rather cryptic assembler.
It is also fairly easy to damage things like files using
\fBQed\fP incorrectly, but
it is much harder (we think) to accidentally cause trouble
with the U. of T. \fBQed\fP than earlier ones,
as the implementers worked very hard at
safeguarding.
The safeguards are strong enough, though,
that occasional users who desire a particular \fBQed\fP
feature for one editing job need not feel in danger of
making a serious mistake!
\fBQed\fP's power can lead
the user astray;
it has far more power than is needed for most editing jobs.
As an illustrative but rather artificial example,
consider the problem of reversing the lines of a file,
so that the last line appears at the top,
and the first at the bottom,
with the contents of the lines unchanged.
At first, this may seem a problem for \fBQed\fP if it is only
to be done once or twice (if it is to be done often,
we would certainly write a C program!),
but it is very easy to do in \fBEd\fP:
.BE
g/^/m0
.EE
(It will be the convention in this tutorial to show user input
in Roman font, and editor output in Italic.)
A second, slightly more complicated example is the problem of
placing two columnar files, say files generated by `ls',
alongside each other in a buffer.
\fBEd\fP can again do the job quite well (assume there are 15 lines in
each listing):
.BE
e file1
\fI142\fP
r file2
\fI153\fP
1ka
15,30 g/^/m'a\e
.ti +3
+ka
g/^/ .,+j/ /
.EE
.ti +5
This preamble may sound discouraging, but it is only
promoting realism.
When used well and carefully, \fBQed\fP can be a great time saver,
fun to work with, and sometimes even elegant.
This document was written using \fBQed\fP,
storing sequences like \efBQed\efP in registers
to save typing, etc.
Well, if you've read this far,
you must be determined,
so you're ready to learn about buffers.
.SE II Buffers
\fBEd\fP has one buffer \(em one scratch area in which to keep text.
\fBQed\fP has 56,
labeled by lower case alphabetics `a' to `z',
upper case alphabetics `A' to `Z',
and for reasons worth ignoring at this point,
the characters `{', `|', `}' and `~'.
Each buffer has its own associated dot, dollar and filename.
The easiest way to see how they work is to
chdir to a directory with about ten C source files and type:
.BE
qed *.c
.EE
(we've already accomplished something impossible in \fBEd\fP!)
\fBQed\fP will print out the character count for each file,
and then wait for a command. Type
.BE
n
.EE
and look at the output.
If you were in the \fBQed\fP source directory, you would see
something like
.BE
.ul
a \fB.\fP90 address.c
.ul
b 90 blkio.c
.ul
c 618 com.c
.ul
d 369 getchar.c
.ul
e 200 getfile.c
.ul
f 113 glob.c
.ul
g 695 main.c
.ul
h 157 misc.c
.ul
i 134 move.c
.ul
j 358 pattern.c
.ul
k 154 putchar.c
.ul
l 36 setaddr.c
.ul
m 106 string.c
.ul
n 407 subs.c
.EE
The first column is the buffer name,
the dot marks the current buffer,
the number is the value of dollar in the buffer, that is, the
number of lines, and the last column is the file name local
to that buffer.
The `n' (for ``names'') command is \fBQed\fP's equivalent of `ls -l'.
Now do an `f' command. You'll see
.BE
.ul
a \fB.\fP90 address.c
.EE
In \fBQed\fP, the `f' command tells you more than just the file name.
Now change something in the file, say substitute out a tab
or delete an empty line, and do another `f':
.BE
.ul
a\(aa\fB.\fP90 address.c
.EE
The prime tells you that the contents of the buffer are
known to differ from the named file.
Now try
.BE
bb f
.ul
b \fB.\fP90 blkio.c
.EE
The ``bb'' and ``f'' can be placed on the same line,
as \fBQed\fP does not require a newline after most commands.
The `bb' says ``change to buffer b''.
Buffer `b' is now the current buffer, as indicated
by the dot.
If you browse around the buffer for a while,
you will see that it is really a world unto itself,
but changing back to buffer `a' by a ``ba'' command
will reset you back to the original file,
with dot still at whatever line it was when you ``bb''d.
.PA
Why have multiple buffers?
For one thing, we can copy or move text between buffers.
Go back to buffer `a' and isolate a subroutine, marking
its beginning with ``ka'' and its last line with ``kb''.
Then type
.BE
\&'a,'b tz0
.EE
This is a regular copy command, but the `z' after the `t'
tells \fBQed\fP that the text is to be copied to buffer `z'.
The `0' is the usual address, but is interpreted in buffer
`z' rather than the current buffer.
Of course, if the character after the `t' is not
a valid buffer name, \fBQed\fP
performs the usual copy command.
Do another `n', you will see that you are currently
in buffer `z', and dot is set to the last line copied.
The move `m' command behaves similarly.
.SE III "Special Characters (I)"
Change to buffer `z' and clear it:
.BE
bz 0,$d
.EE
Note that line `0' is a valid address for deletion.
``*d'' also would work here, and both these addressing modes
will not generate an error if the buffer is empty.
As well, you could have typed
.BE
bz Z
.EE
for ``zero''; the `Z' command unequivocally clears the buffer,
even its remembered file name.
Now do the following:
.BE
ap g/^[a-zA-Z_].*(/p
.ul
g/^[a-zA-Z_].*(/p
.EE
``ap'' appends the single line of text,
printing it afterwards, so
buffer `z' now contains a possibly useful command for \fBQed\fP
(make sure you know what it does!) which we can
call up when desired.
Now read some C source into buffer `a' if there isn't already
some there, and try out the buffer like this:
.BE
ba
\ebz
.ul
int *address(deflt)
.ul
adderr(c)
.EE
The sequence `\ebz' means ``insert
the contents of buffer `z' in my input stream here.''
The final newline is effectively removed from the buffer,
so that if you decided later that you wanted to know the line
numbers as well, you could tag a ``.='' command on the end:
.BE
ba
\ebz .=
.ul
int *address(deflt)
.ul
2
.ul
adderr(c)
.ul
89
.EE
Although most \fBQed\fP commands can be arbitrarily grouped on a line,
the global `g' command, as in \fBEd\fP,
still reads the full line for its command list,
which in this case is ``p .=''.
.PA
The above example is very important, as it uses a mixture of
buffer input and terminal input to run a command,
an all-pervading concept in \fBQed\fP programming.
.PA
`\ebz' is called a ``special character'', although in some sense
it isn't really a character at all, as it gets
completely replaced with the contents of buffer `z'.
The `\ebz' is interpreted
.ul
whenever
input is expected, not just when commands are being read.
Try the following examples:
.BE
by a
\ebz
\&.
p
.ul
g/^[a-zA-Z_].*(/p
ap \ebz
.ul
g/^[a-zA-Z_].*(/p
!echo "\ebz"
.ul
g/^[a-zA-Z_].*(/p
.ul
!
.EE
The buffer could contain multiple lines, which would be handled as usual.
We could, for example, save in a buffer our example from the
introduction, which merged two columnar files alongside each other,
and invoke it when desired just as we invoked the global search above.
But care must be exercised here, as the newlines in the buffer,
except for the last,
are also placed in the input stream. If we were to type, with the
multiline buffer in `z', the command
.BE
s/x/\ebz/p
.EE
mistakenly
expecting that buffer `z' had just a single line of text,
say a frequently typed word,
we would really be saying:
.BE
s/x/1ka
15,30 g/^/m'a\e
.ti +3
+ka
g/^/ .,+j/ //p
.EE
This would, of course, cause an immediate error, and since \fBQed\fP
always returns to teletype input when an error occurs,
no damage would be done.
Sometimes, though, such mistakes can cause strange results!
.PA
If you did try the above command, the error message would be
.BE
?bz2.0 ?x
.EE
\fBQed\fP gives a traceback on errors.
The elements of the traceback are of the form
.BE
?bXM.N
.EE
where X is the buffer name, M the line number, and N the character
number of the character at which the error was recognized.
In the above example, the substitute found a syntax error (``?x'')
when it read the newline,
so the error occurred at the beginning of line 2 of buffer `z'.
If input is nested,
the deepest-called buffer is
printed first.
.PA
It is a good idea to pause here and look carefully over what
has been covered so far, as
the concept of using a buffer to store regular
files or command input interchangeably
is really the heart of \fBQed\fP.
Before reading on,
use \fBQed\fP for a while
to familiarize yourself with the system of buffers,
and try out a few simple buffers
for repetitive editing tasks.
.PA
\fBQed\fP has a fair number of special characters
for various purposes. In the rest of this section
we will look briefly at some of the simpler ones
to give you some insight into how they behave.
First, enter buffer `z' again and append:
.BE
a
\eFa
\eFb
\&.
.EE
and then look at what \fBQed\fP has appended to the buffer.
The special character `\eFa' means ``the file name
for buffer `a',''
and, like all special characters, is
interpreted whenever input is expected.
The special character `\ef' is a shorthand for
``the saved file name in the current buffer.''
Try
.BE
f junk
.ul
z\(aa\fB.\fP2 junk
w
.ul
15
!ls \ef
.ul
junk
.ul
!
.EE
Idioms such as
.BE
!cc \ef
.EE
are very common.
If your file name is long, `\ef' can save much typing.
If the file name is changed, through an `f' or `e' command,
the name actually associated with `\ef' is only changed when
the new name is completely read in.
Thus, you can type
.BE
e \ef
.EE
to reinitialize a buffer,
or
.BE
e /usr/source/s2/\ef
.EE
to edit the system version of a program.
There is another special character like `\ef',
but it is more useful for programming.
\eB means ``the current buffer name.''
Try
.BE
!echo \eB
.ul
z
.ul
!
.EE
.SE IV "Special Characters (II)"
The easiest way to gain familiarity with
the more abstruse characters is to use them
in messages, which are a special case of comments.
A comment starts with a double quote `"' and continues
until the first following double quote, or the end
of the line, whichever is first.
The line is ignored by
\fBQed\fP, except that dot is set to the addressed line, if there is one:
.BE
4 " This comment sets dot to line 4
.EE
Messages are just like comments, except that the first character after
the double quote is another double quote.
If the message ends with a double quote rather than a
newline, no newline is printed:
.BE
" hi
"" hi
.ul
hi
"" hi there "
\fI hi there \fP [cursor is left on this line]
""Current buffer: b\eB
Current buffer: bx
.EE
This last example is mildly interesting.
Can we save the command in, say, buffer `x'
and call it back, from any buffer, when desired?
.BE
bA \ebx
Current buffer: bA
.EE
In principle, it can be done, since the current buffer is the one
we are working on, not the one being read for input.
But, to put the characters `\eB' in a buffer,
we must delay their interpretation so that
they are not replaced with the buffer name until
read back as command input.
In most systems on \fBUNIX\fP, this is done
by typing an extra backslash,
but things are more civilized in \fBQed\fP.
In \fBQed\fP, special characters are
.ul
delayed,
not quoted.
Perhaps it's simplest just to state the rules:
.sp
.in +5
.ti -2
-\ \eX, a special character if X is one of
`b', `B', `c', `f', `F', `l', `N', `p', `r', `z' or `"',
sometimes (as with `\eb') followed by a buffer name,
is interpreted
.ul
immediately.
(We will see what all these special characters are in due course.)
.ti -2
-\ \eZ, where Z is not one of the above, undergoes no interpretation at all.
In particular, the backslash is not stripped away.
.ti -2
-\ \ec is reduced, on scanning, to \e, but not re-scanned.
.ti -2
-\ \e\(aaX is equivalent to \eX, but special characters embedded in \eX
are not interpreted.
.sp
.in
Things are a little different in regular expressions, but
let's ignore them for the moment.
These four rules, simple though they are,
define the interpretation of backslashes in \fBQed\fP.
Note that `\e\eZ', where Z is again not one of the above characters,
remains `\e\eZ', but if Z
.ul
is
special, say `f' when the saved file name is ``junk.c'',
`\e\ef' becomes `\ejunk.c'.
.PA
Now we know how to install a `\eB' in our buffer: we delay
its interpretation by putting a `c'
between the backslash and the `B'.
(The `c' is for ``character'',
or (it is rumoured) for Mr. E. S. Cape, inventor of the backslash.)\
The `\ecB' will reduce to `\eB' when typed in:
.BE
bz ap ""Current buffer: b\ecB
.ul
Current buffer: b\eB
bA \ebz
.ul
Current buffer: bA
.EE
Since `\ecc' will reduce to `\ec',
the number of `c's present is always just the
number of times the interpretation is to be delayed.
.PA
To decide how many delays are necessary, here is the list
of input forms that cause characters to be interpreted:
.sp
.in +5
.ti -2
-\ teletype input
.ti -2
-\ commands or text saved in buffers invoked using a special character
.ti -2
-\ command lines for the `g', `v', `G', `V' or `h' commands (`g' and `v' are
the same as in \fBEd\fP; we'll see the others a little later)
.in
.sp
Note that characters are
.ul
not
interpreted when buffers are read from or written to files,
or moved or copied with the `m' or `t' commands.
Experience is a great help here, so let's look at some examples:
.BE
bx
s/$/\eB/ appends `x' to current line
s/$/\ecB/ appends `\eB' to current line
s/$/\eccB/ appends `\ecB' to current line
.EE
but:
.BE
g/xxxx/ s/$/\eccB/
.EE
appends `\eB' to all lines with ``xxxx'';
the extra `c' is because the command is in a global command string.
Let's say we want to change all the `\en's to be `\en\et'.
There are two ways:
.BE
*s/\en/\en\et/
" equivalent to
*s/\ecn/\ecn\ect/
" or
g/\en/ s/\en/\en\et/
.EE
No delays are necessary because `\en' and `\et' are not special characters,
but delaying them once makes no difference.
(Warning: `\en' has special meaning in the replacement text of a
substitution in the U. of T. \fBEd\fP.)
.PA
While we're dealing with globals, it is a good time to introduce the `\eN'
special character.
It means, simply, a newline,
and is useful primarily because we can delay it in the usual way.
Commands, such as `r', which deal with filenames must often
be followed by a newline, but can be dealt with using `\eN'
in globals.
The \fBEd\fP sequence
.BE
g/xxxx/ r\e
\&.=
.EE
can be put all on one line in \fBQed\fP:
.BE
g/xxxx/ r\ecN .=
.EE
The newline is delayed.
In original version 6 \fBEd\fP, it is impossible to
globally
substitute a newline into lines,
but it's straightforward (by \fBQed\fP standards!) in \fBQed\fP:
.BE
g/xxxx/ s//\e\ecN/p
.EE
The `\e\ecN' is a backslash followed by a delayed newline.
The `\ecN' becomes `\eN' when scanned by the global,
and a newline when read during the substitution.
In \fBQed\fP (and U. of T. \fBEd\fP) we could also do this
by the functionally slightly different
.BE
g/xxxx/ s//\e\e
/p
.EE
[Do you see the difference?].
.PA
Backslashes in general are handled more reasonably in \fBQed\fP
than in other \fBUNIX\fP programs;
because special characters are ``delayed'' rather than ``quoted'',
the number of characters required to insert a special character,
with interpretation delayed
.ul
n
times, is just \fIn\fP+2 or \fIn\fP+3,
rather than exponential in \fIn\fP.
A \fBtroff\fP
line with 31 backslashes, a not-unheard-of
occurrence,
would in \fBQed\fP have a single backslash followed by 5 `c's.
(And would be much easier to understand, text edit, and debug!)
.PA
In particular, \fBQed\fP handles backslashes differently from \fBEd\fP.
As mentioned earlier, the \fBEd\fP command
.BE
s2/"/\e\en"/p
.EE
is simply
.BE
s2/"/\en"/p
.EE
in \fBQed\fP, because `\en' is not a special character.
There are, however, characters which are not ``special'' in the sense
we are using here, but are ``magic'' in that they have non-literal meaning.
The most obvious are characters such as `.' and `$' in regular
expressions, which must be ``quoted'' with a backslash to remove
their special meaning and make them literal.
(It becomes clear after using \fBQed\fP, or even \fBEd\fP, for a while that
.ul
all
the magic characters in regular expressions and the like
should require a backslash to become
.ul
magic,
rather than literal,
but the current choice is too ``wired in'' to the minds of
most \fBEd\fP users to be changed now.)\
Because they are not special characters,
their interpretation need not be delayed \(em
they only mean something to the substitute command.
None of the magic characters in the substitution
.BE
s/\e(\e.*\e)xxx$/\e1/
.EE
require delaying when typed in or run from a global command:
.BE
g/xyz/ s/\e(\e.*\e)xxx$/\e1/
.EE
[Exercise: Is the following command the same as the above substitution?
.BE
s/\ec(\ec.*\ec)xxx$/\ec1/
.EE
Why or why not?
Is this the same as the global substitution?
.BE
g/xyz/ s/\ec(\ec.*\ec)xxx$/\ec1/
.EE
Try it to test your answers.]
.PA
Because of these magic characters, two backslashes in a row `\e\e'
mean a single backslash `\e' in regular expressions;
otherwise it would be impossible to substitute
in a real backslash before a magic character:
.BE
a abc xyz def
s/xyz/\e\e&/p
.ul
abc \exyz def
up
.ul
abc xyz def
s/xyz/\e\e\e&/p
.ul
abc \e& def
.EE
What about sequences like `\e\eB'?
Well, `\eB' is not a character at all,
but a special character (sorry for the terminology)
as it is
.ul
immediately,
at the lowest level
of input, replaced by the current buffer name.
Since `\e\e' is not a special character,
and has non-literal meaning only when found between
regular expression delimiters,
the substitute itself never sees the second backslash.
All interpretation of special characters is done
before the substitute sees them.
If the current buffer is buffer `a',
.BE
s/\e\eB/x/
.EE
does exactly the same thing as
.BE
s/\ea/x/
.EE
Also, because \fBQed\fP converts `\e\e' to `\e' in
regular expressions,
.BE
s2/"/\e\en"/p
.EE
is the same as
.BE
s2/"/\en"/p
.EE
since `\en' is not a special character.
.PA
\fBQed\fP saves the last used regular expression and replacement text
used in an `s' or `j' command,
so that they can be called back using `\ep' (for ``pattern'')
and `\er'.
`\ep' is handy when you want to change the saved pattern.
If, for example, you start searching for ``proc()''
and want the declaration, but find there are very many usages of ``proc()'',
it is simple to find an occurrence of ``proc()'' at the beginning
of a line:
.BE
/proc()/
.ul
x=proc();
//
.ul
x=proc()*2;
/^\ep/
.ul
proc(){
.EE
`\ep' is of somewhat limited usefulness, as the null regular expression
is essentially the same as ``/\ep/'',
but `\er' provides a new convenience.
Browsing throught text doing repetitive substitution
is simplified considerably by using `\er':
.BE
s/apples/mangos and pears/p
.ul
I ain't got no mangos and pears
//
.ul
your mother's apples smelled like they were
s//\er/p
.ul
your mother's mangos and pears smelled like they were
.EE
There is a danger with `\ep' and `\er':
if they contain delayed special characters,
each usage of `\ep' or `\er' removes one delay.
If the current file name is ``wylbur,''
it may be difficult to deal with ``troff'' font changes:
.BE
p
.ul
editors such as Wylbur are so
s/Wylbur/\ecfBWylbur\ecfP/p
.ul
editors such as \efBWylbur\efP are so
//
.ul
Wylbur is also no good for
s//\er/p
.ul
wylburBWylburwylburP is also no good for
" Oops
.EE
This is the sort of trouble which the \e\(aa special character can
circumvent.
`\e\(aar' means the usual `\er', but with special characters inside
uninterpreted.
Let's fix up our second substitution above:
.BE
up
.ul
Wylbur is also no good for
s//\e\(aar/p
.ul
\efBWylbur\efP is also no good for
" Much better
.EE
`\er' is also handy for fixing a certain class of mistakes:
.BE
p
.ul
textp=get(a->text.fdes);
s/text/tbuf/p
.ul
tbufp=get(a->text.fdes);
" Oops again
us2//\e\(aar/p
.ul
textp=get(a->tbuf.fdes);
.EE
``us2//\e\(aar/p'' is a \fBQed\fP idiom
which undoes a substitute and does it again on the second match in the line.
.PA
Now, as an exercise, use \fBQed\fP
for a while until you feel comfortable with
the use of backslashes. If you find them
confusing, work with
\fBQed\fP, doing fancy things if you feel up to it,
until the confusion disappears \(em
what follows will be much stranger...
.SE V "Special Characters (III)"
Now that we've established the ground rules,
we can begin to use some of the fancier stuff in \fBQed\fP.
.PA
The special character `\el' returns a line of text from
standard input,
usually the user at the terminal
(i.e.
if input is in a buffer, it is temporarily redirected to the terminal).
The terminating newline is stripped away.
Since it is interpreted immediately, `\el' is rarely of value
except when delayed, but let's look at how it behaves in ``immediate mode:''
.BE
""\elMessage\eN
.ul
Message
""\elMessage
.ul
Message
""\elMessage
s of words
.ul
Messages of words
.EE
The extra newline, whether provided by the `\eN' or by a second
carriage return, is necessary because the `\el' strips its
terminating newline away, but the comment is looking
for a newline itself in order to terminate.
[Some questions to consider:
If `\ebx' is used instead of `\el',
the second newline is not required. Why?
In the last example above,
which characters are returned by `\el'?
What is the origin of the others, if any?
What would the above examples do if
the comments were terminated with a double quote?]
.PA
Well, `\el' is clearly of little use if not delayed,
but it is important to understand how it behaves.
.PA
An early version of U. of T. \fBQed\fP had only lower case buffer names,
and when the names `{' through `~' were added it was necessary
to go through the manual changing some of the "`z'"s into
"`~'"s, but not all of them.
The following single line made the job very simple:
.BE
g/`z'/ p ""replacement:" s//`\ecl'/p
.EE
Each line with a `z' is printed, the user is prompted for the
replacement, and the response (either a `z' or a `~' in our case)
inserted.
The single delay ensures that `g' places a literal `\el'
in the substitution string,
which is then interpreted when each call to the substitute
builds its replacement (``right-hand side'') text.
This sort of operation can also be performed using an `x' command
driven by a global, but \fBQed\fP can be programmed
to do most of the work.
.PA
Here's another example:
.BE
bz *d
ap ""Comment:\ecl" s|$| /* \ecl */|p
.ul
""Comment:\el" s|$| /* \el */|p
.EE
The first `\el' in the comment ``eats'' the input remaining
on the line after the `\ebz' which invokes the command:
.BE
ba a c.code;
\ebz
\fIComment:\fPstylish
.ul
c.code; /* stylish */
.EE
Of course, if the comment contains the character `|', problems will occur.
If we intend typing the comment on the same line as the invocation of the buffer,
we want neither the ``Comment:'' message nor
the extra `\el' which clears the input line.
.BE
up
.ul
c.code;
bz s/".*" //p
.ul
s|$| /* \el */|p
ba \ebzstylish
.ul
c.code; /* stylish */
.EE
This latter form is likely more useful,
as it can be called from a global
(the previous version could, but required the user
to type extra newlines).
For example, to comment all occurrences of a variable:
.BE
g/\e{var\e}/ p \ecbz
.EE
Each line is printed, and the user's response is appended as a comment.
No extra `\el' is needed at the end, to ``clear'' the input line,
as the `g' reads the line up to and including the terminal newline,
so the first `\el' returns the next line typed in.
Note that the `\ebz' is delayed so that it is interpreted
for each line with ``var''.
We could have set up our buffer so that the `\el' was delayed,
by inserting a `\ecl' instead.
Then, the buffer would be invoked as `\ebz', without a delay.
In effect, then, the `c' in the buffer call delays
the `\el'. If the buffer had only literal text,
no delay would be necessary.
Our choice of where to put the delay
was made by having the buffer be invocable directly from
the keyboard.
Just for the record,
note that we can achieve the effect of `\ecb' above by
typing `\e\(aab', although the manner in which it works
is quite different.
.PA
These examples are somewhat ``low-key'',
but begin to show how the parts of \fBQed\fP fit together.
Later, we will see how the `\el' can be used
to control execution of commands.
.SE VI Registers
\fBQed\fP has 56 registers, with the same names as buffers:
`a' to `z', `A' to `Z', `{', `|', `}' and `~'.
Buffers and registers are otherwise unrelated.
The registers are used to store simple text and short
command sequences.
In fact, most of the command buffers we have created
so far would be better suited to storage in registers;
buffers are generally used for storage of file text proper
and multiline command sequences.
The two main advantages of using registers to store text
are:
they can be set and manipulated without leaving the current buffer,
and they do not appear in the output from `n' commands,
which is significant because a user may typically have
twenty or more defined registers.
.PA
Registers are manipulated with the `z' (for ``zdring''!) command.
The character after the `z' is the name of the register being
operated on,
and the next character is an operation code.
The most straightforward operations are assignment and printing:
.BE
za:procrastination
zap
.ul
procrastination
.EE
The string being assigned to the register is terminated by a newline.
If a newline is to be embedded in the register, `\eN' provides
the cleanest mechanism:
.BE
za:line1\ecNline2
zap
.ul
line1\e\eNline2
a
\eza
\&.
-,.p
.ul
line1
.ul
line2
.EE
Registers are invoked in the obvious way: `\eza' inserts the
contents of register `a' into the input stream.
Note in the above example that the append could not be done
on one line,
as the embedded newline in the register would cause the
first line (``line1'') of the register to be appended,
and the second (``line2'') to be interpreted as command input.
This is another example of embedded newlines causing trouble:
be careful!
.PA
There are many operation characters for registers;
they are listed in full in the manual section.
We can add text at the end or beginning of the register with
``za$<string>'' and ``za^<string>'';
increment and decrement the ASCII value of the characters
in the register with ``za+N'' and ``za-N'', where N is a number;
and do subzdring (!) operations with the ``take'' and ``drop'' functions
``za)N'' and ``za(N''.
One particularly handy form is
.BE
za/regular expression/
.EE
which saves in register `a' the string in the current line
which matches the regular expression.
There are several other register operations we will introduce
when required.
.PA
These operations are quite straightforward; we will see them
all used when we start to program \fBQed\fP.
.PA
Registers can also be manipulated numerically.
When so used,
assignments stop at the first non-numeric character,
comparisons are arithmetic rather than lexical, and so on.
The command syntax is similar, except that a number sign `#'
is placed between the register name and the operation character:
.BE
za#:4
za#*-5
zap
.ul
-20
.EE
If desired, a series of operations can be strung together into
a single command, with some increase in execution efficiency:
.BE
za#:4#*-5#p
.ul
-20
.EE
The main difference between ``zap'' and ``za#p'',
if register `a' is entirely numeric text,
is that `#p' can be appended at the end of a sequence
of numeric operations, as above.
An error occurs
if numeric operations are performed on
a register which does not contain only a number.
.PA
Perhaps the most important use of register numeric operations
is in addressing.
The operation character `a' causes the register to receive
the line number of the address of the command:
.BE
$za#a
.EE
assigns register `a' to be the number
of lines in the current buffer, and
.BE
/xxxx/za#a
.EE
saves in ``za'' the address of the first forward occurrence of ``xxxx''.
The `r' operation character (for ``range'')
stores the first given address in the named register,
and the second address in the register whose name is
lexically one greater:
.BE
1,$ za#r "or" *za#r
.EE
puts `1' in register `a' and the value of `$' in register `b'.
Neither `a' nor `r'
changes the value of dot.
These operations are usually used to pass addresses to an execution buffer;
if the first line of a buffer is
.BE
za#r
.EE
then if the buffer is invoked as
.BE
-5,.\ebz
.EE
registers `a' and `b'
contain the lines to be operated on by the buffer.
.PA
Numerical operations are frequently useful in text editing,
such as when generating defined constants for a table:
.BE
a
read
write
open
close
creat
\&.
" capitalize
?read?,.s/.*/^/p
.ul
CREAT
za#:0
?READ?,.g/^/s/.*/#define & \ecza/p za#+1
.ul
#define READ 0
.ul
#define WRITE 1
.ul
#define OPEN 2
.ul
#define CLOSE 3
.ul
#define CREAT 4
.EE
The `^' (caret) character in the right hand side of a substitute
behaves like `&', but flips the case of alphabetics in the matched string.
.SE VII "Control Structures"
The most commonly used control structure in \fBQed\fP is certainly
the global command, `g', which is remarkably powerful and versatile,
as the previous example demonstrates.
The ability to place several commands on a line,
and the simplicity of `\eN', make globals even easier to use in \fBQed\fP
than in \fBEd\fP.
.PA
Along with the concept of a line-by-line execution goes that of buffer-by-buffer execution,
which is provided in \fBQed\fP
by the `globuf' commands `G' and `V'.
They are quite simple to use:
their format is identical to regular globals,
but the regular expression is used to match the
output which would be produced by an `f' command in each buffer.
Only buffers which contain text or have a remembered file name
are tested for a match.
If a buffer matches the regular expression, the command list
is executed in that buffer.
For example,
.BE
G/.\(aa.* ./w
.EE
writes out all buffers which have been modified since
last written.
The white space in the above example is a tab,
which is the actual delimiter used by the `f' and `n' commands
between the number of lines in the buffer and the file name.
Here's a fancier example:
.BE
G/./ g/thing/ ""\ecB \ecf: " p
.EE
It scans through all non-null buffers for occurences of ``thing'',
and prints the buffer, file name and line for
each occurrence.
.PA
\fBQed\fP also has a loop control structure, the `h' command (for ``\fIh\fPuntil'').
`h', like `g', takes a line of commands and executes it repeatedly.
It has four forms:
.in +10
.ti -5
hN
\h'|1.5i'executes the line N times
.ti -5
ht
\h'|1.5i'executes the line until the truth flag is `true'
.ti -5
hf
\h'|1.5i'executes the line until the truth flag is `false'
.ti -5
ha
\h'|1.5i'(`always') executes the line forever, or until an error
.in 0
Although the loop is an ``until'',
.BE
h0 p
.EE
is guaranteed to execute zero times.
.PA
The truth flag is set by substitutions and comparisons in registers.
When a register is compared to some value, the truth flag is
set according to the success of the comparison.
When a substitution is made, the truth flag is set
if a substitution was performed.
As a simple example, say you have prepared a letter to be sent to
someone, using \fBQed\fP, only to find that the erase character
is a backspace, not `#' as you had been using.
To fix the problem,
.BE
g/^/ hf s/.#//
.EE
[Why does ``s/.#//g'' not work?]
Note that ``huntil''s can be run inside globals,
and, in fact, can be nested arbitrarily deep.
Globals can also be run from huntils;
the only restriction is that globals cannot be called
from globals, as \fBQed\fP can only mark a line for a global once.
Similarly, globufs cannot be called from globufs.
.PA
As in globals, huntils stop the scan of the command sequence
at the first newline.
To build an alphabet in register A:
.BE
za:a
zA:
h26 zA$\ecza\ecNza+1
.EE
Note that \fBQed\fP code is not always easy to read!
If you happen to know that the character below `a' in ASCII
is a back quote, you could build the alphabet a little more simply:
.BE
za:`
zA:
h26 za+1 zA$\ecza
.EE
Register `a' could also be used in auto-increment mode to simplify
things even further:
.BE
za:`
zA:
h26 zA$\ecz+a
.EE
The `+' between the `z' and `a' in the register call cause the register
to be incremented
.ul
before
placed in the input stream.
Auto-decrements are also possible (`z-a') as are numerical
increments and decrements (`z#+a' and `z#-a').
Only increments and decrements of one unit are possible.
.PA
As a less frivolous example (one that was used in writing this
tutorial), a huntil makes it simple to convert, say, the ``troff''
command ``.ul 5'' to five ``.ul''s, one after each affected line:
.BE
g/^\e.ul [0-9]+/ zn/[0-9]+/ zn#-1 s/ [0-9]+// h\eczn +a .ul
.EE
It looks horrible, but it works, and can save much trouble
if there are (as in the tutorial) twenty or more places
where the fix needs to be made.
(The `+' character in regular expressions is like `*',
but guarantees at least one match.)
Of course, until familiarity with \fBQed\fP is developed,
the mental effort required to write a line like this and have
it work is probably considerably greater than the
physical effort required to type in the changes individually.
Even for beginning users, though, saving the complicated
patterns and commands such as ``ap\ .ul'' in registers
would make the job much more pleasant.
.PA
Again, care must be taken when invoking registers or buffers in
huntils;
.BE
h20 \ebz
" or "
h20 \ecbz
.EE
will likely not do what is expected if buffer `z' contains
more than one line.
.PA
The other major new control structure in \fBQed\fP is the `y' command
(for ``\fIy\fPump'';
think of ``jump'' pronounced with a Swedish accent).
The syntax is:
.BE
y[tf][N o \(aalabel \(galabel]
.EE
which translates as follows:
If the `t' or `f' is present,
jump only if the appropriate condition is
satisfied; otherwise jump always.
The `N', a number, is interpreted as a line number in the current
executing buffer which is to be the next line read for commands.
The `o' (for ``out'') causes the current input source,
such as a global command string or buffer, to be terminated.
If the input source is a buffer, the effect is to return from the buffer;
if a global, the execution of the global (or huntil) is stopped.
For example,
.BE
za#:1
h50 za#+1 za#>20 yto
.EE
executes 21 times, leaving register `a' set to ``21''.
The forms
.BE
y[tf]\(aalabel
.EE
and
.BE
y[tf]\(galabel
.EE
are similar to ``y[tf]N'', but the line to which control is transferred
is the first line found, searching forward in the buffer (or backward, if the
back quote is the operation character)
which begins with the comment
.BE
"label
.EE
Initial blanks and tabs are ignored, and the scan of the
label stops at the first blank, tab, newline or double quote.
If no matching label is found, execution resumes at the first
character past the label in the yump command.
Note that the label must be matched exactly;
it is not interpreted as a regular expression.
.PA
There are few non-trivial small examples which illustrate the use of yumps,
but they will be used extensively later on in the tutorial.
For the moment, a remark on style.
Clearly, with only a ``goto'', flow of control in \fBQed\fP
can become messy if care is not taken.
It is recommended that yumps only be used in
easily identifiable forms such as
.BE
<condition> yf\(aaelse
...
y\(aafi
"else
...
"fi
.EE
and
.BE
"do
...
<condition> yf\(gado
"od
.EE
or
.BE
"{
<condition> yf\(aa}
...
y\(ga{
"}
.EE
One particularly useful form of labeled yumps is a switch statement
based on a line of input from the user. This mechanism makes
command interpretation very simple; it is essentially a fancy switch statement:
.BE
y\(aaX\el
"default:
...
yo
"Xcase1
...
yo
"Xcase2
...
yo
etc.
.EE
One other form of yump exists; it is intended primarily to
skip the rest of a global or huntil command sequence, without
stopping the execution completely.
Its form is simply ``yt'' or ``yf''.
When invoked, it jumps over the current input source
up to and including the next newline.
It can also be used as a shorthand in buffers,
but such usage is discouraged.
.SE VIII "Calling the Shell"
\fBQed\fP has two methods of calling the Shell aside from the `!' (``bang'')
command: ``crunch'' (`<') and ``zap'' (`>').
Crunch takes the standard output from the Shell command
and reads it into the current buffer,
as if the Shell were run into a temporary file which was then read in
with an `r' command.
Like the `r' command, `<' takes an optional address which
specifies the line (defaulting to `$') at which
the text is read in.
.BE
< ls
.ul
162
.ul
!
.EE
appends a list of the files in the current directory.
One very common usage of the crunch command is to
find out what needs to be done, by a command such as
.BE
bz < grep "\e{var\e}" *.c
.ul
434
.ul
!
.EE
and using buffer `z' as a sort of checklist for
making modifications to source files and the like:
.BE
bz < cc -c *.c | tee /dev/tty
.ul
... diagnostic messages ...
.ul
282
.ul
!
.EE
saves the listing of the compile errors so you can let ``cc''
run through everything before fixing typing mistakes, etc.
.PA
Zap is to crunch what `w' is to `r': it writes the contents
of the addressed lines, defaulting to the entire current buffer,
out as standard input to the Shell command.
It is frequently used to send mail.
The letter can be prepared in a buffer, edited as desired,
and then sent easily by
.BE
> mail joe
.ul
!
.EE
or even
.BE
0a .pl 1
> nroff | mail joe
.EE
Zap and crunch work nicely together.
We can perform a ``dsw''-like function using crunch
to read the files in,
modifying the list as appropriate,
and sending it out to ``args'':
.BE
< ls
.ul
162
.ul
!
... editing commands ...
> args rm
.ul
!
.EE
(Args takes each line on its standard input and makes it
an argument to the command, which is then exec'd in the normal manner.)
The following commands can initiate the construction of
a dependency-list file for ``make'':
.BE
<grep #include *.c
.ul
482
.ul
!
*s/:#include[ ]"/ /
*s/"$//p
.ul
sh.c sh.h
.EE
At U. of T., the Shell takes a `-e' option which tells it to
echo on the diagnostic output
the commands it is executing, which works nicely with zap:
.BE
a
command1
command2
command3
\&.
> sh -e
.ul
% command1
.ul
% command2
.ul
% command3
.ul
% !
.EE
In short, the crunch and zap commands are used very frequently.
.SE IX "Programming (I)"
Now that we've seen all the primitives,
we can begin using buffers and registers to build
more sophisticated commands.
The first step is to
assemble a few useful command sequences in
registers.
Harking back to our function-declaration-finding
buffer in section III, define register `f' (for ``function''):
.BE
zf:-/^[a-zA-Z_].*(/
zfp
.ul
-/^[a-zA-Z_].*(/
.EE
As a global search, this regular expression found
all function declarations, provided, of course,
that the usual paragraphing style is used.
.PA
[Exercise: Write another definition to perform this function
which uses the ``beginning of identifier'' (``\e{'') metacharacter.]
.PA
Now, enclosed in slashes, with a leading minus sign
(in U. of T. \fBEd\fP, ``\(em/regexp/'' is the same as ``?regexp?''),
register `f'
finds the first
.ul
previous
function declaration.
This seems like an odd concept at first, but works well.
For example,
to see which function's source is being browsed:
.BE
\ezf
.ul
function(x)
.EE
Or to find the declaration of a local variable:
.BE
p
.ul
variable=0;
\ezf/ variable/
.ul
register variable;
.EE
(No semicolon is needed between search strings in U. of T. \fBEd\fP.)
To print out the ``current function'' on the line printer:
.BE
\ezf, /^}/ w /dev/lp
.EE
or
.BE
\ezf, /^}/ > opr
.EE
There are fancier things, too.
If we want to know which subroutines call ``proc()'',
we can use ``\ezf'':
.BE
g/proc()/\ezf
.ul
func1(x)
.ul
func2(y)
.ul
func3()
.EE
After using macros like ``\ezf'' for a while,
they become familiar to the point
that they become idiomatic, a part of the \fBQed\fP language.
To help the user develop a personal working environment,
\fBQed\fP provides
a simple mechanism for initializing.
Typing
(to the Shell)
.BE
qed -x qfile file1 file2
.EE
causes \fBQed\fP to load the named ``qfile'' into buffer `~' (`tilde')
and execute it before reading in the
files to be edited and beginning the normal editing session.
Typically, the startup file is used to initialize options
and registers; it might contain something like
.BE
""Qed
zc:s@$@ /* \ecl */@p
zf:-/^[a-zA-Z_].*(/
b~Z " destroy buffer after execution
.EE
which prints a message,
defines a couple of handy registers,
and obliterates itself.
If no ``-x'' option is given,
\fBQed\fP looks up, in /etc/qedfile, the name of a file containing the default
initialization buffer for each user,
and executes that.
The default file is settable through the ``qedfile'' program,
documented in the \fBUNIX\fP Programmer's Manual.
.PA
Browsing through the startup buffers of a few experienced \fBQed\fP hacks,
a few interesting things come to light.
One simple but rather pretty option is
.BE
ob""\e032"+p
.EE
ASCII 032 is a reverse line-feed on most of the U. of T. terminals;
the line above is appears as it would if it were displayed
with an `l' command.
The `b' (for ``browse'') option defines a special register
which is executed, if defined, when a simple newline
is typed at the terminal, rather than doing the usual ``+p''.
Printing a reverse line-feed before the ``+p'' means
that no empty lines appear on the screen when browsing through
text.
It is sometimes useful to set the browse register to
something like ``+b'' for easy paging through text,
or to `P' or `L', which cause the line to
be displayed in the format of `p' or `l', but
with line numbers at the beginning of the line:
.BE
22i Line 22
p
.ul
Line 22
l
.ul
Line\et22
P
.ul
22 Line 22
L
.ul
22 Line\et22
.EE
These other display formats are sometimes handy in global searches, etc.:
.BE
g/proc()/ \ezf P
.ul
104 func1(x)
.ul
118 func2(y)
.ul
221 func3()
.EE
.ti +5
Another nice register to have tucked away (as it is above) is the commenting
command from section V:
.BE
zc:s@$@ /* \ecl */@ p
.EE
We can call it up when desired:
.BE
p
.ul
bizarre();
\ezc(A Kludge)
.ul
bizarre(); /* (A Kludge) */
g/xxxxx/ p \eczc
.ul
yyy xxxxx yyy
needles
.ul
yyy xxxxx yyy /* needles */
etc.
.EE
The following register definition allows the user to
specify a buffer by its file name:
.BE
zb:G/ \ecl/ f\ecN
\ezbfile
.ul
g\(aa\fB.\fP34 file.c
.EE
We don't even need to type the terminal `.c'!
.PA
Here is a rather complicated, but conceptually simple,
register, ``\ezs'' (for ``search''), which globally searches
for a pattern in all the buffers from `a' through `z',
and leaves dot at the last occurrence found.
For readability, the newlines in the string shown here have
been converted from `\eN's to real newlines.
Unlike the examples above, the output here
is the contents of register `s',
so the special characters do not have to be delayed:
.BE
zB:\eB
zP:\el
zI:`
h26 zI+1 b\eczI $zD#a#=0 yf g/\ezP/ ""\ecB:" PzB:\ecB
b\ezB
.EE
.ft R
What does this mean (!)?
One step at a time:
The first two lines set register `B' to be the current buffer,
and register `P' to be the pattern we are searching for.
(If there were special characters in the pattern,
we would probably have to delay them once more than usual
to achieve the desired result.)
Register `I', a counter,
is set to a back quote, the character below `a' in ASCII.
The next line does all the work, and reads something like:
.nf
.sp
.in +5
for 26 times do
.in +5
increment zI
change to buffer `\ezI'
set zD to be the value of `$'
if zD != 0
.in +5
globally look for the pattern;
.in +2
on every line matched,
.in +3
print the buffer name
print the line & line number
set zB to the current buffer
.in 0
.fi
.sp
After execution, `zB' contains the last buffer name in which a match
was found, and \fBQed\fP automatically
keeps track of the line number on which the match was found.
The last line of `zs' therefore changes back to
buffer `\ezB', which leaves dot at the last
line printed, similar to ``g/xxx/p''.
.PA
Got that?
.PA
Make sure you understand how the `s' register operates,
as it utilizes many of the standard \fBQed\fP programming techniques,
such as nesting a global inside a huntil.
To load the command into a register, of course, you would have
to delay the special characters one more time.
.PA
Well, that was instructive, but rather revolting.
If you understood how the search register works,
you're doing very well, but it's not a good example of
how to program \fBQed\fP, just a pedagogical one.
Here's how to really do it:
.BE
G/^[a-zA-Z]/ g/\el/ ""\ecB:"P
.EE
You'll find as you gain experience that huntils are rarely used,
but they do have their moments.
.PA
Using the register is quite easy;
just type `\ezs' followed by the pattern being searched for:
.BE
\ezs^func()
.ul
a:86 func()
.ul
b:102 func() {
f
.ul
b .209 junk.c
.EE
.ti +5
[Exercise: Set up your startup buffer to include the original definition of ``zs''
using delayed `\eN's where necessary.
Is a delayed newline necessary at the end of the register?
Why or why not?
(Hint: where does the newline at the end of the invocation line end up?)
Define a second register like `s',
but which executes a definable register, say `e' for ``execute'',
rather than just printing the line.
You can use our intelligent version here.
What useful things might be put in register `e'?]
.PA
Registers can also be used to call the Shell.
Register `d', defined below,
calls ``pwd'' to
get the current directory, saving the result
in register `e',
so that the user can quickly return after changing
working directory.
.BE
zd:ovr zB:\ecB\ecN bX <pwd \ecN ze. d b\eczB ovs zep zB:
.EE
This definition of register `d' is exactly as it would appear
in a startup file.
The ``ze.'' command
puts a copy of the current line in register `e'.
The delayed newlines are necessary; unpacked, the string looks like
.BE
ovr zB:\eB
bX <pwd
ze. d b\ezB ovs zep zB:
.EE
Briefly: turn verbose mode off;
save the current buffer name;
change to buffer X and get the directory;
save it in register `e';
delete the line from the buffer;
change back to the original buffer
and reset the flags;
print the directory;
and clear register `B'.
.SE X "Programming (II)"
So far, the emphasis has been on using registers as programming elements,
primarily because the size and complexity of the problems
being handled has been small enough that registers are really
the way to deal with them.
Ultimately, though,
more complicated problems arise and it becomes necessary to store
command sequences in buffers.
In light of that, one more register definition, `r' for ``run,''
will make using program buffers somewhat simpler.
Called as
.BE
\ezrbuffer
.EE
it reads ``buffer.q,'' prepended by the search path
stored in register `q', into a scratch buffer, executes it,
and clears the scratch buffer.
Typically, register `q' would be set by the startup buffer to contain
something like ``/usr/rob/q/,''
so
.BE
\ezrcommand
.EE
runs the buffer in the file ``/usr/rob/q/command.q.''
Register `r' is long but linear, having no loops.
Unpacked, with newlines, it looks like:
.BE
zL#r
z{:\el
z|:\eB
ovr b{
e \ezq\ez{.q
ovs b\ez| \eb{
b{ Z
b\ez|
.EE
This looks considerably more bizarre than our earlier definitions,
because it follows some conventions that have proven useful.
The registers and buffers with funny names (`{', `|', `}' and `~')
are (unofficially) reserved as scratch areas: anything you put
in one is not guaranteed to stay there
if you call in an external buffer.
``zr'' uses registers `{' and `|' to hold
the program name typed by the user and the buffer the register
was called from, and buffer `{' to hold the program.
\fBQed\fP itself uses register `~' to hold the initialization code
to bootstrap the startup buffer,
but clears it before going to the terminal for input.
(A side effect of this is that your initialization code can
zap z~ to alter the bootstrap procedure.)\
Other conventions are that upper case registers and buffers
are reserved for use by program buffers, such as the ones
we will be developing in this section, and lower case letters
are reserved for the user.
``zr'' stores in registers `L' and `M' the addressed
lines for the buffer being called
(via ``zL#r'').
Following these conventions means that a user can call someone
else's program buffer, for example, without worrying about
which registers and buffers it uses.
.PA
The ``ovr'' and ``ovs'' calls in register `r' set the verbose flag
off and on when appropriate to suppress the spurious
character counts on i/o.
The register as defined here actually works, but what we really want
is something a bit spiffier, so we use \ezr to load a buffer:
.BE
zr:zL#r z}:\ecB\ecN ovr b~e \eczqrun\ecN \ecb~\ecN b\ecz}
.EE
This loads buffer `~' with the file (say) /usr/rob/q/run.
The buffer is then executed, and the user is returned to
the original buffer.
The `run' buffer looks like this:
.BE
" Run a qed buffer `off line'
z{:\el
z{C
" the next line puts a space at the end of the register
z{$
" the next line looks for a space in the argument string
z|'{ z{[
z~#c z{)\ez~ z|(\ez~ z|C
" z{: command z|: argument string z}: return buffer set by zr
b{ e \ezq\ez{.q
ovs
b\ez} \eb{
" Note! ok to ZERO buffer ~ (this buffer); the line will finish executing
b{Z b~Z
.EE
The `zXC' command is kludgey but handy:
it collapses multiple blanks and tabs in the register to single blanks,
and deletes leading blanks.
The first few lines of the buffer put the command and its arguments
(if present)
into registers `{' and `|'.
The new register commands are:
`zx[string' stores in the `count'
the starting index of `string' in register `x',
`zy#c' saves the count in register `y',
`zx)N' truncates the register at the N+1st location,
and `zx(N' drops the first N characters from the register.
Although it's a little unfair to show these commands to you this late in the
game, they didn't really need showing earlier on, and they are
quite simple to master.
The `count' also hasn't shown up before,
so we'd best explain it now.
It is a special place, something like the `truth',
which gets set to the number of characters transferred
during i/o operations, the number of substitions made during
an `s' command, and to other such numbers, as above.
Although rarely used, it, too, has its moments.
.PA
The requested buffer is then loaded into buffer `}' and executed.
Finally, the loaded buffer and buffer `~' are zeroed,
and `run' returns.
.PA
Although its coding is not particularly pretty,
the power register `r' gives us is very useful.
It is really part of the \fBQed\fP language,
since it allows the user to store
many command buffers in the file
system, but get at them easily and in a mnemonic fashion.
It itself employs two conventions which are therefore ubiquitous:
registers `L' and `M' hold the lines being addressed by a buffer call,
and buffers `{' and `~' are off limits to command buffers.
The latter point, of course, shows the weakness of a language
in which all the variables are global,
but let's ignore that theoretical issue for the moment;
\fBQed\fP has many other weaknesses which are far more important!
.PA
``zr'' is only useful if we have some buffers to drive with it.
For starters, we can take our `search' register and put it in
a buffer (say /usr/rob/q/grep.q):
.BE
" Grep for z| (possibly set by caller) in all buffers
z|=
yf'fi
""pattern:" z|:\el
"fi
G/^[a-zA-Z]/ g/\z|/ ""\ecB:" P
.EE
A few interesting issues pop up.
Firstly, we can prompt the user for missing arguments.
If the user types
.BE
\ezr grep expr
.EE
(notice the blanks, which are deleted by the `run' buffer)
we can search for expr directly, but if no expression is
specified, we just ask for it.
Secondly, putting the code into a buffer means
everything can be delayed one less time, which makes it more
readable,
and the initialization and cleanup code is shared by all command buffers,
providing a clean and uniform interface.
Also, after execution, register `r' returns the user to the
buffer he started in, rather than leaving him in some random place.
For this example, it may or may not matter, but in some
cases it is advantageous to return `home'.
.PA
Here is a new example; it right justifies the addressed lines,
something of mild utility but too special purpose to keep around
as a real program.
It only takes a couple of minutes, though, to write a \fBQed\fP buffer
to do it, which can then be saved away:
.BE
" Right justify addressed lines (default to (1,$))
zL#=\ezM yf'fi
1,$zL#r
"fi
" The white space below is a space and a tab
\ezL,\ezMs/^[ ]*//
\ezL,\ezMs/[ ]*$//
zW:0
\ezL,\ezM g/^/ zC#l#<\eczW yt zW:\eczC
zW#>35 yf zW:35
zD: |
zD)\ezW
\ezL,\ezM s/^/\ezD/
" Turn spaces into periods
zD+14
\ezL,\ezM s/^ *\e(\ezD\e)$/\e1/
zD-14
\ezL,\ezM s/^\ezD//
zL:\eN zM:\eN zC:\eN zD:\eN zW:
.EE
(Another new command (sorry): `zC#l' sets register C
to the length of the current line.)\
This buffer illustrates how command buffers use the (zL,zM)
address pair.
Clearing the registers afterwards is a good practice for program buffers
to follow.
[Exercise: Why is there no `\eN' on the end of the last line?]
To invoke this program on a suitable buffer full of, say, words,
one to a line, we save it away in
``/usr/rob/q/right.q'' and type:
.BE
ba " where the data is
*p
.ul
excle
.ul
ficatings
.ul
criminter
.ul
con
.ul
explasence
.ul
des
.ul
ofh
.ul
fultesibe
.ul
shispensitment
.ul
dedgearing
.ul
expers
" yes, they're random words
\ezrright
*p
.cs I 24
.ul
excle
.ul
ficatings
.ul
criminter
.ul
con
.ul
explasence
.ul
des
.ul
ofh
.ul
fultesibe
.ul
shispensitment
.ul
dedgearing
.ul
expers
.cs I
.EE
As the Ronco man would say, ``Isn't that amazing!"
.PA
Can we do anything useful with all this power?
Well, we can write a buffer ``un'' (for ``run'' or ``unix'')
which pipes the addressed lines out to
a shell command line, and replaces them in the buffer
with the output of the command:
.BE
" un.q -- replace addressed lines of current buffer by result
" of passing them through pipeline
" Looks in z| for pipeline; if empty, prompts & reads from terminal
" Called as addr1, addr2 \e zrun; defaults to (1,$).
z|=
yf'fi
""<> "
z|:\el
"fi
zL#=\ezM yf 1,$zL#r
ovr
\ezL,\ezM > \ez| > /tmp/qed
zT#t " zT gets return status
\ezMr /tmp/qed
!rm /tmp/qed
ovs
zT#=0 yt'else
""Invalid status return - lines not deleted
y'fi
"else
\ezL,\ezMd
"fi
zL:\eNzM:\eNzT:
""!\eN
.EE
The prompt is reminiscent of ``crunch-zap.''
The ``yf\(aaelse'' tests the status return of the
command, and decides not to delete the original lines
if the status was bad.
Using the ``\ezrun'' combination,
we can process the data in a buffer
through any arbitrary pipeline, such as
.BE
*p
.ul
excle
.ul
ficatings
.ul
criminter
.ul
con
.ul
explasence
.ul
des
.ul
ofh
.ul
fultesibe
.ul
shispensitment
.ul
dedgearing
.ul
expers
\ezrun sort
.ul
!
*p
.ul
con
.ul
criminter
.ul
dedgearing
.ul
des
.ul
excle
.ul
expers
.ul
explasence
.ul
ficatings
.ul
fultesibe
.ul
ofh
.ul
shispensitment
.EE
To send out only a portion of the buffer to the pipeline,
the usual convention is used:
.BE
\&.,/xyz/ \ezrun sort
.EE
.PA
Well, if you've made it this far, you're certainly ready to become
a \fBQed\fP hack.
Have fun!