transcendental-lisp/design.txt

48 lines
2.9 KiB
Plaintext
Raw Normal View History

2016-12-07 14:16:45 -05:00
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.