Added unit tests and refactored setf
This commit is contained in:
		
							parent
							
								
									6ed24e85fe
								
							
						
					
					
						commit
						919644d9c0
					
				@ -20,7 +20,7 @@ public class UserDefinedFunction extends LispFunction {
 | 
				
			|||||||
        this.name = name;
 | 
					        this.name = name;
 | 
				
			||||||
        this.body = body;
 | 
					        this.body = body;
 | 
				
			||||||
        this.lambdaExpression = new Cons(new Symbol(name), new Cons(lambdaList, body));
 | 
					        this.lambdaExpression = new Cons(new Symbol(name), new Cons(lambdaList, body));
 | 
				
			||||||
        this.environment = SETF.getEnvironment();
 | 
					        this.environment = SETF.getSymbolTable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (this.formalParameters = new ArrayList<>(); lambdaList.consp(); lambdaList = (Cons) lambdaList.getCdr())
 | 
					        for (this.formalParameters = new ArrayList<>(); lambdaList.consp(); lambdaList = (Cons) lambdaList.getCdr())
 | 
				
			||||||
            this.formalParameters.add(lambdaList.getCar().toString());
 | 
					            this.formalParameters.add(lambdaList.getCar().toString());
 | 
				
			||||||
@ -45,11 +45,11 @@ public class UserDefinedFunction extends LispFunction {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // store the environment of the S-expression that called this function
 | 
					        // store the environment of the S-expression that called this function
 | 
				
			||||||
        // (the current environment)
 | 
					        // (the current environment)
 | 
				
			||||||
        SymbolTable currentEnvironment = SETF.getEnvironment();
 | 
					        SymbolTable currentEnvironment = SETF.getSymbolTable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // replace the current environment with the environment of this
 | 
					        // replace the current environment with the environment of this
 | 
				
			||||||
        // function
 | 
					        // function
 | 
				
			||||||
        SETF.setEnvironment(environment);
 | 
					        SETF.setSymbolTable(environment);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Cons currentSExpression = body;
 | 
					        Cons currentSExpression = body;
 | 
				
			||||||
        SExpression retval = null;
 | 
					        SExpression retval = null;
 | 
				
			||||||
@ -62,7 +62,7 @@ public class UserDefinedFunction extends LispFunction {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // replace the environment of the S-expression that called this
 | 
					        // replace the environment of the S-expression that called this
 | 
				
			||||||
        // function
 | 
					        // function
 | 
				
			||||||
        SETF.setEnvironment(currentEnvironment);
 | 
					        SETF.setSymbolTable(currentEnvironment);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // remove the bindings of the arguments to the formal parameter names
 | 
					        // remove the bindings of the arguments to the formal parameter names
 | 
				
			||||||
        // in the environment of this function
 | 
					        // in the environment of this function
 | 
				
			||||||
 | 
				
			|||||||
@ -48,7 +48,7 @@ public class EVAL extends LispFunction {
 | 
				
			|||||||
            return new Symbol(symbolName);
 | 
					            return new Symbol(symbolName);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return SETF.lookup(symbolName);
 | 
					        return SETF.lookupSymbolValue(symbolName);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static boolean isDotted(Cons list) {
 | 
					    public static boolean isDotted(Cons list) {
 | 
				
			||||||
 | 
				
			|||||||
@ -18,13 +18,13 @@ public class LET extends LispFunction {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // create a new symbol table on top of the current environment to add
 | 
					        // create a new symbol table on top of the current environment to add
 | 
				
			||||||
        // all the local variables to
 | 
					        // all the local variables to
 | 
				
			||||||
        SymbolTable environment = new SymbolTable(SETF.getEnvironment());
 | 
					        SymbolTable environment = new SymbolTable(SETF.getSymbolTable());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        SExpression car = argList.getCar();
 | 
					        SExpression car = argList.getCar();
 | 
				
			||||||
        Cons cdr = (Cons) argList.getCdr();
 | 
					        Cons cdr = (Cons) argList.getCdr();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        addVariablesToTable(environment, car);
 | 
					        addVariablesToTable(environment, car);
 | 
				
			||||||
        SETF.setEnvironment(environment);
 | 
					        SETF.setSymbolTable(environment);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        SExpression retval = Nil.getInstance();
 | 
					        SExpression retval = Nil.getInstance();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -35,7 +35,7 @@ public class LET extends LispFunction {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // restore the environment to its original value
 | 
					        // restore the environment to its original value
 | 
				
			||||||
        SETF.setEnvironment(environment.getParent());
 | 
					        SETF.setSymbolTable(environment.getParent());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return retval;
 | 
					        return retval;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,108 +1,68 @@
 | 
				
			|||||||
package function.builtin.special;
 | 
					package function.builtin.special;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import function.*;
 | 
					import function.*;
 | 
				
			||||||
import function.builtin.*;
 | 
					import function.builtin.EVAL;
 | 
				
			||||||
import function.builtin.cons.LENGTH;
 | 
					 | 
				
			||||||
import sexpression.*;
 | 
					import sexpression.*;
 | 
				
			||||||
import table.SymbolTable;
 | 
					import table.SymbolTable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * <code>SETF</code> represents the SETF form in Lisp.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class SETF extends LispFunction {
 | 
					public class SETF extends LispFunction {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static SymbolTable environment = new SymbolTable();
 | 
					    private static SymbolTable symbolTable = new SymbolTable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    public static SExpression lookupSymbolValue(String symbolName) {
 | 
				
			||||||
     * Look up the value of a symbol using its name.
 | 
					        for (SymbolTable t = symbolTable; t != null; t = t.getParent())
 | 
				
			||||||
     *
 | 
					            if (t.contains(symbolName))
 | 
				
			||||||
     * @param symbolName
 | 
					                return t.get(symbolName);
 | 
				
			||||||
     *  the name of the symbol to look up
 | 
					 | 
				
			||||||
     * @return
 | 
					 | 
				
			||||||
     *  the value of <code>symbolName</code> if it has one; null otherwise
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public static SExpression lookup(String symbolName) {
 | 
					 | 
				
			||||||
        SymbolTable current = environment;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        while (current != null) {
 | 
					 | 
				
			||||||
            if (current.contains(symbolName)) {
 | 
					 | 
				
			||||||
                return current.get(symbolName);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            current = current.getParent();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    public static void setSymbolTable(SymbolTable newSymbolTable) {
 | 
				
			||||||
     * Set the current environment to the specified value.
 | 
					        symbolTable = newSymbolTable;
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param newEnvironment
 | 
					 | 
				
			||||||
     *  the value to set the environment to
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public static void setEnvironment(SymbolTable newEnvironment) {
 | 
					 | 
				
			||||||
        environment = newEnvironment;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    public static SymbolTable getSymbolTable() {
 | 
				
			||||||
     * Retrieve the current environment.
 | 
					        return symbolTable;
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return
 | 
					 | 
				
			||||||
     *  the current environment
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public static SymbolTable getEnvironment() {
 | 
					 | 
				
			||||||
        return environment;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // The number of arguments that SETF takes.
 | 
					    private ArgumentValidator argumentValidator;
 | 
				
			||||||
    private static final int NUM_ARGS = 2;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public SExpression call(Cons argList) {
 | 
					    public SETF() {
 | 
				
			||||||
        // retrieve the number of arguments passed to SETF
 | 
					        this.argumentValidator = new ArgumentValidator("SETF");
 | 
				
			||||||
        int argListLength = LENGTH.getLength(argList);
 | 
					        this.argumentValidator.setExactNumberOfArguments(2);
 | 
				
			||||||
 | 
					        this.argumentValidator.setFirstArgumentExpectedType(Symbol.class);
 | 
				
			||||||
        // make sure we have received the proper number of arguments
 | 
					 | 
				
			||||||
        if (argListLength != NUM_ARGS) {
 | 
					 | 
				
			||||||
            Cons originalSExpr = new Cons(new Symbol("SETF"), argList);
 | 
					 | 
				
			||||||
            String errMsg = "too " +
 | 
					 | 
				
			||||||
                            ((argListLength > NUM_ARGS) ? "many" : "few") +
 | 
					 | 
				
			||||||
                            " arguments given to SETF: " + originalSExpr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            throw new RuntimeException(errMsg);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        SExpression symbol = argList.getCar();
 | 
					    public SExpression call(Cons argumentList) {
 | 
				
			||||||
 | 
					        argumentValidator.validate(argumentList);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // make sure the first argument is a symbol
 | 
					        Cons cdr = (Cons) argumentList.getCdr();
 | 
				
			||||||
        if (! symbol.symbolp()) {
 | 
					        SExpression symbol = argumentList.getCar();
 | 
				
			||||||
            throw new RuntimeException("SETF: " + symbol + " is not a symbol");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Cons cdr = (Cons) argList.getCdr();
 | 
					 | 
				
			||||||
        SExpression value = EVAL.eval(cdr.getCar());
 | 
					        SExpression value = EVAL.eval(cdr.getCar());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        SymbolTable current = environment;
 | 
					        SymbolTable table = findTableForSymbol(symbol);
 | 
				
			||||||
 | 
					        table.put(symbol.toString(), value);
 | 
				
			||||||
        // set 'current' to the symbol table that contains 'symbol' or the
 | 
					 | 
				
			||||||
        // global symbol table if 'symbol' is not in the environment
 | 
					 | 
				
			||||||
        while ((! current.contains(symbol.toString())) &&
 | 
					 | 
				
			||||||
               (current.getParent() != null)) {
 | 
					 | 
				
			||||||
            current = current.getParent();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        current.put(symbol.toString(), value);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return value;
 | 
					        return value;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    private SymbolTable findTableForSymbol(SExpression symbol) {
 | 
				
			||||||
     * Determine if the arguments passed to this Lisp function should be
 | 
					        SymbolTable table = symbolTable;
 | 
				
			||||||
     * evaluated.
 | 
					
 | 
				
			||||||
     *
 | 
					        while (!isSymbolInTable(symbol, table) && !isGlobalTable(table))
 | 
				
			||||||
     * @return
 | 
					            table = table.getParent();
 | 
				
			||||||
     *  <code>false</code>
 | 
					
 | 
				
			||||||
     */
 | 
					        return table;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean isSymbolInTable(SExpression symbol, SymbolTable table) {
 | 
				
			||||||
 | 
					        return table.contains(symbol.toString());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean isGlobalTable(SymbolTable table) {
 | 
				
			||||||
 | 
					        return table.getParent() == null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public boolean evaluateArguments() {
 | 
					    public boolean evaluateArguments() {
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										86
									
								
								test/function/builtin/special/SETFTester.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								test/function/builtin/special/SETFTester.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					package function.builtin.special;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.junit.Assert.assertNull;
 | 
				
			||||||
 | 
					import static testutil.TestUtilities.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.junit.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import function.ArgumentValidator.*;
 | 
				
			||||||
 | 
					import function.builtin.EVAL.UndefinedSymbolException;
 | 
				
			||||||
 | 
					import sexpression.LispNumber;
 | 
				
			||||||
 | 
					import table.SymbolTable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class SETFTester {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Before
 | 
				
			||||||
 | 
					    public void setUp() {
 | 
				
			||||||
 | 
					        SETF.setSymbolTable(new SymbolTable());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void testSetf() {
 | 
				
			||||||
 | 
					        evaluateString("(setf a 23)");
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(evaluateString("23"), evaluateString("a"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void lookupDefinedSymbol() {
 | 
				
			||||||
 | 
					        evaluateString("(setf a 23)");
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(evaluateString("23"), SETF.lookupSymbolValue("A"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void lookupUndefinedSymbol() {
 | 
				
			||||||
 | 
					        assertNull(SETF.lookupSymbolValue("A"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void setfGlobalVariable() {
 | 
				
			||||||
 | 
					        evaluateString("(setf a 23)");
 | 
				
			||||||
 | 
					        SymbolTable global = SETF.getSymbolTable();
 | 
				
			||||||
 | 
					        SETF.setSymbolTable(new SymbolTable(global));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        evaluateString("(setf a 94)");
 | 
				
			||||||
 | 
					        SETF.setSymbolTable(global);
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(evaluateString("94"), evaluateString("a"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = UndefinedSymbolException.class)
 | 
				
			||||||
 | 
					    public void setfLocalVariableDefined_DoesNotSetGlobal() {
 | 
				
			||||||
 | 
					        SymbolTable global = SETF.getSymbolTable();
 | 
				
			||||||
 | 
					        SymbolTable local = new SymbolTable(global);
 | 
				
			||||||
 | 
					        local.put("A", new LispNumber("99"));
 | 
				
			||||||
 | 
					        SETF.setSymbolTable(local);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        evaluateString("(setf a 94)");
 | 
				
			||||||
 | 
					        SETF.setSymbolTable(global);
 | 
				
			||||||
 | 
					        evaluateString("a");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void setfLocalVariableUndefined_SetsGlobal() {
 | 
				
			||||||
 | 
					        SymbolTable global = SETF.getSymbolTable();
 | 
				
			||||||
 | 
					        SymbolTable local = new SymbolTable(global);
 | 
				
			||||||
 | 
					        SETF.setSymbolTable(local);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        evaluateString("(setf a 94)");
 | 
				
			||||||
 | 
					        SETF.setSymbolTable(global);
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(evaluateString("94"), evaluateString("a"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = BadArgumentTypeException.class)
 | 
				
			||||||
 | 
					    public void testSetfWithNonSymbol() {
 | 
				
			||||||
 | 
					        evaluateString("(setf 1 2)");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = TooFewArgumentsException.class)
 | 
				
			||||||
 | 
					    public void testSetfWithTooFewArguments() {
 | 
				
			||||||
 | 
					        evaluateString("(setf x)");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = TooManyArgumentsException.class)
 | 
				
			||||||
 | 
					    public void testSetfWithTooManyArguments() {
 | 
				
			||||||
 | 
					        evaluateString("(setf a b c)");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user