Added special functions (forms) and refactored internal s-expression predicates

This commit is contained in:
Mike Cifelli 2017-02-25 17:29:37 -05:00
parent b9466d2c5d
commit 4f4bc8f71a
36 changed files with 142 additions and 152 deletions

View File

@ -65,10 +65,10 @@ public class ArgumentValidator {
private void validateListNotDotted(Cons argumentList) {
SExpression next = argumentList.getRest();
for (Cons current = argumentList; next.consp(); next = current.getRest())
for (Cons current = argumentList; next.isCons(); next = current.getRest())
current = (Cons) next;
if (!next.nullp())
if (!next.isNull())
throw new DottedArgumentListException(functionName, argumentList);
}
@ -114,7 +114,7 @@ public class ArgumentValidator {
}
private boolean isFirstArgumentValid(Cons argumentList) {
return argumentList.nullp() || isExpectedFirstArgumentType(argumentList.getFirst());
return argumentList.isNull() || isExpectedFirstArgumentType(argumentList.getFirst());
}
private boolean isExpectedFirstArgumentType(SExpression firstArgument) {
@ -122,11 +122,11 @@ public class ArgumentValidator {
}
private boolean isDisallowedNil(SExpression argument) {
return !isNilAcceptable && argument.nullp();
return !isNilAcceptable && argument.isNull();
}
private void validateTrailingArguments(Cons argumentList) {
for (Cons cons = (Cons) argumentList.getRest(); !cons.nullp(); cons = (Cons) cons.getRest())
for (Cons cons = (Cons) argumentList.getRest(); !cons.isNull(); cons = (Cons) cons.getRest())
if (!isExpectedTrailingArgumentType(cons.getFirst()))
throw new BadArgumentTypeException(functionName, cons.getFirst(), trailingArgumentType);
}

View File

@ -6,11 +6,6 @@ public abstract class LispFunction {
public abstract SExpression call(Cons argList);
/**
* Determine if the arguments passed to this Lisp function should be evaluated. A subclass
* should override this method to return false if it does not want its arguments to be evaluated
* prior to being passed.
*/
public boolean evaluateArguments() {
return true;
}

View File

@ -0,0 +1,10 @@
package function;
public abstract class LispSpecialFunction extends LispFunction {
@Override
public boolean evaluateArguments() {
return false;
}
}

View File

@ -24,7 +24,7 @@ public class UserDefinedFunction extends LispFunction {
this.executionContext = ExecutionContext.getInstance();
this.functionScope = executionContext.getScope();
for (this.formalParameters = new ArrayList<>(); lambdaList.consp(); lambdaList = (Cons) lambdaList.getRest())
for (this.formalParameters = new ArrayList<>(); lambdaList.isCons(); lambdaList = (Cons) lambdaList.getRest())
this.formalParameters.add(lambdaList.getFirst().toString());
this.argumentValidator = new ArgumentValidator(this.name);
@ -38,10 +38,7 @@ public class UserDefinedFunction extends LispFunction {
SymbolTable callingScope = executionContext.getScope();
executionContext.setScope(functionScope);
SExpression lastEvaluation = Nil.getInstance();
for (Cons expression = body; expression.consp(); expression = (Cons) expression.getRest())
lastEvaluation = eval(expression.getFirst());
SExpression lastEvaluation = evaluateBody();
executionContext.setScope(callingScope);
releaseParameterValues();
@ -59,6 +56,15 @@ public class UserDefinedFunction extends LispFunction {
}
}
private SExpression evaluateBody() {
SExpression lastEvaluation = Nil.getInstance();
for (Cons expression = body; expression.isCons(); expression = (Cons) expression.getRest())
lastEvaluation = eval(expression.getFirst());
return lastEvaluation;
}
private void releaseParameterValues() {
functionScope = new SymbolTable(functionScope.getParent());
}

View File

@ -0,0 +1,16 @@
package function;
import sexpression.Cons;
public class UserDefinedSpecialFunction extends UserDefinedFunction {
public UserDefinedSpecialFunction(String name, Cons lambdaList, Cons body) {
super(name, lambdaList, body);
}
@Override
public boolean evaluateArguments() {
return false;
}
}

View File

@ -21,7 +21,7 @@ public class EVAL extends LispFunction {
}
private static LispFunction createLambdaFunction(SExpression lambdaExpression) {
if (lambdaExpression.functionp())
if (lambdaExpression.isFunction())
return ((LambdaExpression) lambdaExpression).getFunction();
else if (LAMBDA.isLambdaExpression(lambdaExpression))
return LAMBDA.createFunction((Cons) lambdaExpression);
@ -59,14 +59,14 @@ public class EVAL extends LispFunction {
SExpression argument = argumentList.getFirst();
if (argument.listp()) {
if (argument.consp())
if (argument.isList()) {
if (argument.isCons())
return evaluateList((Cons) argument);
return argument; // NIL
}
if (argument.symbolp()) {
if (argument.isSymbol()) {
SExpression symbolValue = lookupSymbol(argument.toString());
if (symbolValue != null)
@ -95,7 +95,7 @@ public class EVAL extends LispFunction {
}
private Cons evaluateArgList(Cons arguments) {
if (arguments.nullp())
if (arguments.isNull())
return Nil.getInstance();
SExpression first = eval(arguments.getFirst());

View File

@ -36,7 +36,7 @@ public class LENGTH extends LispFunction {
Cons list = (Cons) argumentList.getFirst();
Cons restOfList = LIST.makeList(list.getRest());
if (list.nullp())
if (list.isNull())
return new LispNumber(accumulatedLength);
return callTailRecursive(increment(accumulatedLength), restOfList);

View File

@ -22,7 +22,7 @@ public class LIST extends LispFunction {
}
private Cons callRecursive(Cons argumentList) {
if (argumentList.nullp())
if (argumentList.isNull())
return Nil.getInstance();
SExpression firstArgument = argumentList.getFirst();

View File

@ -20,7 +20,7 @@ class MathFunction {
SExpression firstArgument = argumentList.getFirst();
LispNumber number1 = (LispNumber) firstArgument;
if (remainingArguments.nullp())
if (remainingArguments.isNull())
return singleValueOperation.apply(number1);
SExpression secondArgument = remainingArguments.getFirst();
@ -28,7 +28,7 @@ class MathFunction {
LispNumber operationResult = multipleValueOperation.apply(number1, number2);
SExpression remainingNumbers = remainingArguments.getRest();
if (remainingNumbers.nullp())
if (remainingNumbers.isNull())
return operationResult;
return callTailRecursive(new Cons(operationResult, remainingNumbers));

View File

@ -16,7 +16,7 @@ public class ATOM extends LispFunction {
argumentValidator.validate(argumentList);
SExpression argument = argumentList.getFirst();
return argument.atomp() ? Symbol.T : Nil.getInstance();
return argument.isAtom() ? Symbol.T : Nil.getInstance();
}
}

View File

@ -30,7 +30,7 @@ public class EQ extends LispFunction {
}
private boolean isAtomPair(SExpression firstArgument, SExpression secondArgument) {
return firstArgument.atomp() && secondArgument.atomp();
return firstArgument.isAtom() && secondArgument.isAtom();
}
private SExpression atomEq(SExpression firstArgument, SExpression secondArgument) {

View File

@ -41,7 +41,7 @@ public class EQUAL extends LispFunction {
}
private boolean isListPair(SExpression firstArgument, SExpression secondArgument) {
return firstArgument.consp() && secondArgument.consp();
return firstArgument.isCons() && secondArgument.isCons();
}
private SExpression equal(SExpression firstArgument, SExpression secondArgument) {

View File

@ -22,7 +22,7 @@ public class EQUALSP extends LispFunction {
private SExpression callTailRecursive(Cons argumentList) {
Cons remainingArguments = (Cons) argumentList.getRest();
if (remainingArguments.nullp())
if (remainingArguments.isNull())
return Symbol.T;
SExpression firstArgument = argumentList.getFirst();

View File

@ -22,7 +22,7 @@ public class GREATERP extends LispFunction {
private SExpression callTailRecursive(Cons argumentList) {
Cons remainingArguments = (Cons) argumentList.getRest();
if (remainingArguments.nullp())
if (remainingArguments.isNull())
return Symbol.T;
SExpression firstArgument = argumentList.getFirst();

View File

@ -22,7 +22,7 @@ public class LESSP extends LispFunction {
private SExpression callTailRecursive(Cons argumentList) {
Cons remainingArguments = (Cons) argumentList.getRest();
if (remainingArguments.nullp())
if (remainingArguments.isNull())
return Symbol.T;
SExpression firstArgument = argumentList.getFirst();

View File

@ -15,7 +15,7 @@ public class LISTP extends LispFunction {
public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
return argumentList.getFirst().listp() ? Symbol.T : Nil.getInstance();
return argumentList.getFirst().isList() ? Symbol.T : Nil.getInstance();
}
}

View File

@ -15,7 +15,7 @@ public class NULL extends LispFunction {
public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
return argumentList.getFirst().nullp() ? Symbol.T : Nil.getInstance();
return argumentList.getFirst().isNull() ? Symbol.T : Nil.getInstance();
}
}

View File

@ -5,7 +5,7 @@ import static function.builtin.EVAL.eval;
import function.*;
import sexpression.*;
public class AND extends LispFunction {
public class AND extends LispSpecialFunction {
private ArgumentValidator argumentValidator;
@ -23,17 +23,13 @@ public class AND extends LispFunction {
SExpression currentValue = eval(argumentList.getFirst());
Cons remainingValues = (Cons) argumentList.getRest();
if (argumentList.nullp())
if (argumentList.isNull())
return lastValue;
if (currentValue.nullp())
if (currentValue.isNull())
return currentValue;
return callTailRecursive(remainingValues, currentValue);
}
public boolean evaluateArguments() {
return false;
}
}

View File

@ -5,7 +5,7 @@ import static function.builtin.EVAL.eval;
import function.*;
import sexpression.*;
public class COND extends LispFunction {
public class COND extends LispSpecialFunction {
private ArgumentValidator argumentValidator;
@ -22,7 +22,7 @@ public class COND extends LispFunction {
}
private SExpression callTailRecursive(Cons argumentList) {
if (argumentList.nullp())
if (argumentList.isNull())
return Nil.getInstance();
Cons clause = (Cons) argumentList.getFirst();
@ -42,7 +42,7 @@ public class COND extends LispFunction {
private SExpression evaluateResult(Cons clause, SExpression test) {
SExpression lastResultValue = test;
for (SExpression result = clause.getRest(); result.consp(); result = advanceCons(result))
for (SExpression result = clause.getRest(); result.isCons(); result = advanceCons(result))
lastResultValue = eval(getFirst(result));
return lastResultValue;
@ -56,8 +56,4 @@ public class COND extends LispFunction {
return ((Cons) knownCons).getFirst();
}
public boolean evaluateArguments() {
return false;
}
}

View File

@ -9,7 +9,7 @@ import function.builtin.cons.LIST;
import sexpression.*;
import table.FunctionTable;
public class DEFUN extends LispFunction {
public class DEFUN extends LispSpecialFunction {
private ArgumentValidator argumentValidator;
private ArgumentValidator lambdaListIsListValidator;
@ -52,10 +52,6 @@ public class DEFUN extends LispFunction {
return functionName;
}
public boolean evaluateArguments() {
return false;
}
public class RedefiningFunctionWarning extends LispWarning {
private static final long serialVersionUID = 1L;

View File

@ -5,7 +5,7 @@ import static function.builtin.EVAL.eval;
import function.*;
import sexpression.*;
public class IF extends LispFunction {
public class IF extends LispSpecialFunction {
private ArgumentValidator argumentValidator;
@ -26,7 +26,7 @@ public class IF extends LispFunction {
}
private boolean isNil(SExpression test) {
return test.nullp();
return test.isNull();
}
private SExpression getThenForm(Cons argumentList) {
@ -45,8 +45,4 @@ public class IF extends LispFunction {
return getRestOfList(expressions).getFirst();
}
public boolean evaluateArguments() {
return false;
}
}

View File

@ -5,10 +5,10 @@ import static function.builtin.cons.LIST.makeList;
import function.*;
import sexpression.*;
public class LAMBDA extends LispFunction {
public class LAMBDA extends LispSpecialFunction {
public static boolean isLambdaExpression(SExpression sexpr) {
if (sexpr.consp()) {
if (sexpr.isCons()) {
SExpression first = ((Cons) sexpr).getFirst();
return "LAMBDA".equals(first.toString());
@ -59,8 +59,4 @@ public class LAMBDA extends LispFunction {
return new Cons(new Symbol("LAMBDA"), argumentList);
}
public boolean evaluateArguments() {
return false;
}
}

View File

@ -6,7 +6,7 @@ import function.*;
import sexpression.*;
import table.*;
public class LET extends LispFunction {
public class LET extends LispSpecialFunction {
private ArgumentValidator argumentValidator;
private ArgumentValidator variableDefinitionListValidator;
@ -55,7 +55,7 @@ public class LET extends LispFunction {
private void addVariablesToScope(SymbolTable scope, Cons variableDefinitions) {
variableDefinitionListValidator.validate(variableDefinitions);
for (; variableDefinitions.consp(); variableDefinitions = (Cons) variableDefinitions.getRest())
for (; variableDefinitions.isCons(); variableDefinitions = (Cons) variableDefinitions.getRest())
addPairToScope((Cons) variableDefinitions.getFirst(), scope);
}
@ -72,7 +72,7 @@ public class LET extends LispFunction {
private SExpression evaluateBody(Cons body) {
SExpression lastEvaluation = Nil.getInstance();
for (; body.consp(); body = (Cons) body.getRest())
for (; body.isCons(); body = (Cons) body.getRest())
lastEvaluation = eval(body.getFirst());
return lastEvaluation;
@ -82,8 +82,4 @@ public class LET extends LispFunction {
executionContext.setScope(localScope.getParent());
}
public boolean evaluateArguments() {
return false;
}
}

View File

@ -5,7 +5,7 @@ import static function.builtin.EVAL.eval;
import function.*;
import sexpression.*;
public class OR extends LispFunction {
public class OR extends LispSpecialFunction {
private ArgumentValidator argumentValidator;
@ -23,14 +23,10 @@ public class OR extends LispFunction {
SExpression currentValue = eval(argumentList.getFirst());
Cons remainingValues = (Cons) argumentList.getRest();
if (remainingValues.nullp() || !currentValue.nullp())
if (remainingValues.isNull() || !currentValue.isNull())
return currentValue;
return callTailRecursive(remainingValues);
}
public boolean evaluateArguments() {
return false;
}
}

View File

@ -3,7 +3,7 @@ package function.builtin.special;
import function.*;
import sexpression.*;
public class QUOTE extends LispFunction {
public class QUOTE extends LispSpecialFunction {
private ArgumentValidator argumentValidator;
@ -18,8 +18,4 @@ public class QUOTE extends LispFunction {
return argumentList.getFirst();
}
public boolean evaluateArguments() {
return false;
}
}

View File

@ -6,7 +6,7 @@ import function.*;
import sexpression.*;
import table.*;
public class SETF extends LispFunction {
public class SETF extends LispSpecialFunction {
private ArgumentValidator argumentValidator;
private ExecutionContext executionContext;
@ -48,8 +48,4 @@ public class SETF extends LispFunction {
return table.getParent() == null;
}
public boolean evaluateArguments() {
return false;
}
}

View File

@ -9,7 +9,7 @@ public abstract class Atom extends SExpression {
this.text = text;
}
public boolean atomp() {
public boolean isAtom() {
return true;
}

View File

@ -27,7 +27,7 @@ public class Cons extends SExpression {
this.rest = rest;
}
public boolean consp() {
public boolean isCons() {
return true;
}
@ -36,9 +36,9 @@ public class Cons extends SExpression {
}
private String toStringAux() {
if (rest.nullp())
if (rest.isNull())
return (first.toString() + ")");
else if (rest.consp())
else if (rest.isCons())
return (first.toString() + " " + ((Cons) rest).toStringAux());
return (first.toString() + " . " + rest.toString() + ")");

View File

@ -13,7 +13,7 @@ public class LambdaExpression extends SExpression {
this.function = function;
}
public boolean functionp() {
public boolean isFunction() {
return true;
}

View File

@ -29,7 +29,7 @@ public class LispNumber extends Atom {
this.value = value;
}
public boolean numberp() {
public boolean isNumber() {
return true;
}

View File

@ -7,7 +7,7 @@ public class LispString extends Atom {
super(text);
}
public boolean stringp() {
public boolean isString() {
return true;
}

View File

@ -16,19 +16,19 @@ public class Nil extends Cons {
super.setRest(this);
}
public boolean nullp() {
public boolean isNull() {
return true;
}
public boolean atomp() {
public boolean isAtom() {
return true;
}
public boolean consp() {
public boolean isCons() {
return false;
}
public boolean symbolp() {
public boolean isSymbol() {
return true;
}

View File

@ -3,35 +3,35 @@ package sexpression;
@DisplayName("s-expression")
public abstract class SExpression {
public boolean nullp() {
public boolean isNull() {
return false;
}
public boolean atomp() {
public boolean isAtom() {
return false;
}
public boolean consp() {
public boolean isCons() {
return false;
}
public boolean listp() {
return (consp() || nullp());
public boolean isList() {
return (isCons() || isNull());
}
public boolean numberp() {
public boolean isNumber() {
return false;
}
public boolean symbolp() {
public boolean isSymbol() {
return false;
}
public boolean functionp() {
public boolean isFunction() {
return false;
}
public boolean stringp() {
public boolean isString() {
return false;
}

View File

@ -13,7 +13,7 @@ public class Symbol extends Atom {
super(text.toUpperCase());
}
public boolean symbolp() {
public boolean isSymbol() {
return true;
}

View File

@ -5,9 +5,6 @@ import java.util.function.Supplier;
import file.FilePosition;
import sexpression.*;
/**
* A token in Lisp.
*/
public abstract class Token {
private String text;
@ -26,10 +23,12 @@ public abstract class Token {
return position;
}
// sExpr ::= NUMBER | IDENTIFIER | STRING | QUOTE_MARK sExpr | LEFT_PAREN sExprTail
// s-expression ::= NUMBER | IDENTIFIER | STRING | QUOTE_MARK s-expression | LEFT_PAREN
// s-expression-tail
// s-expression-tail ::= RIGHT_PAREN | s-expression s-expression-tail
public abstract SExpression parseSExpression(Supplier<Token> getNextToken);
// sExprTail ::= RIGHT_PAREN | sExpr sExprTail
public SExpression parseSExpressionTail(Supplier<Token> getNextToken) {
SExpression first = parseSExpression(getNextToken);

View File

@ -7,61 +7,61 @@ import sexpression.*;
public final class TypeAssertions {
public static void assertList(SExpression sExpression) {
assertFalse(sExpression.atomp());
assertTrue(sExpression.consp());
assertFalse(sExpression.functionp());
assertTrue(sExpression.listp());
assertFalse(sExpression.nullp());
assertFalse(sExpression.numberp());
assertFalse(sExpression.stringp());
assertFalse(sExpression.symbolp());
assertFalse(sExpression.isAtom());
assertTrue(sExpression.isCons());
assertFalse(sExpression.isFunction());
assertTrue(sExpression.isList());
assertFalse(sExpression.isNull());
assertFalse(sExpression.isNumber());
assertFalse(sExpression.isString());
assertFalse(sExpression.isSymbol());
}
public static void assertNil(SExpression sExpression) {
assertEquals(Nil.getInstance(), sExpression);
assertTrue(sExpression.atomp());
assertFalse(sExpression.consp());
assertFalse(sExpression.functionp());
assertTrue(sExpression.listp());
assertTrue(sExpression.nullp());
assertFalse(sExpression.numberp());
assertFalse(sExpression.stringp());
assertTrue(sExpression.symbolp());
assertTrue(sExpression.isAtom());
assertFalse(sExpression.isCons());
assertFalse(sExpression.isFunction());
assertTrue(sExpression.isList());
assertTrue(sExpression.isNull());
assertFalse(sExpression.isNumber());
assertFalse(sExpression.isString());
assertTrue(sExpression.isSymbol());
}
public static void assertNumber(SExpression sExpression) {
assertTrue(sExpression.atomp());
assertFalse(sExpression.consp());
assertFalse(sExpression.functionp());
assertFalse(sExpression.listp());
assertFalse(sExpression.nullp());
assertTrue(sExpression.numberp());
assertFalse(sExpression.stringp());
assertFalse(sExpression.symbolp());
assertTrue(sExpression.isAtom());
assertFalse(sExpression.isCons());
assertFalse(sExpression.isFunction());
assertFalse(sExpression.isList());
assertFalse(sExpression.isNull());
assertTrue(sExpression.isNumber());
assertFalse(sExpression.isString());
assertFalse(sExpression.isSymbol());
}
public static void assertString(SExpression sExpression) {
assertTrue(sExpression.atomp());
assertFalse(sExpression.consp());
assertFalse(sExpression.functionp());
assertFalse(sExpression.listp());
assertFalse(sExpression.nullp());
assertFalse(sExpression.numberp());
assertTrue(sExpression.stringp());
assertFalse(sExpression.symbolp());
assertTrue(sExpression.isAtom());
assertFalse(sExpression.isCons());
assertFalse(sExpression.isFunction());
assertFalse(sExpression.isList());
assertFalse(sExpression.isNull());
assertFalse(sExpression.isNumber());
assertTrue(sExpression.isString());
assertFalse(sExpression.isSymbol());
}
public static void assertSymbol(SExpression sExpression) {
assertTrue(sExpression.atomp());
assertFalse(sExpression.consp());
assertFalse(sExpression.functionp());
assertFalse(sExpression.listp());
assertFalse(sExpression.nullp());
assertFalse(sExpression.numberp());
assertFalse(sExpression.stringp());
assertTrue(sExpression.symbolp());
assertTrue(sExpression.isAtom());
assertFalse(sExpression.isCons());
assertFalse(sExpression.isFunction());
assertFalse(sExpression.isList());
assertFalse(sExpression.isNull());
assertFalse(sExpression.isNumber());
assertFalse(sExpression.isString());
assertTrue(sExpression.isSymbol());
}
public static void assertT(SExpression sExpression) {