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;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										112
									
								
								src/eval/argument/ArgumentValidator.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/eval/argument/ArgumentValidator.java
									
									
									
									
									
										Normal 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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										131
									
								
								test/eval/argument/ArgumentValidatorTester.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								test/eval/argument/ArgumentValidatorTester.java
									
									
									
									
									
										Normal 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));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user