parent
655f49e612
commit
698305f07f
|
@ -1,10 +0,0 @@
|
|||
package function;
|
||||
|
||||
public abstract class LispMacro extends LispSpecialFunction {
|
||||
|
||||
@Override
|
||||
public boolean isMacro() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue