Added unit tests and refactored the let form
Wrote a lisp program to calculate the number of bi-weekly paydays in a year
This commit is contained in:
		
							parent
							
								
									db2817f7be
								
							
						
					
					
						commit
						d7ca5d09da
					
				
							
								
								
									
										11
									
								
								lisp/paydays.lisp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								lisp/paydays.lisp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					(let ((*first-payday-day* 6)
 | 
				
			||||||
 | 
					      (*leap-year* 0)
 | 
				
			||||||
 | 
					      (*days-in-year* 365)
 | 
				
			||||||
 | 
					      (*two-weeks* 14))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (+ 1
 | 
				
			||||||
 | 
					     (/ (- (+ *days-in-year*
 | 
				
			||||||
 | 
					              *leap-year*)
 | 
				
			||||||
 | 
					           *first-payday-day*)
 | 
				
			||||||
 | 
					        *two-weeks*))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@ -7,93 +7,81 @@ import table.*;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public class LET extends LispFunction {
 | 
					public class LET extends LispFunction {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ArgumentValidator argumentValidator;
 | 
				
			||||||
    private ExecutionContext executionContext;
 | 
					    private ExecutionContext executionContext;
 | 
				
			||||||
 | 
					    private SymbolTable localScope;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public LET() {
 | 
					    public LET() {
 | 
				
			||||||
 | 
					        this.argumentValidator = new ArgumentValidator("LET");
 | 
				
			||||||
 | 
					        this.argumentValidator.setMinimumNumberOfArguments(1);
 | 
				
			||||||
 | 
					        this.argumentValidator.setFirstArgumentExpectedType(Cons.class);
 | 
				
			||||||
        this.executionContext = ExecutionContext.getInstance();
 | 
					        this.executionContext = ExecutionContext.getInstance();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public SExpression call(Cons argList) {
 | 
					    public SExpression call(Cons argumentList) {
 | 
				
			||||||
        // make sure we have received at least one argument
 | 
					        argumentValidator.validate(argumentList);
 | 
				
			||||||
        if (argList.nullp()) {
 | 
					        Cons variableDefinitions = (Cons) argumentList.getCar();
 | 
				
			||||||
            throw new RuntimeException("too few arguments given to LET");
 | 
					        Cons body = (Cons) argumentList.getCdr();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return evaluateInScope(variableDefinitions, body);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // create a new symbol table on top of the current environment to add
 | 
					    private SExpression evaluateInScope(Cons variableDefinitions, Cons body) {
 | 
				
			||||||
        // all the local variables to
 | 
					        createLocalScope(variableDefinitions);
 | 
				
			||||||
        SymbolTable environment = new SymbolTable(executionContext.getScope());
 | 
					        SExpression lastEvaluation = evaluateBody(body);
 | 
				
			||||||
 | 
					        restorePreviousScope();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        SExpression car = argList.getCar();
 | 
					        return lastEvaluation;
 | 
				
			||||||
        Cons cdr = (Cons) argList.getCdr();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        addVariablesToTable(environment, car);
 | 
					 | 
				
			||||||
        executionContext.setScope(environment);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        SExpression retval = Nil.getInstance();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // evaluate all S-expression in the body
 | 
					 | 
				
			||||||
        while (cdr.consp()) {
 | 
					 | 
				
			||||||
            retval = EVAL.eval(cdr.getCar());
 | 
					 | 
				
			||||||
            cdr = (Cons) cdr.getCdr();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // restore the environment to its original value
 | 
					    private SymbolTable createLocalScope(Cons variableDefinitions) {
 | 
				
			||||||
        executionContext.setScope(environment.getParent());
 | 
					        localScope = new SymbolTable(executionContext.getScope());
 | 
				
			||||||
 | 
					        addVariablesToScope(localScope, variableDefinitions);
 | 
				
			||||||
 | 
					        executionContext.setScope(localScope);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return retval;
 | 
					        return localScope;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Add a list of variables and their values to the specified symbol table.
 | 
					    private void addVariablesToScope(SymbolTable scope, Cons variableDefinitions) {
 | 
				
			||||||
    //
 | 
					        validateAllPairsAreLists(variableDefinitions);
 | 
				
			||||||
    // Parameters: environment - the symbol table to add the variables and
 | 
					
 | 
				
			||||||
    // their values to (must not be null)
 | 
					        for (; variableDefinitions.consp(); variableDefinitions = (Cons) variableDefinitions.getCdr()) {
 | 
				
			||||||
    // vars - a list of variable/value pairs (must be either a
 | 
					            Cons symbolValuePair = (Cons) variableDefinitions.getCar();
 | 
				
			||||||
    // proper list of pairs or NIL)
 | 
					
 | 
				
			||||||
    // Throws: RuntimeException - Indicates that 'vars' is not a proper list or
 | 
					            validatePair(symbolValuePair);
 | 
				
			||||||
    // that it contains an member that is not a
 | 
					
 | 
				
			||||||
    // variable/value pair.
 | 
					            Cons restOfPair = (Cons) symbolValuePair.getCdr();
 | 
				
			||||||
    // Precondition: 'environment' and 'vars' must not be null.
 | 
					            SExpression symbol = symbolValuePair.getCar();
 | 
				
			||||||
    // Postcondition: All of the variables in 'vars' have been placed into
 | 
					            SExpression value = restOfPair.getCar();
 | 
				
			||||||
    // 'environment' with their values.
 | 
					
 | 
				
			||||||
    private void addVariablesToTable(SymbolTable environment, SExpression vars) {
 | 
					            scope.put(symbol.toString(), EVAL.eval(value));
 | 
				
			||||||
        // makes sure the list of variable/value pairs is a list
 | 
					        }
 | 
				
			||||||
        if (!vars.listp()) {
 | 
					 | 
				
			||||||
            throw new RuntimeException("LET: " + vars + " is not a properly formatted" + " variable/value pair list");
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // add all variables in 'vars' to 'environment'
 | 
					    private void validateAllPairsAreLists(Cons variableDefinitions) {
 | 
				
			||||||
        while (vars.consp()) {
 | 
					        ArgumentValidator variableDefinitionListValidator = new ArgumentValidator("LET|argumentList|");
 | 
				
			||||||
            Cons varList = (Cons) vars;
 | 
					        variableDefinitionListValidator.setEveryArgumentExpectedType(Cons.class);
 | 
				
			||||||
            SExpression varListCar = varList.getCar();
 | 
					        variableDefinitionListValidator.validate(variableDefinitions);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            // make sure this variable/value pair is a list
 | 
					 | 
				
			||||||
            if (!varListCar.consp()) {
 | 
					 | 
				
			||||||
                throw new RuntimeException("LET: " + varListCar + " is not a properly formatted"
 | 
					 | 
				
			||||||
                                           + " variable/value pair");
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Cons varSpec = (Cons) varListCar;
 | 
					    private void validatePair(Cons symbolValuePair) {
 | 
				
			||||||
            SExpression symbol = varSpec.getCar();
 | 
					        ArgumentValidator pairValidator = new ArgumentValidator("LET|pair|");
 | 
				
			||||||
            SExpression varSpecCdr = varSpec.getCdr();
 | 
					        pairValidator.setExactNumberOfArguments(2);
 | 
				
			||||||
 | 
					        pairValidator.setFirstArgumentExpectedType(Symbol.class);
 | 
				
			||||||
            // make sure this variable pair has a value associated with it
 | 
					        pairValidator.validate((Cons) symbolValuePair);
 | 
				
			||||||
            if (!varSpecCdr.consp()) {
 | 
					 | 
				
			||||||
                throw new RuntimeException("LET: illegal variable " + "specification " + varSpec);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Cons varValue = (Cons) varSpecCdr;
 | 
					    private SExpression evaluateBody(Cons body) {
 | 
				
			||||||
            SExpression value = varValue.getCar();
 | 
					        SExpression lastEvaluation = Nil.getInstance();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // make sure there are no more members of this variable/value pair
 | 
					        for (; body.consp(); body = (Cons) body.getCdr())
 | 
				
			||||||
            // and that 'symbol' is actually a symbol
 | 
					            lastEvaluation = EVAL.eval(body.getCar());
 | 
				
			||||||
            if ((!varValue.getCdr().nullp()) || (!symbol.symbolp())) {
 | 
					
 | 
				
			||||||
                throw new RuntimeException("LET: illegal variable " + "specification " + varSpec);
 | 
					        return lastEvaluation;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            environment.put(symbol.toString(), value);
 | 
					    private void restorePreviousScope() {
 | 
				
			||||||
 | 
					        executionContext.setScope(localScope.getParent());
 | 
				
			||||||
            vars = varList.getCdr();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public boolean evaluateArguments() {
 | 
					    public boolean evaluateArguments() {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										141
									
								
								test/function/builtin/special/LETTester.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								test/function/builtin/special/LETTester.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,141 @@
 | 
				
			|||||||
 | 
					package function.builtin.special;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static testutil.TestUtilities.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.junit.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import function.ArgumentValidator.*;
 | 
				
			||||||
 | 
					import function.builtin.EVAL.UndefinedSymbolException;
 | 
				
			||||||
 | 
					import sexpression.*;
 | 
				
			||||||
 | 
					import table.ExecutionContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class LETTester {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ExecutionContext executionContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public LETTester() {
 | 
				
			||||||
 | 
					        this.executionContext = ExecutionContext.getInstance();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Before
 | 
				
			||||||
 | 
					    public void setUp() {
 | 
				
			||||||
 | 
					        executionContext.clearContext();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void simpleLet() {
 | 
				
			||||||
 | 
					        String input = "(let ((x 1)) x)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("1"), evaluateString(input));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void emptyLet_ReturnsNil() {
 | 
				
			||||||
 | 
					        String input = "(let ())";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(Nil.getInstance(), evaluateString(input));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void letWithSetf_DoesNotAlterGlobalVariable() {
 | 
				
			||||||
 | 
					        String before = "(setf x 22)";
 | 
				
			||||||
 | 
					        String input = "(let ((x 1)) x)";
 | 
				
			||||||
 | 
					        String after = "x";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("22"), evaluateString(before));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("1"), evaluateString(input));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("22"), evaluateString(after));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void letWithNestedSetf_DoesNotAlterGlobalVariable() {
 | 
				
			||||||
 | 
					        String before = "(setf x 22)";
 | 
				
			||||||
 | 
					        String input = "(let ((x 33)) (setf x 44) x)";
 | 
				
			||||||
 | 
					        String after = "x";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("22"), evaluateString(before));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("44"), evaluateString(input));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("22"), evaluateString(after));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void nestedLet() {
 | 
				
			||||||
 | 
					        String input = "(let ((x 1)) (let ((y (+ 1 x))) y))";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("2"), evaluateString(input));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void alterGlobalVariableFromLet() {
 | 
				
			||||||
 | 
					        String before = "(setf x 1)";
 | 
				
			||||||
 | 
					        String input = "(let ((y 1)) (setf x 2))";
 | 
				
			||||||
 | 
					        String after = "x";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("1"), evaluateString(before));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("2"), evaluateString(input));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("2"), evaluateString(after));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void accessGlobalVariableFromLet() {
 | 
				
			||||||
 | 
					        String before = "(setf x 1)";
 | 
				
			||||||
 | 
					        String input = "(let () x)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("1"), evaluateString(before));
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(new LispNumber("1"), evaluateString(input));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = UndefinedSymbolException.class)
 | 
				
			||||||
 | 
					    public void letDoesNotSetGlobalVariable() {
 | 
				
			||||||
 | 
					        String input = "(let ((x 1)) nil)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        evaluateString(input);
 | 
				
			||||||
 | 
					        evaluateString("x");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = BadArgumentTypeException.class)
 | 
				
			||||||
 | 
					    public void testLetWithNonList() {
 | 
				
			||||||
 | 
					        evaluateString("(let a)");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = BadArgumentTypeException.class)
 | 
				
			||||||
 | 
					    public void testLetWithNoPairs() {
 | 
				
			||||||
 | 
					        evaluateString("(let (a))");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = TooFewArgumentsException.class)
 | 
				
			||||||
 | 
					    public void testLetWithTooFewItemsInPair() {
 | 
				
			||||||
 | 
					        evaluateString("(let ((a)))");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = TooManyArgumentsException.class)
 | 
				
			||||||
 | 
					    public void testLetWithTooManyItemsInPair() {
 | 
				
			||||||
 | 
					        evaluateString("(let ((a b c)))");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = BadArgumentTypeException.class)
 | 
				
			||||||
 | 
					    public void testLetWithNonSymbolInPair() {
 | 
				
			||||||
 | 
					        evaluateString("(let ((1 b)))");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = TooFewArgumentsException.class)
 | 
				
			||||||
 | 
					    public void testLetWithTooFewArguments() {
 | 
				
			||||||
 | 
					        evaluateString("(let)");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = DottedArgumentListException.class)
 | 
				
			||||||
 | 
					    public void testLetWithDottedArgumentList() {
 | 
				
			||||||
 | 
					        evaluateString("(apply 'let (cons 'a 'b))");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = DottedArgumentListException.class)
 | 
				
			||||||
 | 
					    public void testLetWithDottedPairList() {
 | 
				
			||||||
 | 
					        evaluateString("(apply 'let (cons (cons 'a 'b) nil))");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = DottedArgumentListException.class)
 | 
				
			||||||
 | 
					    public void testLetWithDottedPair() {
 | 
				
			||||||
 | 
					        evaluateString("(apply 'let (cons (cons (cons 'a 'b) nil) nil))");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user