Add the backtick facility

Resolves #20
This commit is contained in:
Mike Cifelli 2017-03-11 15:41:07 -05:00
parent 655f49e612
commit 698305f07f
22 changed files with 802 additions and 50 deletions

View File

@ -1,10 +0,0 @@
package function;
public abstract class LispMacro extends LispSpecialFunction {
@Override
public boolean isMacro() {
return true;
}
}

View File

@ -0,0 +1,181 @@
package function.builtin;
import static function.builtin.EVAL.eval;
import static sexpression.Nil.NIL;
import error.LispException;
import sexpression.*;
class BackTickEvaluator {
private BackTickExpression backTick;
private Cons resolvedList;
private Cons leader;
private Cons follower;
public BackTickEvaluator(BackTickExpression backTick) {
this.backTick = backTick;
this.resolvedList = new Cons(NIL, NIL);
this.leader = resolvedList;
this.follower = resolvedList;
}
public SExpression evaluate() {
SExpression expression = backTick.getExpression();
if (expression.isCons())
expression = resolveList((Cons) expression);
else if (expression.isComma())
expression = eval(((CommaExpression) expression).getExpression());
else if (expression.isAtSign())
throw new AtSignNotInCommaException();
return expression;
}
public SExpression resolveList(Cons list) {
createResolvedList(list);
return resolvedList;
}
private void createResolvedList(Cons list) {
for (; list.isCons(); list = (Cons) list.getRest())
resolveExpression(list.getFirst());
follower.setRest(NIL);
}
private void resolveExpression(SExpression expression) {
if (expression.isAtSign())
throw new AtSignNotInCommaException();
else if (expression.isComma())
resolveCommaExpression(expression);
else
addResolvedExpression(expression);
}
private void resolveCommaExpression(SExpression expression) {
CommaEvaluationResult result = evaluateComma((CommaExpression) expression);
if (result.isAtSign())
unpackResolvedList((Cons) result.getResult());
else
addResolvedExpression(result.getResult());
}
private CommaEvaluationResult evaluateComma(CommaExpression comma) {
SExpression expression = comma.getExpression();
validateCommaExpression(expression);
if (expression.isAtSign())
return new CommaEvaluationAtSignResult(evaluateAtSign((AtSignExpression) expression));
else
return new CommaEvaluationResult(eval(expression));
}
private void validateCommaExpression(SExpression expression) {
if (expression.isComma())
throw new NestedCommaException();
}
private Cons evaluateAtSign(AtSignExpression atSign) {
SExpression expression = atSign.getExpression();
validateAtSignExpression(expression);
SExpression result = eval(expression);
if (!result.isList())
throw new AtSignNotListException();
return (Cons) result;
}
private void validateAtSignExpression(SExpression expression) {
if (expression.isComma())
throw new NestedCommaException();
else if (expression.isAtSign())
throw new NestedAtSignException();
}
private void unpackResolvedList(Cons list) {
for (; list.isCons(); list = (Cons) list.getRest())
addResolvedExpression(list.getFirst());
}
private void addResolvedExpression(SExpression expression) {
leader.setFirst(expression);
leader.setRest(new Cons(NIL, NIL));
follower = leader;
leader = (Cons) leader.getRest();
}
private static class CommaEvaluationResult {
private SExpression result;
public CommaEvaluationResult(SExpression result) {
this.result = result;
}
public SExpression getResult() {
return result;
}
public boolean isAtSign() {
return false;
}
}
private static class CommaEvaluationAtSignResult extends CommaEvaluationResult {
public CommaEvaluationAtSignResult(SExpression result) {
super(result);
}
@Override
public boolean isAtSign() {
return true;
}
}
public static class NestedCommaException extends LispException {
private static final long serialVersionUID = 1L;
@Override
public String getMessage() {
return "nested comma";
}
}
public static class NestedAtSignException extends LispException {
private static final long serialVersionUID = 1L;
@Override
public String getMessage() {
return "nested at sign";
}
}
public static class AtSignNotInCommaException extends LispException {
private static final long serialVersionUID = 1L;
@Override
public String getMessage() {
return "at sign not in comma";
}
}
public static class AtSignNotListException extends LispException {
private static final long serialVersionUID = 1L;
@Override
public String getMessage() {
return "at sign did not evaluate to a list";
}
}
}

View File

@ -68,9 +68,14 @@ public class EVAL extends LispFunction {
private SExpression evaluateExpression(SExpression argument) {
if (argument.isList())
return evaluateList(argument);
if (argument.isSymbol())
else if (argument.isSymbol())
return evaluateSymbol(argument);
else if (argument.isBackTick())
return evaluateBackTick(argument);
else if (argument.isComma())
throw new UnmatchedCommaException();
else if (argument.isAtSign())
throw new UnmatchedAtSignException();
return argument; // NUMBER or STRING
}
@ -127,6 +132,12 @@ public class EVAL extends LispFunction {
throw new UndefinedSymbolException(argument);
}
private SExpression evaluateBackTick(SExpression argument) {
BackTickEvaluator evaluator = new BackTickEvaluator((BackTickExpression) argument);
return evaluator.evaluate();
}
public static class UndefinedFunctionException extends LispException {
private static final long serialVersionUID = 1L;
@ -157,4 +168,24 @@ public class EVAL extends LispFunction {
}
}
public static class UnmatchedCommaException extends LispException {
private static final long serialVersionUID = 1L;
@Override
public String getMessage() {
return "unmatched comma";
}
}
public static class UnmatchedAtSignException extends LispException {
private static final long serialVersionUID = 1L;
@Override
public String getMessage() {
return "unmatched at sign";
}
}
}

View File

@ -0,0 +1,23 @@
package sexpression;
public class AtSignExpression extends SExpression {
private SExpression expression;
public AtSignExpression(SExpression expression) {
this.expression = expression;
}
public SExpression getExpression() {
return expression;
}
public boolean isAtSign() {
return true;
}
public String toString() {
return "@" + expression;
}
}

View File

@ -0,0 +1,23 @@
package sexpression;
public class BackTickExpression extends SExpression {
private SExpression expression;
public BackTickExpression(SExpression expression) {
this.expression = expression;
}
public SExpression getExpression() {
return expression;
}
public boolean isBackTick() {
return true;
}
public String toString() {
return "`" + expression;
}
}

View File

@ -0,0 +1,23 @@
package sexpression;
public class CommaExpression extends SExpression {
private SExpression expression;
public CommaExpression(SExpression expression) {
this.expression = expression;
}
public SExpression getExpression() {
return expression;
}
public boolean isComma() {
return true;
}
public String toString() {
return "," + expression;
}
}

View File

@ -35,4 +35,16 @@ public abstract class SExpression {
return false;
}
public boolean isBackTick() {
return false;
}
public boolean isComma() {
return false;
}
public boolean isAtSign() {
return false;
}
}

22
src/token/AtSign.java Normal file
View File

@ -0,0 +1,22 @@
package token;
import java.util.function.Supplier;
import file.FilePosition;
import sexpression.*;
public class AtSign extends Token {
public AtSign(String text, FilePosition position) {
super(text, position);
}
@Override
public SExpression parseSExpression(Supplier<Token> getNextToken) {
Token nextToken = getNextToken.get();
SExpression argument = nextToken.parseSExpression(getNextToken);
return new AtSignExpression(argument);
}
}

22
src/token/BackTick.java Normal file
View File

@ -0,0 +1,22 @@
package token;
import java.util.function.Supplier;
import file.FilePosition;
import sexpression.*;
public class BackTick extends Token {
public BackTick(String text, FilePosition position) {
super(text, position);
}
@Override
public SExpression parseSExpression(Supplier<Token> getNextToken) {
Token nextToken = getNextToken.get();
SExpression argument = nextToken.parseSExpression(getNextToken);
return new BackTickExpression(argument);
}
}

22
src/token/Comma.java Normal file
View File

@ -0,0 +1,22 @@
package token;
import java.util.function.Supplier;
import file.FilePosition;
import sexpression.*;
public class Comma extends Token {
public Comma(String text, FilePosition position) {
super(text, position);
}
@Override
public SExpression parseSExpression(Supplier<Token> getNextToken) {
Token nextToken = getNextToken.get();
SExpression argument = nextToken.parseSExpression(getNextToken);
return new CommaExpression(argument);
}
}

View File

@ -22,6 +22,12 @@ 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 AT_SIGN:
return new AtSign(text, position);
case COMMA:
return new Comma(text, position);
default:
if (isNumeric(firstCharacter, text)) {
return new Number(text, position);

View File

@ -8,7 +8,10 @@ public final class Characters {
public static final int EOF = -1;
public static final char AT_SIGN = '@';
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 = '\"';
public static final char HASH = '#';
@ -21,22 +24,23 @@ public final class Characters {
public static final char RIGHT_SQUARE_BRACKET = ']';
public static final char SEMICOLON = ';';
public static final char SINGLE_QUOTE = '\'';
public static final char TICK_MARK = '`';
public static final Set<Character> illegalIdentifierCharacters = new HashSet<>();
static {
illegalIdentifierCharacters.add(DOUBLE_QUOTE);
illegalIdentifierCharacters.add(SINGLE_QUOTE);
illegalIdentifierCharacters.add(AT_SIGN);
illegalIdentifierCharacters.add(BACKSLASH);
illegalIdentifierCharacters.add(TICK_MARK);
illegalIdentifierCharacters.add(LEFT_PARENTHESIS);
illegalIdentifierCharacters.add(RIGHT_PARENTHESIS);
illegalIdentifierCharacters.add(LEFT_SQUARE_BRACKET);
illegalIdentifierCharacters.add(RIGHT_SQUARE_BRACKET);
illegalIdentifierCharacters.add(BACK_TICK);
illegalIdentifierCharacters.add(COMMA);
illegalIdentifierCharacters.add(DOUBLE_QUOTE);
illegalIdentifierCharacters.add(HASH);
illegalIdentifierCharacters.add(LEFT_PARENTHESIS);
illegalIdentifierCharacters.add(LEFT_SQUARE_BRACKET);
illegalIdentifierCharacters.add(PERIOD);
illegalIdentifierCharacters.add(RIGHT_PARENTHESIS);
illegalIdentifierCharacters.add(RIGHT_SQUARE_BRACKET);
illegalIdentifierCharacters.add(SEMICOLON);
illegalIdentifierCharacters.add(SINGLE_QUOTE);
}
public static boolean isLegalIdentifierCharacter(char c) {

View File

@ -0,0 +1,135 @@
package function.builtin;
import static sexpression.Nil.NIL;
import static testutil.TestUtilities.*;
import org.junit.Test;
import function.builtin.BackTickEvaluator.*;
import sexpression.*;
public class BackTickEvaluatorTester {
private BackTickEvaluator createBackTickEvaluator(SExpression expression) {
return new BackTickEvaluator(new BackTickExpression(expression));
}
@Test
public void evaluateNil() {
BackTickEvaluator evaluator = createBackTickEvaluator(NIL);
assertSExpressionsMatch(NIL, evaluator.evaluate());
}
@Test
public void evaluateNumber() {
SExpression input = new LispNumber("99");
BackTickEvaluator evaluator = createBackTickEvaluator(input);
assertSExpressionsMatch(input, evaluator.evaluate());
}
@Test
public void evaluateList() {
SExpression input = makeList(new LispNumber("1"), new LispNumber("99"));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
assertSExpressionsMatch(input, evaluator.evaluate());
}
@Test
public void evaluateComma() {
SExpression input = new CommaExpression(makeList(new Symbol("+"), new LispNumber("1"), new LispNumber("9")));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
assertSExpressionsMatch(new LispNumber("10"), evaluator.evaluate());
}
@Test
public void evaluateListWithComma() {
SExpression input = makeList(new CommaExpression(makeList(new Symbol("+"), new LispNumber("1"),
new LispNumber("9"))));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
assertSExpressionsMatch(makeList(new LispNumber("10")), evaluator.evaluate());
}
@Test(expected = NestedCommaException.class)
public void evaluateListWithNestedComma() {
SExpression input = makeList(new CommaExpression(new CommaExpression(new Symbol("+"))));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
evaluator.evaluate();
}
@Test(expected = AtSignNotInCommaException.class)
public void evaluateListWithNoCommaPrecedingAtSign() {
SExpression input = makeList(new AtSignExpression(makeList(new Symbol("+"))));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
evaluator.evaluate();
}
@Test(expected = AtSignNotInCommaException.class)
public void evaluateAtSign() {
SExpression input = new AtSignExpression(makeList(new Symbol("+")));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
evaluator.evaluate();
}
@Test(expected = NestedAtSignException.class)
public void evaluateListWithNestedAtSigns() {
SExpression input = makeList(new CommaExpression(new AtSignExpression(new AtSignExpression(makeList(new Symbol("+"))))));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
evaluator.evaluate();
}
@Test(expected = NestedCommaException.class)
public void evaluateListWithCommaAfterAtSign() {
SExpression input = makeList(new CommaExpression(new AtSignExpression(new CommaExpression(makeList(new Symbol("+"))))));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
evaluator.evaluate();
}
@Test
public void evaluateListWithAtSign() {
SExpression input = makeList(new CommaExpression(new AtSignExpression(makeList(new Symbol("LIST"),
new LispNumber("1"),
new LispNumber("9")))));
SExpression expected = makeList(new LispNumber("1"), new LispNumber("9"));
BackTickEvaluator evaluator = createBackTickEvaluator(input);
assertSExpressionsMatch(expected, evaluator.evaluate());
}
@Test(expected = AtSignNotListException.class)
public void atSignDoesNotEvaluateToList() {
SExpression input = makeList(new CommaExpression(new AtSignExpression(new LispNumber("1"))));
BackTickEvaluator evaluator = createBackTickEvaluator(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\""));
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);
assertSExpressionsMatch(expected, evaluator.evaluate());
}
}

View File

@ -9,6 +9,7 @@ import static testutil.TestUtilities.*;
import org.junit.Test;
import function.ArgumentValidator.*;
import function.builtin.BackTickEvaluator.AtSignNotInCommaException;
import function.builtin.EVAL.*;
public class EVALTester {
@ -99,4 +100,46 @@ public class EVALTester {
assertTrue(e.getMessage().length() > 0);
}
@Test(expected = UnmatchedCommaException.class)
public void evalComma() {
String input = ",a";
evaluateString(input);
}
@Test(expected = UnmatchedAtSignException.class)
public void evalAtSign() {
String input = "@a";
evaluateString(input);
}
@Test
public void evalBackTick() {
String input = "`(a b c)";
assertSExpressionsMatch(parseString("(a b c)"), evaluateString(input));
}
@Test
public void evalBackTickWithCommasAndAtSigns() {
String input = "(let ((x '(1 2 3)) (y '(4 5 6)) (z 'apple)) `(start ,x ,@y ,z end))";
assertSExpressionsMatch(parseString("(start (1 2 3) 4 5 6 apple end)"), evaluateString(input));
}
@Test
public void evalBackTickOnComma() {
String input = "`,9";
assertSExpressionsMatch(parseString("9"), evaluateString(input));
}
@Test(expected = AtSignNotInCommaException.class)
public void evalBackTickOnAtSign() {
String input = "`@9";
assertSExpressionsMatch(parseString("9"), evaluateString(input));
}
}

View File

@ -310,4 +310,61 @@ public class LispParserTester {
parser.getNextSExpression();
}
@Test
public void givenBackTickExpression_CreatesCorrectSExpression() {
String input = "`(list ,a ,@b)";
LispParser parser = createLispParser(input);
assertBackTickExpression(parser.getNextSExpression());
assertTrue(parser.isEof());
}
@Test
public void givenComma_CreatesCorrectSExpression() {
String input = ",a";
LispParser parser = createLispParser(input);
assertCommaExpression(parser.getNextSExpression());
assertTrue(parser.isEof());
}
@Test
public void givenAtSignExpression_CreatesCorrectSExpression() {
String input = "@b";
LispParser parser = createLispParser(input);
assertAtSignExpression(parser.getNextSExpression());
assertTrue(parser.isEof());
}
@Test
public void backTickIsNotPartOfIdentifier() {
String input = "id`ab";
LispParser parser = createLispParser(input);
assertSymbol(parser.getNextSExpression());
assertBackTickExpression(parser.getNextSExpression());
assertTrue(parser.isEof());
}
@Test
public void commaIsNotPartOfIdentifier() {
String input = "id,ab";
LispParser parser = createLispParser(input);
assertSymbol(parser.getNextSExpression());
assertCommaExpression(parser.getNextSExpression());
assertTrue(parser.isEof());
}
@Test
public void atSignIsNotPartOfIdentifier() {
String input = "id@ab";
LispParser parser = createLispParser(input);
assertSymbol(parser.getNextSExpression());
assertAtSignExpression(parser.getNextSExpression());
assertTrue(parser.isEof());
}
}

View File

@ -107,4 +107,12 @@ public class LispScannerTextTester {
assertTokenTextMatches(input, expected);
}
@Test
public void givenBackTickExpression_RecordsCorrectText() {
String input = "`(list ,a ,@b)";
String[] expected = { "`", "(", "list", ",", "a", ",", "@", "b", ")" };
assertTokenTextMatches(input, expected);
}
}

View File

@ -278,4 +278,20 @@ public class LispScannerTypeTester {
assertTokenTypesMatch(input);
}
@Test
public void givenBackTickExpression_ReturnsCorrectTypes() {
String input = "`(list ,a ,@b)";
expectedTypes.add(BackTick.class);
expectedTypes.add(LeftParenthesis.class);
expectedTypes.add(Identifier.class);
expectedTypes.add(Comma.class);
expectedTypes.add(Identifier.class);
expectedTypes.add(Comma.class);
expectedTypes.add(AtSign.class);
expectedTypes.add(Identifier.class);
expectedTypes.add(RightParenthesis.class);
assertTokenTypesMatch(input);
}
}

View File

@ -3,6 +3,7 @@ package sexpression;
import static error.ErrorManager.Severity.ERROR;
import static org.junit.Assert.*;
import static sexpression.Nil.NIL;
import static testutil.TestUtilities.*;
import java.math.BigInteger;
@ -21,59 +22,58 @@ public class SExpressionTester {
public void setUp() throws Exception {}
@Test
public void nilToString() {
public void nil_ToString() {
String input = "NIL";
assertSExpressionMatchesString(input, NIL);
}
@Test
public void numberToString() {
public void number_ToString() {
String input = "12";
assertSExpressionMatchesString(input, new LispNumber(input));
}
@Test
public void numberValueToString() {
public void numberValue_ToString() {
String expected = "12";
assertSExpressionMatchesString(expected, new LispNumber("12"));
}
@Test
public void stringToString() {
public void string_ToString() {
String input = "\"hi\"";
assertSExpressionMatchesString(input, new LispString(input));
}
@Test
public void symbolToString() {
public void symbol_ToString() {
String input = "symbol";
assertSExpressionMatchesString(input.toUpperCase(), new Symbol(input));
}
@Test
public void simpleConsToString() {
public void simpleCons_ToString() {
String expected = "(1)";
Cons cons = new Cons(new LispNumber("1"), NIL);
Cons cons = makeList(new LispNumber("1"));
assertSExpressionMatchesString(expected, cons);
}
@Test
public void complexConsToString() {
public void complexCons_ToString() {
String expected = "(1 A \"string\")";
Cons list = new Cons(new LispNumber("1"),
new Cons(new Symbol("a"), new Cons(new LispString("\"string\""), NIL)));
Cons list = makeList(new LispNumber("1"), new Symbol("a"), new LispString("\"string\""));
assertSExpressionMatchesString(expected, list);
}
@Test
public void improperListToString() {
public void improperList_ToString() {
String expected = "(A . B)";
Cons list = new Cons(new Symbol("A"), new Symbol("B"));
@ -81,26 +81,26 @@ public class SExpressionTester {
}
@Test
public void lambdaExpressionToString() {
public void lambdaExpression_ToString() {
String expected = "(LAMBDA)";
LambdaExpression lambda = new LambdaExpression(new Cons(new Symbol("lambda"), NIL), null);
LambdaExpression lambda = new LambdaExpression(makeList(new Symbol("lambda")), null);
assertSExpressionMatchesString(expected, lambda);
}
@Test
public void lambdaExpressionGetLambdaExpression() {
public void lambdaExpression_GetLambdaExpression() {
String expected = "(LAMBDA)";
LambdaExpression lambda = new LambdaExpression(new Cons(new Symbol("lambda"), NIL), null);
LambdaExpression lambda = new LambdaExpression(makeList(new Symbol("lambda")), null);
assertSExpressionMatchesString(expected, lambda.getLambdaExpression());
}
@Test
public void lambdaExpressionGetFunction() {
public void lambdaExpression_GetFunction() {
String expected = "(LAMBDA)";
UserDefinedFunction function = new UserDefinedFunction(expected, NIL, NIL);
LambdaExpression lambda = new LambdaExpression(new Cons(new Symbol("lambda"), NIL), function);
LambdaExpression lambda = new LambdaExpression(makeList(new Symbol("lambda")), function);
assertEquals(function, lambda.getFunction());
}
@ -163,4 +163,61 @@ public class SExpressionTester {
assertEquals(BigInteger.ONE, LispNumber.ONE.getValue());
}
@Test
public void backTickExpression_ToString() {
String expected = "`(TEST)";
SExpression backTick = new BackTickExpression(makeList(new Symbol("TEST")));
assertSExpressionMatchesString(expected, backTick);
}
@Test
public void commaExpression_ToString() {
String expected = ",A";
SExpression comma = new CommaExpression(new Symbol("A"));
assertSExpressionMatchesString(expected, comma);
}
@Test
public void atSignExpression_ToString() {
String expected = "@A";
SExpression atSign = new AtSignExpression(new Symbol("A"));
assertSExpressionMatchesString(expected, atSign);
}
@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")))));
assertSExpressionMatchesString(expected, backTick);
}
@Test
public void backTickExpression_GetExpression() {
SExpression expression = makeList(new Symbol("TEST"));
BackTickExpression backTick = new BackTickExpression(expression);
assertSExpressionsMatch(expression, backTick.getExpression());
}
@Test
public void commaExpression_GetExpression() {
SExpression expression = new Symbol("A");
CommaExpression comma = new CommaExpression(expression);
assertSExpressionsMatch(expression, comma.getExpression());
}
@Test
public void atSignExpression_GetExpression() {
SExpression expression = new Symbol("A");
AtSignExpression atSign = new AtSignExpression(expression);
assertSExpressionsMatch(expression, atSign.getExpression());
}
}

View File

@ -2,11 +2,13 @@ package testutil;
import static function.builtin.EVAL.eval;
import static org.junit.Assert.*;
import static sexpression.Nil.NIL;
import java.io.*;
import java.util.Arrays;
import parser.LispParser;
import sexpression.SExpression;
import sexpression.*;
public final class TestUtilities {
@ -41,4 +43,13 @@ public final class TestUtilities {
assertNotEquals(one.toString(), two.toString());
}
public static Cons makeList(SExpression... expressionList) {
if (expressionList.length == 0)
return NIL;
Cons rest = makeList(Arrays.copyOfRange(expressionList, 1, expressionList.length));
return new Cons(expressionList[0], rest);
}
}

View File

@ -17,6 +17,9 @@ public final class TypeAssertions {
assertFalse(sExpression.isNumber());
assertFalse(sExpression.isString());
assertFalse(sExpression.isSymbol());
assertFalse(sExpression.isBackTick());
assertFalse(sExpression.isComma());
assertFalse(sExpression.isAtSign());
}
public static void assertNil(SExpression sExpression) {
@ -30,7 +33,9 @@ public final class TypeAssertions {
assertFalse(sExpression.isNumber());
assertFalse(sExpression.isString());
assertTrue(sExpression.isSymbol());
assertFalse(sExpression.isBackTick());
assertFalse(sExpression.isComma());
assertFalse(sExpression.isAtSign());
}
public static void assertNumber(SExpression sExpression) {
@ -42,6 +47,9 @@ public final class TypeAssertions {
assertTrue(sExpression.isNumber());
assertFalse(sExpression.isString());
assertFalse(sExpression.isSymbol());
assertFalse(sExpression.isBackTick());
assertFalse(sExpression.isComma());
assertFalse(sExpression.isAtSign());
}
public static void assertString(SExpression sExpression) {
@ -53,6 +61,9 @@ public final class TypeAssertions {
assertFalse(sExpression.isNumber());
assertTrue(sExpression.isString());
assertFalse(sExpression.isSymbol());
assertFalse(sExpression.isBackTick());
assertFalse(sExpression.isComma());
assertFalse(sExpression.isAtSign());
}
public static void assertSymbol(SExpression sExpression) {
@ -64,10 +75,55 @@ public final class TypeAssertions {
assertFalse(sExpression.isNumber());
assertFalse(sExpression.isString());
assertTrue(sExpression.isSymbol());
assertFalse(sExpression.isBackTick());
assertFalse(sExpression.isComma());
assertFalse(sExpression.isAtSign());
}
public static void assertT(SExpression sExpression) {
assertEquals(T, sExpression);
}
public static void assertBackTickExpression(SExpression sExpression) {
assertFalse(sExpression.isAtom());
assertFalse(sExpression.isCons());
assertFalse(sExpression.isFunction());
assertFalse(sExpression.isList());
assertFalse(sExpression.isNull());
assertFalse(sExpression.isNumber());
assertFalse(sExpression.isString());
assertFalse(sExpression.isSymbol());
assertTrue(sExpression.isBackTick());
assertFalse(sExpression.isComma());
assertFalse(sExpression.isAtSign());
}
public static void assertCommaExpression(SExpression sExpression) {
assertFalse(sExpression.isAtom());
assertFalse(sExpression.isCons());
assertFalse(sExpression.isFunction());
assertFalse(sExpression.isList());
assertFalse(sExpression.isNull());
assertFalse(sExpression.isNumber());
assertFalse(sExpression.isString());
assertFalse(sExpression.isSymbol());
assertFalse(sExpression.isBackTick());
assertTrue(sExpression.isComma());
assertFalse(sExpression.isAtSign());
}
public static void assertAtSignExpression(SExpression sExpression) {
assertFalse(sExpression.isAtom());
assertFalse(sExpression.isCons());
assertFalse(sExpression.isFunction());
assertFalse(sExpression.isList());
assertFalse(sExpression.isNull());
assertFalse(sExpression.isNumber());
assertFalse(sExpression.isString());
assertFalse(sExpression.isSymbol());
assertFalse(sExpression.isBackTick());
assertFalse(sExpression.isComma());
assertTrue(sExpression.isAtSign());
}
}

View File

@ -84,22 +84,14 @@ public class TokenFactoryTester {
}
@Test
public void emptyTokenTextException_ContainsCorrectSeverity() {
try {
createToken("");
} catch (EmptyTokenTextException e) {
assertEquals(CRITICAL, e.getSeverity());
}
}
@Test
public void emptyTokenTextException_ContainsMessage() {
public void emptyTokenTextException_ContainsCorrectAttributes() {
try {
createToken("");
} catch (EmptyTokenTextException e) {
String message = e.getMessage();
assertNotNull(message);
assertTrue(message.length() > 0);
assertEquals(CRITICAL, e.getSeverity());
}
}
@ -108,4 +100,22 @@ public class TokenFactoryTester {
createToken("[abc]");
}
@Test
public void backTickCreation() {
String text = "`";
assertTrue(createToken(text) instanceof BackTick);
}
@Test
public void commaCreation() {
String text = ",";
assertTrue(createToken(text) instanceof Comma);
}
@Test
public void atSignCreation() {
String text = "@";
assertTrue(createToken(text) instanceof AtSign);
}
}