Added an argument validator and unit tests

This commit is contained in:
Mike Cifelli 2016-12-19 11:38:14 -05:00
parent 5f2c3dc469
commit fbd2b3207c
9 changed files with 254 additions and 49 deletions

View File

@ -13,11 +13,6 @@ public abstract class LineColumnException extends LispException {
this.position = position; this.position = position;
} }
@Override
public int getSeverity() {
return 0;
}
@Override @Override
public String getMessage() { public String getMessage() {
return MessageFormat.format("{0} - line {1}, column {2}", getMessagePrefix(), position.getLineNumber(), return MessageFormat.format("{0} - line {1}, column {2}", getMessagePrefix(), position.getLineNumber(),

View File

@ -4,6 +4,8 @@ public abstract class LispException extends RuntimeException {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public abstract int getSeverity(); public int getSeverity() {
return 0;
}
} }

View File

@ -0,0 +1,112 @@
package eval.argument;
import java.text.MessageFormat;
import error.LispException;
import eval.LENGTH;
import sexpression.*;
public class ArgumentValidator {
private Class<? extends SExpression> argumentType;
private String functionName;
private Integer maximumNumberOfArguments;
private Integer minimumNumberOfArguments;
public ArgumentValidator(String functionName) {
this.argumentType = SExpression.class;
this.functionName = functionName;
this.minimumNumberOfArguments = null;
this.maximumNumberOfArguments = null;
}
public void setArgumentType(Class<? extends SExpression> argumentType) {
this.argumentType = argumentType;
}
public void setMaximumNumberOfArguments(int maximumNumberOfArguments) {
this.maximumNumberOfArguments = maximumNumberOfArguments;
}
public void setMinimumNumberOfArguments(int minimumNumberOfArguments) {
this.minimumNumberOfArguments = minimumNumberOfArguments;
}
public void setExactNumberOfArguments(int exactNumberOfArguments) {
this.minimumNumberOfArguments = exactNumberOfArguments;
this.maximumNumberOfArguments = exactNumberOfArguments;
}
public void validate(Cons argumentList) {
if (containsTooFewArguments(argumentList))
throw new TooFewArgumentsException(functionName, argumentList);
else if (containsTooManyArguments(argumentList))
throw new TooManyArgumentsException(functionName, argumentList);
else if (!isExpectedArgumentType(argumentList.getCar()))
throw new BadArgumentTypeException(functionName, argumentList.getCar());
}
private boolean containsTooFewArguments(Cons argumentList) {
return (minimumNumberOfArguments != null) && (LENGTH.getLength(argumentList) < minimumNumberOfArguments);
}
private boolean containsTooManyArguments(Cons argumentList) {
return (maximumNumberOfArguments != null) && (LENGTH.getLength(argumentList) > maximumNumberOfArguments);
}
private boolean isExpectedArgumentType(SExpression firstArgument) {
return argumentType.isInstance(firstArgument);
}
public static class TooFewArgumentsException extends LispException {
private static final long serialVersionUID = 1L;
private String functionName;
private Cons originalSExpression;
public TooFewArgumentsException(String functionName, Cons argumentList) {
this.functionName = functionName;
this.originalSExpression = new Cons(new Symbol(this.functionName), argumentList);
}
@Override
public String getMessage() {
return MessageFormat.format("too few arguments given to {0}: {1}", functionName, originalSExpression);
}
}
public static class TooManyArgumentsException extends LispException {
private static final long serialVersionUID = 1L;
private String functionName;
private Cons originalSExpression;
public TooManyArgumentsException(String functionName, Cons argumentList) {
this.functionName = functionName;
this.originalSExpression = new Cons(new Symbol(this.functionName), argumentList);
}
@Override
public String getMessage() {
return MessageFormat.format("too many arguments given to {0}: {1}", functionName, originalSExpression);
}
}
public static class BadArgumentTypeException extends LispException {
private static final long serialVersionUID = 1L;
private String functionName;
private String argument;
public BadArgumentTypeException(String functionName, SExpression argument) {
this.functionName = functionName;
this.argument = argument.toString();
}
@Override
public String getMessage() {
return MessageFormat.format("{0}: {1} is not the expected type", functionName, argument);
}
}
}

View File

@ -9,8 +9,8 @@ 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, ErrorManager errorManager, PrintStream outputStream) { public InteractiveLispInterpreter(InputStream inputStream, PrintStream outputStream, ErrorManager errorManager) {
super(inputStream, errorManager, outputStream); super(inputStream, outputStream, errorManager);
} }
protected void printGreeting() { protected void printGreeting() {

View File

@ -17,7 +17,7 @@ public class LispInterpreter {
private ErrorManager errorManager; private ErrorManager errorManager;
protected PrintStream outputStream; protected PrintStream outputStream;
public LispInterpreter(InputStream inputStream, ErrorManager errorManager, PrintStream outputStream) { public LispInterpreter(InputStream inputStream, PrintStream outputStream, ErrorManager errorManager) {
this.errorManager = errorManager; this.errorManager = errorManager;
this.parser = new LispParser(inputStream, inputStream.toString()); this.parser = new LispParser(inputStream, inputStream.toString());
this.outputStream = outputStream; this.outputStream = outputStream;
@ -63,11 +63,6 @@ public class LispInterpreter {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override
public int getSeverity() {
return 0;
}
@Override @Override
public String getMessage() { public String getMessage() {
return e.getMessage(); return e.getMessage();

View File

@ -11,30 +11,22 @@ public class LispMain {
public static void main(String[] args) { public static void main(String[] args) {
LispInterpreter interpreter = null; LispInterpreter interpreter = null;
ErrorManager errorManager = new ErrorManager(new TerminateInterpreter(), System.err::print); ErrorManager errorManager = new ErrorManager(() -> System.exit(1), System.err::print);
if (args.length > 0) { if (args.length > 0) {
String fileName = args[0]; String fileName = args[0];
try { try {
interpreter = new LispInterpreter(new FileInputStream(fileName), errorManager, System.out); interpreter = new LispInterpreter(new FileInputStream(fileName), System.out, errorManager);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
errorManager.generateError(new LispFileNotFoundException(e)); errorManager.generateError(new LispFileNotFoundException(e));
} }
} else } else
interpreter = new InteractiveLispInterpreter(System.in, errorManager, System.out); interpreter = new InteractiveLispInterpreter(System.in, System.out, errorManager);
interpreter.interpret(); interpreter.interpret();
} }
private static class TerminateInterpreter implements Runnable {
@Override
public void run() {
System.exit(1);
}
}
public static class LispFileNotFoundException extends LispException { public static class LispFileNotFoundException extends LispException {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@ -41,11 +41,6 @@ public class LispNumber extends Atom {
this.text = text; this.text = text;
} }
@Override
public int getSeverity() {
return 0;
}
@Override @Override
public String getMessage() { public String getMessage() {
return MessageFormat.format("{0} is not a valid integer", text); return MessageFormat.format("{0} is not a valid integer", text);

View File

@ -5,7 +5,6 @@ import static org.junit.Assert.assertTrue;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -18,23 +17,7 @@ public class ErrorManagerTester {
private Set<String> indicatorSet; private Set<String> indicatorSet;
private ErrorManager createErrorManagerWithIndicators() { private ErrorManager createErrorManagerWithIndicators() {
Runnable terminationFunction = new Runnable() { return new ErrorManager(() -> indicatorSet.add(TERMINATED), (String) -> indicatorSet.add(MESSAGE));
@Override
public void run() {
indicatorSet.add(TERMINATED);
}
};
Consumer<String> outputFunction = new Consumer<String>() {
@Override
public void accept(String t) {
indicatorSet.add(MESSAGE);
}
};
return new ErrorManager(terminationFunction, outputFunction);
} }
private LispException createLispException(int severity) { private LispException createLispException(int severity) {

View File

@ -0,0 +1,131 @@
package eval.argument;
import static org.junit.Assert.*;
import org.junit.*;
import error.ErrorManager;
import eval.argument.ArgumentValidator.*;
import sexpression.*;
public class ArgumentValidatorTester {
private ArgumentValidator validator;
private Cons makeArgumentListOfSize(int size) {
Cons argumentList = Nil.getUniqueInstance();
for (int i = 0; i < size; i++)
argumentList = new Cons(Nil.getUniqueInstance(), argumentList);
return argumentList;
}
@Before
public void setUp() throws Exception {
validator = new ArgumentValidator("TEST");
}
@Test
public void noConstraints_DoesNotThrowExceptionWithNoArguments() {
validator.validate(makeArgumentListOfSize(0));
}
@Test
public void noConstraints_DoesNotThrowExceptionWithOneArgument() {
validator.validate(makeArgumentListOfSize(1));
}
@Test
public void noConstraints_DoesNotThrowExceptionWithManyArguments() {
validator.validate(makeArgumentListOfSize(20));
}
@Test(expected = TooFewArgumentsException.class)
public void tooFewArgumentsWithMinimumSet_ThrowsException() {
validator.setMinimumNumberOfArguments(1);
validator.validate(makeArgumentListOfSize(0));
}
@Test(expected = TooManyArgumentsException.class)
public void tooManyArgumentsWithMaximumSet_ThrowsException() {
validator.setMaximumNumberOfArguments(1);
validator.validate(makeArgumentListOfSize(2));
}
@Test
public void exactNumberOfArguments_DoesNotThrowException() {
validator.setExactNumberOfArguments(5);
validator.validate(makeArgumentListOfSize(5));
}
@Test(expected = TooFewArgumentsException.class)
public void tooFewArgumentsWithExactSet_ThrowsException() {
validator.setExactNumberOfArguments(3);
validator.validate(makeArgumentListOfSize(2));
}
@Test(expected = TooManyArgumentsException.class)
public void tooManyArgumentsWithExactSet_ThrowsException() {
validator.setExactNumberOfArguments(3);
validator.validate(makeArgumentListOfSize(4));
}
@Test
public void tooManyArgumentsException_HasCorrectSeverity() {
TooManyArgumentsException e = new TooManyArgumentsException("TEST", Nil.getUniqueInstance());
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
}
@Test
public void tooManyArgumentsException_HasMessageText() {
TooManyArgumentsException e = new TooManyArgumentsException("TEST", Nil.getUniqueInstance());
assertNotNull(e.getMessage());
assertTrue(e.getMessage().length() > 0);
}
@Test
public void tooFewArgumentsException_HasCorrectSeverity() {
TooFewArgumentsException e = new TooFewArgumentsException("TEST", Nil.getUniqueInstance());
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
}
@Test
public void tooFewArgumentsException_HasMessageText() {
TooFewArgumentsException e = new TooFewArgumentsException("TEST", Nil.getUniqueInstance());
assertNotNull(e.getMessage());
assertTrue(e.getMessage().length() > 0);
}
@Test
public void BadArgumentTypeException_HasCorrectSeverity() {
BadArgumentTypeException e = new BadArgumentTypeException("TEST", Nil.getUniqueInstance());
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
}
@Test
public void BadArgumentTypeException_HasMessageText() {
BadArgumentTypeException e = new BadArgumentTypeException("TEST", Nil.getUniqueInstance());
assertNotNull(e.getMessage());
assertTrue(e.getMessage().length() > 0);
}
@Test
public void correctArgumentType_DoesNotThrowException() {
validator.setArgumentType(Nil.class);
validator.validate(makeArgumentListOfSize(1));
}
@Test(expected = BadArgumentTypeException.class)
public void badArgumentType_ThrowsException() {
validator.setArgumentType(LispString.class);
validator.validate(makeArgumentListOfSize(1));
}
}