Implement RECUR
This commit is contained in:
		
							parent
							
								
									cd551c5a1c
								
							
						
					
					
						commit
						b4229c6ac1
					
				@ -2,11 +2,14 @@ package function;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import static function.builtin.EVAL.eval;
 | 
					import static function.builtin.EVAL.eval;
 | 
				
			||||||
import static java.text.MessageFormat.format;
 | 
					import static java.text.MessageFormat.format;
 | 
				
			||||||
 | 
					import static recursion.tail.TailCalls.done;
 | 
				
			||||||
import static sexpression.Nil.NIL;
 | 
					import static sexpression.Nil.NIL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import error.LispException;
 | 
					import error.LispException;
 | 
				
			||||||
 | 
					import recursion.tail.TailCall;
 | 
				
			||||||
 | 
					import recursion.tail.TailCalls;
 | 
				
			||||||
import sexpression.Cons;
 | 
					import sexpression.Cons;
 | 
				
			||||||
import sexpression.SExpression;
 | 
					import sexpression.SExpression;
 | 
				
			||||||
import sexpression.Symbol;
 | 
					import sexpression.Symbol;
 | 
				
			||||||
@ -25,7 +28,7 @@ public class UserDefinedFunction extends LispFunction {
 | 
				
			|||||||
    private ArrayList<String> formalParameters;
 | 
					    private ArrayList<String> formalParameters;
 | 
				
			||||||
    private ArgumentValidator argumentValidator;
 | 
					    private ArgumentValidator argumentValidator;
 | 
				
			||||||
    private String keywordRestParameter;
 | 
					    private String keywordRestParameter;
 | 
				
			||||||
    private boolean isKeywordRest;
 | 
					    private boolean isKeywordRestPresent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public UserDefinedFunction(String name, Cons lambdaList, Cons body) {
 | 
					    public UserDefinedFunction(String name, Cons lambdaList, Cons body) {
 | 
				
			||||||
        this.name = name;
 | 
					        this.name = name;
 | 
				
			||||||
@ -34,7 +37,7 @@ public class UserDefinedFunction extends LispFunction {
 | 
				
			|||||||
        this.executionContext = ExecutionContext.getInstance();
 | 
					        this.executionContext = ExecutionContext.getInstance();
 | 
				
			||||||
        this.functionScope = executionContext.getScope();
 | 
					        this.functionScope = executionContext.getScope();
 | 
				
			||||||
        this.keywordRestParameter = null;
 | 
					        this.keywordRestParameter = null;
 | 
				
			||||||
        this.isKeywordRest = false;
 | 
					        this.isKeywordRestPresent = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        createFormalParameters(lambdaList);
 | 
					        createFormalParameters(lambdaList);
 | 
				
			||||||
        setupArgumentValidator();
 | 
					        setupArgumentValidator();
 | 
				
			||||||
@ -60,7 +63,7 @@ public class UserDefinedFunction extends LispFunction {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private Cons extractKeywordRestParameter(Cons lambdaList) {
 | 
					    private Cons extractKeywordRestParameter(Cons lambdaList) {
 | 
				
			||||||
        isKeywordRest = true;
 | 
					        isKeywordRestPresent = true;
 | 
				
			||||||
        lambdaList = advanceCons(lambdaList);
 | 
					        lambdaList = advanceCons(lambdaList);
 | 
				
			||||||
        keywordRestParameter = lambdaList.getFirst().toString();
 | 
					        keywordRestParameter = lambdaList.getFirst().toString();
 | 
				
			||||||
        lambdaList = advanceCons(lambdaList);
 | 
					        lambdaList = advanceCons(lambdaList);
 | 
				
			||||||
@ -78,7 +81,7 @@ public class UserDefinedFunction extends LispFunction {
 | 
				
			|||||||
    private void setupArgumentValidator() {
 | 
					    private void setupArgumentValidator() {
 | 
				
			||||||
        argumentValidator = new ArgumentValidator(this.name);
 | 
					        argumentValidator = new ArgumentValidator(this.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (isKeywordRest)
 | 
					        if (isKeywordRestPresent)
 | 
				
			||||||
            argumentValidator.setMinimumNumberOfArguments(this.formalParameters.size());
 | 
					            argumentValidator.setMinimumNumberOfArguments(this.formalParameters.size());
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
            argumentValidator.setExactNumberOfArguments(this.formalParameters.size());
 | 
					            argumentValidator.setExactNumberOfArguments(this.formalParameters.size());
 | 
				
			||||||
@ -86,9 +89,25 @@ public class UserDefinedFunction extends LispFunction {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public SExpression call(Cons argumentList) {
 | 
					    public SExpression call(Cons argumentList) {
 | 
				
			||||||
 | 
					        return callTailRecursive(argumentList).invoke();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private TailCall<SExpression> callTailRecursive(Cons argumentList) {
 | 
				
			||||||
        argumentValidator.validate(argumentList);
 | 
					        argumentValidator.validate(argumentList);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return evaluateInFunctionScope(argumentList);
 | 
					        // if recur
 | 
				
			||||||
 | 
					        // clear recur indicator
 | 
				
			||||||
 | 
					        // validate function list
 | 
				
			||||||
 | 
					        // do not evaluate arguments - will have been done already if necessary
 | 
				
			||||||
 | 
					        // tailCall(evaluatedArguments)
 | 
				
			||||||
 | 
					        SExpression result = evaluateInFunctionScope(argumentList);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (executionContext.isRecur()) {
 | 
				
			||||||
 | 
					            executionContext.clearRecur();
 | 
				
			||||||
 | 
					            return TailCalls.tailCall(() -> callTailRecursive((Cons) result));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return done(result);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private SExpression evaluateInFunctionScope(Cons argumentList) {
 | 
					    private SExpression evaluateInFunctionScope(Cons argumentList) {
 | 
				
			||||||
@ -111,7 +130,7 @@ public class UserDefinedFunction extends LispFunction {
 | 
				
			|||||||
            argumentList = (Cons) argumentList.getRest();
 | 
					            argumentList = (Cons) argumentList.getRest();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (isKeywordRest)
 | 
					        if (isKeywordRestPresent)
 | 
				
			||||||
            executionScope.put(keywordRestParameter, argumentList);
 | 
					            executionScope.put(keywordRestParameter, argumentList);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return executionScope;
 | 
					        return executionScope;
 | 
				
			||||||
@ -120,7 +139,21 @@ public class UserDefinedFunction extends LispFunction {
 | 
				
			|||||||
    private SExpression evaluateBody() {
 | 
					    private SExpression evaluateBody() {
 | 
				
			||||||
        SExpression lastEvaluation = NIL;
 | 
					        SExpression lastEvaluation = NIL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (Cons expression = body; expression.isCons(); expression = (Cons) expression.getRest())
 | 
					        // recur sets indicator in execution context
 | 
				
			||||||
 | 
					        // eval can't be called when recur indicator is set - or error occurs (and recur indicator
 | 
				
			||||||
 | 
					        // is cleared)
 | 
				
			||||||
 | 
					        // recur checks to see if in function call, if not an error occurs (stack of objects
 | 
				
			||||||
 | 
					        // (function call types) in execution context)
 | 
				
			||||||
 | 
					        // if not, set recur indicator somewhere, (can't be in symbol table because LET creates
 | 
				
			||||||
 | 
					        // scopes as well)
 | 
				
			||||||
 | 
					        //
 | 
				
			||||||
 | 
					        // on exception - clear function call counter and recur indicator in main loop
 | 
				
			||||||
 | 
					        // recur evaluates its arguments (or not, according to function type) in its scope, then
 | 
				
			||||||
 | 
					        // returns the list
 | 
				
			||||||
 | 
					        //
 | 
				
			||||||
 | 
					        // eval adds function call type to execution context stack of function calls when user
 | 
				
			||||||
 | 
					        // defined function encountered
 | 
				
			||||||
 | 
					        for (Cons expression = body; expression.isCons() /* and not recur indicator */; expression = (Cons) expression.getRest())
 | 
				
			||||||
            lastEvaluation = eval(expression.getFirst());
 | 
					            lastEvaluation = eval(expression.getFirst());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return lastEvaluation;
 | 
					        return lastEvaluation;
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@ import error.LispException;
 | 
				
			|||||||
import function.ArgumentValidator;
 | 
					import function.ArgumentValidator;
 | 
				
			||||||
import function.FunctionNames;
 | 
					import function.FunctionNames;
 | 
				
			||||||
import function.LispFunction;
 | 
					import function.LispFunction;
 | 
				
			||||||
 | 
					import function.UserDefinedFunction;
 | 
				
			||||||
import sexpression.BackquoteExpression;
 | 
					import sexpression.BackquoteExpression;
 | 
				
			||||||
import sexpression.Cons;
 | 
					import sexpression.Cons;
 | 
				
			||||||
import sexpression.LambdaExpression;
 | 
					import sexpression.LambdaExpression;
 | 
				
			||||||
@ -28,6 +29,10 @@ public class EVAL extends LispFunction {
 | 
				
			|||||||
        return lookupFunction("EVAL").call(argumentList);
 | 
					        return lookupFunction("EVAL").call(argumentList);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Cons evalArgumentList(Cons argumentList) {
 | 
				
			||||||
 | 
					        return ((EVAL) lookupFunction("EVAL")).evaluateArgumentList(argumentList);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static LispFunction lookupFunctionOrLambda(SExpression functionExpression) {
 | 
					    public static LispFunction lookupFunctionOrLambda(SExpression functionExpression) {
 | 
				
			||||||
        LispFunction function = lookupFunction(functionExpression.toString());
 | 
					        LispFunction function = lookupFunction(functionExpression.toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -58,10 +63,12 @@ public class EVAL extends LispFunction {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private ArgumentValidator argumentValidator;
 | 
					    private ArgumentValidator argumentValidator;
 | 
				
			||||||
 | 
					    private ExecutionContext executionContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public EVAL(String name) {
 | 
					    public EVAL(String name) {
 | 
				
			||||||
        this.argumentValidator = new ArgumentValidator(name);
 | 
					        this.argumentValidator = new ArgumentValidator(name);
 | 
				
			||||||
        this.argumentValidator.setExactNumberOfArguments(1);
 | 
					        this.argumentValidator.setExactNumberOfArguments(1);
 | 
				
			||||||
 | 
					        this.executionContext = ExecutionContext.getInstance();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@ -109,6 +116,9 @@ public class EVAL extends LispFunction {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private SExpression callFunction(LispFunction function, Cons argumentList) {
 | 
					    private SExpression callFunction(LispFunction function, Cons argumentList) {
 | 
				
			||||||
 | 
					        if (function instanceof UserDefinedFunction)
 | 
				
			||||||
 | 
					            executionContext.pushFunctionCall(function);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (function.isArgumentListEvaluated())
 | 
					        if (function.isArgumentListEvaluated())
 | 
				
			||||||
            argumentList = evaluateArgumentList(argumentList);
 | 
					            argumentList = evaluateArgumentList(argumentList);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -117,6 +127,9 @@ public class EVAL extends LispFunction {
 | 
				
			|||||||
        if (function.isMacro())
 | 
					        if (function.isMacro())
 | 
				
			||||||
            result = eval(result);
 | 
					            result = eval(result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (function instanceof UserDefinedFunction)
 | 
				
			||||||
 | 
					            executionContext.popFunctionCall();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return result;
 | 
					        return result;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -45,7 +45,7 @@ public class SET extends LispFunction {
 | 
				
			|||||||
    private SymbolTable findScopeOfSymbol(SExpression symbol) {
 | 
					    private SymbolTable findScopeOfSymbol(SExpression symbol) {
 | 
				
			||||||
        SymbolTable table = executionContext.getScope();
 | 
					        SymbolTable table = executionContext.getScope();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        while (!isSymbolInTable(symbol, table) && !isGlobalTable(table))
 | 
					        while (!isSymbolInTable(symbol, table) && !table.isGlobal())
 | 
				
			||||||
            table = table.getParent();
 | 
					            table = table.getParent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return table;
 | 
					        return table;
 | 
				
			||||||
@ -55,8 +55,4 @@ public class SET extends LispFunction {
 | 
				
			|||||||
        return table.contains(symbol.toString());
 | 
					        return table.contains(symbol.toString());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private boolean isGlobalTable(SymbolTable table) {
 | 
					 | 
				
			||||||
        return table.getParent() == null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										54
									
								
								src/function/builtin/special/RECUR.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/function/builtin/special/RECUR.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					package function.builtin.special;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static function.builtin.EVAL.evalArgumentList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import error.LispException;
 | 
				
			||||||
 | 
					import function.ArgumentValidator;
 | 
				
			||||||
 | 
					import function.FunctionNames;
 | 
				
			||||||
 | 
					import function.LispFunction;
 | 
				
			||||||
 | 
					import function.LispSpecialFunction;
 | 
				
			||||||
 | 
					import sexpression.Cons;
 | 
				
			||||||
 | 
					import sexpression.SExpression;
 | 
				
			||||||
 | 
					import table.ExecutionContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@FunctionNames({ "RECUR" })
 | 
				
			||||||
 | 
					public class RECUR extends LispSpecialFunction {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ArgumentValidator argumentValidator;
 | 
				
			||||||
 | 
					    private ExecutionContext executionContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public RECUR(String name) {
 | 
				
			||||||
 | 
					        this.argumentValidator = new ArgumentValidator(name);
 | 
				
			||||||
 | 
					        this.executionContext = ExecutionContext.getInstance();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public SExpression call(Cons argumentList) {
 | 
				
			||||||
 | 
					        argumentValidator.validate(argumentList);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!executionContext.isInFunctionCall()) {
 | 
				
			||||||
 | 
					            throw new RecurOutsideOfFunctionException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LispFunction currentFunction = executionContext.getCurrentFunction();
 | 
				
			||||||
 | 
					        Cons recurArguments = argumentList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (currentFunction.isArgumentListEvaluated())
 | 
				
			||||||
 | 
					            recurArguments = evalArgumentList(argumentList);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        executionContext.setRecur();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return recurArguments;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static class RecurOutsideOfFunctionException extends LispException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public String getMessage() {
 | 
				
			||||||
 | 
					            return "recur called outide of function";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,5 +1,8 @@
 | 
				
			|||||||
package table;
 | 
					package table;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Stack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import function.LispFunction;
 | 
				
			||||||
import sexpression.SExpression;
 | 
					import sexpression.SExpression;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class ExecutionContext {
 | 
					public class ExecutionContext {
 | 
				
			||||||
@ -11,9 +14,13 @@ public class ExecutionContext {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private SymbolTable scope;
 | 
					    private SymbolTable scope;
 | 
				
			||||||
 | 
					    private Stack<LispFunction> functionCalls;
 | 
				
			||||||
 | 
					    private boolean recur;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private ExecutionContext() {
 | 
					    private ExecutionContext() {
 | 
				
			||||||
        this.scope = new SymbolTable();
 | 
					        this.scope = new SymbolTable();
 | 
				
			||||||
 | 
					        this.functionCalls = new Stack<>();
 | 
				
			||||||
 | 
					        this.clearRecur();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public SymbolTable getScope() {
 | 
					    public SymbolTable getScope() {
 | 
				
			||||||
@ -26,6 +33,8 @@ public class ExecutionContext {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public void clearContext() {
 | 
					    public void clearContext() {
 | 
				
			||||||
        this.scope = new SymbolTable();
 | 
					        this.scope = new SymbolTable();
 | 
				
			||||||
 | 
					        this.functionCalls = new Stack<>();
 | 
				
			||||||
 | 
					        this.clearRecur();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public SExpression lookupSymbolValue(String symbolName) {
 | 
					    public SExpression lookupSymbolValue(String symbolName) {
 | 
				
			||||||
@ -36,4 +45,32 @@ public class ExecutionContext {
 | 
				
			|||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void pushFunctionCall(LispFunction function) {
 | 
				
			||||||
 | 
					        functionCalls.push(function);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void popFunctionCall() {
 | 
				
			||||||
 | 
					        functionCalls.pop();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isInFunctionCall() {
 | 
				
			||||||
 | 
					        return !functionCalls.empty();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public LispFunction getCurrentFunction() {
 | 
				
			||||||
 | 
					        return functionCalls.peek();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isRecur() {
 | 
				
			||||||
 | 
					        return recur;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setRecur() {
 | 
				
			||||||
 | 
					        recur = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void clearRecur() {
 | 
				
			||||||
 | 
					        recur = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -50,6 +50,7 @@ import function.builtin.special.LET_STAR;
 | 
				
			|||||||
import function.builtin.special.OR;
 | 
					import function.builtin.special.OR;
 | 
				
			||||||
import function.builtin.special.PROGN;
 | 
					import function.builtin.special.PROGN;
 | 
				
			||||||
import function.builtin.special.QUOTE;
 | 
					import function.builtin.special.QUOTE;
 | 
				
			||||||
 | 
					import function.builtin.special.RECUR;
 | 
				
			||||||
import function.builtin.special.SETQ;
 | 
					import function.builtin.special.SETQ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class FunctionTable {
 | 
					public class FunctionTable {
 | 
				
			||||||
@ -94,6 +95,7 @@ public class FunctionTable {
 | 
				
			|||||||
        allBuiltIns.add(PRINT.class);
 | 
					        allBuiltIns.add(PRINT.class);
 | 
				
			||||||
        allBuiltIns.add(PROGN.class);
 | 
					        allBuiltIns.add(PROGN.class);
 | 
				
			||||||
        allBuiltIns.add(QUOTE.class);
 | 
					        allBuiltIns.add(QUOTE.class);
 | 
				
			||||||
 | 
					        allBuiltIns.add(RECUR.class);
 | 
				
			||||||
        allBuiltIns.add(REST.class);
 | 
					        allBuiltIns.add(REST.class);
 | 
				
			||||||
        allBuiltIns.add(SET.class);
 | 
					        allBuiltIns.add(SET.class);
 | 
				
			||||||
        allBuiltIns.add(SETQ.class);
 | 
					        allBuiltIns.add(SETQ.class);
 | 
				
			||||||
 | 
				
			|||||||
@ -34,4 +34,8 @@ public class SymbolTable {
 | 
				
			|||||||
        return parent;
 | 
					        return parent;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isGlobal() {
 | 
				
			||||||
 | 
					        return parent == null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										39
									
								
								test/function/builtin/special/RECURTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								test/function/builtin/special/RECURTest.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					package function.builtin.special;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static testutil.TestUtilities.assertSExpressionsMatch;
 | 
				
			||||||
 | 
					import static testutil.TestUtilities.evaluateString;
 | 
				
			||||||
 | 
					import static testutil.TestUtilities.parseString;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import function.ArgumentValidator.BadArgumentTypeException;
 | 
				
			||||||
 | 
					import function.builtin.special.RECUR.RecurOutsideOfFunctionException;
 | 
				
			||||||
 | 
					import testutil.SymbolAndFunctionCleaner;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class RECURTest extends SymbolAndFunctionCleaner {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = RecurOutsideOfFunctionException.class)
 | 
				
			||||||
 | 
					    public void recurOutsideOfFunction_ThrowsException() {
 | 
				
			||||||
 | 
					        evaluateString("(recur)");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = RecurOutsideOfFunctionException.class)
 | 
				
			||||||
 | 
					    public void recurOutsideOfFunction_AfterFunctionCall_ThrowsException() {
 | 
				
			||||||
 | 
					        evaluateString("(defun f (n) (if (= n  0) 'ZERO n))");
 | 
				
			||||||
 | 
					        evaluateString("(f 2)");
 | 
				
			||||||
 | 
					        evaluateString("(recur)");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void recurCallsCurrentFunction() {
 | 
				
			||||||
 | 
					        evaluateString("(defun tail-recursive (n) (if (= n  0) 'PASS (recur (- n 1))))");
 | 
				
			||||||
 | 
					        assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = BadArgumentTypeException.class)
 | 
				
			||||||
 | 
					    public void recurInSpecialFunction_DoesNotEvaluateArguments() {
 | 
				
			||||||
 | 
					        evaluateString("(define-special tail-recursive (n) (if (= n  0) 'PASS (recur (- n 1))))");
 | 
				
			||||||
 | 
					        evaluateString("(tail-recursive 900)");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user