Add unit tests for the backquote facility

Renamed BackTick to Backquote globally.
This commit is contained in:
Mike Cifelli 2017-03-12 12:19:45 -04:00
parent 698305f07f
commit e89a6f2dde
14 changed files with 137 additions and 85 deletions

View File

@ -4,16 +4,21 @@ import static function.builtin.EVAL.eval;
import static sexpression.Nil.NIL;
import error.LispException;
import function.ArgumentValidator;
import sexpression.*;
class BackTickEvaluator {
class BackquoteEvaluator {
private BackTickExpression backTick;
private ArgumentValidator listValidator;
private ArgumentValidator atSignValidator;
private BackquoteExpression backTick;
private Cons resolvedList;
private Cons leader;
private Cons follower;
public BackTickEvaluator(BackTickExpression backTick) {
public BackquoteEvaluator(BackquoteExpression backTick) {
this.listValidator = new ArgumentValidator("`|list|");
this.atSignValidator = new ArgumentValidator("@|list|");
this.backTick = backTick;
this.resolvedList = new Cons(NIL, NIL);
this.leader = resolvedList;
@ -34,6 +39,7 @@ class BackTickEvaluator {
}
public SExpression resolveList(Cons list) {
listValidator.validate(list);
createResolvedList(list);
return resolvedList;
@ -81,22 +87,29 @@ class BackTickEvaluator {
private Cons evaluateAtSign(AtSignExpression atSign) {
SExpression expression = atSign.getExpression();
validateAtSignExpression(expression);
SExpression result = eval(expression);
validateAtSignUnevaluatedExpression(expression);
SExpression evaluation = eval(expression);
if (!result.isList())
throw new AtSignNotListException();
return (Cons) result;
return getValidatedList(evaluation);
}
private void validateAtSignExpression(SExpression expression) {
private void validateAtSignUnevaluatedExpression(SExpression expression) {
if (expression.isComma())
throw new NestedCommaException();
else if (expression.isAtSign())
throw new NestedAtSignException();
}
private Cons getValidatedList(SExpression evaluation) {
if (!evaluation.isList())
throw new AtSignNotListException();
Cons evaluatedList = (Cons) evaluation;
atSignValidator.validate(evaluatedList);
return evaluatedList;
}
private void unpackResolvedList(Cons list) {
for (; list.isCons(); list = (Cons) list.getRest())
addResolvedExpression(list.getFirst());

View File

@ -70,7 +70,7 @@ public class EVAL extends LispFunction {
return evaluateList(argument);
else if (argument.isSymbol())
return evaluateSymbol(argument);
else if (argument.isBackTick())
else if (argument.isBackquote())
return evaluateBackTick(argument);
else if (argument.isComma())
throw new UnmatchedCommaException();
@ -133,7 +133,7 @@ public class EVAL extends LispFunction {
}
private SExpression evaluateBackTick(SExpression argument) {
BackTickEvaluator evaluator = new BackTickEvaluator((BackTickExpression) argument);
BackquoteEvaluator evaluator = new BackquoteEvaluator((BackquoteExpression) argument);
return evaluator.evaluate();
}

View File

@ -1,10 +1,10 @@
package sexpression;
public class BackTickExpression extends SExpression {
public class BackquoteExpression extends SExpression {
private SExpression expression;
public BackTickExpression(SExpression expression) {
public BackquoteExpression(SExpression expression) {
this.expression = expression;
}
@ -12,7 +12,7 @@ public class BackTickExpression extends SExpression {
return expression;
}
public boolean isBackTick() {
public boolean isBackquote() {
return true;
}

View File

@ -35,7 +35,7 @@ public abstract class SExpression {
return false;
}
public boolean isBackTick() {
public boolean isBackquote() {
return false;
}

View File

@ -5,9 +5,9 @@ import java.util.function.Supplier;
import file.FilePosition;
import sexpression.*;
public class BackTick extends Token {
public class Backquote extends Token {
public BackTick(String text, FilePosition position) {
public Backquote(String text, FilePosition position) {
super(text, position);
}
@ -16,7 +16,7 @@ public class BackTick extends Token {
Token nextToken = getNextToken.get();
SExpression argument = nextToken.parseSExpression(getNextToken);
return new BackTickExpression(argument);
return new BackquoteExpression(argument);
}
}

View File

@ -22,8 +22,8 @@ public class TokenFactoryImpl implements TokenFactory {
return new QuoteMark(text, position);
case DOUBLE_QUOTE:
return new QuotedString(text, position);
case BACK_TICK:
return new BackTick(text, position);
case BACKQUOTE:
return new Backquote(text, position);
case AT_SIGN:
return new AtSign(text, position);
case COMMA:

View File

@ -9,8 +9,8 @@ public final class Characters {
public static final int EOF = -1;
public static final char AT_SIGN = '@';
public static final char BACKQUOTE = '`';
public static final char BACKSLASH = '\\';
public static final char BACK_TICK = '`';
public static final char COMMA = ',';
public static final char DASH = '-';
public static final char DOUBLE_QUOTE = '\"';
@ -29,8 +29,8 @@ public final class Characters {
static {
illegalIdentifierCharacters.add(AT_SIGN);
illegalIdentifierCharacters.add(BACKQUOTE);
illegalIdentifierCharacters.add(BACKSLASH);
illegalIdentifierCharacters.add(BACK_TICK);
illegalIdentifierCharacters.add(COMMA);
illegalIdentifierCharacters.add(DOUBLE_QUOTE);
illegalIdentifierCharacters.add(HASH);

View File

@ -1,22 +1,24 @@
package function.builtin;
import static sexpression.Nil.NIL;
import static sexpression.Symbol.T;
import static testutil.TestUtilities.*;
import org.junit.Test;
import function.builtin.BackTickEvaluator.*;
import function.ArgumentValidator.DottedArgumentListException;
import function.builtin.BackquoteEvaluator.*;
import sexpression.*;
public class BackTickEvaluatorTester {
public class BackquoteEvaluatorTester {
private BackTickEvaluator createBackTickEvaluator(SExpression expression) {
return new BackTickEvaluator(new BackTickExpression(expression));
private BackquoteEvaluator createBackquoteEvaluator(SExpression expression) {
return new BackquoteEvaluator(new BackquoteExpression(expression));
}
@Test
public void evaluateNil() {
BackTickEvaluator evaluator = createBackTickEvaluator(NIL);
BackquoteEvaluator evaluator = createBackquoteEvaluator(NIL);
assertSExpressionsMatch(NIL, evaluator.evaluate());
}
@ -24,7 +26,7 @@ public class BackTickEvaluatorTester {
@Test
public void evaluateNumber() {
SExpression input = new LispNumber("99");
BackTickEvaluator evaluator = createBackTickEvaluator(input);
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
assertSExpressionsMatch(input, evaluator.evaluate());
}
@ -32,7 +34,7 @@ public class BackTickEvaluatorTester {
@Test
public void evaluateList() {
SExpression input = makeList(new LispNumber("1"), new LispNumber("99"));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
assertSExpressionsMatch(input, evaluator.evaluate());
}
@ -40,7 +42,7 @@ public class BackTickEvaluatorTester {
@Test
public void evaluateComma() {
SExpression input = new CommaExpression(makeList(new Symbol("+"), new LispNumber("1"), new LispNumber("9")));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
assertSExpressionsMatch(new LispNumber("10"), evaluator.evaluate());
}
@ -49,7 +51,7 @@ public class BackTickEvaluatorTester {
public void evaluateListWithComma() {
SExpression input = makeList(new CommaExpression(makeList(new Symbol("+"), new LispNumber("1"),
new LispNumber("9"))));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
assertSExpressionsMatch(makeList(new LispNumber("10")), evaluator.evaluate());
}
@ -57,7 +59,7 @@ public class BackTickEvaluatorTester {
@Test(expected = NestedCommaException.class)
public void evaluateListWithNestedComma() {
SExpression input = makeList(new CommaExpression(new CommaExpression(new Symbol("+"))));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
evaluator.evaluate();
}
@ -65,7 +67,7 @@ public class BackTickEvaluatorTester {
@Test(expected = AtSignNotInCommaException.class)
public void evaluateListWithNoCommaPrecedingAtSign() {
SExpression input = makeList(new AtSignExpression(makeList(new Symbol("+"))));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
evaluator.evaluate();
}
@ -73,7 +75,7 @@ public class BackTickEvaluatorTester {
@Test(expected = AtSignNotInCommaException.class)
public void evaluateAtSign() {
SExpression input = new AtSignExpression(makeList(new Symbol("+")));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
evaluator.evaluate();
}
@ -81,7 +83,7 @@ public class BackTickEvaluatorTester {
@Test(expected = NestedAtSignException.class)
public void evaluateListWithNestedAtSigns() {
SExpression input = makeList(new CommaExpression(new AtSignExpression(new AtSignExpression(makeList(new Symbol("+"))))));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
evaluator.evaluate();
}
@ -89,7 +91,7 @@ public class BackTickEvaluatorTester {
@Test(expected = NestedCommaException.class)
public void evaluateListWithCommaAfterAtSign() {
SExpression input = makeList(new CommaExpression(new AtSignExpression(new CommaExpression(makeList(new Symbol("+"))))));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
evaluator.evaluate();
}
@ -100,7 +102,7 @@ public class BackTickEvaluatorTester {
new LispNumber("1"),
new LispNumber("9")))));
SExpression expected = makeList(new LispNumber("1"), new LispNumber("9"));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
assertSExpressionsMatch(expected, evaluator.evaluate());
}
@ -108,28 +110,50 @@ public class BackTickEvaluatorTester {
@Test(expected = AtSignNotListException.class)
public void atSignDoesNotEvaluateToList() {
SExpression input = makeList(new CommaExpression(new AtSignExpression(new LispNumber("1"))));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
evaluator.evaluate();
}
@Test
public void evaluateListWithCommasAndAtSign() {
SExpression input = makeList(new LispNumber("78"),
new CommaExpression(new AtSignExpression(makeList(new Symbol("LIST"),
new LispNumber("1"),
new LispNumber("9")))),
new CommaExpression(makeList(new Symbol("+"), new LispNumber("20"),
new LispNumber("5"))),
new CommaExpression(makeList(new Symbol("LIST"), new LispNumber("7"),
new LispNumber("6"))),
new LispString("\"sky\""));
Cons list1 = makeList(new Symbol("LIST"), new LispNumber("1"), new LispNumber("9"));
Cons list2 = makeList(new Symbol("+"), new LispNumber("20"), new LispNumber("5"));
Cons list3 = makeList(new Symbol("LIST"), new LispNumber("7"), new LispNumber("6"));
SExpression input = makeList(new LispNumber("78"), new CommaExpression(new AtSignExpression(list1)),
new CommaExpression(list2), new CommaExpression(list3), new LispString("\"sky\""));
SExpression expected = makeList(new LispNumber("78"), new LispNumber("1"), new LispNumber("9"),
new LispNumber("25"), makeList(new LispNumber("7"), new LispNumber("6")),
new LispString("\"sky\""));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
assertSExpressionsMatch(expected, evaluator.evaluate());
}
@Test(expected = DottedArgumentListException.class)
public void evaluateDottedList() {
BackquoteEvaluator evaluator = createBackquoteEvaluator(new Cons(T, T));
evaluator.evaluate();
}
@Test(expected = DottedArgumentListException.class)
public void atSignWithDottedList() {
SExpression input = makeList(new CommaExpression(new AtSignExpression(makeList(new Symbol("CONS"), T, T))));
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
evaluator.evaluate();
}
@Test
public void backquoteExceptionsHaveCorrectAttributes() {
assertIsErrorWithMessage(new NestedCommaException());
assertIsErrorWithMessage(new NestedAtSignException());
assertIsErrorWithMessage(new AtSignNotInCommaException());
assertIsErrorWithMessage(new AtSignNotListException());
}
}

View File

@ -1,15 +1,14 @@
package function.builtin;
import static error.ErrorManager.Severity.ERROR;
import static function.builtin.EVAL.lookupSymbol;
import static org.junit.Assert.*;
import static org.junit.Assert.assertNull;
import static sexpression.Nil.NIL;
import static testutil.TestUtilities.*;
import org.junit.Test;
import function.ArgumentValidator.*;
import function.builtin.BackTickEvaluator.AtSignNotInCommaException;
import function.builtin.BackquoteEvaluator.AtSignNotInCommaException;
import function.builtin.EVAL.*;
public class EVALTester {
@ -84,20 +83,12 @@ public class EVALTester {
@Test
public void undefinedFunctionException_HasCorrectAttributes() {
UndefinedFunctionException e = new UndefinedFunctionException(NIL);
assertEquals(ERROR, e.getSeverity());
assertNotNull(e.getMessage());
assertTrue(e.getMessage().length() > 0);
assertIsErrorWithMessage(new UndefinedFunctionException(NIL));
}
@Test
public void undefinedSymbolException_HasCorrectAttributes() {
UndefinedSymbolException e = new UndefinedSymbolException(NIL);
assertEquals(ERROR, e.getSeverity());
assertNotNull(e.getMessage());
assertTrue(e.getMessage().length() > 0);
assertIsErrorWithMessage(new UndefinedSymbolException(NIL));
}
@Test(expected = UnmatchedCommaException.class)
@ -137,9 +128,24 @@ public class EVALTester {
@Test(expected = AtSignNotInCommaException.class)
public void evalBackTickOnAtSign() {
String input = "`@9";
evaluateString("`@9");
}
@Test
public void evalNestedBackquotes() {
String input = "`,`,`,`,9";
assertSExpressionsMatch(parseString("9"), evaluateString(input));
}
@Test
public void unmatchedCommaException_HasCorrectAttributes() {
assertIsErrorWithMessage(new UnmatchedCommaException());
}
@Test
public void unmatchedAtSignException_HasCorrectAttributes() {
assertIsErrorWithMessage(new UnmatchedAtSignException());
}
}

View File

@ -281,7 +281,7 @@ public class LispScannerTypeTester {
@Test
public void givenBackTickExpression_ReturnsCorrectTypes() {
String input = "`(list ,a ,@b)";
expectedTypes.add(BackTick.class);
expectedTypes.add(Backquote.class);
expectedTypes.add(LeftParenthesis.class);
expectedTypes.add(Identifier.class);
expectedTypes.add(Comma.class);

View File

@ -166,7 +166,7 @@ public class SExpressionTester {
@Test
public void backTickExpression_ToString() {
String expected = "`(TEST)";
SExpression backTick = new BackTickExpression(makeList(new Symbol("TEST")));
SExpression backTick = new BackquoteExpression(makeList(new Symbol("TEST")));
assertSExpressionMatchesString(expected, backTick);
}
@ -190,8 +190,9 @@ public class SExpressionTester {
@Test
public void complexBackTickExpression_ToString() {
String expected = "`(LIST ,A ,@B)";
SExpression backTick = new BackTickExpression(makeList(new Symbol("LIST"), new CommaExpression(new Symbol("A")),
new CommaExpression(new AtSignExpression(new Symbol("B")))));
SExpression backTick = new BackquoteExpression(makeList(new Symbol("LIST"),
new CommaExpression(new Symbol("A")),
new CommaExpression(new AtSignExpression(new Symbol("B")))));
assertSExpressionMatchesString(expected, backTick);
}
@ -199,7 +200,7 @@ public class SExpressionTester {
@Test
public void backTickExpression_GetExpression() {
SExpression expression = makeList(new Symbol("TEST"));
BackTickExpression backTick = new BackTickExpression(expression);
BackquoteExpression backTick = new BackquoteExpression(expression);
assertSExpressionsMatch(expression, backTick.getExpression());
}

View File

@ -1,5 +1,6 @@
package testutil;
import static error.ErrorManager.Severity.ERROR;
import static function.builtin.EVAL.eval;
import static org.junit.Assert.*;
import static sexpression.Nil.NIL;
@ -7,6 +8,7 @@ import static sexpression.Nil.NIL;
import java.io.*;
import java.util.Arrays;
import error.LispException;
import parser.LispParser;
import sexpression.*;
@ -35,14 +37,6 @@ public final class TestUtilities {
return new LispParser(stringInputStream, "testFile").getNextSExpression();
}
public static void assertSExpressionsMatch(SExpression one, SExpression two) {
assertEquals(one.toString(), two.toString());
}
public static void assertSExpressionsDoNotMatch(SExpression one, SExpression two) {
assertNotEquals(one.toString(), two.toString());
}
public static Cons makeList(SExpression... expressionList) {
if (expressionList.length == 0)
return NIL;
@ -52,4 +46,18 @@ public final class TestUtilities {
return new Cons(expressionList[0], rest);
}
public static void assertSExpressionsMatch(SExpression one, SExpression two) {
assertEquals(one.toString(), two.toString());
}
public static void assertSExpressionsDoNotMatch(SExpression one, SExpression two) {
assertNotEquals(one.toString(), two.toString());
}
public static void assertIsErrorWithMessage(LispException e) {
assertEquals(ERROR, e.getSeverity());
assertNotNull(e.getMessage());
assertTrue(e.getMessage().length() > 0);
}
}

View File

@ -17,7 +17,7 @@ public final class TypeAssertions {
assertFalse(sExpression.isNumber());
assertFalse(sExpression.isString());
assertFalse(sExpression.isSymbol());
assertFalse(sExpression.isBackTick());
assertFalse(sExpression.isBackquote());
assertFalse(sExpression.isComma());
assertFalse(sExpression.isAtSign());
}
@ -33,7 +33,7 @@ public final class TypeAssertions {
assertFalse(sExpression.isNumber());
assertFalse(sExpression.isString());
assertTrue(sExpression.isSymbol());
assertFalse(sExpression.isBackTick());
assertFalse(sExpression.isBackquote());
assertFalse(sExpression.isComma());
assertFalse(sExpression.isAtSign());
}
@ -47,7 +47,7 @@ public final class TypeAssertions {
assertTrue(sExpression.isNumber());
assertFalse(sExpression.isString());
assertFalse(sExpression.isSymbol());
assertFalse(sExpression.isBackTick());
assertFalse(sExpression.isBackquote());
assertFalse(sExpression.isComma());
assertFalse(sExpression.isAtSign());
}
@ -61,7 +61,7 @@ public final class TypeAssertions {
assertFalse(sExpression.isNumber());
assertTrue(sExpression.isString());
assertFalse(sExpression.isSymbol());
assertFalse(sExpression.isBackTick());
assertFalse(sExpression.isBackquote());
assertFalse(sExpression.isComma());
assertFalse(sExpression.isAtSign());
}
@ -75,7 +75,7 @@ public final class TypeAssertions {
assertFalse(sExpression.isNumber());
assertFalse(sExpression.isString());
assertTrue(sExpression.isSymbol());
assertFalse(sExpression.isBackTick());
assertFalse(sExpression.isBackquote());
assertFalse(sExpression.isComma());
assertFalse(sExpression.isAtSign());
}
@ -93,7 +93,7 @@ public final class TypeAssertions {
assertFalse(sExpression.isNumber());
assertFalse(sExpression.isString());
assertFalse(sExpression.isSymbol());
assertTrue(sExpression.isBackTick());
assertTrue(sExpression.isBackquote());
assertFalse(sExpression.isComma());
assertFalse(sExpression.isAtSign());
}
@ -107,7 +107,7 @@ public final class TypeAssertions {
assertFalse(sExpression.isNumber());
assertFalse(sExpression.isString());
assertFalse(sExpression.isSymbol());
assertFalse(sExpression.isBackTick());
assertFalse(sExpression.isBackquote());
assertTrue(sExpression.isComma());
assertFalse(sExpression.isAtSign());
}
@ -121,7 +121,7 @@ public final class TypeAssertions {
assertFalse(sExpression.isNumber());
assertFalse(sExpression.isString());
assertFalse(sExpression.isSymbol());
assertFalse(sExpression.isBackTick());
assertFalse(sExpression.isBackquote());
assertFalse(sExpression.isComma());
assertTrue(sExpression.isAtSign());
}

View File

@ -103,7 +103,7 @@ public class TokenFactoryTester {
@Test
public void backTickCreation() {
String text = "`";
assertTrue(createToken(text) instanceof BackTick);
assertTrue(createToken(text) instanceof Backquote);
}
@Test