diff --git a/src/error/ErrorManager.java b/src/error/ErrorManager.java index 2a20dc6..5a069e5 100644 --- a/src/error/ErrorManager.java +++ b/src/error/ErrorManager.java @@ -1,6 +1,7 @@ package error; import java.text.MessageFormat; +import java.util.function.Consumer; /** * Prints error messages and potentially terminates the application. @@ -14,15 +15,23 @@ public class ErrorManager { public static final String ANSI_YELLOW = "\u001B[33m"; public static final String ANSI_PURPLE = "\u001B[35m"; - public static void generateError(LispException lispException) { + private Runnable systemTerminatingFunction; + private Consumer outputFunction; + + public ErrorManager(Runnable systemTerminatingFunction, Consumer outputFunction) { + this.systemTerminatingFunction = systemTerminatingFunction; + this.outputFunction = outputFunction; + } + + public void generateError(LispException lispException) { String color = (lispException.getSeverity() >= CRITICAL_LEVEL) ? ANSI_PURPLE : ANSI_RED; - String formattedMessage = MessageFormat.format("{0}error: {1}{2}", color, lispException.getMessage(), + String formattedMessage = MessageFormat.format("{0}error: {1}{2}\n", color, lispException.getMessage(), ANSI_RESET); - System.out.println(formattedMessage); + outputFunction.accept(formattedMessage); if (lispException.getSeverity() >= CRITICAL_LEVEL) { - System.exit(1); + systemTerminatingFunction.run(); } } diff --git a/src/eval/LispFunction.java b/src/eval/LispFunction.java index 324e24f..2513e5c 100644 --- a/src/eval/LispFunction.java +++ b/src/eval/LispFunction.java @@ -23,10 +23,8 @@ public abstract class LispFunction { /** * Determine if the arguments passed to this Lisp function should be evaluated. A subclass - * should override this method to return false if it does not want its arguments to - * be evaluated prior to being passed. - * - * @return true + * should override this method to return false if it does not want its arguments to be evaluated + * prior to being passed. */ public boolean evaluateArguments() { return true; diff --git a/src/main/LispInterpreter.java b/src/main/LispInterpreter.java index 10ae185..e35df95 100644 --- a/src/main/LispInterpreter.java +++ b/src/main/LispInterpreter.java @@ -6,14 +6,15 @@ package main; -import parser.*; -import sexpression.SExpression; -import eval.*; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.text.MessageFormat; + import error.ErrorManager; import error.LispException; - -import java.io.*; -import java.text.MessageFormat; +import eval.EVAL; +import parser.LispParser; +import sexpression.SExpression; /** * This is an interpreter for the Lisp programming language. It takes the name of a file as a @@ -36,6 +37,7 @@ public class LispInterpreter { */ public static void main(String[] args) { LispParser parser = null; + ErrorManager errorManager = new ErrorManager(new TerminateInterpreter(), System.out::print); boolean interactive = false; if (args.length > 0) { @@ -44,7 +46,7 @@ public class LispInterpreter { try { parser = new LispParser(new FileInputStream(args[0]), args[0]); } catch (FileNotFoundException e) { - ErrorManager.generateError(new LispException() { + errorManager.generateError(new LispException() { private static final long serialVersionUID = 1L; @@ -78,10 +80,10 @@ public class LispInterpreter { System.out.println(result); } catch (LispException e) { LispInterpreter.erasePrompt(interactive); - ErrorManager.generateError(e); + errorManager.generateError(e); } catch (RuntimeException e) { LispInterpreter.erasePrompt(interactive); - ErrorManager.generateError(new LispException() { + errorManager.generateError(new LispException() { private static final long serialVersionUID = 1L; @@ -113,4 +115,12 @@ public class LispInterpreter { } } + private static class TerminateInterpreter implements Runnable { + + @Override + public void run() { + System.exit(1); + } + } + } diff --git a/test/error/ErrorManagerTester.java b/test/error/ErrorManagerTester.java new file mode 100644 index 0000000..fe63912 --- /dev/null +++ b/test/error/ErrorManagerTester.java @@ -0,0 +1,101 @@ +package error; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Consumer; + +import org.junit.Test; + +public class ErrorManagerTester { + + private static final String TERMINATED = "terminated"; + private static final String MESSAGE = "message"; + + private ErrorManager createErrorManagerWithIndicators(Set indicatorSet) { + Runnable terminationFunction = new Runnable() { + + @Override + public void run() { + indicatorSet.add(TERMINATED); + } + }; + + Consumer outputFunction = new Consumer() { + + @Override + public void accept(String t) { + indicatorSet.add(MESSAGE); + } + }; + + return new ErrorManager(terminationFunction, outputFunction); + } + + private LispException createLispException(int severity) { + return new LispException() { + + private static final long serialVersionUID = 1L; + + @Override + public int getSeverity() { + return severity; + } + + @Override + public String getMessage() { + return MESSAGE; + } + }; + } + + @Test + public void givenCriticalExceptionSeverity_RunsProvidedTerminationFunction() { + Set indicatorSet = new HashSet<>(); + ErrorManager errorManager = createErrorManagerWithIndicators(indicatorSet); + + errorManager.generateError(createLispException(ErrorManager.CRITICAL_LEVEL)); + assertTrue(indicatorSet.contains(TERMINATED)); + } + + @Test + public void givenNonCriticalExceptionSeverity_DoesNotRunProvidedTerminationFunction() { + Set indicatorSet = new HashSet<>(); + ErrorManager errorManager = createErrorManagerWithIndicators(indicatorSet); + + errorManager.generateError(createLispException(0)); + assertFalse(indicatorSet.contains(TERMINATED)); + } + + @Test + public void noMessageDisplayedBeforeError() { + Set indicatorSet = new HashSet<>(); + createErrorManagerWithIndicators(indicatorSet); + + assertFalse(indicatorSet.contains(TERMINATED)); + assertFalse(indicatorSet.contains(MESSAGE)); + } + + @Test + public void usesOutputFunctionToDisplayMessages_NoTermination() { + Set indicatorSet = new HashSet<>(); + ErrorManager errorManager = createErrorManagerWithIndicators(indicatorSet); + + errorManager.generateError(createLispException(0)); + assertFalse(indicatorSet.contains(TERMINATED)); + assertTrue(indicatorSet.contains(MESSAGE)); + } + + @Test + public void usesOutputFunctionToDisplayMessages_WithTermination() { + Set indicatorSet = new HashSet<>(); + ErrorManager errorManager = createErrorManagerWithIndicators(indicatorSet); + + errorManager.generateError(createLispException(ErrorManager.CRITICAL_LEVEL)); + assertTrue(indicatorSet.contains(TERMINATED)); + assertTrue(indicatorSet.contains(MESSAGE)); + } + +}