Introduced global abstractions of input, output, and termination
This commit is contained in:
parent
217c215efe
commit
a4cb521c7d
|
@ -0,0 +1,56 @@
|
||||||
|
package environment;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class Environment {
|
||||||
|
|
||||||
|
private static final Environment instance = new Environment();
|
||||||
|
|
||||||
|
public static Environment getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream input;
|
||||||
|
private PrintStream output;
|
||||||
|
private PrintStream errorOutput;
|
||||||
|
private Runnable terminate;
|
||||||
|
|
||||||
|
private Environment() {}
|
||||||
|
|
||||||
|
public void setInput(InputStream input) {
|
||||||
|
this.input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutput(PrintStream output) {
|
||||||
|
this.output = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorOutput(PrintStream errorOutput) {
|
||||||
|
this.errorOutput = errorOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTerminate(Runnable terminate) {
|
||||||
|
this.terminate = terminate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getInput() {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInputName() {
|
||||||
|
return input.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrintStream getOutput() {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrintStream getErrorOutput() {
|
||||||
|
return errorOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void terminate() {
|
||||||
|
terminate.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
package error;
|
package error;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
import environment.Environment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints error messages and potentially terminates the application.
|
* Prints error messages and potentially terminates the application.
|
||||||
|
@ -14,25 +16,28 @@ public class ErrorManager {
|
||||||
private static final String ANSI_RED = "\u001B[31m";
|
private static final String ANSI_RED = "\u001B[31m";
|
||||||
private static final String ANSI_PURPLE = "\u001B[35m";
|
private static final String ANSI_PURPLE = "\u001B[35m";
|
||||||
|
|
||||||
private Runnable systemTerminatingFunction;
|
private Environment environment;
|
||||||
private Consumer<String> outputFunction;
|
|
||||||
|
|
||||||
public ErrorManager(Runnable systemTerminatingFunction, Consumer<String> outputFunction) {
|
public ErrorManager() {
|
||||||
this.systemTerminatingFunction = systemTerminatingFunction;
|
this.environment = Environment.getInstance();
|
||||||
this.outputFunction = outputFunction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateError(LispException lispException) {
|
public void generateError(LispException lispException) {
|
||||||
outputFunction.accept(formatMessage(lispException));
|
printError(lispException);
|
||||||
|
|
||||||
if (isCritical(lispException))
|
if (isCritical(lispException))
|
||||||
systemTerminatingFunction.run();
|
environment.terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printError(LispException lispException) {
|
||||||
|
String formattedMessage = formatMessage(lispException);
|
||||||
|
environment.getErrorOutput().println(formattedMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String formatMessage(LispException lispException) {
|
private String formatMessage(LispException lispException) {
|
||||||
String color = isCritical(lispException) ? ANSI_PURPLE : ANSI_RED;
|
String color = isCritical(lispException) ? ANSI_PURPLE : ANSI_RED;
|
||||||
|
|
||||||
return MessageFormat.format("{0}error: {1}{2}\n", color, lispException.getMessage(), ANSI_RESET);
|
return MessageFormat.format("{0}error: {1}{2}", color, lispException.getMessage(), ANSI_RESET);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isCritical(LispException lispException) {
|
private boolean isCritical(LispException lispException) {
|
||||||
|
|
|
@ -1,35 +1,25 @@
|
||||||
package function.builtin;
|
package function.builtin;
|
||||||
|
|
||||||
import function.LispFunction;
|
import environment.Environment;
|
||||||
import function.builtin.cons.LENGTH;
|
import function.*;
|
||||||
import sexpression.*;
|
import sexpression.*;
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>PRINT</code> represents the PRINT function in Lisp.
|
|
||||||
*/
|
|
||||||
public class PRINT extends LispFunction {
|
public class PRINT extends LispFunction {
|
||||||
|
|
||||||
// The number of arguments that PRINT takes.
|
private ArgumentValidator argumentValidator;
|
||||||
private static final int NUM_ARGS = 1;
|
private Environment environment;
|
||||||
|
|
||||||
|
public PRINT() {
|
||||||
|
this.argumentValidator = new ArgumentValidator("PRINT");
|
||||||
|
this.argumentValidator.setExactNumberOfArguments(1);
|
||||||
|
this.environment = Environment.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
public SExpression call(Cons argList) {
|
||||||
// retrieve the number of arguments passed to PRINT
|
SExpression argument = argList.getCar();
|
||||||
int argListLength = LENGTH.getLength(argList);
|
environment.getOutput().println(argument);
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
return argument;
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("PRINT"), argList);
|
|
||||||
String errMsg = "too " + ((argListLength > NUM_ARGS) ? "many" : "few") + " arguments given to PRINT: "
|
|
||||||
+ originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression arg = argList.getCar();
|
|
||||||
|
|
||||||
System.out.println(arg);
|
|
||||||
|
|
||||||
return arg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +1,32 @@
|
||||||
package interpreter;
|
package interpreter;
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
import error.ErrorManager;
|
|
||||||
|
|
||||||
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 4.4.3";
|
||||||
private static final String PROMPT = "~ ";
|
private static final String PROMPT = "~ ";
|
||||||
|
|
||||||
public InteractiveLispInterpreter(InputStream inputStream, PrintStream outputStream, ErrorManager errorManager) {
|
@Override
|
||||||
super(inputStream, outputStream, errorManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void printGreeting() {
|
protected void printGreeting() {
|
||||||
outputStream.println(GREETING);
|
environment.getOutput().println(GREETING);
|
||||||
outputStream.println();
|
environment.getOutput().println();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected void displayPrompt() {
|
protected void displayPrompt() {
|
||||||
outputStream.print(PROMPT);
|
environment.getOutput().print(PROMPT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected void erasePrompt() {
|
protected void erasePrompt() {
|
||||||
for (int i = 0; i < PROMPT.length(); i++) {
|
for (int i = 0; i < PROMPT.length(); i++) {
|
||||||
outputStream.print("\b");
|
environment.getOutput().print("\b");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected void printFarewell() {
|
protected void printFarewell() {
|
||||||
outputStream.println();
|
environment.getOutput().println();
|
||||||
outputStream.println();
|
environment.getOutput().println();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package interpreter;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
|
import environment.Environment;
|
||||||
import error.*;
|
import error.*;
|
||||||
import function.builtin.EVAL;
|
import function.builtin.EVAL;
|
||||||
import parser.LispParser;
|
import parser.LispParser;
|
||||||
|
@ -15,12 +16,25 @@ public class LispInterpreter {
|
||||||
|
|
||||||
private LispParser parser;
|
private LispParser parser;
|
||||||
private ErrorManager errorManager;
|
private ErrorManager errorManager;
|
||||||
protected PrintStream outputStream;
|
protected Environment environment;
|
||||||
|
|
||||||
public LispInterpreter(InputStream inputStream, PrintStream outputStream, ErrorManager errorManager) {
|
public LispInterpreter() {
|
||||||
this.errorManager = errorManager;
|
this.environment = Environment.getInstance();
|
||||||
this.parser = new LispParser(inputStream, inputStream.toString());
|
this.errorManager = new ErrorManager();
|
||||||
this.outputStream = outputStream;
|
this.parser = new LispParser(this.environment.getInput(), this.environment.getInputName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public LispInterpreter(String fileName) {
|
||||||
|
this.environment = Environment.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());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void interpret() {
|
public void interpret() {
|
||||||
|
@ -53,7 +67,7 @@ public class LispInterpreter {
|
||||||
String result = MessageFormat.format("{0}{1}{2}", ANSI_GREEN, EVAL.eval(sExpression), ANSI_RESET);
|
String result = MessageFormat.format("{0}{1}{2}", ANSI_GREEN, EVAL.eval(sExpression), ANSI_RESET);
|
||||||
|
|
||||||
erasePrompt();
|
erasePrompt();
|
||||||
outputStream.println(result);
|
environment.getOutput().println(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void erasePrompt() {}
|
protected void erasePrompt() {}
|
||||||
|
@ -71,7 +85,27 @@ public class LispInterpreter {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void printFarewell() {
|
protected void printFarewell() {
|
||||||
outputStream.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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package interpreter;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public interface LispInterpreterBuilder {
|
||||||
|
|
||||||
|
void setInput(InputStream inputStream);
|
||||||
|
|
||||||
|
void setOutput(PrintStream outputStream);
|
||||||
|
|
||||||
|
void setErrorOutput(PrintStream errorOutputStream);
|
||||||
|
|
||||||
|
void setTerminate(Runnable terminationFunction);
|
||||||
|
|
||||||
|
void useFile(String fileName);
|
||||||
|
|
||||||
|
LispInterpreter build();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package interpreter;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
import environment.Environment;
|
||||||
|
|
||||||
|
public class LispInterpreterBuilderImpl implements LispInterpreterBuilder {
|
||||||
|
|
||||||
|
private Environment environment;
|
||||||
|
private String fileName;
|
||||||
|
private boolean isInteractive;
|
||||||
|
|
||||||
|
public LispInterpreterBuilderImpl() {
|
||||||
|
this.environment = Environment.getInstance();
|
||||||
|
this.fileName = "";
|
||||||
|
this.isInteractive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setInput(InputStream inputStream) {
|
||||||
|
this.environment.setInput(inputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOutput(PrintStream outputStream) {
|
||||||
|
this.environment.setOutput(outputStream);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setErrorOutput(PrintStream errorOutputStream) {
|
||||||
|
this.environment.setErrorOutput(errorOutputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTerminate(Runnable terminationFunction) {
|
||||||
|
this.environment.setTerminate(terminationFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void useFile(String fileName) {
|
||||||
|
this.fileName = fileName;
|
||||||
|
this.isInteractive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LispInterpreter build() {
|
||||||
|
if (isInteractive)
|
||||||
|
return new InteractiveLispInterpreter();
|
||||||
|
else
|
||||||
|
return new LispInterpreter(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,8 +1,5 @@
|
||||||
package main;
|
package main;
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
import error.*;
|
|
||||||
import interpreter.*;
|
import interpreter.*;
|
||||||
|
|
||||||
public class LispMain {
|
public class LispMain {
|
||||||
|
@ -10,41 +7,21 @@ public class LispMain {
|
||||||
private LispMain() {}
|
private LispMain() {}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
LispInterpreter interpreter = null;
|
LispInterpreter interpreter = buildInterpreter(args);
|
||||||
ErrorManager errorManager = new ErrorManager(() -> System.exit(1), System.err::print);
|
|
||||||
|
|
||||||
if (args.length > 0) {
|
|
||||||
String fileName = args[0];
|
|
||||||
|
|
||||||
try {
|
|
||||||
interpreter = new LispInterpreter(new FileInputStream(fileName), System.out, errorManager);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
errorManager.generateError(new LispFileNotFoundException(e));
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
interpreter = new InteractiveLispInterpreter(System.in, System.out, errorManager);
|
|
||||||
|
|
||||||
interpreter.interpret();
|
interpreter.interpret();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class LispFileNotFoundException extends LispException {
|
private static LispInterpreter buildInterpreter(String[] args) {
|
||||||
|
LispInterpreterBuilder builder = new LispInterpreterBuilderImpl();
|
||||||
|
builder.setInput(System.in);
|
||||||
|
builder.setOutput(System.out);
|
||||||
|
builder.setErrorOutput(System.err);
|
||||||
|
builder.setTerminate(() -> System.exit(1));
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
if (args.length > 0)
|
||||||
private String message;
|
builder.useFile(args[0]);
|
||||||
|
|
||||||
public LispFileNotFoundException(FileNotFoundException e) {
|
return builder.build();
|
||||||
this.message = e.getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSeverity() {
|
|
||||||
return ErrorManager.CRITICAL_LEVEL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
package error;
|
package error;
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.io.*;
|
||||||
import java.util.Set;
|
import java.util.*;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.*;
|
||||||
import org.junit.Test;
|
|
||||||
|
import environment.Environment;
|
||||||
|
|
||||||
public class ErrorManagerTester {
|
public class ErrorManagerTester {
|
||||||
|
|
||||||
|
@ -15,9 +15,13 @@ public class ErrorManagerTester {
|
||||||
private static final String MESSAGE = "message";
|
private static final String MESSAGE = "message";
|
||||||
|
|
||||||
private Set<String> indicatorSet;
|
private Set<String> indicatorSet;
|
||||||
|
private ByteArrayOutputStream outputStream;
|
||||||
|
|
||||||
private ErrorManager createErrorManagerWithIndicators() {
|
private ErrorManager createErrorManagerWithIndicators() {
|
||||||
return new ErrorManager(() -> indicatorSet.add(TERMINATED), (String) -> indicatorSet.add(MESSAGE));
|
Environment.getInstance().setTerminate(() -> indicatorSet.add(TERMINATED));
|
||||||
|
Environment.getInstance().setErrorOutput(new PrintStream(outputStream));
|
||||||
|
|
||||||
|
return new ErrorManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
private LispException createLispException(int severity) {
|
private LispException createLispException(int severity) {
|
||||||
|
@ -37,9 +41,26 @@ public class ErrorManagerTester {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertTerminated() {
|
||||||
|
assertTrue(indicatorSet.contains(TERMINATED));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNotTerminated() {
|
||||||
|
assertFalse(indicatorSet.contains(TERMINATED));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertErrorMessageNotWritten() {
|
||||||
|
assertTrue(outputStream.toByteArray().length == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertErrorMessageWritten() {
|
||||||
|
assertTrue(outputStream.toByteArray().length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
this.indicatorSet = new HashSet<>();
|
this.indicatorSet = new HashSet<>();
|
||||||
|
this.outputStream = new ByteArrayOutputStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -47,7 +68,7 @@ public class ErrorManagerTester {
|
||||||
ErrorManager errorManager = createErrorManagerWithIndicators();
|
ErrorManager errorManager = createErrorManagerWithIndicators();
|
||||||
|
|
||||||
errorManager.generateError(createLispException(ErrorManager.CRITICAL_LEVEL));
|
errorManager.generateError(createLispException(ErrorManager.CRITICAL_LEVEL));
|
||||||
assertTrue(indicatorSet.contains(TERMINATED));
|
assertTerminated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -55,15 +76,7 @@ public class ErrorManagerTester {
|
||||||
ErrorManager errorManager = createErrorManagerWithIndicators();
|
ErrorManager errorManager = createErrorManagerWithIndicators();
|
||||||
|
|
||||||
errorManager.generateError(createLispException(0));
|
errorManager.generateError(createLispException(0));
|
||||||
assertFalse(indicatorSet.contains(TERMINATED));
|
assertNotTerminated();
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void noMessageDisplayedBeforeError() {
|
|
||||||
createErrorManagerWithIndicators();
|
|
||||||
|
|
||||||
assertFalse(indicatorSet.contains(TERMINATED));
|
|
||||||
assertFalse(indicatorSet.contains(MESSAGE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -71,8 +84,16 @@ public class ErrorManagerTester {
|
||||||
ErrorManager errorManager = createErrorManagerWithIndicators();
|
ErrorManager errorManager = createErrorManagerWithIndicators();
|
||||||
|
|
||||||
errorManager.generateError(createLispException(0));
|
errorManager.generateError(createLispException(0));
|
||||||
assertFalse(indicatorSet.contains(TERMINATED));
|
assertNotTerminated();
|
||||||
assertTrue(indicatorSet.contains(MESSAGE));
|
assertErrorMessageWritten();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void noMessageDisplayedBeforeError() {
|
||||||
|
createErrorManagerWithIndicators();
|
||||||
|
|
||||||
|
assertNotTerminated();
|
||||||
|
assertErrorMessageNotWritten();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -80,8 +101,8 @@ public class ErrorManagerTester {
|
||||||
ErrorManager errorManager = createErrorManagerWithIndicators();
|
ErrorManager errorManager = createErrorManagerWithIndicators();
|
||||||
|
|
||||||
errorManager.generateError(createLispException(ErrorManager.CRITICAL_LEVEL));
|
errorManager.generateError(createLispException(ErrorManager.CRITICAL_LEVEL));
|
||||||
assertTrue(indicatorSet.contains(TERMINATED));
|
assertTerminated();
|
||||||
assertTrue(indicatorSet.contains(MESSAGE));
|
assertErrorMessageWritten();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue