Started refactoring the sexpression package and cleaned up some unit test code

This commit is contained in:
Mike Cifelli 2016-12-15 11:19:03 -05:00
parent c4e3740dfb
commit 7b7556cc65
29 changed files with 73 additions and 437 deletions

View File

@ -1,3 +0,0 @@
<body>
Provides a class for managing errors in the Lisp Interpreter.
</body>

View File

@ -67,7 +67,7 @@ public class DEFUN extends LispFunction {
// place the function in the function table
functionTable.put(name.toString(),
new UDFunction(name.toString(), lambdaList, body));
new UserDefinedFunction(name.toString(), lambdaList, body));
return name;
}

View File

@ -47,7 +47,7 @@ public class LAMBDA extends LispFunction {
* @throws RuntimeException
* Indicates that <code>lexpr</code> is not a valid lambda expression.
*/
public static UDFunction createFunction(Cons lexpr) {
public static UserDefinedFunction createFunction(Cons lexpr) {
LAMBDA lambda = new LAMBDA();
SExpression cdr = lexpr.getCdr();
@ -92,7 +92,7 @@ public class LAMBDA extends LispFunction {
Cons lambdaList = (Cons) car;
Cons body = (Cons) argList.getCdr();
Cons lexpr = new Cons(new Symbol("LAMBDA"), argList);
UDFunction function = new UDFunction(":LAMBDA", lambdaList, body);
UserDefinedFunction function = new UserDefinedFunction(":LAMBDA", lambdaList, body);
return new LambdaExpression(lexpr, function);
}

View File

@ -16,7 +16,7 @@ import sexpression.SExpression;
public class LambdaExpression extends SExpression {
private Cons lexpr;
private UDFunction function;
private UserDefinedFunction function;
/**
* Create a new FUNCTION with the specified lambda expression and
@ -27,7 +27,7 @@ public class LambdaExpression extends SExpression {
* @param function
* the internal representation of this FUNCTION
*/
public LambdaExpression(Cons lexpr, UDFunction function) {
public LambdaExpression(Cons lexpr, UserDefinedFunction function) {
this.lexpr = lexpr;
this.function = function;
}
@ -58,7 +58,7 @@ public class LambdaExpression extends SExpression {
* @return
* the user-defined function of this FUNCTION
*/
public UDFunction getFunction() {
public UserDefinedFunction getFunction() {
return function;
}

View File

@ -44,10 +44,10 @@ public class SYMBOL_FUNCTION extends LispFunction {
// make sure the function actually exists
if (function != null) {
if (function instanceof UDFunction) {
if (function instanceof UserDefinedFunction) {
// this is a user-defined function
UDFunction udFunction = (UDFunction) function;
UserDefinedFunction udFunction = (UserDefinedFunction) function;
return udFunction.getLexpr();
}

View File

@ -1,23 +1,10 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
import sexpression.Cons;
import sexpression.SExpression;
import sexpression.Symbol;
import java.util.ArrayList;
/**
* A <code>UDFunction</code> is an internal representation of a user-defined
* function in the Lisp programming language.
*/
public class UDFunction extends LispFunction {
import sexpression.*;
public class UserDefinedFunction extends LispFunction {
// the number of arguments that this user-defined function takes.
private final int NUM_ARGS;
@ -40,7 +27,7 @@ public class UDFunction extends LispFunction {
* @param body
* the body of this user-defined function (MUST BE A PROPER LIST)
*/
public UDFunction(String name, Cons lambdaList, Cons body) {
public UserDefinedFunction(String name, Cons lambdaList, Cons body) {
this.name = name;
this.body = body;
this.lexpr = new Cons(new Symbol(name), new Cons(lambdaList, body));

View File

@ -1,4 +0,0 @@
<body>
Provides functions and forms to be used during the evaluation of an
S-expression.
</body>

View File

@ -1,7 +1,5 @@
package file;
import java.util.Objects;
public class FilePosition {
private String fileName;
@ -32,8 +30,4 @@ public class FilePosition {
this.columnNumber = columnNumber;
}
public boolean isEqual(FilePosition otherFilePosition) {
return Objects.equals(this.fileName, otherFilePosition.fileName) && (this.lineNumber == otherFilePosition.lineNumber)
&& (this.columnNumber == otherFilePosition.columnNumber);
}
}

View File

@ -16,11 +16,10 @@ import java.io.*;
import java.text.MessageFormat;
/**
* <code>LispInterpreter</code> is an interpreter for the Lisp programming
* language. It takes the name of a file as a command-line argument, evaluates
* the S-expressions found in the file and then prints the results to the
* console. If no file name is provided at the command-line, this program will
* read from standard input.
* This is an interpreter for the Lisp programming language. It takes the name of a file as a
* command-line argument, evaluates the s-expressions found in the file and then prints the results
* to the console. If no file name is provided at the command-line, this program will read from
* standard input.
*/
public class LispInterpreter {
@ -31,15 +30,9 @@ public class LispInterpreter {
public static final String ANSI_GREEN = "\u001B[32m";
/**
* Evaluate the S-expressions found in the file given as a command-line
* argument and print the results to the console. If no file name was given,
* retrieve the S-expressions from standard input.
*
* @param args
* the command-line arguments:
* <ul>
* <li><code>args[0]</code> - file name (optional)</li>
* </ul>
* Evaluate the s-expressions found in the file given as a command-line argument and print the
* results to the console. If no file name was given, retrieve the s-expressions from standard
* input.
*/
public static void main(String[] args) {
LispParser parser = null;

View File

@ -1,3 +0,0 @@
<body>
Provides test drivers for the various stages of the Lisp Interpreter.
</body>

View File

@ -9,8 +9,7 @@ import token.Eof;
import token.Token;
/**
* Converts a stream of bytes into internal representations of Lisp
* S-expressions.
* Converts a stream of bytes into internal representations of Lisp s-expressions.
*/
public class LispParser {
@ -50,7 +49,7 @@ public class LispParser {
isNextTokenStored = false;
return nextToken.sExpr(scanner::getNextToken);
return nextToken.parseSExpression(scanner::getNextToken);
}
private void throwDelayedExceptionIfNecessary() {

View File

@ -1,4 +0,0 @@
<body>
Provides the classes necessary for creating an internal representation of
the Lisp programming language.
</body>

View File

@ -1,3 +0,0 @@
<body>
Provides the classes necessary to perform a lexical analysis of the Lisp programming language.
</body>

View File

@ -1,44 +1,17 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package sexpression;
/**
* This class represents an ATOM in the PL-Lisp implementation.
*/
public class Atom extends SExpression {
public abstract class Atom extends SExpression {
private String text;
/**
* Create a new ATOM with the specified text.
*
* @param text
* the text representing this ATOM
*/
public Atom(String text) {
this.text = text;
}
/**
* Test if this S-expression is an ATOM.
*
* @return
* <code>true</code>
*/
public boolean atomp() {
return true;
}
/**
* Returns a string representation of this ATOM.
*
* @return
* a string representation of this ATOM
*/
public String toString() {
return text;
}

View File

@ -1,88 +1,35 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package sexpression;
/**
* This class represents a Lisp CONS cell in the PL-Lisp implementation.
*/
public class Cons extends SExpression {
private SExpression car;
private SExpression cdr;
/**
* Create a new CONS cell with the specified car and cdr.
*
* @param car
* the car of this CONS cell
* @param cdr
* the cdr of this CONS cell
*/
public Cons(SExpression car, SExpression cdr) {
this.car = car;
this.cdr = cdr;
}
/**
* Retrieve the car of this CONS cell.
*
* @return
* the car of this CONS cell
*/
public SExpression getCar() {
return car;
}
/**
* Retrieve the cdr of this CONS cell.
*
* @return
* the cdr of this CONS cell
*/
public SExpression getCdr() {
return cdr;
}
/**
* Set the car of this CONS cell to the specified value.
*
* @param newCar
* the value to assign to the car of this CONS cell
*/
public void setCar(SExpression newCar) {
car = newCar;
}
/**
* Set the cdr of this CONS cell to the specified value.
*
* @param newCdr
* the value to assign to the cdr of this CONS cell
*/
public void setCdr(SExpression newCdr) {
cdr = newCdr;
}
/**
* Test if this S-expression is a CONS cell.
*
* @return
* <code>true</code>
*/
public boolean consp() {
return true;
}
/**
* Returns a string representation of this CONS cell.
*
* @return
* a string representation of this CONS cell
*/
public String toString() {
return ("(" + toStringAux());
}
@ -94,17 +41,12 @@ public class Cons extends SExpression {
// this method. When used in conjunction with the 'toString' method of this
// class, this method provides a means for creating the correct string
// representation of a list.
//
// Returns: a string representation of the car of a CONS cell followed by
// its cdr
private String toStringAux() {
if (cdr.nullp()) {
if (cdr.nullp())
return (car.toString() + ")");
} else if (cdr.consp()) {
else if (cdr.consp())
return (car.toString() + " " + ((Cons) cdr).toStringAux());
}
// the cdr of this CONS cell is not a list
return (car.toString() + " . " + cdr.toString() + ")");
}

View File

@ -1,65 +1,29 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package sexpression;
/**
* This class represents a NUMBER in the PL-Lisp implementation.
*/
public class LispNumber extends Atom {
private int value;
/**
* Create a new NUMBER with the specified text.
*
* @param text
* the text representing this NUMBER
* @throws IllegalArgumentException
* Indicates that <code>text</code> does not represent a valid integer.
*/
public LispNumber(String text) {
super(text.replaceFirst("^0+(?!$)", ""));
try {
this.value = Integer.parseInt(text);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(text +
" is not a valid integer");
throw new IllegalArgumentException(text + " is not a valid integer");
}
}
/**
* Create a new NUMBER with the specified value.
*
* @param value
* the integer value of this NUMBER
*/
public LispNumber(int value) {
super(Integer.toString(value));
this.value = value;
}
/**
* Test if this S-expression is a NUMBER.
*
* @return
* <code>true</code>
*/
public boolean numberp() {
return true;
}
/**
* Retrieve the integer value of this NUMBER.
*
* @return
* the integer value of this NUMBER
*/
public int getValue() {
return value;
}

View File

@ -1,32 +1,11 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package sexpression;
/**
* This class represents a STRING in the PL-Lisp implementation.
*/
public class LispString extends Atom {
/**
* Create a new STRING with the specified text.
*
* @param text
* the text representing this STRING
*/
public LispString(String text) {
super(text);
}
/**
* Test if this S-expression is a STRING.
*
* @return
* <code>true</code>
*/
public boolean stringp() {
return true;
}

View File

@ -1,102 +1,48 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package sexpression;
/**
* This class represents NIL in the PL-Lisp implementation.
*/
public class Nil extends Cons {
private static Nil uniqueInstance = new Nil();
/**
* Retrieve the single unique instance of NIL.
*
* @return
* the single unique instance of NIL
*/
public static Nil getUniqueInstance() {
return uniqueInstance;
}
// We are using the Singleton pattern, so the constructor for 'Nil' is
// private.
private Nil() {
super(null, null);
// the car and cdr of NIL both refer to NIL
super.setCar(this);
super.setCdr(this);
}
/**
* Test if this S-expression is NULL.
*
* @return
* <code>true</code>
*/
public boolean nullp() {
return true;
}
/**
* Test if this S-expression is an ATOM.
*
* @return
* <code>true</code>
*/
public boolean atomp() {
return true;
}
/**
* Test if this S-expression is a CONS cell.
*
* @return
* <code>false</code>
*/
public boolean consp() {
return false;
}
/**
* Test if this S-expression is a SYMBOL.
*
* @return
* <code>true</code>
*/
public boolean symbolp() {
return true;
}
/**
* Set the car of this CONS cell to the specified value. This method does
* nothing (the car of NIL can not be changed).
*
* @param newCar
* the value to assign to the car of this CONS cell
* Set the car of this CONS cell to the specified value. This method does nothing (the car of
* NIL can not be changed).
*/
public void setCar(SExpression newCar) {}
/**
* Set the cdr of this CONS cell to the specified value. This method does
* nothing (the cdr of NIL can not be changed).
*
* @param newCdr
* the value to assign to the cdr of this CONS cell
* Set the cdr of this CONS cell to the specified value. This method does nothing (the cdr of
* NIL can not be changed).
*/
public void setCdr(SExpression newCdr) {}
/**
* Returns a string representation of NIL.
*
* @return
* a string representation of NIL
*/
public String toString() {
return "NIL";
}

View File

@ -1,120 +1,49 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package sexpression;
/**
* This is the base class for memory in the PL-Lisp implementation.
*/
public class SExpression {
public abstract class SExpression {
// for mark and sweep garbage collection
private boolean marked = false;
/**
* Determine if this <code>SExpression</code> is marked.
*
* @return
* <code>true</code> if this <code>SExpression</code> is marked;
* <code>false</code> otherwise
*/
public final boolean isMarked() {
return marked;
}
/**
* Set the marked status of this S-expression to the specified value.
*
* @param value
* the value to assign to this S-expression's marked status
*/
public final void setMarked(final boolean value) {
marked = value;
}
// Lisp type predicates;
// by default, all return false (an SExpression effectively has NO type);
// Lisp type predicates...
// overridden in subclasses to describe their Lisp type
/**
* Test if this S-expression is NULL.
*
* @return
* <code>false</code>
*/
public boolean nullp() {
return false;
}
/**
* Test if this S-expression is an ATOM.
*
* @return
* <code>false</code>
*/
public boolean atomp() {
return false;
}
/**
* Test if this S-expression is a CONS cell.
*
* @return
* <code>false</code>
*/
public boolean consp() {
return false;
}
/**
* Test if this S-expression is a LIST.
*
* @return
* the value of <code>(consp() || nullp())</code>
*/
public boolean listp() {
return (consp() || nullp());
}
/**
* Test if this S-expression is a NUMBER.
*
* @return
* <code>false</code>
*/
public boolean numberp() {
return false;
}
/**
* Test if this S-expression is a SYMBOL.
*
* @return
* <code>false</code>
*/
public boolean symbolp() {
return false;
}
/**
* Test if this S-expression is a FUNCTION.
*
* @return
* <code>false</code>
*/
public boolean functionp() {
return false;
}
/**
* Test if this S-expression is a STRING.
*
* @return
* <code>false</code>
*/
public boolean stringp() {
return false;
}

View File

@ -1,35 +1,17 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package sexpression;
/**
* This class represents a SYMBOL in the PL-Lisp implementation.
*/
public class Symbol extends Atom {
/** This SYMBOL represents TRUE in the PL-Lisp implementation. */
public static final Symbol T = new Symbol("T");
/**
* Create a new SYMBOL with the specified text.
*
* @param text
* the text representing this SYMBOL
*/
public static Symbol createQuote() {
return new Symbol("QUOTE");
}
public Symbol(String text) {
super(text.toUpperCase());
}
/**
* Test if this S-expression is a SYMBOL.
*
* @return
* <code>true</code>
*/
public boolean symbolp() {
return true;
}

View File

@ -13,7 +13,7 @@ public class Eof extends Token {
}
@Override
public SExpression sExpr(Supplier<Token> getNextToken) {
public SExpression parseSExpression(Supplier<Token> getNextToken) {
throw new EofEncounteredException(this);
}

View File

@ -13,7 +13,7 @@ public class Identifier extends Token {
}
@Override
public SExpression sExpr(Supplier<Token> getNextToken) {
public SExpression parseSExpression(Supplier<Token> getNextToken) {
return new Symbol(getText());
}

View File

@ -12,10 +12,10 @@ public class LeftParenthesis extends Token {
}
@Override
public SExpression sExpr(Supplier<Token> getNextToken) {
public SExpression parseSExpression(Supplier<Token> getNextToken) {
Token nextToken = getNextToken.get();
return nextToken.sExprTail(getNextToken);
return nextToken.parseSExpressionTail(getNextToken);
}
}

View File

@ -13,7 +13,7 @@ public class Number extends Token {
}
@Override
public SExpression sExpr(Supplier<Token> getNextToken) {
public SExpression parseSExpression(Supplier<Token> getNextToken) {
return new LispNumber(getText());
}

View File

@ -12,11 +12,11 @@ public class QuoteMark extends Token {
}
@Override
public SExpression sExpr(Supplier<Token> getNextToken) {
public SExpression parseSExpression(Supplier<Token> getNextToken) {
Token nextToken = getNextToken.get();
SExpression argument = nextToken.sExpr(getNextToken);
SExpression argument = nextToken.parseSExpression(getNextToken);
return new Cons(new Symbol("QUOTE"), new Cons(argument, Nil.getUniqueInstance()));
return new Cons(Symbol.createQuote(), new Cons(argument, Nil.getUniqueInstance()));
}
}

View File

@ -13,7 +13,7 @@ public class QuotedString extends Token {
}
@Override
public SExpression sExpr(Supplier<Token> getNextToken) {
public SExpression parseSExpression(Supplier<Token> getNextToken) {
return new LispString(getText());
}

View File

@ -14,12 +14,12 @@ public class RightParenthesis extends Token {
}
@Override
public SExpression sExpr(Supplier<Token> getNextToken) {
public SExpression parseSExpression(Supplier<Token> getNextToken) {
throw new StartsWithRightParenthesisException(this);
}
@Override
public SExpression sExprTail(Supplier<Token> getNextToken) {
public SExpression parseSExpressionTail(Supplier<Token> getNextToken) {
return Nil.getUniqueInstance();
}

View File

@ -35,16 +35,15 @@ public abstract class Token {
return position.getColumnNumber();
}
// sExpr ::= NUMBER | IDENTIFIER | STRING | QUOTE_MARK sExpr |
// LEFT_PAREN sExprTail
public abstract SExpression sExpr(Supplier<Token> getNextToken);
// sExpr ::= NUMBER | IDENTIFIER | STRING | QUOTE_MARK sExpr | LEFT_PAREN sExprTail
public abstract SExpression parseSExpression(Supplier<Token> getNextToken);
// sExprTail ::= RIGHT_PAREN | sExpr sExprTail
public SExpression sExprTail(Supplier<Token> getNextToken) {
SExpression car = sExpr(getNextToken);
public SExpression parseSExpressionTail(Supplier<Token> getNextToken) {
SExpression car = parseSExpression(getNextToken);
Token nextToken = getNextToken.get();
SExpression cdr = nextToken.sExprTail(getNextToken);
SExpression cdr = nextToken.parseSExpressionTail(getNextToken);
return new Cons(car, cdr);
}

View File

@ -1,8 +1,9 @@
package file;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Objects;
import org.junit.Before;
import org.junit.Test;
@ -20,81 +21,46 @@ public class FilePositionTrackerTester {
return position;
}
private void assertTrackerPositionEquals(FilePosition expectedPosition) {
assertTrue(arePositionsEqual(expectedPosition, trackerUnderTest.getCurrentPosition()));
}
private boolean arePositionsEqual(FilePosition position1, FilePosition position2) {
return Objects.equals(position1.getFileName(), position2.getFileName())
&& Objects.equals(position1.getLineNumber(), position2.getLineNumber())
&& Objects.equals(position1.getColumnNumber(), position2.getColumnNumber());
}
@Before
public void setUp() throws Exception {
trackerUnderTest = new FilePositionTracker(FILE_NAME);
}
@Test
public void filePositionEquality_CorrectlyReturnsTrue() {
FilePosition positionOne = createFilePosition(5, 9);
FilePosition positionTwo = createFilePosition(5, 9);
assertTrue(positionOne.isEqual(positionTwo));
}
@Test
public void filePositionEquality_CorrectlyReturnsFalseWithDifferentLine() {
FilePosition positionOne = createFilePosition(5, 9);
FilePosition positionTwo = createFilePosition(8, 9);
assertFalse(positionOne.isEqual(positionTwo));
}
@Test
public void filePositionEquality_CorrectlyReturnsFalseWithDifferentColumn() {
FilePosition positionOne = createFilePosition(5, 9);
FilePosition positionTwo = createFilePosition(5, 10);
assertFalse(positionOne.isEqual(positionTwo));
}
@Test
public void filePositionEquality_CorrectlyReturnsFalseWithDifferentFileName() {
FilePosition positionOne = new FilePosition("FileOne");
positionOne.setLineNumber(5);
positionOne.setColumnNumber(9);
FilePosition positionTwo = new FilePosition("FileTwo");
positionTwo.setLineNumber(5);
positionTwo.setColumnNumber(9);
assertFalse(positionOne.isEqual(positionTwo));
}
@Test
public void noMovement_ReturnsInitialPosition() {
FilePosition expectedPosition = createFilePosition(1, 0);
assertTrue(expectedPosition.isEqual(trackerUnderTest.getCurrentPosition()));
assertTrackerPositionEquals(createFilePosition(1, 0));
}
@Test
public void advanceOneColumn_ReturnsCorrectPosition() {
FilePosition expectedPosition = createFilePosition(1, 1);
trackerUnderTest.incrementColumn();
assertTrue(expectedPosition.isEqual(trackerUnderTest.getCurrentPosition()));
assertTrackerPositionEquals(createFilePosition(1, 1));
}
@Test
public void advanceOneLine_ReturnsCorrectPosition() {
FilePosition expectedPosition = createFilePosition(2, 0);
trackerUnderTest.incrementLine();
assertTrue(expectedPosition.isEqual(trackerUnderTest.getCurrentPosition()));
assertTrackerPositionEquals(createFilePosition(2, 0));
}
@Test
public void advanceOneLine_ResetsColumn() {
FilePosition expectedPosition = createFilePosition(2, 0);
trackerUnderTest.incrementColumn();
trackerUnderTest.incrementLine();
assertTrue(expectedPosition.isEqual(trackerUnderTest.getCurrentPosition()));
assertTrackerPositionEquals(createFilePosition(2, 0));
}
}