.so crisp.mac
.SP 9 
.ce
\s+5\s+9\s+9\f(HBC\s-9RISP  Programmers  Guide\fR\s-9\s-5
.SP 9 
.nf
.in +2i
\s+5\f(HBVersion:	2.2

Author:	Paul Fox

Date:		July 1991
.in
\s-5\fR
.SP 9
.in +1i
.ll -1i
.ce
\f(HBCopyright Notice\fR
.SP 2
.fi
This document and the software it describes is Copyright (C) 1991
by Foxtrot Systems Ltd and Paul Fox. This copyright notice will remain
in effect at all times.
This manual may be reproduced under the terms of the CRISP copyright notice
associated with the distribution software.
.in
.ll
.SP 5
.nf
Foxtrot Systems Ltd
8, Theobalds Way
Frimley,
Surrey,
England GU16 5RF
.fi
.bp
.PH "'''Page %'"
.PF "'CRISP Programmers Guide'May 1991'Release 2.1'"
.H 1 Introduction
.P
This guide covers detailed aspects of the programming language \f(HBCRUNCH\fR
available to users of CRISP. It describes the primitive objects supported
by CRISP and how to write macros to customise CRISP for your
own purposes. It assumes a level of knowledge of how to use
CRISP, and assumes that the user is familiar
with programming, especially the \fBC\fR language.
.P
For information on how to \fIuse\fR CRISP, please consult the
\f(HBCRISP User Guide\fR.
.P
This manual should be read in conjunction with the \fHCRUNCH Library
Reference Guide\fR, which describes each primitive in detail.
.P
.H 1 "An Overview of the Programming Facilities"
.P
CRISP itself is a fairly simple program consisting of an interpreter
which supports a number of different datatypes, including integers,
floating point numbers, buffers, and dynamic lists. The functionality
of CRISP is provided by a set of macros. By splitting
the functionality this way, the aim is to keep the executable binary,
which is machine and operating system specific, to be as small
as possible and allow users to tailor the look, feel, and functionality
of the end product without needing to understand how CRISP is
implemented or the vaguaries of the development environment on
which CRISP is running.
.P
The macros themselves represent a compact representation of
the instructions they implement. Macros are written in one of
two languages -- one of which is Lisp like, and the other C like.
.P
Macro programs are normally compiled before being loaded by CRISP.
Compiling speeds up the loading phase of CRISP and also allows
certains features to be exploited which the loader inside CRISP
does not necessarily support. 
.P
The Lisp-like language has no official name, but files written
using this syntax normally have a '\f(HB.m\fR' file extension.
The crunch language names files with a \f(HB.cr\fR file extension.
Compiled macros have a \f(HB.cm\fR extension.
.P
The lisp language may be considered the \fIassembly\fR language of
CRISP. The language can be used when certain constructs are
not accessible from the crunch language (these are explained later).
However, crunch offers more added value than lisp. Crunch
is a full compiler understanding most of the ANSI C syntax.
.H 1 "The .m language"
.P
This language describes ascpects of the .m language which
are not covered by the description of crunch. The .m language
is a deprecated language in the sense that no active support
for this language will continue in future versions of CRISP. All
current and future macros will be written in the crunch language, and
the .m language will only be supported in its current form with
no guarantee that any new features will be added to it.
.P
The .m language may be considered as an assembly language, and the
crunch compiler can be made to generate the equivalent .m style programs
from it.
.P
The .m source files are compiled with the \f(HBcm\fR program.
.P
.ce
*** NOTE ***
.SP 1
.in +1i
.ll -1i
The name 'cm' conflicts with the calendar manager utility which
is available under SunOS 4.1 when using OpenLOOK. Because of this
conflict, you may have problems accessing the correct 'cm' program
when you try to run this program from within XView. You can
use the crunch compiler to compile '.m' files and this knows how
to avoid running the wrong 'cm' program so it is advisable
to use this.
.ll
.in
.P
The 'cm' compiler converts macros in source form (.m files) to
a compact compiled form (.cm) files. CRISP can read
\.m files or .cm files at run time, but the .cm format loads
faster and avoids CRISP having to parse an input file, skipping
over comments etc.
.P
The command line syntax for 'cm' is:
.DS
.ft CW
    	cm [-aLl] [-o output_file] file1.m file2.m ..
.DE
The usual way to invoke cm is simply:
.DS
.ft CW
    	cm fred.m
.DE
.P
This compiles the file fred.m, and creates a file fred.cm in
the same directory as fred.m. Compiled macro files are loaded
significantly more quickly and execute slightly more
efficiently than pure source files.
.P
cm  checks  for  syntax  errors  as  it  goes  along - the most
common  errors  are unmatched brackets, and unterminated string
constants.
.P
cm has a number of switches. Most of these are used for
debugging the compiler itself and for disassembling compiled
macro files.
.VL 8
.LI -a
This switch is used to print out the relative amount of space
used by individual components of the compiled language. It is
used for fine tuning the pseudo code generated by the
compiler. Most users can ignore this switch. This switch
currently does not work.
.LI -l
This switch is used to print out each macro so that the
internal parsing can be checked. This switch currently does
not work.
.LI -L
This switch is used to print out the internal pseudo code
used to represent a macro and allows debugging of the
internal code generator. This switch currently does not work.
.LE
.H 1 "Objects supported by CRISP"
.H 2 "Buffers, Files and Windows"
.P
In order to understand CRISP better, it is important to understand
the basic concepts of buffers, files and windows as they are used
within CRISP. 

A buffer is a way of manipulating files. A buffer is created when
a file is edited and stores the entire contents of a file, and keeps
track of the changes being done to the file. The user can make
arbitrary changes to the buffer without actually destroying or modifying
original file. Buffers have an undo-list associated with them.
The undo-list allows the user to undo any editing operation
applied to the buffer. CRISP can keep track of an unlimited number
buffers at any time.
.P
A file has the obvious meaning associated with the underlying filing
system. Files can be read into buffers and manipulated, and not
until the buffer is written away is the file on disk actually
modified. This allows the user to maintain a long editing session
and only when the user is satisfied with the changes is the file
updated. Also, if the system crashes during an editing session, the
original file will not be damaged. CRISP contains facilities for
keeping backup copies of files, so that even after an editing session
the user can go back to previous versions of the file.

A window is a way of seeing a part of a buffer on the screen. On startup,
only one window is visible, occupying the whole of the screen. This
initial window can be split (known as tiling because the windows always
cover the whole of the screen and abut each other). Windows can be split
and new windows created. The only limitation is the size of the screen.
If you have too many windows on display, they will be too small
to display any meaningful part of a buffer.

Each window is independent of the others, and the user can select a
window and pan around a buffer. Different windows can display different
parts of the same buffer; for example, the user may be looking
at the declarations at the top of a C program, and modifying a piece
of code in another window.

CRISP also supports popup windows. Popup windows are typically used
to display information on a temporary basis, e.g. a list of buffers
currently being edited, or a help menu. Popup windows obscure the
background windows normally used for editing. Popup windows are
normally created by special purpose macros. CRISP contains many
macros which use popups to display data.

CRISP stores all buffers and files in memory; therefore, CRISP is limited
to editing files which are no larger tan the amount of swap space free.
In practise this limitation is very rarely a problem. Most virtual
memory systems support more swap than most of the standard editors can actually
use. For example, vi is limited by the number of lines in the file rather
than amount of memory available, and Emacs is limited to a maximum of 16MB
for all editing. CRISP can cope with very large files on most systems.

There is no limit to the number of files, buffers or windows which can be
created at any one time (except for swap space). Also, files
have NO line length limitation. This makes CRISP useful for editing 
certain types of files which many other editors or standard utilities cannot
cope with.
.H 2 "Buffer Attributes"
.P
Each buffer has a number of attributes or modes associated with
it. The following is a summary of these attributes, and the following
sections explains the purpose of these attributes in further detail.
.DS
	- Ansi mode
	- Backup flag
	- Binary flag
	- Buffer contents
	- Buffer name, buffer ID and Filename
	- Carriage-return flag
	- Current cursor position
	- Modified flag
	- Permissions & Read-only Flag
	- Process Buffers
	- Region marker
	- Symbol table
	- System Buffers
	- Tab settings
	- Undo information
.DE
.H 3 "Ansi Mode"
.P
Ansi mode is a special mode normally used with process
buffers, in which Ansi escape sequences embedded within a buffer
are interpreted and displayed correctly, rather than being displayed
literally. For example, the string "<ESC>[34m" can be used on
an Ansi conforming display (e.g. PC console, or VT-300 compatible terminal)
to set the foreground color to dark blue. When ansi mode is set, all 
characters to the right of the escape sequence will actually appear
dark blue. This facility is used mainly for process buffers where some
program is run which uses Ansi color sequences and cursor movement
facilities. This mode allows CRISP to display the screen output
in a window correctly. For example, it is possible to use
the 'vi' editor inside a process buffer.
.P
Ansi mode can be used independently of a process buffer, for example
to review output from a program which uses these escape sequences.
.P
Another possibility, not yet presently implemented in CRISP, is to write
a macro which changes the colors of keywords in the buffer, e.g. in a C
program comments could be in green, and keywords in cyan. (This
is really only feasible if the text within a buffer is easily parsable).
.P
Another use is to look at the system manual pages inside a CRISP buffer.
Normally looking at the output of the \fIman(1)\fR command is a bit
difficult because of the underlinings.
.P
The \f(HBansi\fR macro can be used by the user to turn on or off the
ANSI attribute for a buffer.
.H 3 "The Backup Flag"
.P
When a file is read in to a new buffer, a flag is set, called
the \fIbackup flag\fR, which is used to indicate that when the
buffer is saved a backup of the file should be made (either in the
backup directory as specified by the BBACKUP environment variable or
with a .bak file extension). This backup is only made \fBonce\fR during
each editing session. This allows the user to make lots of changes to a file
and save it frequently and still be able to access the
previous version of the file before editing started.
.P
This flag may be set or cleared by a user macro, e.g. if the
user doesn't want a backup made for a particular file, or
maybe the user wants backups to be made after the file is
written away each time.
.P
This flag is used by the rebackup mechanism to allow changed buffers to 
force creation of new backups after a specified period. (Refer to the
description of the \fIautosave\fR variable in the .crinit file in the
User Guide.
.H 3 "The Binary Flag"
.P
When a file is read into a buffer, CRISP tries to determine whether
the file is a text file or a binary file. CRISP can handle
either, but response can become sluggish when lines are
very long. (CRISP can handle infinitely long lines, but it
is expensive to compute how long they are for screen purposes).
To make it easier to edit these files, CRISP breaks up
binary files into lines containing only 32 characters. To
avoid problems if the file is modified and written away,
CRISP sets the binary flag. This flag means that the
newline character normally written at the end of each line
is not saved for binary files, and therefore the edited
binary is what the user expects. If this flag were not set
or if
the user macro turns it off, then newline characters would
appear at 32 character intervals in the output file.
.H 3 "Buffer Contents"
.P
A buffer may be considered as an array of lines, where a
line is stored internally using a length + data. This
means that each line can contain an unlimited amount of
data and that
any characters may appear with a line, even newlines (for example
with binary files). CRISP does not suffer the problem that
\fIvi(1)\fR does of stripping out unprintable control characters.
.P
CRISP has no limit on the number of lines in a buffer
(subject to memory limitations).
.H 3 "Buffer name, buffer ID and File name"
.P
Each buffer is identified by an integer number internally. Macros
which manipulate buffers or switch between buffers use the buffer ID.
Buffer IDs are not normally visible to users. Instead, they see a buffer name
and a file name.
.P
The filename is the name of the file stored in a buffer. Buffers don't necessarily
have to have a file stored in them. Some buffers are used internally to
store information about the current editing session, e.g. the scrap
buffer. The filename is used by users to distinguish one buffer from
another.
.P
The buffer name is a sort of shorthand name for a buffer. Normally
when a file is read into a buffer, the actual filename of
the file is used as the buffer name. Different buffers may contain the
same buffer name. For example, if the user edits the file /usr/fox/test.doc
and /usr/fox/backup/test.doc, there will be two buffers called
test.doc. However the full-filename associated with each buffer allows
the user to distinguish them. This full-filename is the file written
to when the buffer is saved. The buffer name is the string printed at the top
of the window when it is \fIattached\fR.
.H 3 "The Carriage-Return flag"
.P
This flag indicates whether the carriage-returns (CR) characters should
be appended to the end of each line on output. This would typically
be used for editing DOS text files which use the CR-LF sequence
to terminate files, rather than the Unix standard of just plain LF.
.P
When a file is read in, if CR characters are found before the LF at
the end of the line, then this attribute will be set automatically.
.H 3 "Current cursor position"
.P
CRISP maintains two separate cursor for each buffer - the
normal cursor, as displayed on the screen, and a parallel
cursor for process buffers. (See below for a description
of process buffers).
.P
If a buffer is not being displayed in a window, then the
cursor position changes as a result of text insertions,
deletions, pattern searches, etc. When the buffer is
attached to a window, the cursor is copied into the
windows data structure. This allows each window to
maintain a separate view of a buffer.
.P
When a buffer is detached from a window, the cursor is
copied into the buffers data structure.
.H 3 "Modified Flag"
.P
Each buffer has a flag called the \fImodified flag\fR.
This flag is set whenever a change is made to the buffer,
e.g. an insertion or deletion. The flag is used to
indicate that the
buffer needs to be saved before terminating CRISP, and is
used, for example, by the autosave macro.
.H 3 "Permissions & Read-only Flag"
.P
When a file is read into a buffer, a note is made of the
original protection bits on that file. This is needed so
that when the file is written away, it can give the output
file the same protection bits as the original file. For
example, when editing an executable shell script, the
execute bits are turned on for the modified file so that
it is still executable.
.P
If a read-only file is edited, then CRISP does not allow
any changes
to be made to the buffer. This stops the user making
changes and then finding out at the end of the editing
session that they cannot be saved (a frequently annoying
problem with \fIvi(1)\fR). The user can clear the read-only bit for
a buffer by calling the \f(HBmake_writable\fR macro (available from
the features menu) or by typing \fH<Ctrl-A><Ctrl-W>\fR.
.H 3 "Process Buffers"
.P
Each buffer can have an external system process associated with it.
These output from these process is automatically inserted into the buffer
as it appears. Input from the keyboard can be directed to these processes.
These process buffers are normally use to access the command line
interpreter, e.g. the shell, so that instead of having to keep stopping
CRISP to access some system program, e.g. \fImake(1)\fR, the user can view
output in a window. This is similar to having xterm
windows under the X11 windowing system. 
.P
Process buffers are useful because all output from the
process is easily accessible by scrolling the window
using the normal editing keys, so that error messages,
etc. which have disappeared from view can be seen.
.P
Process buffers are supported by a number of macros to
aid in using them.
.P
These processes are sometimes referred to as pty's, because on systems that
support them, pty's are used to implement this functionality. This term
(as far as I know) originates from the ancient TOPS-10
operating system (circa late 60's). A PTY is like a
normal terminal, except there is no physical
manifestation of the terminal. Instead of input coming
from a real user, the input comes from another program;
output from the PTY can be read by the controlling
program, e.g. CRISP.
.P
The pty facility in many Unix implementations is used
where possible. Under other systems, pipes are used.
Pipes are not as good as pty's, because for example it is not
possible to run vi in a process buffer on systems where
this is true.
.H 3 "Region Markers"
.P
Each buffer can have an optional \fImarker\fR or
\fIanchor\fR associated with it. A marker is used to
hilight a section of the buffer when it is displayed on
the screen. This allows the user to hilight a section of
text and then apply some operation to it, e.g. cutting
it to the scrap. A buffer can only have a single marker
at any one time, but each window which is displaying a
buffer can maintain its own marker.
.H 3 "Symbol Table"
.P
Each buffer can have a set of user defined variables
associated with it. These are referred to as \fIbuffer
local\fR variables. These variables exist only whilst
the buffer is the current buffer. This facility allows macros
to maintain their own data structures on a per buffer basis.
.P
Refer to the description of the \fImake_local_variable\fR macro.
.H 3 "System Buffers"
.P
System buffers are special buffers which tend to be
associated with the implementation of certain macros.
System buffers tend to have two attributes which make
them useful -- they do not store undo information, and
thus buffer modifications are faster, and also they do
not appear in the buffer list (available via \fH<Alt-B>\fR).
.P
Because system buffers are marked differently to normal
buffers, user macros can avoid accessing these buffers
unless they themselves created them. For example, the
autosave macro does not attempt to autosave the command
history buffer.
.P
In CRISP, the system buffer flag and the undo-flag for a
buffer are two separate buffers, allowing non-system
buffers to have the undo turned off for a buffer.
.H 3 "Tab Settings"
.P
Each buffer has a set of tab stops defined for it. These
tab stops are used when the buffer is displayed in a
window. Having the tab stop as an attribute of a buffer
allows different buffers and files to have different tab stops.
.H 3 "Undo Information"
.P
Each buffer has an undo list associated with it. The
undo list allows changes made to the buffer to be
undone. This feature can be turned off for any buffers.
Turning off the undo feature can be useful for two
reasons. Firstly, operations are faster if this
information does not need to be saved. Secondly, disk
space can be saved if a voluminous edit is to be made on
a very large file, e.g. global translate. 
.P
The undo information is stored in a temporary file,
which is not visible via the normal \fIls(1)\fR command.
.P
All undo information is undoable itself. This is termed \fIredo\fR.
.H 3 "Character Maps"
.P
A character map is a way of viewing data through a window.
Character maps are used to implement the 'view' and 'literal' macros.
.P
A character map is simply an array of strings corresponding to each
of the possible 256 byte values. When a particular byte is to be displayed
in a window, the corresponding string is displayed. For example, when
the hexadecimal character 0x41 is to be displayed, the letter 'A' appears
on the window. This is configurable, and if the user wants to use
a non-ASCII character set, then the character map can be modified so
that the EBCDIC entry for 'A' is used instead.
.P
The 8-bit character set has four important regions:
.DL
.LI
Characters 0x00..0x1f (the control characters)
.LI
Characters 0x20..0x7e (the printable ASCII character set).
.LI
Character 0x7f (ASCII DEL)
.LI
Characters 0x80..0xff
.LE
.P
All terminals support the ability to display the ASCII printable characters
but few terminals provide facilities to display the other characters.
In \fIvi(1)\fR, for instance, control characters are displayed as a caret
followed by the non-control character. These characters take up two
character positions on the screen.
.P
CRISP supports terminals which can print all 8-bit characters, e.g. PC
terminals amongst others. However, each user may have a preference for
how these non-printing characters are to be printed, e.g. whether in
C-style octal notation, hexadecimal notation, or maybe in the DEC
editor EDT style (using keywords inside angle brackets, e.g. <TAB>).
.P
Even if a user is happy with some means of displaying characters, he/she
may find this scheme inflexible sometimes. For example, if the terminal
does not support a full 8-bit character set, then control-A appears
as '^A'. When viewing a binary file, it may be better to have each character
occupying the same width on the screen so that it is easier to look at
character offsets.
.P
All this functionality is supported by the character maps.
(For real examples of using the character maps, refer to the view.cr
macro).
.P
CRISP supports an arbitrary number of character maps.
Initially CRISP starts off with a default character map,
where the
non-printing ASCII characters are dependent on whether the
terminal supports 8-bit characters or not. If not, control
characters print in up-arrow format, and the characters
with the top bit set print in the "\x" style notation.
.P
Character maps are created via the \f(BHcreate_char_map()\fR primitive.
This primitive is supplied with a list of strings, normally corresponding
to each ASCII character position, i.e. a list of 256
strings should be specified to remap the entire display
character set. Any characters not defined are inherited
from the base character map. Character maps are given
identifiers which are used with the other character map primitives.
.P
Each character may have associated with it a flag. These
flags are used to handle the TAB and BACKSPACE characters.
It is not sufficient to map TABs to some fixed output
string -- the tab character may need to be translated to a
variable number of spaces. Likewise, in ANSI mode, the
BACKSPACE character may need interpreting to implement
bolding and underlining, e.g. when viewing the output of
the 'man' command. The character map system is so generic
that any character can be defined to be a TAB on display
(even more than one at the same time).
.P
Character maps provide a view onto a buffer, via a window.
It is important to understand what character maps are
doing because you can get into strange corners if they are
not treated with respect.
.P
Consider the case where you want to look at a binary file,
e.g. a
Unix directory. Unix directories are like normal files but
include text (filenames) and binary data (inode numbers).
Looking at a directory in pure text mode is untidy because
of the variable width nature of the binary character set.
Looking at a directory in pure binary mode makes viewing
the filenames painful. What you can do is create two
windows onto the same buffer -- one displaying in binary
(type 'view hex'), and another viewing the file in ASCII
(normal mode).
.P
Each window and buffer has a character map associated with
it. By default buffers have no character map assocaited
with it. When they are displayed in a window the character
map associated with the window is used to view the buffer.
(Windows always have a character map associated with them).
If a buffer has a character map associated with it (via
set_buffer_cmap()), then this is used to override the window definition.
.P
Associating character maps with buffers is important to
understand because it controls the mapping between the
externally visible column position in a line and the internal
character pointer to the text. This is important because
of the effect of inserting text in one window, and doing
an undo in another which is using an alternate character map.
.P
Having a fixed width character map can make it easier to
write a hex mode editor to move from one character
position to another.
.H 2 "Files"
.H 3 "File Types -- Text files and Binary Files"
.P
CRISP allows any file type to be edited, whether the file is binary
or text. Editing binary files is allowed although CRISP does not contain
any special facilities (yet) to perform editing on these files.
.P
Binary files are identified by looking for non-printable characters in
the first 100 or so characters of the file. If there are too many,
CRISP reads the file in, but makes each \fIline\fR only 32 characters
wide. This avoids performance problems with CRISP whereby if it did
not do this, then it would read the whole file into a single line, making
editing and scanning difficult.
.P
Editing binary files can be useful, e.g. as an alternative to the
\fIstrings(1)\fR utility, to look at the printable strings in a binary.
It can also be useful to make small edits to binary files. For example,
to patch string literals in the file.
Care is needed when editing executable files, since the file size needs
to be exactly the same before editing the file as afterwards.
.P
CRISP notes buffers containing binary files, to ensure that an
arbitrary newline character is not inserted at the end of each 32-character
line.
.H 3 "Backing up Files"
.P
Backups are created when a file is actually saved. Every time a new
file is edited, the old file is renamed so that even after writing a file
away to the disk, the previous version of the file can be recovered.
In the absence of the BBACKUP environment variable, CRISP renames
the original file with a '.bak' extension. To some users this may
be distracting to have lots of directories filled with these files. If
the BBACKUP environment variable is set to the name of a directory, then the
original file is \fImoved\fR to the specified directory, i.e. all backups
are located in a single directory, making it easier to find and/or remove
backups if disk space becomes short.
.P
In addition, CRISP attempts to emulate the VMS file version feature.
If the BVERSION environment variable is set, then multiple copies
of backup files are kept. To use this feature BVERSION should be set to
the number of backups to be kept. Users who are low on disk space
should use this feature sparingly, otherwise you could soon run out.
When this variable is set, crisp creates a set of directories called
$BBACKUP/0, $BBACKUP/1, $BBACKUP/$BVERSION-1. When a file is written away,
the most recent backup is put in the BBACKUP directory. The previous version
in BACKUP is moved to BBACKUP/0, and so on. That is, the BBACKUP directory
contains the most recent backup, BBACKUP/0 contains the second most
recent backup, and BBACKUP/BVERSION-1 contains the oldest backup.
.P
It is sometimes useful to turn off backup file generation for certain
files, e.g. very large log files; you can tell CRISP
to turn off creating a backup file for the current file by using the
\fIset_backup\fR macro.
.P
In addition to the above two features, CRISP has a builtin facility to
save buffers in an emergency. If CRISP detects an internal error
(e.g. segmentation violation) all the currently modified buffers are
written away to the current directory, with filenames called BUFFER-0,
BUFFER-1, and so on. This should never happen, but bugs have
a habit of laying dormant until the most unfriendly time.
.P
To optimise performance, CRISP uses the following algorithm when trying
to backup a file:
.AL
.LI
If the file has multiple links to it, the file
is copied to the destination directory. (See below).
If the file has only a single link, then it
may be link()ed to the destination directory.
.P
(Linking is much faster than copying because the actual
contents of the file need not be copied -- just the file name
is changed).
.LI
For each directory in the BBACKUP environment
variable, try to link() the file to the
specified directory. This operation may fail
if the directory is on a different file
system.
.LI
If step 2 fails, then for each directory in
the BBACKUP environment variable, try and copy
the file to the resultant directory. This may
fail because of permissions.
.LI
If steps 2 & 3 fail, try and create a file with
a .bak extension. If the file has multiple links,
then the old file is copied to the new file. If the
file has a single link, then it is link()ed to the .bak
file.
.LE
.H 3 "Autosaving"
.P
Autosaving is a feature enabled by default, whereby any modified
files (buffers) within the current editing session are saved at
60 second intervals when there is no keyboard activity. This
ensures that if you walk away from an editing session and the system
crashes, that a copy of your work will be saved. The autosaved file
is given a different name to the real file name to avoid prematurely
committing changes to the file. These autosaved files are automatically
deleted when the user exits from CRISP.
.P
CRISP itself does not directly implement autosaving;
instead, it is a macro which is supplied with CRISP.
CRISP supports the mechanism for implementing the
autosave feature. If any user does not like the
mechanism, then he/she can modify the macro to make it
do what is really wanted.
.P
Autosaving is implemented via the idle timer. The idle
timer is a timer which is maintained by CRISP. The idle
timer goes off when the user hasn't typed anything for
60 seconds. (This is configurable via the command line).
The autosave macros looks at each non-system buffer, and
writes out the buffer if it has been modified.
.P
Autosaved files are written to a file with a different
filename from the original to avoid destroying the
original before the user is committed to keeping it.
The actual filename used is operating system independent, and
is a function of the real file name.
Under Unix, if the file is called foo.c, then the file is saved in a
file called \f(HB@foo.c@\fR. The use of the \f(HB@\fR's is to make
the files stand out when performing a directory listing.
The prefix '@' is useful since if for some reason CRISP
does not delete the autosaved files, then it is easier and
safer to issue an 'rm @*' command than an individual rm
for each file. The trailing '@' is added on so that if
the user issues a command such as:
.P
ls *.c
.P
then the autosaved files don't get included in the list.
The major objective here is to avoid sticking on a long
suffix (e.g. .asv) to a file name on systems such as V.3
Unix, which only have 14 character file names anyway).
Adding suffixes to a filename is a dangerous game since
the original filename may already be at the 14-character
filename limit, and the suffix would get ignored. Having
a prefix and suffix seems to be the best of both worlds.
.P
Under OS/2 with the FAT file system, a \f(HB.asv\fR
extension is used instead, due to limitations on the
lengths of filenames.
.P
These autosaved files are deleted when CRISP exits.
.P
When the autosave macro runs, it prints the message
\f(HBAutosaving...\fR followed by \f(HBAutosave complete\fR when it
has finished.

.H 3 "Core dumping"
.P
In the case the previous two mechanisms are insufficient
to save the files you are editing on, CRISP has a further \fIdramatic\fR
file saving feature. If CRISP detects an internal
segmentation violation or other error which might cause
CRISP to die, then a special emergency macro is
executed. This macro is implemented via the REG_INTERNAL
registered macro. The code for this may be found in the \fHcore.cr\fR
file. It basically saves away all modified files (after
prompting the user), to files called \fHBUFFER.1\fR,
\fHBUFFER.2\fR, etc.
.P
This macro is designed to be as minimal as possible and
not rely on all aspects of CRISP being able to work.
.P
If CRISP crashes it is most likely to be due to some
strange combination of events which caused it to corrupt
memory, and so the \fHcore\fR macro has a good
probability of succeeding in its attempt to save files.
.H 2 "Regions and markers"
.P
A region, or marker, is an area of the current buffer which can be
manipulated independently of the rest of the text in the
buffer. Most commonly regions are used to hilight a
block of text which is to subsequently be deleted or
copied to some other buffer. A region is created by
typing one of the four keys: \fH<Alt-A>, <Alt-C>,
<Alt-L>\fR or \fH<Alt-M>\fR. Each of these different
keys creates a different type of region. When the cursor
is moved, the area of text from where the marker was
dropped and the current cursor position is called a \fIregion\fR.
Certain editing commands change their functionality
whilst a region is hilighted, e.g. the \fH<Del>\fR key
deletes the currently hilighted region, rather than the
character the
cursor is on.
.P
Column regions are not implemented directly by CRISP, but instead
are manipulated by various macros. CRISP only supports the screen
drawing necessary to draw a rectangular marker.
.P
There are four different types of regions, each one
accessible from a different function key:
.TS H
center allbox;
cB cB
l lw(4i).
Key	Description
=
.TH
<Alt-L>	T{
Drops a line marker. When a cut or copy command is issued, whole lines
will be affected in the operation.
T}
<Alt-C>	T{
Drops a column marker. Text falling within a rectangular region from
where the anchor was dropped to the current cursor will be hilighted.
T}
<Alt-M>	T{
Drops an inclusive marker. When a cut or copy command is issued, the character
under the cursor will be included in the operation.
T}
<Alt-A>	T{
Drops a non-inclusive marker. When a cut or copy command is 
issued, the character under the cursor will not be included in the operation.
T}
.TE
.H 2 "Searching for text -- Regular Expressions"
.P
A Regular expression is a term used to describe a string
of characters used in pattern matching. Regular
expressions allow certain classes of strings to be
matched, and provide a flexible way of matching
\'token's. CRISP provides a variety of features when performing
pattern matching:
.DS
	o   literal pattern matching.
	o   character class matching
	o   wild-card matching
	o   grouping
	o   alternation
	o   repeated expressions
.DE
.P
Many non-alphanumeric characters have a special purpose in a regular
expression, indicating a special action to perform. The following
table shows the regular expression matching idioms available, with
the highest priority at the top of the table.
are as follows:
.TS
box;
c c
lf(CW) lw(4i).
Character	Meaning
=
\\\\x	treat x as a normal character.
_
@	matches zero or more of the previous expressions.
+	matches one or more of the previous expressions.
{..}	groups a regular expression.
|	performs alternation.
_
<, %, ^	matches the beginning of a line.
>, $	matches the end of a line.
?	matches any single character.
*	matches zero or more characters
[..]	matches any character within the [..]
\\\\c	used to set the cursor after a match.
\\\\<	Match beginning of line or non-word character.
\\\\>	Match end of line or non-word character.
_
	Two regular expressions juxtaposed allow concatenation.
.TE
.P
An implied precedence is used with these characters, and it may be
necessary to use the '\\x' character to avoid certain characters being
treated as special characters.
.P
Technically, a regular expression consists of a sequence
of one or more simple expressions. A simple expression
(SE) is one of the following:
.DS
    a sequence of characters
    < or ^ or %
    > or $
    [..]
    ?
    *
.DE
.P
A simple regular expression (SRE) is a simple
expression, optionally followed or enclosed in a modifier:
.DS
    {SE}
    SE@
    SE+
    SE
.DE
.P
A regular expression is a sequence of simple regular
expressions as follows:
.DS
    SRE SRE      (Concatenation)
    SRE | SRE    (Alternation)
.DE
.H 3 "Character Escaping"
.P
The backslash character may be used to precede any
character to turn off any special effects the character
has. For example to match an asterisk in the text, the
sequence "\f(HB\\*\fR" would be used.

A common form of error when writing macros is to forget
that the macro compiler strips off the first level of
backslash characters. For example, if the user wants to
match an asterisk in a macro, he/she might write:
.DS
.ft CW
	search_fwd("\\\\*");
.DE

However, this is wrong. The macro compiler strips off
the '\\' and leaves the expression as "*" which matches
every line. In order to escape this character properly,
the following should be used:
.DS
.ft CW
	search_fwd("\\\\\\\\*");
.DE

In this case, the macro compiler strips off the first backslash
leaving "\f(HB\\*\fR" for the regular expression parser
to translate.

.H 3 "The wild card operators: ? and *"
.P
The '?' operator matches a single character; '*' matches
zero or more characters.

The number of characters matched by a '*' depends on
what follows the '*' and the search mode. (See section
on Search Modes).

For example, the following expression:

    cat*dog

matches any line which contains the word cat followed by
somewhere else on the line, the word dog.

.H 3 "Character Class: [..] and \\<..\\>"
.P
The square bracket operators are used to match one or
more characters from a class of characters. If the
expression is of the form '[..]' then a match is
successful if the character being matched is any of the
characters within the square brackets. If the first
character after the '[' is either a '^' or '~', then the
match is successful if the character is NOT equal to any
of the characters in the matched class.

The characters within the square brackets form either an
enumeration or a range of characters. '[ABC]' is an
example of an enumeration. It matches the single
character 'A', or 'B', or 'C'.

\'[a-z]' is an example of a range. It matches any lower
case alphabetic character.

Ranges and enumerations may be combined, for example the
following may be used to match a C symbol:

    [_A-Za-z][_A-Za-z0-9]@

which defines a regular expression expression consisting
of a single character of '_', an upper or lower case
alphabetic, followed by zero or more characters from the
class '_', A-Z, a-z or 0-9.

Special characters may be enclosed in the character
class construct using the \ syntax. Eg \n matches a
new-line; \t matches a tab.

The characters -, and ] may be included in the class by
preceding them with a backslash (e.g. \- or \]).

The regular expression characters \\< and \\> can be used
as word delimiters. The \\< sequence matches either a beginning
of line or any non-word character. The \\> sequence matches either
the end of line or any non-word character. A \fIword-character\fR
is defined as any of: [A-Za-z0-9_]. These two regular expressions can
be used as a short hand way of finding a word without matching the
word embedded in a larger word, e.g. \f(HB\\<begin\\>\fR matches
the word \fIbegin\fR but will not match the word inside \fIbeginning\fR
for example.

.H 3 "Repetition: @ and +"
.P
The @ and + are used to indefinitely match a previously
specified pattern. A simple regular expression followed
by '@' will be matched zero or more times; an SRE
followed by '+' will be matched one or more times.

For example, the following regular expression can be
used to match a sequence of words followed by a comma
(e.g. a sub-phrase of a sentence):

    {[A-Za-z]+[ ]+}+,

[A-Za-z]+ matches any word of one or more alphabetic
characters; the [ ]+ matches one or more spaces between
each word. The final }+ sequence means repeat the
previous expression one or more times.

The following example shows how to match the last word
of one sentence and the first word of the following
sentence:

    [A-Za-z]+.[ ]@[A-Z]

[A-Za-z]]+ matches the final word in a sentence. The '.'
matches the full-stop after it. The expression [ ]@
matches zero or more spaces which may separate the
full-stop and the first letter of the next sentence.

.H 3 "Regular Expression Grouping: {..}"
.P
The regular expression grouping characters are used for
one of two purposes - alter the precedence in which the
regular expression is parsed, and to define groupings of
regular expressions for use by the translation mechanism.

By and large, the regular expressions:

    xyz  and {xyz}

are equivalent. The major use is for bracketing in the
presence of the following operators: @, +, and |. For
example:

    {hello}@
    {cat}|{dog}

The other use for the bracket operators is to define a
sub-part of a regular expression for use in translation.
Each occurence of brackets is defined as a grouping. The
first occurence of {..} is group 1, the next is group 2.
By grouping parts of a regular expression, translations
can be made which swap fields around.

For example, say we have a piece of C code which defines
a table as follows:

    "string1",	number1,
    "string2",	number2,
      ..

If we need to swap the fields around so that we have the
numbers first on the line, and the strings following
them, then the regular expression search pattern can be
defined as:

    <[ \t]@{"[^"]@",}[ \t]@{[0-9]+,}

This breaks down as follows: <[ \t]@ matches the spaces
and tabs at the beginning of the line. {"[^"]@",}
matches the string field (quote followed by zero or more
non-quote characters terminated by a quote and a comma).
This is the first group. [ \t]@ matches the zero or more
spaces or tabs between the columns. {[0-9]+,} is the
second grouping and matches the number followed by a
comma.

If the translation replacement pattern (see (translate))
is defined as follows:

    \t\1\t\0

then this effects the field swap. The sequence \N where
N is in the range 0-9 means insert the matched group
designated by N.

.H 3 "Minimal and Maximal Matching"
.P
All Unix regular expression parsers use the '*' and '+'
operators to mean repeat the previous expression zero or
more, or one or more times respectively. CRISP uses the
\'@' and '+' operators for the same effect.

However, all Unix parsers, when matching repeated groups
will always try to match the longest string. Under Unix,
if we have the string:

    abbbbbbbc

and issue the search pattern:

    b*

then this will match the 7 b's between the 'a' and 'c'.
By default, CRISP (and BRIEF) perform a shortest match.
This means that the regular expression:

    b@

will match only the first 'b' after the 'a'. For pure
searches, the difference hardly ever matters, but when
translations are performed the different is very
important. In the above example, using the following
translation from 'vi' will result in the following string:

    s/ab*/X/p
    Xc

This is what happens with CRISP:

    (translate "ab@" "X")
    Xbbbbbbbc

This is simply because the Unix parsers try to match the
longest string, whereas CRISP tries to match the
shortest string.

CRISP provides the ability to modify this default
behaviour. BRIEF calls this behaviour minimal/maximal
matching and backward matching. BRIEF tries to combine
these concepts without clearly explaining what they are
doing.

CRISP operates in a compatible manner to BRIEF, and so
the same terminology will be adhered to.

The search macros - search_fwd, search_back, translate,
search_string and search_list have a parameter, labelled
\'re' which is used to control the minimal/maximal
matching. This parameter can have one of seven valid
values: -3, -2, -1, 0, 1, 2, 3.

The seven case are as follows:
.DS
    -3   maximal, backward 
    -2   maximal, same as search direction
    -1   maximal, forward
     0   forward
     1   minimal, forward
     2   minimal, same as in search direction
     3   minimal, backward 
.DE
0 is used for non-regular expressions. The maximal
matching modes are compatible with Unix.

.H 3 "Matching Direction"
.P
In the table above, the values show what form of
matching is to be performed. BRIEF and CRISP support the
concept of a forward and backward matching mechanism.
This terminology comes from BRIEF. The terminology is
actually not a very good one.

The difference between the two mainly comes into effect
when performing a backward match. Consider the following
line:

    the cat sat on the mat
    _^

with the cursor placed on the 'h' of the first word,
\'the'. In forward matching, if we are searching for the
word 'the', then the string 'he cat sat on the mat' will
be searched, and the match will be on the word 'the'
before 'mat'. In backwards matching mode, the search can
start before the cursor, and will match the word 'the'
at the start of the line.

.H 3 "Differences between BRIEF and CRISP"
.P
BRIEF was developed under DOS and provides a rich set of
regular expression operators. However, Unix already has
a de facto set of regular expression operators, which
are shared among programs, such as \fIvi(1)\fR, 
\fIed(1)\fR, \fIawk(1)\fR and \fIsed(1)\fR.

The full set of BRIEF operators are supported so that
BRIEF macros can be run under CRISP with no
modification. In some places extensions have been made
to the regular expressions accepted by CRISP to make
Unix users more at home. The following sections describe
the differences a regular BRIEF user will see when using
CRISP regular expressions, and the differences a Unix
user will see when using the CRISP regular expressions.

The following is a list of differences which BRIEF users
may see:
.DL
.LI
The following extensions have been added:
.LI
The ^ operator is valid as an alternative to '<' to
match the beginning of a line.
.LI
The sequence [^..] is equivalent to [~..] (matches all
characters not in the character class).
.LI
The character $ is valid as a match for the end of line
character, and is an alternative to '>'.
.LI
CRISP cannot perform regular expression matches over
line boundaries.
.LE
.P
The following describes the differences between Unix
regular expression syntax and the CRISP regular
expression syntax. 
.DL
.LI
The character '<' may be used as an alternative to '^'
to match the beginning of a line.
.LI
The character '>' may be used as an alternative to '$'
to match the end of line.
.LI
The sequence [~..] is equivalent to [^..] and is used to
match all characters not in the character class.
.LI
Users of egrep will note that the precedence of '|' is
different in CRISP. In egrep the expression 'dog|cat' is
bracketed as '{dog}|{cat}'; in CRISP, the implied
bracketing is '{do}{g|c}{at}'.
.LI
The \c pattern is available for cursor positioning; this
is not available under other Unix regular expression
parsers.
.LI
Under Unix, '*' is usually used as a postfix operator to
mean one or more occurences of the previous expression,
eg .* matches zero or more characters. In CRISP, '*' is
equivalent to .*, and is an operator in its own right.
.LI
The '?' in CRISP is equivalent to '.' in Unix.
.LI
The operator '@' in CRISP is equivalent to the '*'
operator in other Unix parsers.
.LI
Other Unix program bracket regular expressions using the \\(..\\) 
syntax. CRISP uses '{..}' instead.
.LE
.H 3 "Regular Expression Syntax Mode"
.P
CRISP allows the user to select between pure CRISP
regular expression syntax, as described above, or the
more familiar Unix syntax. This is done by calling the
\fIre_syntax()\fR primitive or by setting the SF_UNIX
flag to the \fIre_search()/re_translate()\fR primitives. 
In CRISP mode, regular
expressions are exactly as described in the previous
sections of this chapter.

In Unix mode, the following features are enabled/disabled:

The '.' character replaces the '?' wild-card character.

The '*' character means zero or more iterations of the
previous expression. This disables the CRISP '@' zero or
more character.

The sequence \(..\) replaces the use of {..}. The { and
} characters become normal characters.
.H 2 "Macros"
.P
One of the most important features about CRISP is that it is 
extensible, i.e. if you do not like some aspect of the user interface,
you can change it without having to recompile the source code
(usually). This extensibility is provided by macros.
.P
A macro is a sequence of instructions which performs a
high-level function. Different people have different
ideas about what a programming editor should look like.
Some people want simple options, others need complicated
macros which can perform tasks as complicated as sort
the functions in a .c file into alphabetical order.

CRISP is designed to allow it to be easy to customise
the editing tasks that a user wants, no matter how
complicated the editing facility required.

Rather than complicate CRISP internally with hard to
change ideas about what facilities should be available,
CRISP provides a set of builtin primitives which
manipulate the objects CRISP knows about, e.g. files,
buffer, windows, etc.

CRISP has a programming language which allows users to
combine these primitives into more complex functions.

For example, CRISP provides a set of functions to search
the current buffer for a string. One of the high level
macros provided with CRISP matches braces, i.e. it
checks that there are an equal number of opening and
closing brackets. This is an example of a macro. The
macro is built from the primitives which CRISP has
compiled into its code.

If you do not like the way these macros are written,
then you may easily customise them.
.P
CRISP provides a complete language for writing macros in.
This language, called \f(HBcrunch\fR, is similar to ANSI C.
Experienced C programmers should have little problem understanding
the macros supplied with CRISP, and can use a lot of ideas
from C in writing macros.
.H 2 "Registered Macros"
.P
Registered  macros  are a means of creating a macro which gets
called when certain events internal to CRISP happen.
These definitions are available in the crisp.h macro
file, and documented in the table below.

Any macro can be registered for one of these events by
calling the \fIregister_macro()\fR primitive. Whenever the
event is triggered the macro will be called. A macro can
be unregistered by calling the primitive
\fIunregister_macro()\fR.

An arbitrary number of macros may be registered for the
same event. If more than one macro is registered for the
same event, then the macros are called in the order they
were registered.

Registered macros are convenient short-hand ways of
intercepting certain operations within CRISP.
.TS H
allbox;
c c
l lw(4i).
Type	Description
=
.TH
0	T{
(REG_TYPED) This macro is called every time a character
is inserted into a buffer via (self_insert).
T}
1	T{
(REG_EDIT) This macro is called whenever (edit_file) is
called.
T}
2	T{
(REG_ALT_H)
This  macro  is  called  whenever  the  user presses <Alt-H>
whilst at the command prompts.
T}
3	T{
(REG_UNASSIGNED)
This  macro  is  called if the user presses a key which does
not have a macro bound to it.
T}
4	T{
(REG_IDLE) This macro is called whenever the idle timer
goes off. The idle timer is set by the CRISP command
line switch (-i).
It defaults to 60 seconds.
T}
5	T{
(REG_EXIT)
This macro is called when CRISP is about to exit. It is
designed to allow macros to tidy up after themselves
(e.g. delete temporary files).
T}
6	T{
(REG_NEW)
This macro is called whenever a new file is read into a
buffer via (edit_file).
T}
7	T{
(REG_CTRLC)
This macro is called whenever the interrupt key is
pressed. To avoid confusion, CRISP sets the interrupt
key 'out of the way' to <Ctrl-Y>. (<Ctrl-C> is usually
mapped to the center-line-in-window macro).

This  feature  allows  macros  to  be interrupted. Following
the  usual  safe  programming  style, it is only a good idea
to  set  a  flag in the interrupt handler and test its value
in the main-line macro.
T}
8	T{
(REG_INVALID)
This macro is called if the user types an invalid key at
the command prompt. This macro allows the abbreviations
and command history feature to be implemented.
T}
9	T{
(REG_INTERNAL) This is called when CRISP detects a segmentation
violation. This can be used to write all buffers away to disk in an
emergency. (Refer to \f(HBcore.cr\fR for an example of this macro).
T}
10	T{
(REG_MOUSE) Called when a mouse button is pressed or released.
T}
.TE
.bp
.H 1 "The CRUNCH Language"
.P
The crunch language is the language used to write macros for
CRISP. The crunch language looks and feels a lot like the C
language, and this should help users who are writing macros for
the first time, but there are significant differences, which the
user should be aware of.
.P
The CRISP language supports a number of primitive data types:
.DS
	32-bit integers (int)
	64-bit floating point numbers (float/double)
	strings (string)
	lists or arrays (list)
.DE
.P
CRISP acts as an interpreter for the language. The programs which
the user writes are first compiled to a compact pseudo code,
compatible with the \f(HB.cm\fR format generated by the older
cm compiler. Although CRISP is designed to run as fast as possible
and use as little CPU resources as possible, the design of the
interpreted language is aimed at keeping the size of the macros
as small as possible. Writing macros in the crunch language
allows the internal architecture of CRISP to be extended and improved upon
in the future whilst maintaining compatability, at the source code
level for user written extensions to the editor.
.P
Please note that the crunch compiler is implemented using a full
yacc grammar of the ANSI C language, and although many constructs
may be accepted by the compiler, they may not generate any code,
wrong coed, or cause the compiler to crash. For example, crunch knows
how to parse structure definitions, but they should not currently
be used for writing CRISP macros. (They were a temporary experiment
for another project).
.H 1 "Using the crunch compiler"
.P
The \f(HBcrunch\fR program is the crunch compiler. It takes
a source file (with \f(HB.cr\fR extension) and creates a \f(HB.cm\fR
file, ready for loading into CRISP. The crunch program uses
the systems C preprocessor (\fI/lib/cpp\fR under Unix, and \fIcl -E\fR
under OS/2) to first preprocess the source file. This allows the user
to take full advantage of the standard preprocessor facilities.
(It is advisable to avoid relying on non-ANSI-C preprocessor
facilities if you want your macros to be portable to other systems).
.P
Next the intermediate file is converted directly to the binary
output file. Crunch has a number of switches:
.VL 10
.LI -c
Compiles the source file to a \f(HB.m\fR file. This is useful
for understanding the translation process or to check for
bugs in the compiler. If you have any problems understanding
what crunch is doing, then use this switch.
.LI -Dvar
Used to \fI#define\fR constants before preprocessing. This switch
is passed directly to the preprocessor.
.LI -f
Used to flush output during debugging. Causes the output to be written
to the terminal. This is useful if crunch core-dumps and you
want to try and ascertain at what point during code generation the
problem is occuring.
.LI -Ipath
Add a path to search for include files. This switch is passed 
directly to the preprocessor.
.LI -g
Used to insert debugging information into the compiled code.
This switch is not guaranteed to do anything useful until
the CRISP macro debugging facility has been fully tested.
.LI -m
This is the make flag. Tests the modification time of the output file
versus the source file and only recompiles if it is necessary. This
allows trivial makefiles to be built rather than having to face the bugs
in standard make. (See the distribution makefile how to use this).
.LI -n
Print out the names of files which would be compiled, but don't compile
them. This flag is useful with the '-m' (make) flag to verify what files
will be recompiled.
.LI "-o file"
Specifies the name of the output file to create. The file 
parameter can be the name of a directory in which case the output
file is put into the specified directory.
.LI "-p cpp"
Used to specify the path of the C preprocessor to execute if
the one on your system does not conform to the standard
used by the current Unix versions, e.g. if you are using
Turbo C, or you have a POSIX compliant C compiler.
.LI -q
If more than one source file is specified on the command line, crunch
normally prints the name of each file as it is being compiled. This
switch can be used to turn off this feature.
.LI -S
Special non supported feature. Used to dump a symbol table.
.LI -Uvar
Make the named variable undefined. Passed directly to the C
pre-processor.
.LI -V
Prints version number of compiler.
.LI -#
Prints each pass of the compilation process as it procedes.
.LI +struct
Special non supported feature. Used to print structure definitions
for easy parsing.
.LE
.P
The crunch compiler more or less understands the full
ANSI C syntax, including structure definitions, bit
fields and typedefs. You may write code using this feature and
the compiler will accept it, but may cause core dumps,
or incorrect code to be generated. You are advised to
avoid using such features. The crunch compiler has been
used for other tasks (writing an assembler->C
disassembler) and support for structure passing was
added to allow the standard include files to be parsed.
This feature is NOT supported, so you have been warned.
.H 1 "Variables - types, scoping, argument passing"
.P
CRISP supports a minimal set of data types necessary to allow
sophisticated editing macros to be written. Crunch requires
that all variables to be used be declared before they are used. This
is similar to the C language, and is a useful feature since it avoids
bugs being introduced due to spelling errors. The compiler
will complain about references to variables which have not been declared.
.P
Although crunch may be classed as a fairly strongly typed language,
it has mechanisms for processing arbitrary variable types. For
example, a macro could be written to return the minimum
value of the arguments passed:
.DS
.ft CW
int
min(int a, int b)
{
	return a < b ? a : b;
}
.DE
.P
This is fine, but doesn't allow the user to write a generic macro
which can handle arbitrary variable types. For example, if two strings
were passed, maybe the shortest length string should be returned. This
can be handled by crunch with variables which are called \fIpolymorphic\fR.
The term polymorphic means that a variable can have an arbitrary type
and value. The type is dependent on its context. Originally, polymorphic
variables were added to facilitate the processing of lists, which are
sequences of values of arbitrary type. To write a more generic \f(HBmin()\fR
function, one could write:
.DS
.ft CW
declare
min(declare a, declare b)
{
	if (typeof(a) != typeof(b)) {
		error("Incompatible types.");
		return -1;
		}
	switch (typeof(a)) {
		case "integer":
		case "float":
			return a < b ? a : b;
		case "string":
			return strlen(a) < strlen(b) ? a : b;
		case "list":
			return length_of_list(a) < length_of_list(b) ?
				a : b;
		default:
			error("Unknown type");
			return -1;
		}
}
.DE
.P
Because the type of a polymorphic variable may change, CRISP supports
functions for determining the type of the variable and macros can
ensure that they don't attempt to perform an invalid operation.
.H 2 Scoping
.P
All variables created have a \fIscope\fR of visibility. 
CRISP supports essentially two types of variable definition -- global
and local. Global variables are always available to macros and retain
their values from one function to another. Local variables exist from
the point at which they are defined to the end of the \fIcurrent block\fR.
The current block is defined as the current level of curly brackets.
For example:
.DS
.ft CW
int fred = 99;
main()
{
	string	fred = "hello mum";
	int	i;
	
	for (i = 0; i < 99; i++) {
		list fred = quote_list(1, 2, 3);
		}
}
.DE
In this example, there are three occurences of the variable \f(BHfred\fR.
The first one is a global variable, and is assigned the value
99 when the macro is loaded. When the function \f(HBmain()\fR is called,
the global fred is saved, and a new variable is created, of type string.
The \fIfor\fR loop demonstrates a new occurence of fred being defined
purely for the scope of the loop. Within the loop, fred is a list.
When the loop exits, the string version of fred is accessible. Eventually
when the function terminates, the integer value for fred is accessible.
.P
Internally, scoping is implemented by associating a block level
with the definition of each variable. Global variables are defined
in block 0, which is never exited. Conceptually, each time an open
curly bracket is seen, a new level is entered. In the example
above, the string version of fred is defined at block level 1. When
the close curly bracket is seen the block level is decremented and
and variables defined in that scope are removed from
the symbol table.
.P
Because CRISP is an interpreter, certain features of the language
become available for very little interpretive overhead. One of these
features is \fIdynamic scoping\fR. Dynamic scoping is similar to
the scoping rules of \fBPascal\fR rather than \fBC\fR. It is easiest
to explain dynamic scoping together with an example:
.DS
.ft CW
int
func1()
{	int	a = 1,
		b = 2;
		
	func2();
}
void
func2()
{
	extern int a, b;
	
	message("a=%d b=%d", a, b);
}
.DE
.P
In this example, the function \f(HBfunc2()\fR is called from \f(HBfunc1()\fR.
The declaration:
.DS
.ft CW
	extern int a, b;
.DE
.P
is used to tell the crunch compiler that the variables \f(HBa\fR and
\f(HBb\fR will be accessible at run-time, even if there is no
definition of a and b within the current scope. Essentially, it just
tells the compiler to not complain about undefined variable references.
.P
When the line:
.DS
.ft CW
	message("a=%d b=%d", a, b);
.DE
.P
is executed, CRISP searches the current block level for a definition of
the variables a and b. Since these are not found, CRISP then searches
the current block level - 1. At this block level, the definitions are
found.
.H 2 "Argument Passing"
.P
CRISP supports a special form of argument passing, known as
lazy evaluation. The arguments to a function are not evaluated
at the time a function is called (as it is in C). Instead they
are evaluated at the time they are referenced in the called function.
This can lead to some difficult to understand code and hard to find
bugs, so it is important that the user understand this concept. 
This feature offers a lot of flexibility. 
.P
Before showing an example of this lazy evaluation scheme, it is necessary
to discuss the mechanism used to implement argument passing.
Writing a function which takes parameters, and calling that function
looks and mostly feels like 'C'. For example, to define a function
which takes three parameters, the first an integer, the second a
string, and the third a list would look like this:
.DS 
.ft CW
int
func(int arg1, string arg2, list arg3)
{
	...
}
.DE
.P
(Note that although Crunch will accept the old style C function
declaration, but will then ignore these arguments. This is a bug and
will be fixed at some stage in the future). The code above is treated
as if the function was implemented as follows:
.DS
.ft CW
int func()
{
	int	arg1;
	string	arg2;
	list	arg3;
	
	get_parm(0, arg1);
	get_parm(1, arg2);
	get_parm(2, arg3);
	
	...
}
.DE
.P
The user can pick either form for functions. Normally it is best to
use the pure C style for function definitions, and use the
\f(HBget_parm()\fR primitive when a non-C compatible calling sequence
is required, or for \fIvarargs\fR support.
.P
For example, consider a macro which adds up all the integer parameters
passed to it:
.DS
.ft CW
int sum()
{	int	arg_no = 0;
	int	sum = 0;
	int	arg;
	
	while (get_parm(arg_no, sum) > 0)
		sum += arg;
	return sum;
}
.DE
.P
Although crunch does not currently validate parameters against prototypes,
it will do someday. It is useful therefore to tell crunch the
types of variables expected, using the ANSI-C style declaration.
However, crunch can/will do more checking the pure ANSI-C, which cannot
check arguments at the end of a varargs list. (ANSI-C uses the ellipsis \fB...\fR 
to do this). In crunch, it is possible to indicate that
an argument may be optional. This is done by preceeding the type
specifier with a tilde:
.DS
.ft CW
int
func(int arg1, ~list, string arg3)
{
	...
}
.DE
.P
This causes the variables arg1 and arg3 to be set up on entry to
the function, but it is the functions responsibilty to get the value
for the second parameter. 
.P
Given the above descriptions, it is now possible to understand the
lazy evaluation scheme more easily.
Consider the following program:
.DS
.ft CW
find_strlen(string str)
{	int	i = 0;
	int	len;
	
	len = iterate_strlen(str, ++i);
	
	/* At this point i is 1 greater than len. */
	message("len=%d i=%d", len, i);
}
int
iterate_strlen(string str, ~int)
{	int	len = 0;
	int	arg;
	
	while (1) {
		get_parm(1, arg);
		if (substr(str, arg, 1) == "")
			break;
		len++;
		}
	return len;
}
.DE
.P
In the call to the function \f(HBiterate_strlen\fR, the second parameter
is specified as \(HB++i\fR. This does not cause \f(HBi\fR to
be incremented until it is \fBreferenced\fR in the function
\f(HBiterate_strlen()\fR. This occurs at the line:
.DS
.ft CW
	get_parm(1, arg);
.DE
.P
Lazy evaluation is used in the supplied macros mainly to
allow specifying a private key mapping for pop up windows.
For example, the function \f(HBselect_buffer\fR takes an optional
parameter, (the 4th one), which is not evaluated directly on
entry to the function, but after the keyboard mappings have been
set up. This allows the calling macro to specify the name of a function
to call to do whatever is necessary just before the user is shown the
popup window.
.H 2 "The int data type"
.P
The \f(HBint\fR keyword is used to declare integer variables, i.e.
variables which can hold only integral values. CRISP currently
only supports 32-bit integers (i.e. chars and longs are not supported
nor their signed/unsigned counterparts). Integer variables are
32-bit twos complement numbers. The 32-bit word size is chosen for
maximum portability and usefulness.
.P
Integer variables are used for many reasons -- as counters, indices,
buffer identifiers, etc.
The full complement of C operators are supported for manipulating integer
variables.
.P
Integers are always stored in macros in a machine independent fashion. This
means that compiled macros using integer variables are portable to
machines with different byte orderings.
.H 2 "The float/double data type"
.P
The \f(HBfloat\fR and \f(HBdouble\fR data types are supported to
facilitate implementation of macros which need to use floating
point numbers, e.g. the calculator macros, and the \f(HBsum\fR macro.
CRISP has no internal use for floating point numbers. 
.P
CRISP stores floating point numbers using the native C compilers
\fIdouble\fR keyword, normally corresponding to a 64-bit quantity.
.P
Floating point numbers are declared using the \f(HBfloat\fR or \f(HBdouble\fR
keywords. Currently these two keywords are treated as being
identical. It is recommended that users use the \f(HBfloat\fR or
\f(HBdouble\fR keywords as appropriate to the task in hand. Later versions
of CRISP may support a shorter floating point type for efficiency.
.P
Floating point constants compiled into macros are NOT stored in a
machine independent manner, and thus compiled macros may not be
portable to different machines.
.P
Floating point numbers may be implicitly cast into integer values
under certain circumstances.
.H 2 "The string datatype"
.P
CRISP supports a dynamic string data type. Strings variables may
be used to store arbitrary length strings (upto 64K on 16 bit machines and
4GB on 32-bit machines). Strings may be used to store any sequence
of characters, although storing the NULL character (ASCII 0) may cause
problems, e.g. when determining the string length.
.P
Storage for strings is dynamically allocated so no space needs
to be preallocated for them.
.P
Strings may be combined with the other data types to perform concatenation.
.H 2 "The list datatype"
.P
CRISP contains support for lists. Lists are a way of grouping
arbitrary values which may be treated as a single value. Lists may
be treated like arbitrary length arrays, as structures or purely as lists.
Lists were added to the language to make certain tasks easy to
describe and implement. (The laternative to lists would be
to use a string and manually look after element boundaries, or
use a buffer where each line in a buffer contains the appropriate data
type. However, each of these approaches looks and feels messy).
.P
Lists in CRISP are not very efficient data types, but are much better
than the user crafting his/her own using the other supported types.
.P
A list may be used to store any other data type, including lists.
A list may be extended only at its end, by appending data to it. Any
element may be referenced by specifying its ordinal position in
the list.
.P
For example:
.DS
.ft CW
	list	lst;
	
	/* Assign initial value to a list. */
	lst = quote_list(1, 2, 3);
	
	/* Now add something to it. */
	lst[3] = "hello";
	
	/* Now modify element in the middle. */
	lst[2] = quote_list("abc", "def", 1.2);
.DE
.P
List elements (or \fIatoms\fR), are indexed using a zero offset, i.e.
the first element in a list is accessed as \f(HBlist[0]\fR.
.H 2 "The declare datatype"
.P
The \f(HBdeclare\fR keyword is used to create a polymorphic variable.
A polymorphic variable is one in which the type of the variable
stored can be changed. These are normally used as function parameters
when it is not known until run-time what the actual type will be,
or for looking at elements in a list. The actual type of a polymorphic
variable is \fIfrozen\fR when a new value is assigned to it, and
until a new value is assigned the function can treat the type
of the variable as if it were of the type frozen.
For example:
.DS
.ft CW
	declare	var;
	
	var = 1.0;
	var += 2.3;
	/* var now contains value 3.3 */
	
	var = "string";
	var += "fred";
	
	var = NULL;
	/* Variable contains no value. */
.DE
.H 1 "Loading a macro: main, _init"
.P
As explained below, global variables may be initialised with non-constant
expressions, unlike C which is limited to a constant expression.
Because of this facility, CRISP provides a mechanism for ensuring
that these global variables are initialised before the macros
execute. All global variable definitions and initializors are
compiled into a function called \f(HB_init\fR. When a compiled
macro file is loaded (the .cm file), this macro is executed first.
Programmers can put their own one-time initialisation code in the
function \f(HBmain()\fR. All the code in \f(HBmain()\fR is executed
within the context of the function \f(HB_init\fR after the global initialisations.
To understand this better, it is best to compile your code with the
\fB-c\fR switch and look at the lisp code that the compiler generates.
.H 1 "Declarations"
.P
There are two main types of declarations -- function definitions and
data declarations. A function definition defines the body of a
function. A data declaration is used to declare global variables
or specify prototypes.
A data declaration has the form:
.DS
.ft I
[storage_class_specifier] [type_specifier] [declarator [= initializer]] ;
.DE
.P
This is similar to C. The \fIstorage_class_specifier\fR is used
to identify how the variable is to be stored. The currently supported
and meaningful storage class specifiers are:
.VL 12
.LI extern
The variable is defined somewhere else. References to the variable
will be validated against its type information, but no code
will be generated to create the variable. This is typically used
to implement a \fIforward\fR reference mechanism.
.LE
.P
Type specifier should be one of the following:
.VL 12
.LI int
Used to define a variable which will store a 32-bit integral value
or to specify a function which returns an \f(HBint\fR value.
.LI float
.LI double
Used to define a variable which will store a 64-bit floating point value
or to specify a function which returns a \f(HBfloat\fR value.
.LI string
Defines a variable which can store an indefinite length string or
a function which returns a \f(HBstring\fR value.
.LI list
Defines a variable which can store a list value or a function
which returns a \f(HBlist\fR value.
.LI declare
Defines a polymorphic variable which can store any data type,
or a function which can return any type.
.LI void
Used to indicate a function which doesn't return a value.
.LE
.P
A \fIdeclarator\fR is defined as one or more variable names, or function
prototypes separated
by commas. 
.P
An initializor is used to give a variable an initial value, similar to C.
Initializors may be arbitrary expressions, i.e. they are not limited
to constant expressions, even for global variables. Lists may be initialised
somewhat similarly to C structure initialisors.
.P
For example:
.DS
.ft CW
extern list fred;
int	func (int, string, string);
int	a, b = 1;

list	l = {
	"Item-1", {1, 2, 3},
	"Item-2", "hello mum",
	"Item-3", 3.14159,	/* Trailing comma optional */
	};
.DE
.H 1 "Function definitions"
.P
A function definition has the form:
.DS
.ft CW
[storage_class_specified] [type_specifier] function_name
	( [arg_list] )
	{
		function_body
	}
.DE
.P
The \fIstorage_class_specifier\fR is currently ignored, although
a future version of the language will be able to understand the 
\f(HBstatic\fR class specifier.
The \fItype_specifier\fR is used to indicate the return type of
the function. This is currently ignored.
.P
The argument list should be specified in the ANSI-C format, specifying
the type specifiers and optional names. In addition, crunch
supports the syntax:
.DS
.ft CW
	~ type_specifer [name]
.DE
.P
which acts as a place holder for an optional argument or an
argument which is to be handled with different semantics from the
standard C-style.
.P
Crunch also supports the ellipsis (...) to indicate that optional
further arguments may be specified. 
.P
Please note that crunch does not currently check function calls agsinst
prototypes.
.H 1 Expressions
.P
The following table summarises the operator precedence and associativity
of the primitive elements of an expression. This table is a copy of the
table which can be found by executing the \f(HBhier\fR macro at the
command line prompt:
.DS
.ll +1i
Arity       Operator                                   Assoc
--------------------------------------------------------------
binary   ()  []  ->  .                                 l -> r
unary    !   ~   ++  --  -  (type)  *  &  sizeof       r -> l
binary   *   /   %                                     l -> r
binary   +   -                                         l -> r
binary   <<  >>                                        l -> r
binary   <   <=  >   >=                                l -> r
binary   ==  !=                                        l -> r
binary   &                                             l -> r
binary   ^                                             l -> r
binary   |                                             l -> r
binary   &&                                            l -> r
binary   ||                                            l -> r
ternary  ?:                                            r -> l
binary   = += -= *= /= %= >>= <<= &= ^= |=             r -> l
binary   ,                                             l -> r
--------------------------------------------------------------
                                               From K&R, p 49
.ll
.DE
In the above table, the following are \fBnot\fR supported: \f(HB->\fR,
\f(HB.\fR (dot), \f(HB(type)\fR, \f(HBsizeof\fR. Also, crunch does
not support structures, unions, pointers, or explicit dereferencing.
.P
Crunch supports a fair amount of automatic type co-ercion. The
following table lists the co-ercion rules when used with the
arithmetic operators only. (Type co-ercion is not performed for
function arguments). The prefix character is used to
identify the type of the variable or expression - i = integer,
f = float, l = list, s = string, a = any type. \f(HBsym\fR is used to denote
a symbol of the specified type; \f(HBexpr\fR is used to denote
an expression evaluating to the specified type.
(Note that these conversions are implicit -- the typecast
on the right hand side is not a supported feature, currently).
.DS
	isym op= fexpr	=> iexpr op= (int) fexpr
	fsym op= iexpr	=> fsym op= (double) iexpr
	lsym += aexpr	=> append aexpr to end of lsym

	fsym++		=> fsym += 1.0	
	fsym--		=> fsym -= 1.0	etc..
	
	iexpr op fexpr	=> (double) iexpr op fexpr
	fexpr op iexpr	=> fexpr op (double) iexpr
	iexpr + sexpr	=> "iexpr + sexpr"
			   (string concatenation)
	fexpr + sexpr	=> "fexpr + sexpr"
			   (string concatenation)
	aexpr + lexpr	=> new list with aexpr at front
.DE
.P
Basically, a string plus a number (or vice versa) converts the number
to a string and performs string concatenation. A list plus
any type creates a new list by concatenating list and value.
An integer and floating point value when combined results in
a floating point value.
Using these rules, a value of one type can easily be converted to
a value in another type:
.DS
	string	s;
	int	ival;
	
	ival = 99;
	
	s = "Value: " + val + " brass monkeys";
	/* s = "Value: 99 brass monkeys" */
	
	ival = 1000000;
	s = "Value: " + (0.0 + ival) + " big macs";
	/* s = "Value: 1e+06 big macs" */
	
.DE
.P
When converting floating point numbers to strings, the "%g" printf
format specifier is used.
.H 1 "Loop constructs: for, while, do"
.P
There are three main looping constructs, the \fIfor\fR loop, the \fIwhile\fR,
and the \fIdo\fR loop. The for loop is a generalised looping mechanism
supporting the following syntax:
.DS
	for ( init-expr ; while-expr ; post-expr )
		statement
.DE
The init-expr is evaluated first. Next the \fIwhile-expr\fR is evaluated,
and if it evaluates to \fITRUE\fR, then \fIstatement\fR is
executed. After \fIstatement\fR is executed, \fIpost-expr\fR is
executed. The loop continues until \fIwhile-expr\fR evaluates to
\fIFALSE\fR. Any cobination of the \fIinit-expr\fR, \fIwhile-expr\fR,
and \fIpost-expr\fR may be omitted, as in standard C. If \fIwhile-expr\fR
is omitted, then the loop will execute indefinitely. In this case the
only way to terminate the loop is if the \fIstatement\fR part of
the loop contains a \fIreturn\fR or \fIbreak\fR clause.
.P
The \fIwhile\fR looping construct has the syntax:
.DS
	while (expr)
		statement
.DE
.P
\fIexpr\fR is evaluated and if it evaluates to non-zero, then \fIstatement\fR
is evaluated. This process is repeated until \fIexpr\fR evaluates
to FALSE, or the statement clause causes an exit from the loop (either
via \fIbreak\fR or \fIreturn\fR).
While loops always evaluate the expr clause at least once.
.P
The \fIdo\fR looping construct has the syntax:
.DS
	do
		statement
	while (expr)
.DE
.P
In this case, the \fIstatement\fR clause is executed first, and the
\fIexpr\fR clause is tested after the body of the loop has been 
executed. Do loops are useful when you need to guarantee that the body of
the loop is executed at least once.
.H 1 "Testing expressions: if"
.P
The if statement is used to execute a piece of code conditionally.
The syntax is:
.DS
	if (expr)
		statement-1
	[else
		statement-2]
.DE
.P
The \fIexpr\fR is evaluated and if it is non-zero, then \fIstatement-1\fR
is evaluated. If \fIexpr\fR evaluates to zero (false), and if the \fIelse\fR
clause is present, then \fIstatement-2\fR is executed instead.
.P
Crunch also supports the tertiary '?..:' operator which can be used
inside expressions, for example:
.DS
	int	a = b > c ? b : c;
.DE
.H 1 "Selection: switch"
.P
The switch statement is a compact and fast mechanism for selecting
a statement to execute depending on the value of some expression.
The general syntax is:
.DS
	switch (expr) {
	  case expr-1:
	  	statement-list-1
	  case expr-2:
	  	statement-list-2
	  ...
	  default:
	  	statement-list-n
	  }
.DE
.P
Switch statements look and almost feel like C switch statements.
Switches are interpreted as follows: \fIexpr\fR is evaluated and
tested for equality against \fIexpr-1\fR. If it matches, then
\fIstatement-list-1\fR is executed and execution continues after the switch
statement. If the match fails, \fIexpr-2\fR is tested, and so on until 
either a match is found, the \fIdefault\fR clause is reached or
no entry is found. If no entry is found, then execution continues after
the switch statement. If the default clause is reached, then the statements
there are executed.
.P
Crunch allows multiple cases to be associated with a single statement:
.DS
	switch (expr) {
	  case 1:
	  case 2:
	  	do_something();
		break;
	  case 3:
	  case 4:
	  	do_something_else();
		break;
	  }
.DE
.P
The statement-list associated with a \fIcase\fR statement may consist
of zero or more statements, and may optionally be enclosed in braces, e.g.
to allow declaration of local variables:
.DS
	switch (expr) {
	  case 1: {
	  	int	i;
		i = 3 * 4;
		break;
		}
	  ...
	  }
.DE
.P
The \fIbreak\fR statement may be used to terminate the switch statement.
.P
The switch statement has a bug associated with it. Consider
the following example:
.DS
	int i = 0;
	switch (1) {
	  case 1:
	  	i++;
	  case 2:
	  	i++;
	  break;
	  }
.DE
.P
In the C, language, \fIi\fR will have the value of 2 after executing the
switch statement. In crunch, \fIi\fR will have the value
1. This is because crunch does not currently support the flow-thru
facility of C. This may be considered a bug, and user code
should not rely on this as it is liable to change in future versions
of the language and compiler. (The CRISP supplied macros \fBdo\fR
rely on this feature and are wrong!).
.H 1 "Known deficiencies in Crunch"
.P
.AL
.LI
Crunch does not generate code for old style C declarations. The
new style must be used.
.LI
Crunch does not verify that the return value from a function agrees
with the return type declared for the function. (It does check return(expr);
and return; statements against whether the function is void or not).
.LI
Crunch does not check the type of arguments passed to a function
against the prototype for the function.
.LI
There are no prototypes defined for the builtin primitives.
.LI
Crunch does no compile-time checking of the reasonableness
of expressions, regarding types, For example:
.DS
.ft CW
	list	l1, l2;
	
	l1 = l1 * l2;
.DE
.LI
Crunch complains about unused arguments sometimes because it cannot
intuit whether an argument is going to be used via the dynamic
scoping rules.
.LI
If a local variable is declared at the top level of a function definition
with the same name as a parameter to the function, the user is not
warned and possibly incorrect results will occur.
.LI
Crunch warns unnecessarily about declarations of the following form:
.DS
.ft CW
	int	fred(string a, int b, list c);
.DE
.LI
Case statements which flow into each other are not interpreted
the same as C.
.LI
Many other problems and deficiencies too many to mention or remember.
.LE
.TC
