handout #16
CSE341—Programming Languages
Programming Assignment #8 (Scheme)
due 11 pm Friday, 5/29/09
As I mentioned in lecture, every student is
granted a fourth late day that can be used for assignments 7, 8, and 9. You still canÕt use more than two late
days for any given assignment.
In this assignment you will implement a
significant subset of the BASIC programming language as described in the 1964
Dartmouth report available here:
http://www.bitsavers.org/pdf/dartmouth/BASIC_Oct64.pdf
Your solution will build on the parsing
procedures of assignment 7 and you will be provided with a set of utilities
called hw8-support.scm that has code for handling the top-level user
interaction and a procedure called tokenize that will convert user input into
lists of tokens. Your solution
should be called hw8.scm and should include the following procedure calls:
(include "hw7.scm")
(include
"hw8-support.scm")
A solution to hw7 will be emailed to all
students once the final deadline has passed.
The primary task for this assignment is to write
a procedure called run-program. It
will be passed a list of BASIC instructions to execute. Each value in the list will have three
parts: a line number, the original text of the line (something your code will
ignore), and a list of tokens for the input line. For example, the following BASIC program:
10 LET X = 203.4 * 37.7
20 PRINT X
30 LET X = X/2
40 IF X>200 THEN 20
50 END
Would be represented in list form as follows:
((10 " LET X = 203.4 *
37.7" (LET X = 203.4 * 37.7))
(20 " PRINT X" (PRINT X))
(30 " LET X = X/2" (LET X = X / 2))
(40 " IF X>200 THEN 20" (IF X > 200 THEN 20))
(50 " END" (END)))
Variable names in BASIC are very limited. They can be one letter long or one
letter followed by one digit. The
hw8-support file includes a predicate variable? that can be used to test
whether something is a legal variable name.
This writeup lists various error conditions that
you must handle in your code.
Unless otherwise specified, your error string must include the line
number on which the error occurred, as in:
LINE 30: VARIABLE MUST BE FOLLOWED
BY =
You are not required to handle every potential
error, although you can include extra error cases if you want to. You will want to call the parse-test and
parse-expression procedures of assignment 7, but do so, you should instead call
the procedures try-parse-test and try-parse-expression that are provided in
hw8-support. These versions take
an extra parameter that specifies the line number so that if an error occurs,
it will be reported with the line number, as in:
LINE 55: ILLEGAL EXPRESSION
Your run-program procedure should properly
implement the following BASIC commands:
1.
(5
points) REM. The REM command is
for comments (short for REMarks).
The general form is:
REM <text>
A REM command does nothing.
2.
(5
points) END. The END command
terminates a program. The general
form is:
END
Your program should terminate execution when it
encounters this command. The BASIC
language description indicates that this must be the last line of a BASIC
program. We will not enforce this
rule (partly because we are not implementing the STOP command).
3.
(15 points) LET. The LET command assigns a value to a
variable. The general form is:
LET <variable> =
<expression>
The expression will always have a numeric
result. The expression might
include references to other variables.
The LET command can be used to define a new variable or to reassign a
variable that was previously assigned a value. You should make sure that the variable is a legal variable
name in BASIC. The hw8-support
file includes a predicate called variable? that can be used for this
purpose. You should call the
try-parse-expression function to parse the expression.
4.
(5
points) INPUT. The INPUT command
reads a number from the user and stores it in a variable. The general form is:
INPUT <variable>
This command wasnÕt part of the original BASIC
specification, but it is obviously very useful. You should call SchemeÕs read procedure to read the number
(it takes no arguments, as in (read)).
You should make sure that the input you get is either an integer or a
real. If it is, you should assign
the given variable that value.
Otherwise you should call the error procedure with an appropriate
message. You should also call
error if the variable is not a legal BASIC variable name (as noted earlier, the
support file has a predicate called variable? that can be used for this
purpose).
5.
(10 points) PRINT. The PRINT command prints a sequence of
expressions and strings separated by commas. The general form is:
PRINT (<expression> |
<string>) {"," (<expression> | <string>)}
Commas will be represented with the token comma,
as in (PRINT Òx =Ó comma 7.8).
Each String should be displayed and each expression should be evaluated
and displayed (the display procedure works for both strings and numbers). Values should be separated by a space
and each PRINT command should produce a complete line of output (the newline
procedure goes to a new line of output).
You should not display a trailing space on the line. The expressions are guaranteed to be
numeric, although they might refer to variables. You should make an appropriate call on error if a comma or
an expression is missing. You
should call try-parse-expression to parse the expressions.
6.
(10
points) GOTO. The GOTO command
transfers control to another part of the program. The general form is:
GOTO <line number>
A GOTO command transfers control to the given
line number. The line number will
be a simple integer (not an expression).
If the line number does not exist, you should call the error procedure
with an appropriate message.
7.
(5
points) IF. The IF command is for
conditionally transferring control.
The general form is:
IF <test> THEN <line
number>
The test will be of the form described in the
homework 7 grammar. The line
number will be a simple integer.
If the test evaluates to #t, control should be transferred to the given
line number. Otherwise it should
proceed to the next statement, as usual.
If the program attempts to transfer to a line number that does not
exist, you should call the error procedure with an appropriate message. You should call try-parse-test to parse
the test.
8.
(10
points) GOSUB/RETURN. The GOSUB
command transfers control to a subroutine (a section of code that is terminated
with a RETURN command). The
general form is:
GOSUB <line number>
The GOSUB command is similar to GOTO in that it
transfers control to another part of the program. But in the case of GOSUB, a subsequent call on RETURN will
return the program to the statement after the call on GOSUB. The basic form of the RETURN command
is:
RETURN
BASIC supports only a single subroutine
execution at any given time. Your
program should call the error procedure with an appropriate message if the
program attempts to execute two GOSUB commands in a row without a call on
RETURN in between. Your program
should also call the error procedure with an appropriate message if the program
attempts to execute a RETURN command when there is no active subroutine
execution. Your program should
also call the error procedure with an appropriate message if the program
reaches an END while a subroutine execution is active (i.e., without having
encountered a RETURN statement) or if a call on GOSUB is the last line of a
program.
9.
(30 points) FOR/NEXT. The FOR command is used for
looping. The general form is:
FOR <variable> =
<expression> TO <expression>
The expressions represent the starting and
ending values for the loop. The
expressions have to be numerical, but they need not be integers. Each FOR command should be paired with
a corresponding NEXT command using the same variable name:
NEXT <variable>
When the NEXT command is encountered, you
examine the value obtained by incrementing the loop variable (adding 1 to
it). If that value is less than or
equal to the final value for the loop, then you set the variable to that value
and go back to the top of the loop.
If the value after incrementing would be larger than the final value,
then you leave the variable unchanged and transfer control to the statement
that comes after the NEXT command.
For example, consider the following loop:
100 FOR X = 0.5 TO 5
110 PRINT X
120 NEXT X
130 PRINT X
The loop produces the following output:
0.5
1.5
2.5
3.5
4.5
4.5
Notice that the loop stops when X becomes 4.5
because incrementing it again would cause it to be greater than the final value
of 5. Also notice that X has the
value 4.5 after the loop.
The BASIC standard says that if the starting value
for a loop is greater than the ending value, then the body of the loop should
be skipped and afterwards the variable should be one less than the starting
value. For example:
10 FOR X = 5 to 3
20 PRINT X
30 NEXT X
40 PRINT X
The body of the loop is not execute (the PRINT
in line 20) and after the loop the value of X is reported to be 4 (one less
than the starting value).
Loops can be nested, as in:
100 FOR X = 1 TO 3
110 FOR Y = 1 TO X
120 PRINT X, Y, X + Y
130 NEXT Y
140 NEXT X
Loops are required to be properly nested. For example, in the code above you
would not be allowed to switch the order of lines 130 and 140 because then we
would encounter the NEXT command for the outer loop before encountering the NEXT
command for the inner loop.
The BASIC specification describes a loop in
terms of the FOR command that begins the loop, the NEXT command that ends the
loop and the body of commands that appear in between. The nesting requirement says that if one FOR/NEXT loop is
going to appear in the body of another, then the nested loop must appear in its
entirety in the body of the outer loop (i.e., the FOR command, the body of the
inner loop and the NEXT command).
There are some unusual cases that can arise if a
programmer calls GOTO or GOSUB inside of a loop. In general, a programmer should not escape a loop with a GOTO
command. A call on GOSUB can be
okay, although there are some odd cases (for example, what if the GOSUB code
includes a NEXT command for the original loop?). In general, we arenÕt going to worry about many of these
subtle errors.
You should call try-parse-expression to parse
the two expressions. Your code
must call the error procedure with an appropriate message for the following
errors:
á
FOR
not followed by a legal variable name
á
the
variable name not followed by =
á
the
first expression not followed by TO
á
extraneous
tokens after the second expression
á
improperly
nested loops (including using the same variable in both an inner and outer
loop)
á
a
next without a corresponding for
á
reaching
the END command without completing all loops (this will include catching the
case where there is a for without a corresponding next)
10.
(5 points) Program execution should
proceed sequentially from the first line of the program until it encounters an
END command. It should produce
this message when the program finishes executing:
PROGRAM TERMINATED
If it executes all statements of the program
without executing an END command, your program should call the error procedure
with an appropriate message using the line number of the last line of the
program.
The first token after the line number indicates
the kind of command to execute (REM, END, LET, IF, etc.). If your program encounters an illegal
token, it should call the error procedure with an appropriate message.
Other than the specific error handling described
in this writeup, you may assume in writing your code that the program you are
provided is a legal program.
It is helpful to be able to declare a small
number of global variables, although you donÕt want to overuse the
technique. You are allowed to
declare up to 7 global variables in writing your solution (StuartÕs has 5
globals).
The starter code provided in hw8-support.scm
gives the following command options to the user (most of which come from the
BASIC specification):
á
LIST:
show a listing of the current program
á
NEW
erases the current program
á
OLD
allows you to read in a previously saved program
á
RENAME
allows you to change the name of the current program file (only takes effect
for future calls on SAVE or UNSAVE)
á
RENUMBER
renumbers the lines of the current program in increments of 10
á
RUN:
run the current program
á
SAVE:
save the current program in program.bas
á
STOP:
exits the BASIC interpreter
á
UNSAVE:
restore a program from program.bas
For the commands that involve specifying a file
name (NEW, OLD, RENAME), you can either type the command and hit enter or you
can type the command followed by the file name.
In addition to the commands, the user can enter
lines of the program, which will all begin with a line number. They can be entered in any order. The user can also replace a line. To delete a line, the user should type
the line number followed by enter.