A 6.001 User's Guide to the DECsystem-20 Originally by Kevin B. Theobald First draft started 3/17/80, completed 4/22/80 Revisions: KMP, GJS, SK 8/80 HAL 1/81 BOWB 8/81 SHOMIE 8/82 HAL 11/82 This manual is a brief introduction to the TOPS-20 operating system, intended primarily for use by students in 6.001. The manual is divided into three main sections. The first section outlines the general procedure for writing, editing and running SCHEME programs on the DECsystem 20, then introduces the TOPS-20 operating system and its role in this procedure. The second section is a simple introduction to the EMACS text editor. The third section is a brief introduction to SCHEME, the particular LISP interpreter which will be used in 6.001. The manual is geared toward students who may have had prior computer experience, but are unfamiliar with the systems presented here. When first learning to use these systems, the beginner should first scan this manual to gain an overall view of how the system works, then read the manual thoroughly while sitting at a terminal. This manual is designed for this method of learning, so there are many examples showing sample computer input and output. The following symbols, conventions and terms are used throughout this manual: Symbol Description or Term ^ This represents the CTRL (CONTROL) key. The symbol means that the CTRL key should be held down while the character immediately following the "^" symbol is typed. This rule only applies to the first character following the "^". For example, "^XS" represents a "^X" followed by an ordinary "S". Characters generated while the CTRL key is depressed are called control characters. If the computer prints a control character, it will precede the letter with the "^" symbol. Note that there is a "^" character on the keyboard. Typing this character does not have the same effect as the CTRL key. {} Braces indicate that the phrase between them is not a string of characters to be typed in literally, but rather a general term for a variable object. For example, the form delete {filename} means that you should type in "delete" followed by a space and the name of your file. {cr} Indicates that the RETURN (carriage return) key, usually located in the lower-right corner of the keyboard, should be typed. TOPS-20 commands are generally terminated with a {cr}, although this is not true in EMACS. {esc} Indicates that the ESC key (ESCAPE; ALT or ALTMODE on some terminals), located in the upper-left corner of the keyboard, should be typed. When the computer echoes the ESC character (i.e., if it prints it on the screen when you type it), it uses a dollar sign ("$") to designate the ESC. Like the CTRL key and "^", the ESC key is not the same as the "$" key. {del} Indicates that the DEL key (DELETE; RUB or RUBOUT on some terminals), should be typed. When you are typing, {del} will delete the last character typed. {space} Indicates that a space should be typed. case None of the systems described in this manual distinguish between upper- and lower-case letters, so unless you are typing English text, everything can be typed using lower-case letters. In this manual, upper-case letters are used to indicate computer output. For example, the form login{esc} (USER) indicates that the user types login{esc} and the computer responds with (USER). The only exceptions to this rule are the control characters. Control characters do not distinguish between upper- and lower-case letters, so they are conventionally represented by upper-case letters. cursor The cursor is usually a blinking square on the terminal screen. It sometimes masquerades as an underscore and sometimes does not like to blink. The cursor indicates where the next character typed will be inserted. 1. INTRODUCTION TO TOPS-20 The general procedure for using SCHEME on the TOPS-20 system is as follows: 1) Log into the TOPS-20 operating system. 2) Invoke the SCHEME interpreter by typing SCHEME{cr} at the EXEC level of the TOPS-20 system. 3) If you already have a program that you want to work on stored in a TOPS-20 file, load it into SCHEME using the LOAD procedure as described later. If not, go to step 6. 4) Test your program. 5) If you are satisfied with your program, return to TOPS-20 using (QUIT) and log out. If not, you must edit your work. 6) Use the EDIT command of SCHEME to enter EMACS so that you can make a program file or edit your existing file. You will need to visit your file (see the EMACS command ^X^V) with EMACS the first time you use the EDIT command in SCHEME, but from then on your file will remain in EMACS. 7) Make the necessary corrections to your file, then return to SCHEME for testing it (step 4). You may have to mark the regions of the file which you have changed so that the updated version is transmitted back to SCHEME when you return to SCHEME. See the description of the EDIT command in the SCHEME manual for an explanation of how this is done. SCHEME is accessed via the TOPS-20 operating system, but the TOPS-20 system also has commands which allow you to perform "housekeeping" functions with your files, such as listing them and getting rid of those that are old and unneeded. 1.1 LOGGING IN AND OUT To use TOPS-20, EMACS and SCHEME, you must first get into EXEC, the part of TOPS-20 that interprets the commands you give. Then you must log into your account. First, find a free terminal. You should see a message like Welcome to MIT-EECS. at the top of the screen. If the terminal is already on, you should see either the above message or one like LOGOUT JOB 11, USER 001.THEOBALD, ACCOUNT 6001, TTY 6, AT 20-MAR-82 19:21:54, USED 0:1:30 IN 0:50:20 If you don't see such a message, then the last user may have forgotten to log out. Ask a neighbor or a Teaching Assistant if the terminal is really free. If you determine it is (be careful because you may be destroying someone's work), type ^C, then type logout (Terminate this command and all others in this section with {cr} unless otherwise noted.) To get the system's attention, type ^C. The system should respond with a short message identifying itself, then print a '@' character. This character is a prompt, which means that EXEC, the command interpreter, is waiting for you to type something. The format for logging in is login {username} {password} Note that the three words are separated by spaces. Your {username} is the name of your account, and it distinguishes your accounts from others. Your {password} is an access word which only you know. The password prevents others from using your account. In an effort to keep your password secret, the terminal doesn't echo (display your input on the terminal) when you type your password. Therefore, if your username is ls.theobald, the terminal will display @login ls.theobald If you receive a message like ?DOES NOT MATCH DIRECTORY OR USER NAME then you didn't type the username properly. Similarly, the message ?INCORRECT PASSWORD means you typed the password incorrectly. Try logging in again. When you are finished using the terminal, return to EXEC (with a ^C) and type logout 1.2 SPECIAL TOPS-20 CHARACTERS There are several special characters you can use to correct typing mistakes and invoke special TOPS-20 functions. These characters, unlike most commands in this section, are not followed by a {cr}. Character Description {del} This deletes the last character typed. For example, la{del}ogiu{del}{del}out is equivalent to "logout". A video terminal will move the cursor backwards and remove the mistake. A printer will reprint the mistake and follow it with a slash, but the mistake is still removed from EXEC's memory. ^U This deletes the entire line just typed. ^L This clears the screen and redisplays the current line. You can still delete the characters on this redisplayed line. ^Q This is used only if your terminal is in "page" mode (see sec. 1.5). If the terminal has printed a page and has stopped, ^Q will start the printing again. This is explained in further detail in sec. 1.5. {esc} If this is typed while at the EXEC command level and at certain times in EMACS, it will invoke a feature called "recognition input". When {esc} is typed, EXEC will attempt to complete a command field if it is incomplete. A field is a part of the command which is separated from the other parts by spaces or other punctuation marks. For example, the login command has three fields: "login", the username and the password. The partially-completed command field must have enough letters to make it indistinguishable from the other possible commands (see "?" below), otherwise, typing {esc} will do nothing but make the terminal "beep". For example, lo{esc} will not work, since there are commands called load, login and logout. The {esc} command will not complete your password, for obvious reasons, although it will complete your username. When the computer completes the command field, it will also print a phrase indicating what should go in the following command field. For example, if you type log{esc} the computer will complete the field and ask for the next field: logIN (USER) (Even though both "login" and "logout" begin with "log", you only need to type "log" to log in, since EXEC knows that you are trying to log in. However, you must type "logo" to log out.) Once the entire command has been completed, a {cr} will tell EXEC to execute it. Note: you do not need to use the {esc} function to complete a command field. To type a command, you only need to type enough letters to distinguish it from all other commands. For example, you only need to type "di" to use the "directory" command. Once you are familiar with the commands, you may use this time- saving feature. However, the {esc} key MUST be pressed to complete filenames. The characters and fields of the partially/fully completed filename may be edited with {del}, ^W, and ^U as usual. ^W This deletes the command field currently being typed. For example, if you type login lt followed by a ^W, EXEC will delete the "lt"; you may then type in another username. ^? This is a call for help, and it can be used in many ways. (Note: just plain ? works, too.) Typing ? after a @ prompt will produce a list of all EXEC commands. Typing ? instead of a command field will produce a list of all the options you can choose to put in the command field. If {esc} does not complete an incomplete field because the field doesn't have a unique completion, "?" will produce a list of those commands or fields which start with your field. For example, if you type lo{esc} the terminal will "beep", because EXEC cannot uniquely complete this command. If you then type "?", EXEC will respond with lo? COMMAND, ONE OF THE FOLLOWING: LOAD LOGIN LOGOUT @LO Note that your input line is retyped, so you may complete the command as if you had just typed in the first two characters. 1.3 RUNNING PROGRAMS There are two kinds of input that TOPS-20 will accept: commands and program names. Commands, such as login and logout, are routines executed by EXEC. Programs are routines which exist on a lower level, hence, they are not executed by EXEC, but are only accessed by it. EMACS and SCHEME are programs. To invoke a program, type its name to EXEC. Thus, typing "SCHEME" or "SCH"{esc} will invoke SCHEME, our LISP interpreter. The mechanism TOPS-20 uses to keep track of these programs is called a "fork." Each program you start creates a new fork. When you have finished using a program, you can get back to EXEC by typing ^C^C. (Actually, only one ^C is needed if the program is awaiting input, but two ^Cs will always work and the extra ^C will not harm anything.) If you exit a SCHEME or EMACS in this manner, you will not terminate it; it will only be suspended. The fork you created will remain and may be reactivated. (Actually, this is only true for programs, such as SCHEME, which have been set to "autokeep". Exiting other programs via ^C may terminate them.) To reactivate a suspended program, type its name again. Your program will continue where it left off when you suspended it. Do not use the {esc} key to complete the name of a suspended program. For some obscure reason, this will create a new fork rather than resume the old fork. If you follow the steps given at the beginning of sec. 1 for using SCHEME, your session will look something like this: @login ls.theobald JOB 17 ON TTY6 24-MAR-82 20:26 PREVIOUS LOGIN: 21-MAR-82 18:42 @SCHEME [KEEPING] . . {use of SCHEME} . ==> (edit) (=> EMACS) . . {use of EMACS} . ^XZ (SCHEME <=) . . {continuing use of SCHEME} . ==> (edit) . . {repeat until SCHEME program works} . ==> (quit) Now you know the essentials -- how to start, stop, and restart programs. Some of the fine points are revealed below. Skip to the next section if this sort of thing doesn't appeal to you. At EXEC level you can obtain information about the forks you are using by typing information fork-status which can be abbreviated "in fo". You will see something like => SCHEME (1): ^C FROM IO WAIT AT 116710, 0:03:34.7 EMACS (2): HALT at 41301, 0:01:01.9 TOPS-20 assigns each fork a number, in the order that they are created. The arrow by SCHEME indicates that SCHEME is the "current" fork, i.e., the fork used most recently. If you are no longer using a fork, you can remove it by typing reset {fork} where {fork} can be either the name or the number assigned to it. If no argument is given for {fork}, then EXEC will remove all unkept forks (see below). If the current fork is removed, the highest-numbered fork becomes the current fork. If you reset a fork, EXEC will always print the name of the current fork (the new current fork if you reset the old current fork) in brackets. If you type keep {fork} the specified fork (or the current fork if none is specified) will be "kept". If the current fork is "kept", you cannot remove it with the reset command unless you also specify the name or number. This is to prevent you from accidentally removing wanted programs. SCHEME automatically "keeps" itself when it is called. Thus to remove it, one needs to type reset scheme or reset {scheme's fork-number} 1.4 TOPS-20 FILES When you write files they are stored in a directory assigned to your account. A directory is a block of memory with a certain size limitation, which will quickly be exceeded if you leave old, unneeded files in your directory. This section and the next describe the basic "housekeeping" functions of TOPS-20, including those that allow you to remove old files, print and copy files, and list your directory. Each file must have a filename to distinguish it from other files. The basic format for a filename is {device}:<{username}>{name1}.{name2}.{generation number} The device is the name of the main memory structure in which your directory is located, and the name of the device is always followed by a colon. The username is the name of your account. It is enclosed in angle-brackets. If the first two fields are not specified, then your device and username are used as default values. Since you will usually be working with files in your own directory, the only part you need to worry about is the last part: {name1}.{name2}.{generation number} {name1} is a word that describes what is in the file. It can be, for example, the name of a SCHEME procedure or a word describing its purpose or contents. Name2 is another word, but some programs require it be set to a particular string. For example, the SCHEME interpreter requires you to use "SCM" to show that the file contains a SCHEME program. Other standard types include ALG for ALGOL and TXT for text. For this reason, Name2 is often referred to as the "type" of the file. Up to 32 characters are permitted for the "name" part of the filename (Name1 and Name2). Generation numbers are positive integers used to specify the revision number of the file. Generally, the first draft of a file is generation number 1, the first revision is number 2, etc. There is a special feature that allows you to dispense with generation numbers altogether. If you do not specify a generation number when you deal with a file, the system will assign one based on the following rules: 1) If the command reads the file, then EXEC looks through all files which match the name and type specified in the command, and chooses the one with the highest generation number. If no file in your directory matches the specified file name and type, an error message is returned. 2) If the command writes the file, then it creates a new file. The generation number of this file is 1 greater than the highest existing generation number. If no file in your directory matches the specified name and type, the new file is given the generation number 1. 3) Some EXEC commands will act on all files in your directory matching the specified name and type. These commands will be described in sec. 1.6. For example, suppose your directory is empty. If you create a program in EMACS, use the appropriate EMACS command (see sec. 2) to write it into a file, and you only specify program.scm as the file name, your directory will now contain the file program.scm.1. If you perform another write function, your directory will contain program.scm.1 and program.scm.2. If you read a file, and you don't specify a generation number, the file program.scm.2 will be read. If you consistently use this feature, you will always read the most recent revision of your file, and always write revisions into new files. This is almost always the right thing to do. You may then ignore the generation numbers unless you need to recover an old version of a program. Note that only the three most recent generations are kept in your directory. Older generations are automatically expunged. 1.5 EXEC COMMANDS The following section contains a summary of useful "housekeeping" commands. To execute a command, simply type its name. No additional fields are needed unless otherwise specified. The ? and {esc} functions will help you if you aren't sure if additional fields are necessary. 1.5.1 FILE COMMANDS DIRECTORY This command gives a short listing of the files in your directory. If a file has more than one generation number, all the generation numbers are printed on one line, separated by commas. If a file name has more than one type, the additional type names and their corresponding generation numbers are identified and printed below the main file name. For example, the directory listing PS: PROGRAM.SCM.1,2 .BAS.3 indicates that the directory of account ls.THEOBALD, on device PS, has the files PROGRAM.SCM.1, PROGRAM.SCM.2, and PROGRAM.BAS.3. The commands tdirectory, wdirectory and rdirectory will tell you when your files were written, who wrote them, and how long they are, repectively. The command fdirectory gives all this information in one listing. TYPE The command type {filename 1}, {filename 2}, ... {filename n} will type all the files specified on the terminal. If you specify only one file, no comma is necessary. PRINT This command is identical to the type command, except that the output is sent to the lineprinter instead of the terminal. This is useful if you are using a video terminal and you want a hard copy (paper) listing of your file. After executing this command, you may type other commands as soon as the prompt reappears, even if the printer hasn't finished printing your file. If you decide to cancel your listing, type "i out" and note the request number. Then type "cancel print {number}" to cancel the print request. You will only be able to cancel your own print requests. DELETE, UNDELETE The command delete {filename 1}, {filename 2}, ... {filename n} will remove all the specified files from your directory. Directory listings made afterward will not list {filename}. However, these files still reside in memory until they are expunged or you log out. They can be returned to your directory by typing undelete {filename 1}, {filename 2}, ... {filename n} If the generation number is not specified, delete and undelete will affect all generations with the same name and type. EXPUNGE Even though deleted files don't appear in your directory, they still reside in memory. You will quickly fill your alloted space in memory if you don't occasionally eliminate old deleted files. The expunge command, which does not take arguments, will permanently flush all deleted files from memory. Once a file has been expunged, it can't be restored, so you should always list your directory before expunging to make sure you haven't deleted anything you want to keep. Your directory is automatically expunged when you log out. COPY The command copy {filename 1} {filename 2} copies the contents of {filename 1} into {filename 2}; {filename 1} remains intact. The copy command reads {filename 1} and writes onto {filename 2}, so if no generation numbers are specified, the command follows the rules for assigning them, as described in sec. 1.4. RENAME The command rename {filename 1} {filename 2} changes the name of {filename 1} to {filename 2}. If generation numbers are not specified, then they are assigned according to the rules described in sec. 1.4. 1.5.2 MISCELLANEOUS EXEC COMMANDS HELP Typing help {program name} will describe the program {program name} in detail. Typing ? in place of {program name} will print a list of the programs for which help is available. The system's mail handler, MM, is one program that you might need help with. INFORMATION This command will provide information about your use of the system. The second field specifies the type of information wanted. For example, information disk-usage gives information about the directory and the amount of memory it is currently using. information fork lists the forks that are in use. (See sec. 1.3.) information terminal lists information about the terminal, such as the type of terminal and the page dimensions. information ? will list all the options available. MM Electronic mailbox. This is actually a program and not a command. Using MM, you can send mail to your fellow students and teachers, and read mail which has been sent to you. Documentation is available via EXEC's HELP command. SET DIRECTORY PASSWORD This command is used to change the password of your account. The format (including computer output) is set directory password <{username}>{cr} OLD PASSWORD: {old password}{cr} NEW PASSWORD: {new password}{cr} RETYPE NEW PASSWORD: {new password}{cr} The passwords are not echoed by the computer. TERMINAL This command has several options that allow you to change some of the terminal's characteristics. For example, terminal width {width} ({width} a positive integer) will change the line width to {width} characters. terminal page will change your terminal to page mode. In page mode, if you use the type command to print a long file, the printing will stop after a page has been printed. To resume the printing, type ^Q. terminal length {length} ({length} a positive integer) will change the page length to {length} lines. TOPS-20 has correct default values for parameters such as the width and length. Therefore, you generally will not need to change any parameters, unless someone has previously tampered with them. XINFO This program (not a command) puts a wealth of information about the TOPS-20 system at your finger tips. Type an "H" if you wish to take the time required to learn how to use it. 2. INTRODUCTION TO EMACS EMACS is the text editor which allows you to write, edit and correct your files. It is a real-time editor, which means that any changes you make in your file are immediately displayed on the terminal screen. This section describes how to write and modify files using EMACS. If you have the time, you may find it easier to learn EMACS on-line. Run the TEACH-EMACS ("tea{esc}") program and follow the instructions it types at you. Caution: TEACH-EMACS presumes that you are using EMACS by itself rather than as a component of another system (SCHEME). Thus TEACH-EMACS tells you about entering and exiting EMACS in a way relevant to that usage, not our usage. Please remember not to use EMACS this way when you are editing SCHEME programs accessed by the SCHEME (EDIT) command. 2.1 BUFFER STORAGE When editing, your file is held in a buffer. All commands affect the buffer, but not your file. To make your changes perma- nent, you have to write the buffer to the file. More details below. 2.1.1 THE BUFFER POINTER You can add text to the buffer by typing it in. As you type, the screen will be updated continually. It is this feature that makes EMACS a real-time editor. EMACS uses a "buffer pointer" to mark the point in the buffer it is working on. The buffer pointer is represented on the screen by the cursor. The cursor always points to a character or space on the screen, but the buffer pointer is actually between the cursor and the previous character. The character at the cursor is "to the right" of the pointer, while the previous character is "to the left". If you type in text, the pointer will always be to the right of the last character typed. When you first invoke EMACS, there is nothing in the buffer, so the pointer is in the upper left corner. If you now type (define (fact n) (with no {cr}) the screen will look like this: (define (fact n)_ (The underline represents the cursor.) If you now type {cr}, the screen will look like this: (define (fact n) _ EMACS has moved the pointer to the beginning of the next line. EMACS has marked the end of the first line with a {cr}, although the terminal doesn't show it. If you type ^B, the EMACS command to move the buffer pointer back a character, the pointer will move back to the end of the first line. If the {cr} character is deleted, the following line will be added to the end of the first line. For example, if the screen displays (define (fact n)_ (cond ((zero? n) 1) and you type ^D, the command to delete the character pointed to by the pointer, the screen will now display: (define (fact n)_ (cond ((zero? n) 1) 2.2 EMACS COMMANDS Due to space limitations, we only describe a few of the thousands of EMACS commands available. We have listed most commands you will ever need to use, but you can get by with fewer commands if you feel uncomfortable with such a large selection. The choice is yours. As a general rule, control character commands deal with characters, {esc} commands deal with words and sentences, and ^Z commands deal with SCHEME symbolic expressions (S-expressions). 2.2.1 POINTER-DEPENDENT COMMANDS The following commands are pointer-dependent; they operate on characters near the buffer pointer. These commands, and all others given in section 2, are not terminated with a {cr} unless otherwise noted. Type of Command Description command MOVING THE ^F Move the pointer forward one character. BUFFER POINTER ^B Move the pointer backward one character. or ^H ^A Move the pointer to the beginning of the line. ^E Move the pointer to the end of the line. ^N Move the pointer to the next line. ^P Move the pointer to the previous line. {esc}< Moves the pointer to the beginning of the buffer. {esc}> Moves the pointer to the end of the buffer. {esc}F Move the pointer forward until it reaches the end of a word. {esc}B Move the pointer back until it reaches the beginning of a word. {esc}A Move the pointer to beginning of sentence. {esc}E Move the pointer to the end of sentence. ^Z^F Moves the pointer forward an s-expression. ^Z^B Moves the pointer backward an s-expression. ^Z^A Move the pointer back until it either reaches the beginning of the buffer or the beginning of a DEFINE. EMACS defines a DEFINE as a line that begins with a "(". If the "(" is indented (preceded by spaces or tabs), the line is not a DEFINE. In most cases, an EMACS DEFINE will be a SCHEME procedure DEFINEition. ^Z^E Move the pointer forward until it passes the end of a DEFINE. The end of a DEFINE is the {cr} to the right of the closing delimiter (right parenthesis, bracket or brace) that corresponds to the beginning of a DEFINE. This command should only be used when editing SCHEME files, since typing ^Z^E when there are no DEFINEs will result in an error message. For example, suppose the buffer contains the DEFINE (define (print-x x) (print x)) and the pointer is at the end of the first line. Typing ^Z^A will move the pointer to the beginning of the first line, while ^Z^E will move the pointer to the beginning of the third line. ^S This command puts EMACS into an incremental search mode. The format for a search is ^S{string}. As you type {string}, EMACS moves the pointer forward until it reaches the end of an occurrence of the accumulated string. The pointer is moved to the right of the first occurrence. The last character in {string} can be removed by typing {del}. While you are in the search mode, EMACS will remind you of what you are searching for, by displaying I-SEARCH: {string} in the lower-left corner of the screen. If there are no occurrences of {string} to the right of the pointer, EMACS responds with the message FAILING I-SEARCH: {string} and does not move the pointer. At this point, typing ^G will remove characters from the right end of {string} until there is an occurrence of string in the buffer. Once a successful search has been completed, typing {esc} will cause EMACS to leave the search mode, while ^G will cause EMACS to return the pointer to its original location and quit the search mode. Typing ^S will move the pointer to the next occurrence of {string}, and ^R will change the forward search to a reverse search (see below). Typing any other EMACS command will cause EMACS to leave the search mode and execute the command. If {string} is empty and you type ^S, the string used in the last search becomes the new string. (If this is the first search, EMACS will not do anything.) When you leave a search, EMACS remembers {string}. ^R This command is similar to ^S, except that it performs a reverse search. The pointer is moved back to the left of the first occurrence of {string}. When in either search mode, you may switch to the other by typing its command (either ^R or ^S). INSERTING {char- Any ordinary letter, number or punctuation mark AND DELETING acter} is inserted to the left of the pointer. TEXT Everything to the right of the new character is shifted to the right to accommodate the new character. The pointer is moved to the right of the new character. For example, to insert an "m" into the word "terinal", you must move the pointer until the cursor is at the "i", then type "m". The buffer will now contain "terminal" and the cursor will still point to "i", although it is now one character to the right of its original position. ^D The character to the right of the pointer, i.e., the character above the cursor, is deleted from the buffer. Everything in the same line to the right of the deleted character is shifted to the left. The cursor remains at the same spot, although it now points to the character to the right of the deleted character. {esc}D Like ^D except deletes a word instead of a character. The word is saved on the kill ring (see ^Y). ^Z^K Like ^K except kills an s-expression instead of a line. {del} This is identical to ^D, except that the character to the left of the pointer, i.e., left of the cursor, is deleted. If the cursor is at the beginning of the line, the {cr} of the previous line is deleted, causing the pointer's line to be added to the end of the previous line. {esc}{del} Like {del} but deletes a word. The word is saved on the kill ring. (see ^Y) ^Z{del} Like {del} but deletes an s-expression. The s-expression is saved on the kill ring. (see ^Y) ^K This command "kills" (deletes) everything between the pointer and the first {cr} to the right of the pointer. The {cr} is not deleted unless it is the first character to the right of the pointer, in which case, just that {cr} is killed. ^@ This is used in conjunction with ^W (see below). This command marks the pointer's current location in the buffer. ^W This command kills everything, including {cr}'s, between the pointer and the spot marked by the ^@. ^X^X This command marks the current location and moves the cursor to the last location marked. This allows you to check the region to be killed with a ^W command. ^Y This command "yanks" (inserts) the last thing killed (with a ^K, ^W, etc) to the right of the pointer. The pointer is moved to the right of the inserted string. This command, in conjunction with commands like ^K is useful for moving blocks of text around the buffer by killing a part of the text, moving the pointer, then yanking the text. ^Q The character immediately following the ^Q is inserted, regardless of what it is. For example, ^Q^L inserts ^L into the text. Although the ^L is printed as two characters, it is actually only one. ^T If there are no characters on the line to the right of the pointer, transpose the last two characters typed. Otherwise, interchange the characters on each side of the pointer. CHANGING {esc}C Capitalizes the first character of the word to CASE the right of the pointer and lowercasifies the rest of the word. Leaves the pointer after the word. {esc}U Uppercasifies the whole word to the right of the pointer. Leaves the pointer after the word. {esc}L Lowercasifies the word to the right of the pointer. SCHEME {esc}O Transfers the entire program file back to SCHEME. COMMANDS ^Z Y Transfers the current DEFINE back to SCHEME. Assuming the cursor is positioned within some procedure definition, ^Z Y will return to SCHEME and load in the definition. The current DEFINE is taken to be the smallest SCHEME expression which contains the cursor and is preceded by a carriage-return. This usually means the DEFINE the cursor is inside of unless the "(DEFINE .." is not on the left margin or is at the very start of the file. {esc}Z Copies the current DEFINE into a special file which will be loaded into SCHEME when you exit EMACS and return to SCHEME via ^X Z. The "current" DEFINE is the DEFINE containing the cursor. The cursor moves to the start of this DEFINE after it has been copied. Used when several DEFINEs are to loaded into SCHEME, but the entire program file should not be loaded. ^X Z (Type ^X, then type Z. Not to be confused with ^X^Z). Saves the program file, returns to SCHEME, and loads the special file of DEFINEs which was created by {esc}Z or {esc}O. This is the prefered command for leaving EMACS and returning to SCHEME. SPECIAL ^U This command allows you to tell EMACS to COMMANDS perform a command a given number of times. The format is ^U{number}{command} EMACS will perform the command {command} {number} times. For example, ^U5^F will move the pointer forward five characters. If no number is given, a default value of 4 is given. A ^U-modified command is itself a command, so ^U can be used on itself multiplicatively. For example, all the following move the pointer forward 64 times: ^U64^F ^U8^U8^F ^U^U16^F ^U^U^U^F {esc}X This reads an extended command name terminated by a return (see sec. 2.2.4). 2.2.2 BUFFER STORAGE COMMANDS After invoking EMACS from EXEC (see sec. 1.3) you may begin inserting text by simply typing it. However, if you want to modify an existing file, you must load the file into the buffer. To do this, type ^X^V {filename}{cr} You may use the completion and editing conventions of the EXEC to specify the filename. The bottom of the screen should display some information, including the word "Main" followed by your filename. This filename is now the default filename and remains so until a new file is read. If you now type ^X^V with no filename, EMACS will use the default filename. The command ^X^S will save the buffer into the default file IF the buffer has been changed since being read. If the buffer has not been changed, ^X^S will not perform the command. This command follows the rules for saving files described in sec. 1.4 and 1.5. For example, if you type ^X^V program.scm{cr} and the current generation number is 5, then program.scm.5 will be read into the buffer. If the buffer is then changed, typing ^X^S will transfer the buffer to program.scm.6. The command ^X^W {different filename}{cr} will write the buffer onto {different filename}. This command also follows the rules for saving files. Neither ^X^W nor ^X^S erase the buffer. 2.2.3 OTHER GLOBAL COMMANDS The following commands do not depend on the location of the pointer: Command Description ^C Suspend EMACS and return to SCHEME. If EMACS is executing a command, two ^C's are necessary. In most circumstances you will want to use ^XZ to return to SCHEME, though. ^G Aborts the current command. Useful if you type ^Z or {esc} and change your mind. ^L Clear the screen and redisplay the buffer. {esc}? Describe the command immediately following the question mark. For example, {esc}?^D describes the delete command. The description is displayed on the screen, but the buffer is unchanged. The next character typed will remove the description and redisplay the buffer. This character will be treated as a command to be executed by EMACS, so if you want to redisplay the buffer without executing an unwanted command, type ^L after you've read the description that was displayed. ^_ The Help character. Provides general documentation as well as the last 60 characters you typed. Provides specialized information during the execution of some commands (e.g., Query Replace in the next section and ^S). The Help character is ^? on the VT100 terminals. 2.2.4 EXTENDED COMMANDS Only a finite number of commands can be acessed with one or two character command invocations. To allow an infinite number of commands to be used, EMACS provides "named" commands where you type a descriptive name of the command you want to execute. To type an extended command, type "{esc}X". This will echo the bottom of the screen as "M-X". Then type the name of the command you want to execute. You can type ?, {esc}, and {space} to help you complete the command. Typing ? at any point will list the commands that match the text you have typed so far. For example, {esc}X? will list about 350 extended commands available in "bare" EMACS. Typing {esc} will attempt to fully or partially complete the text you have typed so far. If some amount of completion cannot be accomplished (because either the text is ambiguous or does not match any command), nothing will happen and the terminal will "beep." Typing {space} will attempt to complete the word you are typing. If the word is recognized as being valid when you type {esc} or {space}, the initial letter will be capitalized. All M-X commands should be terminated by {cr}. The following list describes the more useful M-X commands: M-X replace{esc}{string 1}{esc}{string 2}{esc} replace all occurrences of {string 1} completely to the right of the pointer with {string 2}. M-X query replace{esc}{string 1}{esc}{string 2}{esc} move pointer to the right of the first occurrence of {string 1} and wait for a response: Response Result {space} Replace this occurrence of {string 1} with {string 2} and move to the next occurrence of {string 1}. {del} Move to the next occurrence without replacing this one. {period} Replace this occurrence and escape the M-X command. {esc} Escape the M-X command without replacing this occurrence. ! Replace all remaining occurrences without asking. All replacements follow the same rules used by replace for upper- or lower-case letters. For a more complete list of responses, type the help character after you enter query replace. M-X q{esc} will also get you the query replace option and involves less typing. M-X describe{esc}{command name} describe {command name} (e.g., M-X describe{esc}query replace), much like {esc}?, but for M-X commands. M-X occur{esc}{string} list all lines containing {string}. M-X apropos{esc}{string} list and briefly describe all commands (M-X and others) whose names contain {string}. M-X info Examine a vast repository of documentation. Same as EXEC's XINFO program. M-X scheme mode change the EMACS mode from fundamental to SCHEME (see sec. 2.3). 2.3 SCHEME MODE EMACS has several features which make SCHEME editing easier. Most of these features have been borrowed from LISP mode, which was written for MACLISP. The ^Z^A and ^Z^E commands (see sec. 2.2.1) enable you to scan through DEFINEs quickly. Another feature involves the closing of delimiters. When you type a closing delimiter (right parenthesis, bracket or brace), EMACS will search back from the pointer until it finds the corresponding opening (left) delimiter. If the opening delimiter is on the screen at the time the closing delimiter is typed, the cursor will momentarily point to the opening delimiter, although the pointer is unchanged. This allows you to see whether or not you have the right arrangement of parentheses in a SCHEME procedure. Note: this feature does not distinguish between parentheses, brackets or braces. If the opening delimiter is off the screen you can find it by using ^Z^B or by asking a TA how to enable off-screen matching. When you invoke EMACS, the bottom of the screen will display a message like EMACS (Scheme) This "modeline" shows that EMACS is in the SCHEME mode. If you ever find that you are in a different mode, you can change this to the SCHEME mode by typing M-X scheme mode (see sec. 2.2.4). Now the screen will read EMACS (Scheme) When in the SCHEME mode, you can use the tab key to invoke a SCHEME pretty-printing procedure. The pretty-printing routine will reposition the current line in accordance with certain rules for properly indenting SCHEME programs. For example, if the buffer contains (define (fact n) (cond ((zero? n) 1) (else (* n (fact (-1+ n)))))) and the pointer is in the second line, typing {tab} will change the buffer to this: (define (fact n) (cond ((zero? n) 1) (else (* n (fact (-1+ n)))))) In fact, in SCHEME mode, a line-feed character, {lf}, used to terminate a line will automatically indent the next line to the "right place" for continuing your program. ^ZQ, done at the beginning of an expression, wiil adjust the indentation of all lines of that expression to be in the best pretty-printed style. 3. INTRODUCTION TO THE SCHEME SYSTEM The LISP which is to be used by 6.001 is called SCHEME. The 6.001 course notes provide an introduction to the SCHEME language. This chapter describes how to use SCHEME as it is implemented on the EECS computer system. It explains how to enter SCHEME, evaluate expressions, create and edit programs, test programs, and obtain printed output. Following this section is the SCHEME reference manual, where the special forms and procedures are individually described. 3.1 INVOKING SCHEME To enter SCHEME, just type SCHEME{cr} to the EXEC. This will enter SCHEME. Once you are in SCHEME, type any symbolic expression (s-expression). It will be evaluated as soon as you're done typing its closing parenthesis and its value will be displayed on the screen for you to see. To evaluate a variable, you will have to type a {space} after its name. This process, in which SCHEME prompts for input, evaluates the expression entered, and prints the result, is called a read-eval-print loop. To exit SCHEME temporarily, type (quit). Example (note: upper and lower case are not distinguished): @SCHEME [Keeping] [SCHEME.290 in MacLISP.2117] ==> (define a (+ 3 4)) A ==> a{space} 7 ==> (quit) @ 3.2 USING PROGRAM FILES 3.2.1 CREATING AND EDITING FILES The EDIT procedure provides a link between SCHEME and EMACS. It is in EMACS that program files will be created and edited. Below is a sample session with the SCHEME system which illustrates the use of the EDIT procedure. The actual editing is not shown, but is described by ";;;" comments. This session proceeds as follows: two procedure definitions are typed in EMACS: SQ, which computes the square of a number, and COTAN, the co-tangent function. These definitions are then loaded into SCHEME using the {esc}O command. Unfortunately, the name of the sine function was inadvertently typed as "sine" in the COTAN definition (the proper name is "sin"), so an error occurs while computing the co-tangent of pi/2. The (edit) command returns to EMACS and the mistake is corrected. Then the new COTAN definition is transmitted back to SCHEME using the ^Z Y command. Finally, the COTAN procedure performs correctly on pi/2. Notice that when we return from EMACS to SCHEME, the working buffer in EMACS is automatically saved as another generation of the file associated with that buffer. If no file has been associated with that buffer, EMACS will prompt the user to supply a file name in which to save the buffer. This feature ensures that your work is saved. @scheme [Keeping] [Scheme.315 in MacLISP.2118] ==> (edit) (=> EMACS) ;;; Type in the following DEFINEs: (define (sq x) (* x x)) (define (cotan x) (/ (cos x) (sine x))) ;;; Enter the following command: {esc}O ;;; Transmit the entire buffer to SCHEME. ;;; The buffer is first saved by EMACS. Returning from Editor SQ COTAN (SCHEME <=) ==> (cotan 1.570796327) Error! Unbound variable referenced SINE Level: 2 Error-> ^G Quit! Level: 1 ==> (edit) (=> EMACS) . . ;;; Change SINE to SIN inside the COTAN . ;;; definition. . ;;; Enter the following command to transmit the cotan ;;; define back to SCHEME. The buffer will be ;;; automatically saved by EMACS. ^ZY Returning from Editor COTAN (SCHEME <=) ==> (cotan 1.570796327) 0 ==> (quit) @ 3.2.2 LOADING FILES If you already have a program file, you can load it into SCHEME without using the EDIT procedure and going to EMACS; the LOAD procedure can load SCHEME code from a disk file. The command is (LOAD {filename}) where {filename} is a symbolic expression and will be evaluated. To load a known file (rather than the value of a variable), use "'s around the name. For example, if you want to load FOO.SCM from the directory, you can say: (LOAD "FOO.SCM") Load will evaluate each expression in the file FOO.SCM. Load accepts an optional second argument which functions as a print flag; if this argument is non-NIL, the value of each expression loaded will be displayed on your console. Conveniently, the value of a procedure definition is a list of the procedure's name and its formal parameters. Hence, supplying a non-NIL print flag will cause the procedure names to be echoed. By default, this flag is NIL. Incidentally, you will be loading many files from the directory during the course of the term. Of course, if you are loading a file from your own directory, you needn't include the <{directory name}> in the file specification. Better yet, if {name2}, the extension, is "SCM", you can omit it from the file specification, leaving only {name1}. If a filename contains no funny characters, such as ".", you can just quote it as usual in SCHEME, using "'", as in: (LOAD 'FOO). 3.3 GETTING HARD COPY If you are on a printing terminal, this section will probably be of little value to you. If you're on a video terminal, however, you may find it nice to know that when your program is running you can create hard copy (printed output) on the line printer. (PHOTO {filename}) The procedure PHOTO will save all subsequent terminal input and output in {filename}. The procedure TOFU closes the shutter. You may then use TOPS-20's PRINT command to print the photo of your session on the 6.001 line-printer. For example, ==> (photo "foo.log") ==> (+ 3 4) 7 ==> (tofu) ==> (quit) @print foo.log will produce an output listing that looks like: ==> (+ 3 4) 7 ==> (tofu) The 6.001 line printer is located in the Course VI terminal room on the third floor of building 38. The output will be preceded by a heading, in large letters, naming the person who requested the it. 3.4 DEBUGGING It will become apparent to you many times during a program's evolution that the program is not entirely right. Instead of blaming the program for these faults, strange creatures called bugs are blamed. This way, the programmer remains free of guilt. Errors and incorrect results are sure signs that bugs lurk inside a program, for the bugs cause the aberrant behavior. Debugging usually depends on empirical testing, thus it can serve to re-enforce the programmer's belief in the correctness of his program, and it can aid in correction of a program which is not behaving as expected, but it cannot PROVE the program's correctness -- if the program succeeds during debugging, there may be some yet-to-be-uncovered bug lurking within, waiting to show itself in some later run. The best protection against program bugs is a program that is well thought out and clearly set down in code. SCHEME provides several features to aid in debugging programs. Among these are the ability to TRACE the execution of a procedure, the ability to examine the current environment, and the ability to set breakpoints. From a breakpoint, the user can examine the recent process history of his program. Sometimes bugs are detected by the occurrence of an error while the program is running. When SCHEME encounters an error condition, execution is abruptly halted, the user is notified, and a breakpoint mode is entered, as demonstrated below. ==> (+ 1 'a) NO-ENVIRONMENT-SAVED Error! NON-NUMERIC VALUE A ([PRIMITIVE +] 1 A) Level: 2 Error-> _ The error message -- a concise description of the error, often containing the name of the code which signalled the error -- is displayed. While the program is suspended, the user can try to locate where the error occurred, i.e., what expressions were being executed at the time of the error. The environment and process history examiners, WHERE and DEBUG, respectively, can help here. Of course, much can be determined about the state of the system at time of the error by simply examining the values of variables at the breakpoint -- to evaluate a variable, simply type its name! The program may run without error, but the result produced may be preposterous -- another indication of bugs. For instance, ==> (prime? 7) NIL In this situation, SCHEME offers no clue as to the location of the bug -- you're on your own. You might produce a theory about the cause of the deviant behavior and then test it using the TRACE facilities. You can also insert checks and traces in your program with the text editor. Debugging, like programming, is an acquired skill and an art. The tools available to you are described in the sections below. Use them creatively. The undergraduate assistants stationed in the EECS terminal room are there to help you. Feel free to ask them for help. 3.4.1 BREAKPOINTS As mentioned above, a breakpoint is entered whenever an error is signalled. This error may have been signalled internally by SCHEME, or it may have been signalled by an ERROR statement that the user inserted in the program himself. Breakpoints of a slightly different flavor can be entered via the BKPT procedure or by simply typing a ^B from the terminal. The SCHEME breakpoint facility allows you to pause execution of a program "in mid-stream," whereupon you can examine and modify the values of local variables, among other things. When you pause execution and enter a breakpoint, SCHEME runs a read-eval-print loop just as it normally does -- you type expressions and the interpreter evaluates them and prints their value. The difference is that the environment in which these expressions are evaluated is not the global environment, but rather the environment from which the breakpoint was entered. Breakpoints can be entered in the following ways: * Typing ^B on the terminal. * Executing a procedure which had a breakpoint installed via one of the commands BREAK, BREAK-ENTRY, or BREAK-EXIT. * Executing the procedure BKPT. * Encountering an error (eg, (+ 'a 'b) will surely give such an error.) Here's a simple scenario which demonstrates how breakpoints can be used: Suppose you modify the familiar recursive FACT procedure to produce the following new procedure: (define (newfact n) (cond ((zero? n) 1) (else (* n (newfact (- n 2)))))) The idea is to compute the product of all the even (resp. odd) numbers up to some even (resp. odd) number n. You try (newfact 6) which gives 48 = 6*4*2 as expected. But when you try (newfact 7) you get no reply. So you type ^B and SCHEME responds with Bkpt! ^B interrupt Level: 2 Bkpt-> The "BKPT" message says that the breakpoint was caused by user intervention, e.g., typing ^B, rather than by an error (in which case SCHEME would have typed "ERROR"). The number preceding "BKPT" tells how many "break levels" you are away from the main read-eval-print loop execution -- if you entered another breakpoint from within this one, that would be Level 2, and so on. The top-level read-eval-print is at level 0. Now that you are within the break, you can examine the value of the local variable n: Bkpt-> n -141 Seeing that n is negative should give you a good clue to uncovering the bug. To continue from a breakpoint, type "{esc}P{space}", which prints as "$P". The interpreter will continue the execution of the procedure from which the breakpoint was called, but, if the breakpoint was forced by ^X (e.g. ^B^X), the continuation may be faulty because SCHEME was not allowed the chance to clean up before breaking. Alternatively, you can type ^G, which halts execution and returns to the top-level read-eval-print loop, or you can type ^U which halts execution and returns to the preceding read-eval-print loop. ^X halts execution locally and resumes the current read-eval-print loop. Breakpoints entered because of an error are special -- they act as error sinks. To avoid confusing you with more than one error at a time, errors encountered in evaluating things typed at the error breakpoint will not sprout new breaklevels. A new error's message is printed, as usual, but execution resumes at the first error's break loop. This way, all attention can be focused on the original error. Although error recovery is often possible, you probably won't want to fool with it. When an error occurs, find out all you can about it using DEBUG and WHERE. Then type ^G to return to top-level, where the error can be permanently fixed. You may find it helpful to know that different prompts are used for the different modes. The top-level prompt is "==>", the break prompt is "Bkpt->", and the error prompt is "Error->". 3.4.2 USEFUL CONTROL-CHARACTERS Certain control characters can be typed in SCHEME during the execution of any operation to achieve useful effects. Most of these manipulate break levels. ^B -- Enters a Breakpoint. ^C -- Returns to the monitor. ^G -- Halts execution at all levels. Returns to toplevel read-eval-print loop. ^L -- Clears the screen. If any input has been typed, it will be redisplayed. ^P -- Abort output. The current print procedure is aborted and execution continues. ^R -- Retype the current input expression (doesn't clear screen). ^U -- Halts execution locally. Returns to the previous (next innermost) breaklevel if it exists, else returns to toplevel. ^X -- Halts execution locally. Returns to the current (innermost) breaklevel, if any exist, or to top level if none exist. After acknowledging a ^B, SCHEME enters a breakpoint at the next convenient time. This may not be convenient for the user, however, so an immediate breakpoint facility is provided. If ^B doesn't seem to have any effect, and an immediate breakpoint is desired, then follow it with a ^X to force it through. Thus, ^B^X enters an immediate breakpoint. Forcing a breakpoint in this manner may, however, prevent you from continuing your program. Proceeding from breakpoints is explained in the next section. 3.4.3 TRACE and BREAK Procedures are provided which allow the user to trace the execution of a program and set breakpoints in the program. A procedure can be traced on entry and/or exit. In addition, a breakpoint can be set when the procedure is entered and/or exited. Example: ==> (define (fact n) (cond ((zero? n) 1) (else (* n (fact (-1+ n)))))) FACT ==> (trace fact) [PROCEDURE FACT] ==> (fact 6) [ Entering (FACT 6) ] [ Entering (FACT 5) ] [ Entering (FACT 4) ] [ Entering (FACT 3) ] [ Entering (FACT 2) ] [ Entering (FACT 1) ] [ Entering (FACT 0) ] [ 1 <== (FACT 0) ] [ 1 <== (FACT 1) ] [ 2 <== (FACT 2) ] [ 6 <== (FACT 3) ] [ 24 <== (FACT 4) ] [ 120 <== (FACT 5) ] [ 720 <== (FACT 6) ] 720 ==> (untrace-exit fact) ([PROCEDURE FACT]) ==> (fact 6) [ Entering (FACT 6) ] [ Entering (FACT 5) ] [ Entering (FACT 4) ] [ Entering (FACT 3) ] [ Entering (FACT 2) ] [ Entering (FACT 1) ] [ Entering (FACT 0) ] 720 ==> (untrace fact) [PROCEDURE FACT] ==> (break-exit fact) [PROCEDURE FACT] ==> (fact 2) [ 1 <== (FACT 0) ] Bkpt! Exiting *result* <== (*proc* . *args*) [ENVIRONMENT] Level: 2 Bkpt-> *proc* [PROCEDURE FACT] Bkpt-> *args* (0) Bkpt-> *result* 1 Bkpt-> (set! *result* -1) -1 Bkpt-> $p [ -1 <== (FACT 1) ] Bkpt! Exiting *result* <== (*proc* . *args*) [ENVIRONMENT] Level: 2 Bkpt-> $p [ -2 <== (FACT 2) ] Bkpt! Exiting *result* <== (*proc* . *args*) [ENVIRONMENT] Level: 2 Bkpt-> $p -2 ==> During the breakpoint inserted by BREAK-EXIT, the global variables *PROC*, *ARGS*, and *RESULT* hold the broken procedure, its argument values, and the computed result (available only during the exit break), respectively, for examination. You may change the value returned by the procedure by assigning to *RESULT* the value to be returned. Type $P, {esc}P, to proceed from the breakpoint and return *RESULT*. In the above example, (FACT 0) returned -1 instead of 1. 3.4.4 THE SCHEME DEBUGGING SYSTEM A breakpoint will allow you only to examine local variables and to evaluate expressions at the breakpoint. Often, however, tracking down a bug will require a more extensive investigation of the circumstances surrounding the error. The SCHEME debugger system provides additional capabilites to aid in debugging. The history examiner, entered using the DEBUG command, enables you to examine the history of execution of an expression after it has been executed. The environment of any procedure application may then be examined and the interrupted process may be proceeded from any of the displayed nodes of evaluation. The environment examiner, entered using the WHERE command, enables you to examine variable bindings in an environment and change these bindings by entering a read-eval-print loop in that environment. Calling the DEBUG procedure from within a SCHEME breakpoint enters a read-execute loop, which accepts single-character commands that enable you to move backwards and forwards in the history of the expressions that have been evaluated, and to examine procedures and variables at the different evaluation levels. The prompt is Debugger-command-character--> The debugger is demonstrated below. (DEFINE (FACT 3) (IF (ZERO? N) L (* N (FACT (-1+ N))))) ==> (fact 3) Error! Unbound variable referenced L Level: 2 Error-> (debug) ;;Here we call the debugger, which prints the current expression ;;being evaluated. The "subproblem" and "reduction" numbers ;;are ways of numbering the expressions in the history. The ;;debugger also indicates the current procedure and the ;;arguments with which is was called Subproblem-level: 1 Reduction number: 0 Expression L Within Procedure [PROCEDURE FACT] applied to (0) Debugger-command-character-->B ;;We now give the B command character, which moves us back to ;;the previous expression the evaluator was working on Subproblem-level: 1 Reduction number: 1 Expression (IF (ZERO? N) L (* N (FACT (-1+ N)))) Within Procedure [PROCEDURE FACT] applied to (0) The above factorial program has a simple typo--the letter "L" is used instead of the number "1" in the second line of code -- resulting in an "Unbound variable" error. The debugger indicates that the error was encountered when FACT was called with 0 as its argument. When we gave the B command, the change in "reduction number" means that the previous expression was a reduction (as opposed to a subproblem) of the expression we just moved to. In general, Scheme's evaluation rules designate that evaluator proceeds from one expression to the next by either starting to work on a subexpression of the given expression, or by reducing the entire expression to a new form. An expression with a lower subproblem number is a piece of an expression with a higher subproblem number. A given subproblem level with a lower reduction number is a simplified form of an expression with a higher reduction number. In evaluating a compound expression, we break it into subproblems and go through many reductions of each subproblem, Each of these levels of evaluation appears in the debugger history. Although the bug in this case is easy to spot, we can continue moving backwards a few steps through the evaluation history, to see how the evaluation was accomplished: Debugger-command-character-->B Subproblem-level: 1 Reduction number: 2 Expression (FACT (-1+ N)) Within Procedure [PROCEDURE FACT] applied to (1) Debugger-command-character-->B Subproblem-level: 2 Reduction number: 0 Expression (* N (FACT (-1+ N))) Within Procedure [PROCEDURE FACT] applied to (1) Debugger-command-character-->B Subproblem-level: 2 Reduction number: 1 Expression (IF (ZERO? N) L (* N (FACT (-1+ N)))) Within Procedure [PROCEDURE FACT] applied to (1) Debugger-command-character-->B Subproblem-level: 2 Reduction number: 2 Expression (FACT (-1+ N)) Within Procedure [PROCEDURE FACT] applied to (2) Debugger-command-character-->B Subproblem-level: 3 Reduction number: 0 Expression (* N (FACT (-1+ N))) Within Procedure [PROCEDURE FACT] applied to (2) ;;and so on, until we reach the initial call to FACT from the ;;read-eval-print loop Subproblem-level: 4 Reduction number: 2 Expression (FACT 3) Within Procedure [LAMBDA PROCEDURE 200352] applied to ([ENVIRONMENT 195068]) Here are the character commands available as part of the DEBUG system. The equivalent procedures used in "debugger extended command mode" (see below) are also shown. The commands to move between levels and reductions are: B (Previous-expression in extended command mode) moves backwards in the history to the expression from which the current one was generated F (Next-expression) moves forwards in the history to the next subproblem or reduction that the current expression generated G (Go), takes two arguments and moves to the reduction and subproblem level specified by them The commands to access information from history are: C (Show-frame) displays the bindings in the current environment frame S (Reduction) displays the current reduction in short form H (All-history) displays all available history Other useful procedures are: W (Debug-where) calls WHERE on the environment of the current reduction, displaying and manipulating it P (Print-procedure) pretty-prints the current procedure V (Eval-in-current-environment) evaluates an expression E (Enter) enters a read-eval-print loop in the current environment R (Return) prompts for a value, binds the current expression to the value, and proceeds with the evaluation in the current environment M (Toggle) enter debugger extended command mode Q (Exit) exits the debugger ? (Help) gives information on the commands Debugger extended command mode (accessed from the debugger via the M command) enters a special READ-EVAL-PRINT loop which has access to the internal functions and variables of the DEBUG examiner, making them available to the user so that he can write his own display and manipulation procedures. For example, the command B above is equivalent to the procedure PREVIOUS-EXPRESSION in extended mode, and one could define and execute procedures that used this operation. If you place the debugger in extended mode, and later re-enter the debugger, it will still be in extended mode. Some additional commands available in extended mode are: (Previous-subproblem) - moves to the subproblem waiting for the value to be returned from the current one (Next-subproblem) - moves to the subproblem whose value the current one is waiting for (All-reductions) - displays all the reductions at the current level (Subexpressions) - displays the subexpressions at this level The environment examiner, entered by calling the WHERE procedure, or with the W command from the debugger, allows the user to examine variable bindings in an environment, or any of its parent frames and to change these bindings by entering a read-eval-print loop in the appropriate environment. The environment examiner takes single letter commands. It prompts with Where-->. The possible actions are: E Enters a read-eval-print loop in the current frame S Finds the frame whose parent frame is the current one and can be accessed from the environment originally given to WHERE P Finds the parent frame of the current one C Displays the bindings in the current frame A Displays the bindings of all the frames that can be accessed in the current chain Q Exits the read-execute-print loop ? , prints help information Here are further examples of the use of the debugger: ==> (pp sqroot) [PROCEDURE (SQROOT X) (DEFINE (SQRT-ITER ANS) (IF (GOOD-ENUF? ANS) ANS (SQRT-ITER (AVERAGE (/ X ANS) ANS)))) (DEFINE (GOOD-ENUF? ANS) (< (ABS (- (* ANS ASN) X)) .0001)) (DEFINE (AVERAGE X Y) (/ (+ X Y) 2)) (SQRT-ITER 1)] ==> (sqroot 3) Error! Unbound variable referenced ASN Level: 2 Error-> (debug) Subproblem level: 1 Reduction number: 0 Expression ASN Within procedure [PROCEDURE GOOD-ENUF?] applied to (1) Debuggger-command-character-->B Subproblem level: 2 Reduction number: 0 Expression (* ANS ASN) Within procedure [PROCEDURE GOOD-ENUF?] applied to (1) Debuggger-command-character-->B Subproblem level: 3 Reduction number: 0 Expression (- (* ANS ASN) X) Within procedure [PROCEDURE GOOD-ENUF?] applied to (1) Debuggger-command-character-->p [PROCEDURE (GOOD-ENUF? ANS) (< (ABS (- (* ANS ASN) X)) .0001)] Debuggger-command-character-->^G Above is a square root program which is appropriately divided into sub-procedures. It harbors a bug. The debugger was called to reveal that the unbound variable ASN is in the expression (* ANS ASN) which is part of the definition of GOOD-ENUF?. The bug in GOOD-ENUF?, which is itself a sub-procedure of SQROOT, can be fixed easily by substituting ANS for ASN. ==> (mapcar (lambda (x) (+ x y)) '(1 2 3)) Error! Unbound variable referenced Y Level: 2 Error-> (debug) Subproblem level: 1 Reduction number: 0 Expression Y Within procedure [LAMBDA-PROCEDURE 175492] applied to (3) ;; Just what is this mysterious procedure, 175492? Debugger-command-character--> p [LAMBDA-PROCEDURE (X) (+ X Y)] ;;Let's look at the entire history, including the part generated ;;by the expansion of MAPCAR Debugger-command-character-->H Subproblem level: 0 Reduction number: 0 Expression (ERROR *MESSAGE* *IRRITANT*) Within procedure [LAMBDA-PROCEDURE 212122] applied to ([ENVIRONMENT 201724]) ;; That was a garbage frame due to the error system. ;; Next we are back to our stuff. Subproblem level: 1 Reduction number: 0 Expression Y Within procedure [LAMBDA-PROCEDURE 175492] applied to (3) Subproblem level: 2 Reduction number: 0 Expression (+ X Y) Within procedure [LAMBDA-PROCEDURE 175492] applied to (3) Subproblem level: 2 Reduction number: 1 Expression (F (CAR L)) Within procedure [PROCEDURE MAPCAR] applied to ([LAMBDA-PROCEDURE 175492] (3)) Subproblem level: 3 Reduction number: 0 Expression (CONS (F (CAR L)) (MAPCAR F (CDR L))) Within procedure [PROCEDURE MAPCAR] applied to ([LAMBDA-PROCEDURE 175492] (3)) Subproblem level: 3 Reduction number: 1 Expression (IF (NULL? L) NIL (CONS (F (CAR L)) (MAPCAR F (CDR L)))) Within procedure [PROCEDURE MAPCAR] applied to ([LAMBDA-PROCEDURE 175492] (3)) Subproblem level: 3 Reduction number: 2 Expression (MAPCAR F (CDR L)) Within procedure [PROCEDURE MAPCAR] applied to ([LAMBDA-PROCEDURE 175492] (2 3)) Subproblem level: 4 Reduction number: 0 Expression (CONS (F (CAR L)) (MAPCAR F (CDR L))) Within procedure [PROCEDURE MAPCAR] applied to ([LAMBDA-PROCEDURE 175492] (2 3)) Subproblem level: 4 Reduction number: 1 Expression (IF (NULL? L) NIL (CONS (F (CAR L)) (MAPCAR F (CDR L)))) Within procedure [PROCEDURE MAPCAR] applied to ([LAMBDA-PROCEDURE 175492] (2 3)) Subproblem level: 4 Reduction number: 2 Expression (MAPCAR F (CDR L)) Within procedure [PROCEDURE MAPCAR] applied to ([LAMBDA-PROCEDURE 175492] (1 2 3)) Subproblem level: 5 Reduction number: 0 Expression (CONS (F (CAR L)) (MAPCAR F (CDR L))) Within procedure [PROCEDURE MAPCAR] applied to ([LAMBDA-PROCEDURE 175492] (1 2 3)) Subproblem level: 5 Reduction number: 1 Expression (IF (NULL? L) NIL (CONS (F (CAR L)) (MAPCAR F (CDR L)))) Within procedure [PROCEDURE MAPCAR] applied to ([LAMBDA-PROCEDURE 175492] (1 2 3)) Subproblem level: 5 Reduction number: 2 Expression (MAPCAR (LAMBDA (X) (+ X Y)) '(1 2 3)) Within procedure [LAMBDA-PROCEDURE 200352] applied to ([ENVIRONMENT 194556]) ;;Show the current expression, again Debugger-command-character-->S Subproblem level: 1 Reduction number: 0 Expression Y Within procedure [LAMBDA-PROCEDURE 175492] applied to (3) Debugger-command-character-->B Subproblem level : 2 Reduction number : 0 Expression (+ X Y) Within procedure [LAMBDA PROCEDURE 183045] applied to (3) ;;We invoke the environment examiner to find out why Y is ;; unbound. Debugger-command-character-->W Where--> C ;displays bindings in ;current environment ((X 3)) ; This is our LAMBDA expression Where--> P ;go to its parent environment -- the ;user's read-eval-print loop. ((*THE-ENVIRONMENT* [ENVIRONMENT 201724]) (FACT [PROCEDURE FACT]) (SQROOT [PROCEDURE SQROOT]) (MAPCAR [PROCEDURE MAPCAR])) ;; We enter a read-eval-print so we can insert the binding of Y. Where--> E You are now in the desired environment Level: 3 Eval-in-env--> (define y 2) Y Eval-in-env--> ^U ;to return to WHERE loop Up! Returning to character loop. Where--> c ;Now it has the binding! ((*THE-ENVIRONMENT* [ENVIRONMENT 201724]) (FACT [PROCEDURE FACT]) (SQROOT [PROCEDURE SQROOT]) (MAPCAR [PROCEDURE MAPCAR]) (Y 2)) Where--> q ;Getting back to debugger Debugger-command-character--> F Subproblem level: 1 Reduction number: 0 Expression Y Within procedure [LAMBDA-PROCEDURE 183426] applied to (3) Debugger-command-character--> Q ;To return to ERROR break Error-> $p ;With error fixed, proceed program (3 4 5) ;Correct answer appears! ==> (tofu) The error was found to be local to the environment created by the evaluation of the LAMBDA expression. To fix the bug, Y was defined globally to be 2. You may have noticed from the order of the expressions in the above example, that the current SCHEME implementation evaluates arguments to procedures in the order right to left. This fact may be helpful in debugging, but your programs should never depend on the order of evaluation, which may differ from implementation to implementation. Here is another example showing error recovery: ==> (define (a x y zee) (+ x y z)) A ==> (define (b z) (- 10 z)) B ==> (define (c) (b (a 1. 2. 3.))) C ==> (c) Error! Unbound variable referenced Z Level: 2 ==> (debug) Subproblem level: 1 Reduction number: 0 Expression Z Within procedure [PROCEDURE A] applied to (1 2 3) ;;We'll give it a value to return in place of Z Debugger-command-character-->R Exp to proceed with: -> zee Confirm: [T or NIL] -> T 4 The command R prompts for a value for the unbound variable Z and resumes the execution. 3.5 MOST RECENT INPUT AND OUTPUT LINES In SCHEME, the procedures %IN and %OUT return the most recently read and printed expressions, respectively. This may be useful if you forget to assign a variable to the result of some calculation until after doing the calculation. This is provided as a debugging aid for use at the terminal; it should never be used in programs that you write. For example: ==> (+ 2 5) 7 ==> (DEFINE SEVEN (%OUT)) SEVEN ==> (%IN) (DEFINE SEVEN (%OUT)) ==> SEVEN 7 3.6 PRINT-LEVEL/PRINT-LENGTH It is possible to create structures in SCHEME which are circular and which cannot be printed to completion. Trying to print such a structure may result in an error; a lot of paper will be used in any event. The global variables *PRINT-DEPTH* and *PRINT-BREADTH* are used to restrain the printer. *PRINT-DEPTH* is the number of nested levels of a structure which will be printed to completion, while *PRINT-BREADTH* is the number of top-level elements which will be printed. Ellipses ("...") appear in the ouput where these limits have been exceeded. You may set these limits to your liking. ==> (define a '(a (b c) (d e (f g h) (i (j k) l) m (n o) p))) A ==> a (A (B C) (D E (F G H) (I (J K) L) M (N O) P)) ==> (set! *print-depth* 2) 2 ==> A (A (B C) (D E (# # #) (# # #) M (# #) P)) ==> (set! *print-breadth* 3) 3 ==> A (A (B C) (D E (# # #) ...)) ==> (SET-CDR! A A) (A A ...) 3.7 THE SCHEME PRETTY-PRINTER A Pretty-Printer is a procedure which will take a data object and try to display it in a nice form which shows up its structure easily. SCHEME has a simple, but useful pretty-printer which is called PP. It evaluates its argument and displays it on the terminal in a more useful way than print might. Since it is slow and can waste paper, it is not the normal printer. Example: ==> (pp fact) [PROCEDURE (FACT N) (IF (ZERO? N) 1 (* N (FACT (-1+ N))))] CONCLUSION We have tried to give an overview of the SCHEME system above, providing examples where most needed. Many procedures were glossed over, while the debugging and error system were covered in great detail. The next section, organized for quick referrence, documents most of the procedures which are available in the SCHEME system.