Added function to the argument validator and refactored some code
This commit is contained in:
parent
9bea0e6533
commit
7de7996a27
|
@ -8,20 +8,31 @@ import sexpression.*;
|
||||||
|
|
||||||
public class ArgumentValidator {
|
public class ArgumentValidator {
|
||||||
|
|
||||||
private Class<? extends SExpression> argumentType;
|
private Class<? extends SExpression> firstArgumentType;
|
||||||
|
private Class<? extends SExpression> trailingArgumentType;
|
||||||
private String functionName;
|
private String functionName;
|
||||||
private Integer maximumNumberOfArguments;
|
private Integer maximumNumberOfArguments;
|
||||||
private Integer minimumNumberOfArguments;
|
private Integer minimumNumberOfArguments;
|
||||||
|
|
||||||
public ArgumentValidator(String functionName) {
|
public ArgumentValidator(String functionName) {
|
||||||
this.argumentType = SExpression.class;
|
this.firstArgumentType = SExpression.class;
|
||||||
|
this.trailingArgumentType = SExpression.class;
|
||||||
this.functionName = functionName;
|
this.functionName = functionName;
|
||||||
this.minimumNumberOfArguments = null;
|
this.minimumNumberOfArguments = null;
|
||||||
this.maximumNumberOfArguments = null;
|
this.maximumNumberOfArguments = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setArgumentType(Class<? extends SExpression> argumentType) {
|
public void setFirstArgumentExpectedType(Class<? extends SExpression> argumentType) {
|
||||||
this.argumentType = argumentType;
|
this.firstArgumentType = argumentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTrailingArgumentExpectedType(Class<? extends SExpression> argumentType) {
|
||||||
|
this.trailingArgumentType = argumentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEveryArgumentExpectedType(Class<? extends SExpression> argumentType) {
|
||||||
|
this.firstArgumentType = argumentType;
|
||||||
|
this.trailingArgumentType = argumentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMaximumNumberOfArguments(int maximumNumberOfArguments) {
|
public void setMaximumNumberOfArguments(int maximumNumberOfArguments) {
|
||||||
|
@ -38,12 +49,27 @@ public class ArgumentValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validate(Cons argumentList) {
|
public void validate(Cons argumentList) {
|
||||||
|
validateListNotDotted(argumentList);
|
||||||
|
|
||||||
if (containsTooFewArguments(argumentList))
|
if (containsTooFewArguments(argumentList))
|
||||||
throw new TooFewArgumentsException(functionName, argumentList);
|
throw new TooFewArgumentsException(functionName, argumentList);
|
||||||
else if (containsTooManyArguments(argumentList))
|
else if (containsTooManyArguments(argumentList))
|
||||||
throw new TooManyArgumentsException(functionName, argumentList);
|
throw new TooManyArgumentsException(functionName, argumentList);
|
||||||
else if (!isExpectedArgumentType(argumentList.getCar()))
|
|
||||||
throw new BadArgumentTypeException(functionName, argumentList.getCar());
|
validateArgumentTypes(argumentList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateListNotDotted(Cons argumentList) {
|
||||||
|
Cons currentCons = argumentList;
|
||||||
|
SExpression nextCons = argumentList.getCdr();
|
||||||
|
|
||||||
|
while (!nextCons.nullp()) {
|
||||||
|
if (!nextCons.consp())
|
||||||
|
throw new DottedArgumentListException(functionName, argumentList);
|
||||||
|
|
||||||
|
currentCons = (Cons) nextCons;
|
||||||
|
nextCons = currentCons.getCdr();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean containsTooFewArguments(Cons argumentList) {
|
private boolean containsTooFewArguments(Cons argumentList) {
|
||||||
|
@ -54,8 +80,25 @@ public class ArgumentValidator {
|
||||||
return (maximumNumberOfArguments != null) && (LENGTH.getLength(argumentList) > maximumNumberOfArguments);
|
return (maximumNumberOfArguments != null) && (LENGTH.getLength(argumentList) > maximumNumberOfArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isExpectedArgumentType(SExpression firstArgument) {
|
private void validateArgumentTypes(Cons argumentList) {
|
||||||
return argumentType.isInstance(firstArgument);
|
if (!isExpectedFirstArgumentType(argumentList.getCar()))
|
||||||
|
throw new BadArgumentTypeException(functionName, argumentList.getCar(), firstArgumentType);
|
||||||
|
|
||||||
|
validateRemainingArguments(argumentList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isExpectedFirstArgumentType(SExpression firstArgument) {
|
||||||
|
return firstArgumentType.isInstance(firstArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateRemainingArguments(Cons cons) {
|
||||||
|
for (cons = (Cons) cons.getCdr(); !cons.nullp(); cons = (Cons) cons.getCdr())
|
||||||
|
if (!isExpectedRemainingArgumentType(cons.getCar()))
|
||||||
|
throw new BadArgumentTypeException(functionName, cons.getCar(), trailingArgumentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isExpectedRemainingArgumentType(SExpression remainingArgument) {
|
||||||
|
return trailingArgumentType.isInstance(remainingArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TooFewArgumentsException extends LispException {
|
public static class TooFewArgumentsException extends LispException {
|
||||||
|
@ -92,20 +135,44 @@ public class ArgumentValidator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class DottedArgumentListException extends LispException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private String functionName;
|
||||||
|
private Cons originalSExpression;
|
||||||
|
|
||||||
|
public DottedArgumentListException(String functionName, Cons argumentList) {
|
||||||
|
this.functionName = functionName;
|
||||||
|
this.originalSExpression = new Cons(new Symbol(this.functionName), argumentList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return MessageFormat.format("dotted argument list given to {0}: {1}", functionName, originalSExpression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class BadArgumentTypeException extends LispException {
|
public static class BadArgumentTypeException extends LispException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private String functionName;
|
private String functionName;
|
||||||
private String argument;
|
private String argument;
|
||||||
|
private Class<? extends SExpression> expected;
|
||||||
|
|
||||||
public BadArgumentTypeException(String functionName, SExpression argument) {
|
public BadArgumentTypeException(String functionName, SExpression argument,
|
||||||
|
Class<? extends SExpression> expected) {
|
||||||
this.functionName = functionName;
|
this.functionName = functionName;
|
||||||
this.argument = argument.toString();
|
this.argument = argument.toString();
|
||||||
|
this.expected = expected;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return MessageFormat.format("{0}: {1} is not the expected type", functionName, argument);
|
DisplayName displayName = expected.getAnnotation(DisplayName.class);
|
||||||
|
String expectedType = (displayName == null) ? "unknown" : displayName.value();
|
||||||
|
|
||||||
|
return MessageFormat.format("{0}: {1} is not the expected type of ''{2}''", functionName, argument,
|
||||||
|
expectedType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,32 +8,20 @@ import table.SymbolTable;
|
||||||
|
|
||||||
public class UserDefinedFunction extends LispFunction {
|
public class UserDefinedFunction extends LispFunction {
|
||||||
|
|
||||||
private ArgumentValidator argumentValidator;
|
|
||||||
private String name;
|
private String name;
|
||||||
private Cons body;
|
private Cons body;
|
||||||
private Cons lambdaExpression;
|
private Cons lambdaExpression;
|
||||||
private SymbolTable environment;
|
private SymbolTable environment;
|
||||||
private ArrayList<String> formalParameters;
|
private ArrayList<String> formalParameters;
|
||||||
|
private ArgumentValidator argumentValidator;
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new user-defined function with the specified name, lambda list and body.
|
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* the name of this user-defined function
|
|
||||||
* @param lambdaList
|
|
||||||
* a list of the formal parameters of this user-defined function (MUST BE A PROPER
|
|
||||||
* LIST)
|
|
||||||
* @param body
|
|
||||||
* the body of this user-defined function (MUST BE A PROPER LIST)
|
|
||||||
*/
|
|
||||||
public UserDefinedFunction(String name, Cons lambdaList, Cons body) {
|
public UserDefinedFunction(String name, Cons lambdaList, Cons body) {
|
||||||
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.getEnvironment();
|
||||||
this.formalParameters = new ArrayList<String>();
|
|
||||||
|
|
||||||
for (; 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());
|
||||||
|
|
||||||
this.argumentValidator = new ArgumentValidator(this.name);
|
this.argumentValidator = new ArgumentValidator(this.name);
|
||||||
|
|
|
@ -5,7 +5,6 @@ import sexpression.*;
|
||||||
|
|
||||||
public class APPLY extends LispFunction {
|
public class APPLY extends LispFunction {
|
||||||
|
|
||||||
private static final int NUMBER_OF_ARGUMENTS = 2;
|
|
||||||
private ArgumentValidator argumentValidator;
|
private ArgumentValidator argumentValidator;
|
||||||
|
|
||||||
public static SExpression apply(Cons argList) {
|
public static SExpression apply(Cons argList) {
|
||||||
|
@ -14,7 +13,8 @@ public class APPLY extends LispFunction {
|
||||||
|
|
||||||
public APPLY() {
|
public APPLY() {
|
||||||
this.argumentValidator = new ArgumentValidator("APPLY");
|
this.argumentValidator = new ArgumentValidator("APPLY");
|
||||||
this.argumentValidator.setExactNumberOfArguments(NUMBER_OF_ARGUMENTS);
|
this.argumentValidator.setExactNumberOfArguments(2);
|
||||||
|
this.argumentValidator.setTrailingArgumentExpectedType(Cons.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
public SExpression call(Cons argList) {
|
||||||
|
@ -23,26 +23,9 @@ public class APPLY extends LispFunction {
|
||||||
Cons cdr = (Cons) argList.getCdr();
|
Cons cdr = (Cons) argList.getCdr();
|
||||||
SExpression functionName = argList.getCar();
|
SExpression functionName = argList.getCar();
|
||||||
SExpression argumentList = cdr.getCar();
|
SExpression argumentList = cdr.getCar();
|
||||||
|
LispFunction function = EVAL.lookupFunctionOrLambda(functionName);
|
||||||
|
|
||||||
if (argumentList.listp()) {
|
return function.call((Cons) argumentList);
|
||||||
LispFunction function = EVAL.lookupFunction(functionName.toString());
|
|
||||||
|
|
||||||
if (function == null) {
|
|
||||||
if (functionName.functionp()) {
|
|
||||||
function = ((LambdaExpression) functionName).getFunction();
|
|
||||||
} else if (LAMBDA.isLambdaExpression(functionName)) {
|
|
||||||
Cons lexpr = (Cons) functionName;
|
|
||||||
|
|
||||||
function = LAMBDA.createFunction(lexpr);
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("undefined function " + functionName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return function.call((Cons) argumentList);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("APPLY: " + argumentList + " is not a list");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,11 @@ import sexpression.*;
|
||||||
|
|
||||||
public class ATOM extends LispFunction {
|
public class ATOM extends LispFunction {
|
||||||
|
|
||||||
private static final int NUMBER_OF_ARGUMENTS = 1;
|
|
||||||
private ArgumentValidator argumentValidator;
|
private ArgumentValidator argumentValidator;
|
||||||
|
|
||||||
public ATOM() {
|
public ATOM() {
|
||||||
this.argumentValidator = new ArgumentValidator("ATOM");
|
this.argumentValidator = new ArgumentValidator("ATOM");
|
||||||
this.argumentValidator.setExactNumberOfArguments(NUMBER_OF_ARGUMENTS);
|
this.argumentValidator.setExactNumberOfArguments(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SExpression call(Cons argumentList) {
|
public SExpression call(Cons argumentList) {
|
||||||
|
|
|
@ -2,19 +2,14 @@ package function.builtin;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import function.*;
|
import function.LispFunction;
|
||||||
import sexpression.*;
|
import sexpression.*;
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>EVAL</code> represents the EVAL function in Lisp.
|
|
||||||
*/
|
|
||||||
public class EVAL extends LispFunction {
|
public class EVAL extends LispFunction {
|
||||||
|
|
||||||
// A table to contain all the built-in and user-defined Lisp functions.
|
|
||||||
private static HashMap<String, LispFunction> functionTable = new HashMap<String, LispFunction>();
|
private static HashMap<String, LispFunction> functionTable = new HashMap<String, LispFunction>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// place all of the built-in functions into the function table
|
|
||||||
functionTable.put("*", new MULTIPLY());
|
functionTable.put("*", new MULTIPLY());
|
||||||
functionTable.put("+", new PLUS());
|
functionTable.put("+", new PLUS());
|
||||||
functionTable.put("-", new MINUS());
|
functionTable.put("-", new MINUS());
|
||||||
|
@ -50,33 +45,32 @@ public class EVAL extends LispFunction {
|
||||||
functionTable.put("SYMBOL-FUNCTION", new SYMBOL_FUNCTION());
|
functionTable.put("SYMBOL-FUNCTION", new SYMBOL_FUNCTION());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the function table.
|
|
||||||
*
|
|
||||||
* @return the function table
|
|
||||||
*/
|
|
||||||
public static HashMap<String, LispFunction> getFunctionTable() {
|
public static HashMap<String, LispFunction> getFunctionTable() {
|
||||||
return functionTable;
|
return functionTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Look up a function by its name.
|
|
||||||
*
|
|
||||||
* @param functionName
|
|
||||||
* the name of the function to look up
|
|
||||||
* @return the function with the name <code>functionName</code> if it exists; null otherwise
|
|
||||||
*/
|
|
||||||
public static LispFunction lookupFunction(String functionName) {
|
public static LispFunction lookupFunction(String functionName) {
|
||||||
return functionTable.get(functionName);
|
return functionTable.get(functionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static LispFunction lookupFunctionOrLambda(SExpression functionExpression) {
|
||||||
* Look up a symbol's value using its name.
|
LispFunction function = lookupFunction(functionExpression.toString());
|
||||||
*
|
|
||||||
* @param symbolName
|
if (function == null)
|
||||||
* the name of the symbol to look up (must not be null)
|
function = createLambdaFunction(functionExpression);
|
||||||
* @return the value of <code>symbolName</code> if it has one; null otherwise
|
|
||||||
*/
|
return function;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LispFunction createLambdaFunction(SExpression lambdaExpression) {
|
||||||
|
if (lambdaExpression.functionp())
|
||||||
|
return ((LambdaExpression) lambdaExpression).getFunction();
|
||||||
|
else if (LAMBDA.isLambdaExpression(lambdaExpression))
|
||||||
|
return LAMBDA.createFunction((Cons) lambdaExpression);
|
||||||
|
else
|
||||||
|
throw new RuntimeException("undefined function " + lambdaExpression);
|
||||||
|
}
|
||||||
|
|
||||||
public static SExpression lookupSymbol(String symbolName) {
|
public static SExpression lookupSymbol(String symbolName) {
|
||||||
if (symbolName.equals("NIL")) {
|
if (symbolName.equals("NIL")) {
|
||||||
return Nil.getUniqueInstance();
|
return Nil.getUniqueInstance();
|
||||||
|
@ -89,13 +83,6 @@ public class EVAL extends LispFunction {
|
||||||
return SETF.lookup(symbolName);
|
return SETF.lookup(symbolName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the given list is dotted.
|
|
||||||
*
|
|
||||||
* @param list
|
|
||||||
* the list to be tested (must not be null)
|
|
||||||
* @return <code>true</code> if <code>list</code> is dotted; <code>false</code> otherwise
|
|
||||||
*/
|
|
||||||
public static boolean isDotted(Cons list) {
|
public static boolean isDotted(Cons list) {
|
||||||
if (list.nullp()) {
|
if (list.nullp()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -111,13 +98,6 @@ public class EVAL extends LispFunction {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Evaluate the given S-expression.
|
|
||||||
*
|
|
||||||
* @param sexpr
|
|
||||||
* the S-expression to evaluate
|
|
||||||
* @return the value of <code>sexpr</code>
|
|
||||||
*/
|
|
||||||
public static SExpression eval(SExpression sexpr) {
|
public static SExpression eval(SExpression sexpr) {
|
||||||
Cons expList = LIST.makeList(sexpr);
|
Cons expList = LIST.makeList(sexpr);
|
||||||
EVAL evalFunction = new EVAL();
|
EVAL evalFunction = new EVAL();
|
||||||
|
@ -125,7 +105,6 @@ public class EVAL extends LispFunction {
|
||||||
return evalFunction.call(expList);
|
return evalFunction.call(expList);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The number of arguments that EVAL takes.
|
|
||||||
private static final int NUM_ARGS = 1;
|
private static final int NUM_ARGS = 1;
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
public SExpression call(Cons argList) {
|
||||||
|
@ -164,29 +143,11 @@ public class EVAL extends LispFunction {
|
||||||
return arg; // 'arg' is a NUMBER or a STRING
|
return arg; // 'arg' is a NUMBER or a STRING
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the specified list.
|
|
||||||
//
|
|
||||||
// Parameters: list - the list to evaluate
|
|
||||||
// Returns: the value of 'list'
|
|
||||||
// Precondition: 'list' must not be null.
|
|
||||||
private SExpression evaluateList(Cons list) {
|
private SExpression evaluateList(Cons list) {
|
||||||
SExpression car = list.getCar();
|
SExpression car = list.getCar();
|
||||||
SExpression cdr = list.getCdr();
|
SExpression cdr = list.getCdr();
|
||||||
|
|
||||||
LispFunction function = lookupFunction(car.toString());
|
LispFunction function = lookupFunctionOrLambda(car);
|
||||||
|
|
||||||
if (function == null) {
|
|
||||||
// check if the car of the list is a lambda expression
|
|
||||||
if (car.functionp()) {
|
|
||||||
function = ((LambdaExpression) car).getFunction();
|
|
||||||
} else if (LAMBDA.isLambdaExpression(car)) {
|
|
||||||
Cons lexpr = (Cons) car;
|
|
||||||
|
|
||||||
function = LAMBDA.createFunction(lexpr);
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("undefined function " + car);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure the list of arguments for 'function' is a list
|
// make sure the list of arguments for 'function' is a list
|
||||||
if (cdr.listp()) {
|
if (cdr.listp()) {
|
||||||
|
@ -210,12 +171,6 @@ public class EVAL extends LispFunction {
|
||||||
throw new RuntimeException("argument list given to " + car + " is dotted: " + list);
|
throw new RuntimeException("argument list given to " + car + " is dotted: " + list);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate a list of arguments for a function.
|
|
||||||
//
|
|
||||||
// Parameters: arguments - a list of arguments for a function
|
|
||||||
// Returns: a list consisting of the values of the S-expressions found in
|
|
||||||
// 'arguments'
|
|
||||||
// Precondition: 'arguments' must not be null.
|
|
||||||
private Cons evaluateArgList(Cons arguments) {
|
private Cons evaluateArgList(Cons arguments) {
|
||||||
if (arguments.nullp()) {
|
if (arguments.nullp()) {
|
||||||
return Nil.getUniqueInstance();
|
return Nil.getUniqueInstance();
|
||||||
|
|
|
@ -3,9 +3,6 @@ package function.builtin;
|
||||||
import function.LispFunction;
|
import function.LispFunction;
|
||||||
import sexpression.*;
|
import sexpression.*;
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>PLUS</code> represents the '+' function in Lisp.
|
|
||||||
*/
|
|
||||||
public class PLUS extends LispFunction {
|
public class PLUS extends LispFunction {
|
||||||
|
|
||||||
public LispNumber call(Cons argList) {
|
public LispNumber call(Cons argList) {
|
||||||
|
@ -13,6 +10,9 @@ public class PLUS extends LispFunction {
|
||||||
return new LispNumber(0);
|
return new LispNumber(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!argList.getCdr().listp())
|
||||||
|
throw new RuntimeException("+: " + argList + " is dotted");
|
||||||
|
|
||||||
SExpression argFirst = argList.getCar();
|
SExpression argFirst = argList.getCar();
|
||||||
Cons argRest = (Cons) argList.getCdr();
|
Cons argRest = (Cons) argList.getCdr();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package sexpression;
|
package sexpression;
|
||||||
|
|
||||||
|
@DisplayName("atom")
|
||||||
public abstract class Atom extends SExpression {
|
public abstract class Atom extends SExpression {
|
||||||
|
|
||||||
private String text;
|
private String text;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package sexpression;
|
package sexpression;
|
||||||
|
|
||||||
|
@DisplayName("list")
|
||||||
public class Cons extends SExpression {
|
public class Cons extends SExpression {
|
||||||
|
|
||||||
private SExpression car;
|
private SExpression car;
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package sexpression;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
public @interface DisplayName {
|
||||||
|
|
||||||
|
String value();
|
||||||
|
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package sexpression;
|
||||||
|
|
||||||
import function.UserDefinedFunction;
|
import function.UserDefinedFunction;
|
||||||
|
|
||||||
|
@DisplayName("lambda-expression")
|
||||||
public class LambdaExpression extends SExpression {
|
public class LambdaExpression extends SExpression {
|
||||||
|
|
||||||
private Cons lambdaExpression;
|
private Cons lambdaExpression;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import java.text.MessageFormat;
|
||||||
|
|
||||||
import error.LispException;
|
import error.LispException;
|
||||||
|
|
||||||
|
@DisplayName("number")
|
||||||
public class LispNumber extends Atom {
|
public class LispNumber extends Atom {
|
||||||
|
|
||||||
private int value;
|
private int value;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package sexpression;
|
package sexpression;
|
||||||
|
|
||||||
|
@DisplayName("string")
|
||||||
public class LispString extends Atom {
|
public class LispString extends Atom {
|
||||||
|
|
||||||
public LispString(String text) {
|
public LispString(String text) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package sexpression;
|
package sexpression;
|
||||||
|
|
||||||
|
@DisplayName("nil")
|
||||||
public class Nil extends Cons {
|
public class Nil extends Cons {
|
||||||
|
|
||||||
private static Nil uniqueInstance = new Nil();
|
private static Nil uniqueInstance = new Nil();
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package sexpression;
|
package sexpression;
|
||||||
|
|
||||||
|
@DisplayName("s-expression")
|
||||||
public abstract class SExpression {
|
public abstract class SExpression {
|
||||||
|
|
||||||
public boolean nullp() {
|
public boolean nullp() {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package sexpression;
|
package sexpression;
|
||||||
|
|
||||||
|
@DisplayName("symbol")
|
||||||
public class Symbol extends Atom {
|
public class Symbol extends Atom {
|
||||||
|
|
||||||
public static final Symbol T = new Symbol("T");
|
public static final Symbol T = new Symbol("T");
|
||||||
|
|
|
@ -103,14 +103,14 @@ public class ArgumentValidatorTester {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void BadArgumentTypeException_HasCorrectSeverity() {
|
public void BadArgumentTypeException_HasCorrectSeverity() {
|
||||||
BadArgumentTypeException e = new BadArgumentTypeException("TEST", Nil.getUniqueInstance());
|
BadArgumentTypeException e = new BadArgumentTypeException("TEST", Nil.getUniqueInstance(), SExpression.class);
|
||||||
|
|
||||||
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
|
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void BadArgumentTypeException_HasMessageText() {
|
public void BadArgumentTypeException_HasMessageText() {
|
||||||
BadArgumentTypeException e = new BadArgumentTypeException("TEST", Nil.getUniqueInstance());
|
BadArgumentTypeException e = new BadArgumentTypeException("TEST", Nil.getUniqueInstance(), SExpression.class);
|
||||||
|
|
||||||
assertNotNull(e.getMessage());
|
assertNotNull(e.getMessage());
|
||||||
assertTrue(e.getMessage().length() > 0);
|
assertTrue(e.getMessage().length() > 0);
|
||||||
|
@ -118,14 +118,70 @@ public class ArgumentValidatorTester {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void correctArgumentType_DoesNotThrowException() {
|
public void correctArgumentType_DoesNotThrowException() {
|
||||||
validator.setArgumentType(Nil.class);
|
validator.setEveryArgumentExpectedType(Nil.class);
|
||||||
validator.validate(makeArgumentListOfSize(1));
|
validator.validate(makeArgumentListOfSize(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = BadArgumentTypeException.class)
|
@Test(expected = BadArgumentTypeException.class)
|
||||||
public void badArgumentType_ThrowsException() {
|
public void badArgumentType_ThrowsException() {
|
||||||
validator.setArgumentType(LispString.class);
|
validator.setEveryArgumentExpectedType(LispString.class);
|
||||||
validator.validate(makeArgumentListOfSize(1));
|
validator.validate(makeArgumentListOfSize(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void correctFirstAndRestArgumentTypes_DoesNotThrowException() {
|
||||||
|
Cons argumentList = new Cons(Symbol.T, new Cons(Nil.getUniqueInstance(), Nil.getUniqueInstance()));
|
||||||
|
|
||||||
|
validator.setFirstArgumentExpectedType(Symbol.class);
|
||||||
|
validator.setTrailingArgumentExpectedType(Cons.class);
|
||||||
|
validator.validate(argumentList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = BadArgumentTypeException.class)
|
||||||
|
public void badFirstArgumentType_ThrowsException() {
|
||||||
|
Cons argumentList = new Cons(Symbol.T, new Cons(Nil.getUniqueInstance(), Nil.getUniqueInstance()));
|
||||||
|
|
||||||
|
validator.setFirstArgumentExpectedType(Cons.class);
|
||||||
|
validator.setTrailingArgumentExpectedType(Cons.class);
|
||||||
|
validator.validate(argumentList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = BadArgumentTypeException.class)
|
||||||
|
public void badTrailingArgumentType_ThrowsException() {
|
||||||
|
Cons argumentList = new Cons(Symbol.T, new Cons(Nil.getUniqueInstance(), Nil.getUniqueInstance()));
|
||||||
|
|
||||||
|
validator.setFirstArgumentExpectedType(Symbol.class);
|
||||||
|
validator.setTrailingArgumentExpectedType(Symbol.class);
|
||||||
|
validator.validate(argumentList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void expectedTypeWithNoDisplayName_DoesNotCauseNPE() {
|
||||||
|
Cons argumentList = new Cons(Symbol.T, new Cons(Nil.getUniqueInstance(), Nil.getUniqueInstance()));
|
||||||
|
SExpression withoutDisplayName = new SExpression() {};
|
||||||
|
|
||||||
|
validator.setEveryArgumentExpectedType(withoutDisplayName.getClass());
|
||||||
|
|
||||||
|
try {
|
||||||
|
validator.validate(argumentList);
|
||||||
|
} catch (BadArgumentTypeException e) {
|
||||||
|
e.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = DottedArgumentListException.class)
|
||||||
|
public void givenDottedArgumentList_ThrowsException() {
|
||||||
|
Cons argumentList = new Cons(Symbol.T, Symbol.T);
|
||||||
|
|
||||||
|
validator.validate(argumentList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void dottedArgumentListException_HasMessageText() {
|
||||||
|
DottedArgumentListException e = new DottedArgumentListException("TEST", Nil.getUniqueInstance());
|
||||||
|
|
||||||
|
assertNotNull(e.getMessage());
|
||||||
|
assertTrue(e.getMessage().length() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ public class APPLYTester {
|
||||||
evaluateString("(apply 'f '(1 2 3))");
|
evaluateString("(apply 'f '(1 2 3))");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = RuntimeException.class)
|
@Test(expected = BadArgumentTypeException.class)
|
||||||
public void testApplyWithNonListSecondArgument() {
|
public void testApplyWithNonListSecondArgument() {
|
||||||
evaluateString("(apply '+ '2)");
|
evaluateString("(apply '+ '2)");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue