Added an argument validator and unit tests
This commit is contained in:
parent
5f2c3dc469
commit
fbd2b3207c
|
@ -13,11 +13,6 @@ public abstract class LineColumnException extends LispException {
|
|||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeverity() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return MessageFormat.format("{0} - line {1}, column {2}", getMessagePrefix(), position.getLineNumber(),
|
||||
|
|
|
@ -4,6 +4,8 @@ public abstract class LispException extends RuntimeException {
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public abstract int getSeverity();
|
||||
public int getSeverity() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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 PROMPT = "~ ";
|
||||
|
||||
public InteractiveLispInterpreter(InputStream inputStream, ErrorManager errorManager, PrintStream outputStream) {
|
||||
super(inputStream, errorManager, outputStream);
|
||||
public InteractiveLispInterpreter(InputStream inputStream, PrintStream outputStream, ErrorManager errorManager) {
|
||||
super(inputStream, outputStream, errorManager);
|
||||
}
|
||||
|
||||
protected void printGreeting() {
|
||||
|
|
|
@ -17,7 +17,7 @@ public class LispInterpreter {
|
|||
private ErrorManager errorManager;
|
||||
protected PrintStream outputStream;
|
||||
|
||||
public LispInterpreter(InputStream inputStream, ErrorManager errorManager, PrintStream outputStream) {
|
||||
public LispInterpreter(InputStream inputStream, PrintStream outputStream, ErrorManager errorManager) {
|
||||
this.errorManager = errorManager;
|
||||
this.parser = new LispParser(inputStream, inputStream.toString());
|
||||
this.outputStream = outputStream;
|
||||
|
@ -63,11 +63,6 @@ public class LispInterpreter {
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public int getSeverity() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return e.getMessage();
|
||||
|
|
|
@ -11,30 +11,22 @@ public class LispMain {
|
|||
|
||||
public static void main(String[] args) {
|
||||
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) {
|
||||
String fileName = args[0];
|
||||
|
||||
try {
|
||||
interpreter = new LispInterpreter(new FileInputStream(fileName), errorManager, System.out);
|
||||
interpreter = new LispInterpreter(new FileInputStream(fileName), System.out, errorManager);
|
||||
} catch (FileNotFoundException e) {
|
||||
errorManager.generateError(new LispFileNotFoundException(e));
|
||||
}
|
||||
} else
|
||||
interpreter = new InteractiveLispInterpreter(System.in, errorManager, System.out);
|
||||
interpreter = new InteractiveLispInterpreter(System.in, System.out, errorManager);
|
||||
|
||||
interpreter.interpret();
|
||||
}
|
||||
|
||||
private static class TerminateInterpreter implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static class LispFileNotFoundException extends LispException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
|
|
@ -41,11 +41,6 @@ public class LispNumber extends Atom {
|
|||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeverity() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return MessageFormat.format("{0} is not a valid integer", text);
|
||||
|
|
|
@ -5,7 +5,6 @@ import static org.junit.Assert.assertTrue;
|
|||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -18,23 +17,7 @@ public class ErrorManagerTester {
|
|||
private Set<String> indicatorSet;
|
||||
|
||||
private ErrorManager createErrorManagerWithIndicators() {
|
||||
Runnable terminationFunction = new Runnable() {
|
||||
|
||||
@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);
|
||||
return new ErrorManager(() -> indicatorSet.add(TERMINATED), (String) -> indicatorSet.add(MESSAGE));
|
||||
}
|
||||
|
||||
private LispException createLispException(int severity) {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue