Refactored the lisp interpreter builder and some error handling

This commit is contained in:
Mike Cifelli 2017-02-09 11:00:23 -05:00
parent daf51fa198
commit 1a25ddc35f
10 changed files with 152 additions and 87 deletions

View File

@ -2,6 +2,8 @@ package environment;
import java.io.*; import java.io.*;
import error.ErrorManager;
public class RuntimeEnvironment { public class RuntimeEnvironment {
private static RuntimeEnvironment uniqueInstance = new RuntimeEnvironment(); private static RuntimeEnvironment uniqueInstance = new RuntimeEnvironment();
@ -10,14 +12,20 @@ public class RuntimeEnvironment {
return uniqueInstance; return uniqueInstance;
} }
private String inputName;
private InputStream input; private InputStream input;
private PrintStream output; private PrintStream output;
private PrintStream errorOutput; private PrintStream errorOutput;
private Runnable terminationFunction; private Runnable terminationFunction;
private Runnable errorTerminationFunction; private Runnable errorTerminationFunction;
private ErrorManager errorManager;
private RuntimeEnvironment() {} private RuntimeEnvironment() {}
public void setInputName(String inputName) {
this.inputName = inputName;
}
public void setInput(InputStream input) { public void setInput(InputStream input) {
this.input = input; this.input = input;
} }
@ -38,12 +46,16 @@ public class RuntimeEnvironment {
this.errorTerminationFunction = errorTerminationFunction; this.errorTerminationFunction = errorTerminationFunction;
} }
public InputStream getInput() { public void setErrorManager(ErrorManager errorManager) {
return input; this.errorManager = errorManager;
} }
public String getInputName() { public String getInputName() {
return input.toString(); return inputName;
}
public InputStream getInput() {
return input;
} }
public PrintStream getOutput() { public PrintStream getOutput() {
@ -62,4 +74,8 @@ public class RuntimeEnvironment {
errorTerminationFunction.run(); errorTerminationFunction.run();
} }
public ErrorManager getErrorManager() {
return errorManager;
}
} }

View File

@ -4,6 +4,18 @@ public abstract class LispException extends RuntimeException {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public static final LispException convertToLispException(RuntimeException e) {
return new LispException() {
private static final long serialVersionUID = 1L;
@Override
public String getMessage() {
return e.getMessage();
}
};
}
public int getSeverity() { public int getSeverity() {
return 0; return 0;
} }

View File

@ -1,9 +1,12 @@
package function.builtin; package function.builtin;
import static error.LispException.convertToLispException;
import java.io.*; import java.io.*;
import java.text.MessageFormat; import java.text.MessageFormat;
import environment.RuntimeEnvironment; import environment.RuntimeEnvironment;
import error.*;
import function.*; import function.*;
import parser.LispParser; import parser.LispParser;
import sexpression.*; import sexpression.*;
@ -12,12 +15,14 @@ public class LOAD extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;
private RuntimeEnvironment environment; private RuntimeEnvironment environment;
private ErrorManager errorManager;
public LOAD() { public LOAD() {
this.argumentValidator = new ArgumentValidator("LOAD"); this.argumentValidator = new ArgumentValidator("LOAD");
this.argumentValidator.setExactNumberOfArguments(1); this.argumentValidator.setExactNumberOfArguments(1);
this.argumentValidator.setEveryArgumentExpectedType(LispString.class); this.argumentValidator.setEveryArgumentExpectedType(LispString.class);
this.environment = RuntimeEnvironment.getInstance(); this.environment = RuntimeEnvironment.getInstance();
this.errorManager = this.environment.getErrorManager();
} }
public SExpression call(Cons argumentList) { public SExpression call(Cons argumentList) {
@ -49,7 +54,7 @@ public class LOAD extends LispFunction {
try { try {
parser = new LispParser(new FileInputStream(fileName), fileName); parser = new LispParser(new FileInputStream(fileName), fileName);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
printCouldNotOpenFileMessage(fileName); errorManager.generateError(new CouldNotLoadFileException(fileName));
} }
return parser; return parser;
@ -58,12 +63,12 @@ public class LOAD extends LispFunction {
private boolean isSuccessfulEvaluation(LispParser parser) { private boolean isSuccessfulEvaluation(LispParser parser) {
while (!parser.isEof()) { while (!parser.isEof()) {
try { try {
SExpression sexpr = parser.getNextSExpression(); EVAL.eval(parser.getNextSExpression());
} catch (LispException e) {
EVAL.eval(sexpr); errorManager.generateError(e);
return false;
} catch (RuntimeException e) { } catch (RuntimeException e) {
printErrorMessage(e.getMessage()); errorManager.generateError(convertToLispException(e));
return false; return false;
} }
} }
@ -71,14 +76,19 @@ public class LOAD extends LispFunction {
return true; return true;
} }
private void printCouldNotOpenFileMessage(String fileName) { public static class CouldNotLoadFileException extends LispException {
String message = MessageFormat.format("could not open ''{0}''", fileName);
printErrorMessage(message);
}
private void printErrorMessage(String errorMessage) { private static final long serialVersionUID = 1L;
String message = MessageFormat.format("LOAD: {0}", errorMessage); private String fileName;
environment.getErrorOutput().println(message);
public CouldNotLoadFileException(String fileName) {
this.fileName = fileName;
}
@Override
public String getMessage() {
return MessageFormat.format("could not load ''{0}''", fileName);
}
} }
} }

View File

@ -2,7 +2,7 @@ package interpreter;
public class InteractiveLispInterpreter extends LispInterpreter { public class InteractiveLispInterpreter extends LispInterpreter {
private static final String GREETING = "SUNY Potsdam Lisp Interpreter - Version 4.4.3"; private static final String GREETING = "SUNY Potsdam Lisp Interpreter - Version 1.0.0";
private static final String PROMPT = "~ "; private static final String PROMPT = "~ ";
@Override @Override

View File

@ -1,6 +1,7 @@
package interpreter; package interpreter;
import java.io.*; import static error.LispException.convertToLispException;
import java.text.MessageFormat; import java.text.MessageFormat;
import environment.RuntimeEnvironment; import environment.RuntimeEnvironment;
@ -14,26 +15,13 @@ public class LispInterpreter {
public static final String ANSI_RESET = "\u001B[0m"; public static final String ANSI_RESET = "\u001B[0m";
public static final String ANSI_GREEN = "\u001B[32m"; public static final String ANSI_GREEN = "\u001B[32m";
private LispParser parser;
private ErrorManager errorManager;
protected RuntimeEnvironment environment; protected RuntimeEnvironment environment;
protected ErrorManager errorManager;
private LispParser parser;
public LispInterpreter() { public LispInterpreter() {
this.environment = RuntimeEnvironment.getInstance(); this.environment = RuntimeEnvironment.getInstance();
this.errorManager = new ErrorManager(); this.errorManager = this.environment.getErrorManager();
this.parser = new LispParser(this.environment.getInput(), this.environment.getInputName());
}
public LispInterpreter(String fileName) {
this.environment = RuntimeEnvironment.getInstance();
this.errorManager = new ErrorManager();
try {
this.environment.setInput(new FileInputStream(fileName));
} catch (FileNotFoundException e) {
this.errorManager.generateError(new LispFileNotFoundException(e));
}
this.parser = new LispParser(this.environment.getInput(), this.environment.getInputName()); this.parser = new LispParser(this.environment.getInput(), this.environment.getInputName());
} }
@ -72,40 +60,8 @@ public class LispInterpreter {
protected void erasePrompt() {} protected void erasePrompt() {}
private LispException convertToLispException(RuntimeException e) {
return new LispException() {
private static final long serialVersionUID = 1L;
@Override
public String getMessage() {
return e.getMessage();
}
};
}
protected void printFarewell() { protected void printFarewell() {
environment.getOutput().println(); environment.getOutput().println();
} }
public static class LispFileNotFoundException extends LispException {
private static final long serialVersionUID = 1L;
private String message;
public LispFileNotFoundException(FileNotFoundException e) {
this.message = e.getMessage();
}
@Override
public int getSeverity() {
return ErrorManager.CRITICAL_LEVEL;
}
@Override
public String getMessage() {
return message;
}
}
} }

View File

@ -4,6 +4,8 @@ import java.io.*;
public interface LispInterpreterBuilder { public interface LispInterpreterBuilder {
void setInputName(String inputName);
void setInput(InputStream inputStream); void setInput(InputStream inputStream);
void setOutput(PrintStream outputStream); void setOutput(PrintStream outputStream);

View File

@ -13,23 +13,28 @@ public class LispInterpreterBuilderImpl implements LispInterpreterBuilder {
return uniqueInstance; return uniqueInstance;
} }
private String inputName;
private InputStream inputStream; private InputStream inputStream;
private PrintStream outputStream; private PrintStream outputStream;
private PrintStream errorOutputStream; private PrintStream errorOutputStream;
private Runnable terminationFunction; private Runnable terminationFunction;
private Runnable errorTerminationFunction; private Runnable errorTerminationFunction;
private RuntimeEnvironment environment; private RuntimeEnvironment environment;
private String fileName;
private boolean isInteractive; private boolean isInteractive;
private boolean isBuilt; private boolean isBuilt;
private LispInterpreterBuilderImpl() { private LispInterpreterBuilderImpl() {
this.environment = RuntimeEnvironment.getInstance(); this.environment = RuntimeEnvironment.getInstance();
this.fileName = ""; this.inputName = "";
this.isInteractive = true; this.isInteractive = true;
this.isBuilt = false; this.isBuilt = false;
} }
@Override
public void setInputName(String inputName) {
this.inputName = inputName;
}
@Override @Override
public void setInput(InputStream inputStream) { public void setInput(InputStream inputStream) {
this.inputStream = inputStream; this.inputStream = inputStream;
@ -58,7 +63,7 @@ public class LispInterpreterBuilderImpl implements LispInterpreterBuilder {
@Override @Override
public void useFile(String fileName) { public void useFile(String fileName) {
this.fileName = fileName; this.inputName = fileName;
this.isInteractive = false; this.isInteractive = false;
} }
@ -71,18 +76,40 @@ public class LispInterpreterBuilderImpl implements LispInterpreterBuilder {
} }
private LispInterpreter buildInterpreter() { private LispInterpreter buildInterpreter() {
environment.setInput(inputStream); configureRuntimeEnvironment();
LispInterpreter lispInterpreter = createInterpreter();
isBuilt = true;
return lispInterpreter;
}
private void configureRuntimeEnvironment() {
ErrorManager errorManager = new ErrorManager();
environment.setOutput(outputStream); environment.setOutput(outputStream);
environment.setErrorOutput(errorOutputStream); environment.setErrorOutput(errorOutputStream);
environment.setTerminationFunction(terminationFunction); environment.setTerminationFunction(terminationFunction);
environment.setErrorTerminationFunction(errorTerminationFunction); environment.setErrorTerminationFunction(errorTerminationFunction);
environment.setErrorManager(errorManager);
configureInput(errorManager);
}
LispInterpreter lispInterpreter = isInteractive ? new InteractiveLispInterpreter() private void configureInput(ErrorManager errorManager) {
: new LispInterpreter(fileName); environment.setInputName(inputName);
isBuilt = true; try {
environment.setInput(getInputStream());
} catch (FileNotFoundException e) {
errorManager.generateError(new LispFileNotFoundException(e));
}
}
return lispInterpreter; private InputStream getInputStream() throws FileNotFoundException {
return isInteractive ? inputStream : new FileInputStream(inputName);
}
private LispInterpreter createInterpreter() {
return isInteractive ? new InteractiveLispInterpreter() : new LispInterpreter();
} }
public class InterpreterAlreadyBuiltException extends LispException { public class InterpreterAlreadyBuiltException extends LispException {
@ -100,4 +127,24 @@ public class LispInterpreterBuilderImpl implements LispInterpreterBuilder {
} }
} }
public static class LispFileNotFoundException extends LispException {
private static final long serialVersionUID = 1L;
private String message;
public LispFileNotFoundException(FileNotFoundException e) {
this.message = e.getMessage();
}
@Override
public int getSeverity() {
return ErrorManager.CRITICAL_LEVEL;
}
@Override
public String getMessage() {
return message;
}
}
} }

View File

@ -13,16 +13,22 @@ public class LispMain {
private static LispInterpreter buildInterpreter(String[] args) { private static LispInterpreter buildInterpreter(String[] args) {
LispInterpreterBuilder builder = LispInterpreterBuilderImpl.getInstance(); LispInterpreterBuilder builder = LispInterpreterBuilderImpl.getInstance();
builder.setInput(System.in);
builder.setOutput(System.out); builder.setOutput(System.out);
builder.setErrorOutput(System.err); builder.setErrorOutput(System.err);
builder.setTerminationFunction(() -> System.exit(0)); builder.setTerminationFunction(() -> System.exit(0));
builder.setErrorTerminationFunction(() -> System.exit(1)); builder.setErrorTerminationFunction(() -> System.exit(1));
configureInput(args, builder);
if (args.length > 0)
builder.useFile(args[0]);
return builder.build(); return builder.build();
} }
private static void configureInput(String[] args, LispInterpreterBuilder builder) {
if (args.length > 0)
builder.useFile(args[0]);
else {
builder.setInputName("stdin");
builder.setInput(System.in);
}
}
} }

View File

@ -6,6 +6,8 @@ import java.util.*;
import org.junit.*; import org.junit.*;
import error.ErrorManager;
public class RuntimeEnvironmentTester { public class RuntimeEnvironmentTester {
private static final String TERMINATED_SUCCESSFULLY = "TERMINATED_SUCCESSFULLY"; private static final String TERMINATED_SUCCESSFULLY = "TERMINATED_SUCCESSFULLY";
@ -23,12 +25,18 @@ public class RuntimeEnvironmentTester {
this.indicatorSet = new HashSet<>(); this.indicatorSet = new HashSet<>();
} }
@Test
public void assignInputName() {
this.environment.setInputName("test");
assertEquals("test", this.environment.getInputName());
}
@Test @Test
public void assignInput() { public void assignInput() {
this.environment.setInput(System.in); this.environment.setInput(System.in);
assertEquals(System.in, this.environment.getInput()); assertEquals(System.in, this.environment.getInput());
assertEquals(System.in.toString(), this.environment.getInputName());
} }
@Test @Test
@ -61,4 +69,12 @@ public class RuntimeEnvironmentTester {
assertTrue(indicatorSet.contains(TERMINATED_EXCEPTIONALLY)); assertTrue(indicatorSet.contains(TERMINATED_EXCEPTIONALLY));
} }
@Test
public void assignErrorManager() {
ErrorManager errorManager = new ErrorManager();
this.environment.setErrorManager(errorManager);
assertEquals(errorManager, this.environment.getErrorManager());
}
} }

View File

@ -1,6 +1,6 @@
package function.builtin; package function.builtin;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static testutil.TestUtilities.*; import static testutil.TestUtilities.*;
import java.io.*; import java.io.*;
@ -15,12 +15,12 @@ public class LOADTester {
private ByteArrayOutputStream outputStream; private ByteArrayOutputStream outputStream;
private void assertPrinted(String expected) { private void assertSomethingPrinted() {
assertEquals(expected, outputStream.toString()); assertTrue(outputStream.toByteArray().length > 0);
} }
private void assertNothingPrinted() { private void assertNothingPrinted() {
assertPrinted(""); assertTrue(outputStream.toByteArray().length == 0);
} }
@Before @Before
@ -42,7 +42,7 @@ public class LOADTester {
String input = "(load \"test/function/builtin/test-files/load-bad.lisp\")"; String input = "(load \"test/function/builtin/test-files/load-bad.lisp\")";
assertSExpressionsMatch(Nil.getInstance(), evaluateString(input)); assertSExpressionsMatch(Nil.getInstance(), evaluateString(input));
assertPrinted("LOAD: expression begins with ')' - line 1, column 1\n"); assertSomethingPrinted();
} }
@Test @Test
@ -50,7 +50,7 @@ public class LOADTester {
String input = "(load \"doesNotExist.lisp\")"; String input = "(load \"doesNotExist.lisp\")";
assertSExpressionsMatch(Nil.getInstance(), evaluateString(input)); assertSExpressionsMatch(Nil.getInstance(), evaluateString(input));
assertPrinted("LOAD: could not open 'doesNotExist.lisp'\n"); assertSomethingPrinted();
} }
@Test(expected = BadArgumentTypeException.class) @Test(expected = BadArgumentTypeException.class)