diff --git a/src/function/builtin/LOAD.java b/src/function/builtin/LOAD.java
index 22e6963..29d9c50 100644
--- a/src/function/builtin/LOAD.java
+++ b/src/function/builtin/LOAD.java
@@ -1,82 +1,84 @@
package function.builtin;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
+import java.io.*;
+import java.text.MessageFormat;
-import function.LispFunction;
-import function.builtin.cons.LENGTH;
+import environment.Environment;
+import function.*;
import parser.LispParser;
import sexpression.*;
-/**
- * LOAD
represents the LOAD function in Lisp.
- */
public class LOAD extends LispFunction {
- // The number of arguments that LOAD takes.
- private static final int NUM_ARGS = 1;
- public SExpression call(Cons argList) {
- // retrieve the number of arguments passed to LOAD
- int argListLength = LENGTH.getLength(argList);
+ private ArgumentValidator argumentValidator;
+ private Environment environment;
- // make sure we have received the proper number of arguments
- if (argListLength != NUM_ARGS) {
- Cons originalSExpr = new Cons(new Symbol("LOAD"), argList);
- String errMsg = "too " +
- ((argListLength > NUM_ARGS) ? "many" : "few") +
- " arguments given to LOAD: " + originalSExpr;
+ public LOAD() {
+ this.argumentValidator = new ArgumentValidator("LOAD");
+ this.argumentValidator.setExactNumberOfArguments(1);
+ this.argumentValidator.setEveryArgumentExpectedType(LispString.class);
+ this.environment = Environment.getInstance();
+ }
- throw new RuntimeException(errMsg);
- }
+ public SExpression call(Cons argumentList) {
+ argumentValidator.validate(argumentList);
- SExpression argCar = argList.getCar();
-
- // make sure the argument is a string
- if (! argCar.stringp()) {
- throw new RuntimeException("LOAD: " + argCar + " is not a string");
- }
-
- LispString quotedName = (LispString) argCar;
- String fileName = quotedName.toString();
-
- // remove the surrounding quotes from the file name
- fileName = fileName.substring(1, (fileName.length() - 1));
+ LispString quotedName = (LispString) argumentList.getCar();
+ String fileName = removeSurroundingQuotes(quotedName.toString());
return processFile(fileName);
}
- // Evaluate all the S-expressions found in the file with the specified
- // name.
- //
- // Parameters: fileName - the name of the file to be evaluated
- // Returns: 'T' if the file was processed successfully; 'NIL' otherwise
+ private String removeSurroundingQuotes(String fileName) {
+ return fileName.substring(1, (fileName.length() - 1));
+ }
+
private SExpression processFile(String fileName) {
+ boolean wasSuccessful = false;
+ LispParser parser = attemptToCreateParserOnFile(fileName);
+
+ if (parser != null)
+ wasSuccessful = isSuccessfulEvaluation(parser);
+
+ return wasSuccessful ? Symbol.T : Nil.getInstance();
+ }
+
+ private LispParser attemptToCreateParserOnFile(String fileName) {
LispParser parser = null;
- // attempt to create a new 'LispParser' on the specified file
try {
parser = new LispParser(new FileInputStream(fileName), fileName);
} catch (FileNotFoundException e) {
- System.out.println("LOAD: could not open " + fileName);
-
- return Nil.getInstance();
+ printCouldNotOpenFileMessage(fileName);
}
- // attempt to evaluate all the S-expressions contained in the file
- while (! parser.isEof()) {
+ return parser;
+ }
+
+ private boolean isSuccessfulEvaluation(LispParser parser) {
+ while (!parser.isEof()) {
try {
SExpression sexpr = parser.getNextSExpression();
EVAL.eval(sexpr);
} catch (RuntimeException e) {
- System.out.println("LOAD: " + e.getMessage());
+ printErrorMessage(e.getMessage());
- return Nil.getInstance();
+ return false;
}
}
+
+ return true;
+ }
- // success!
- return Symbol.T;
+ private void printCouldNotOpenFileMessage(String fileName) {
+ String message = MessageFormat.format("could not open ''{0}''", fileName);
+ printErrorMessage(message);
+ }
+
+ private void printErrorMessage(String errorMessage) {
+ String message = MessageFormat.format("LOAD: {0}", errorMessage);
+ environment.getErrorOutput().println(message);
}
}
diff --git a/src/function/builtin/special/LET.java b/src/function/builtin/special/LET.java
index 8fdcfbb..6a8b04b 100644
--- a/src/function/builtin/special/LET.java
+++ b/src/function/builtin/special/LET.java
@@ -5,9 +5,6 @@ import function.builtin.EVAL;
import sexpression.*;
import table.SymbolTable;
-/**
- * LET
represents the LET form in Lisp.
- */
public class LET extends LispFunction {
public SExpression call(Cons argList) {
@@ -93,11 +90,6 @@ public class LET extends LispFunction {
}
}
- /**
- * Determine if the arguments passed to this Lisp function should be evaluated.
- *
- * @return false
- */
public boolean evaluateArguments() {
return false;
}
diff --git a/test/function/builtin/LOADTester.java b/test/function/builtin/LOADTester.java
new file mode 100644
index 0000000..3ff8f2e
--- /dev/null
+++ b/test/function/builtin/LOADTester.java
@@ -0,0 +1,69 @@
+package function.builtin;
+
+import static org.junit.Assert.assertEquals;
+import static testutil.TestUtilities.*;
+
+import java.io.*;
+
+import org.junit.*;
+
+import environment.Environment;
+import function.ArgumentValidator.*;
+
+public class LOADTester {
+
+ private ByteArrayOutputStream outputStream;
+
+ private void assertPrinted(String expected) {
+ assertEquals(expected, outputStream.toString());
+ }
+
+ private void assertNothingPrinted() {
+ assertPrinted("");
+ }
+
+ @Before
+ public void setUp() {
+ this.outputStream = new ByteArrayOutputStream();
+ Environment.getInstance().setErrorOutput(new PrintStream(outputStream));
+ }
+
+ @Test
+ public void loadGoodFile_ReturnsTAndPrintsNothing() {
+ String input = "(load \"test/function/builtin/test-files/load-good.lisp\")";
+
+ assertSExpressionsMatch(parseString("T"), evaluateString(input));
+ assertNothingPrinted();
+ }
+ @Test
+ public void loadBadFile_ReturnsNilAndPrintsError() {
+ String input = "(load \"test/function/builtin/test-files/load-bad.lisp\")";
+
+ assertSExpressionsMatch(parseString("nil"), evaluateString(input));
+ assertPrinted("LOAD: expression begins with ')' - line 1, column 1\n");
+ }
+
+ @Test
+ public void loadNonExistentFile_ReturnsNilAndPrintsError() {
+ String input = "(load \"doesNotExist.lisp\")";
+
+ assertSExpressionsMatch(parseString("nil"), evaluateString(input));
+ assertPrinted("LOAD: could not open 'doesNotExist.lisp'\n");
+ }
+
+ @Test(expected = BadArgumentTypeException.class)
+ public void testLoadWithBadArgumentType() {
+ evaluateString("(load '1)");
+ }
+
+ @Test(expected = TooManyArgumentsException.class)
+ public void testLoadWithTooManyArguments() {
+ evaluateString("(load \"1\" \"2\")");
+ }
+
+ @Test(expected = TooFewArgumentsException.class)
+ public void testLoadWithTooFewArguments() {
+ evaluateString("(load)");
+ }
+
+}
diff --git a/test/function/builtin/test-files/load-bad.lisp b/test/function/builtin/test-files/load-bad.lisp
new file mode 100644
index 0000000..c078133
--- /dev/null
+++ b/test/function/builtin/test-files/load-bad.lisp
@@ -0,0 +1,50 @@
+)(defun extend-null (the-list)
+ (cond
+ ((equal (length the-list) 0) t)
+ (t nil)
+ )
+)
+
+(defun mapcar (function-name the-list)
+ (cond
+ ((null the-list) nil)
+ (t (cons (funcall function-name (first the-list))
+ (mapcar function-name (rest the-list))))
+ )
+)
+
+(defun maplist (function-name the-list)
+ (cond
+ ((null the-list) nil)
+ (t (cons (funcall function-name the-list)
+ (maplist function-name (rest the-list))))
+ )
+)
+
+(defun extend-apply (function-name param-list)
+ (eval (cons function-name param-list)))
+
+(defun append (listA listB)
+ (cond
+ ((null listA) listB)
+ (t (cons (first listA) (append (rest listA) listB)))
+ )
+)
+
+(defun second (listA) (first (rest listA)))
+(defun third (listA) (first (rest (rest listA))))
+(defun fourth (listA) (first (rest (rest (rest listA)))))
+(defun fifth (listA) (first (rest (rest (rest (rest listA))))))
+(defun sixth (listA) (first (rest (rest (rest (rest (rest listA)))))))
+(defun seventh (listA) (first (rest (rest (rest (rest (rest (rest listA))))))))
+(defun eighth (listA) (first (rest (rest (rest (rest (rest (rest (rest listA)))))))))
+(defun ninth (listA) (first (rest (rest (rest (rest (rest (rest (rest (rest listA))))))))))
+(defun tenth (listA) (first (rest (rest (rest (rest (rest (rest (rest (rest (rest listA)))))))))))
+
+(defun nth (n listA)
+ (cond
+ ((equal 0 n) (first listA))
+ (t (nth (- n 1) (rest listA)))
+ )
+)
+
diff --git a/test/function/builtin/test-files/load-good.lisp b/test/function/builtin/test-files/load-good.lisp
new file mode 100644
index 0000000..403108d
--- /dev/null
+++ b/test/function/builtin/test-files/load-good.lisp
@@ -0,0 +1,50 @@
+(defun extend-null (the-list)
+ (cond
+ ((equal (length the-list) 0) t)
+ (t nil)
+ )
+)
+
+(defun mapcar (function-name the-list)
+ (cond
+ ((null the-list) nil)
+ (t (cons (funcall function-name (first the-list))
+ (mapcar function-name (rest the-list))))
+ )
+)
+
+(defun maplist (function-name the-list)
+ (cond
+ ((null the-list) nil)
+ (t (cons (funcall function-name the-list)
+ (maplist function-name (rest the-list))))
+ )
+)
+
+(defun extend-apply (function-name param-list)
+ (eval (cons function-name param-list)))
+
+(defun append (listA listB)
+ (cond
+ ((null listA) listB)
+ (t (cons (first listA) (append (rest listA) listB)))
+ )
+)
+
+(defun second (listA) (first (rest listA)))
+(defun third (listA) (first (rest (rest listA))))
+(defun fourth (listA) (first (rest (rest (rest listA)))))
+(defun fifth (listA) (first (rest (rest (rest (rest listA))))))
+(defun sixth (listA) (first (rest (rest (rest (rest (rest listA)))))))
+(defun seventh (listA) (first (rest (rest (rest (rest (rest (rest listA))))))))
+(defun eighth (listA) (first (rest (rest (rest (rest (rest (rest (rest listA)))))))))
+(defun ninth (listA) (first (rest (rest (rest (rest (rest (rest (rest (rest listA))))))))))
+(defun tenth (listA) (first (rest (rest (rest (rest (rest (rest (rest (rest (rest listA)))))))))))
+
+(defun nth (n listA)
+ (cond
+ ((equal 0 n) (first listA))
+ (t (nth (- n 1) (rest listA)))
+ )
+)
+