48 lines
2.9 KiB
Plaintext
48 lines
2.9 KiB
Plaintext
|
Mike Cifelli
|
||
|
CIS 443 - Programming Languages
|
||
|
Lisp Interpreter Design Document
|
||
|
|
||
|
My implementation of LispScanner takes in an InputStream in its constructor
|
||
|
that it will use to retrieve the Lisp tokens. It then creates a
|
||
|
BufferedInputStream around this input stream so I can be sure that it will
|
||
|
support the 'mark' and 'reset' methods (which the LispScanner requires to
|
||
|
operate). A LispFilterStream is then created with this BufferedInputStream so I
|
||
|
can retrieve all of the bytes from the original input stream without having to
|
||
|
worry about dealing with Lisp comments.
|
||
|
When the LispScanner looks for the next Lisp token to return it uses a
|
||
|
switch statement to determine the type of the next token (or to skip over
|
||
|
whitespace). In the case of an identifier or number the scanner has to keep
|
||
|
accumulating characters until it sees one that can not be a part of the number
|
||
|
or identifier. Once one is found it has obviously been read from the input
|
||
|
stream and this is not desirable as it is not part of the current token. This
|
||
|
is where I made use of the 'mark' and 'reset' methods in the scanner. I mark
|
||
|
the position before I read each character and when one is found that is not
|
||
|
part of the current token I reset the input stream to its last position before
|
||
|
the token is returned. This effectively unreads the last character so the input
|
||
|
stream is in the proper position for the scanner's next read.
|
||
|
|
||
|
In the design of the LispParser I had some difficulty in implementing the
|
||
|
'eof' method. This was due to the fact that in order to determine if a
|
||
|
LispScanner was at the end of the input stream you have to read in a token.
|
||
|
However, I did not want this method to read in a token since this would
|
||
|
normally be part of an S-expression. Unfortunately, this meant that I would not
|
||
|
be able to detect the end-of-file token until the 'getSExpr' method read it in.
|
||
|
This is too late since the 'eof' method is used to determine when to stop
|
||
|
calling the 'getSExpr' method.
|
||
|
My solution involved reading a token in the 'eof' method and then storing
|
||
|
it in a variable. This would only be done once, until the token was used in the
|
||
|
'getSExpr' method. I also stored any exceptions that were thrown during the
|
||
|
read so that the 'eof' method would not give the impression of having read in
|
||
|
any tokens. Any exception thrown in the 'eof' method during the retrieval of a
|
||
|
token is stored and thrown the next time the 'getSExpr' method is called.
|
||
|
|
||
|
During the evaluation phase of the Lisp interpreter, I made use of the
|
||
|
command pattern during function calls. By creating a LispFunction interface, I
|
||
|
was able to place all of the built-in functions into a hash table mapping
|
||
|
function names to the appropriate LispFunction. By looking up functions in this
|
||
|
hash table, the proper function could be easily called during the evaluation of
|
||
|
a list.
|
||
|
|
||
|
FINAL NOTE: The function table is located in the EVAL class and the symbol
|
||
|
table is located in the SETF class.
|