diff --git a/src/sexpression/LispNumber.java b/src/sexpression/LispNumber.java index 68d9853..a34e6c0 100644 --- a/src/sexpression/LispNumber.java +++ b/src/sexpression/LispNumber.java @@ -1,5 +1,9 @@ package sexpression; +import java.text.MessageFormat; + +import error.LispException; + public class LispNumber extends Atom { private int value; @@ -10,7 +14,7 @@ public class LispNumber extends Atom { try { this.value = Integer.parseInt(text); } catch (NumberFormatException e) { - throw new IllegalArgumentException(text + " is not a valid integer"); + throw new InvalidNumberException(text); } } @@ -28,4 +32,25 @@ public class LispNumber extends Atom { return value; } + public class InvalidNumberException extends LispException { + + private static final long serialVersionUID = 1L; + + private String text; + + public InvalidNumberException(String text) { + this.text = text; + } + + @Override + public int getSeverity() { + return 0; + } + + @Override + public String getMessage() { + return MessageFormat.format("{0} is not a valid integer", text); + } + } + } diff --git a/src/sexpression/SExpression.java b/src/sexpression/SExpression.java index 28a7c4e..cb86b15 100644 --- a/src/sexpression/SExpression.java +++ b/src/sexpression/SExpression.java @@ -2,20 +2,6 @@ package sexpression; public abstract class SExpression { - // for mark and sweep garbage collection - private boolean marked = false; - - public final boolean isMarked() { - return marked; - } - - public final void setMarked(final boolean value) { - marked = value; - } - - // Lisp type predicates... - // overridden in subclasses to describe their Lisp type - public boolean nullp() { return false; } diff --git a/src/token/Eof.java b/src/token/Eof.java index 6e8b22b..9302858 100644 --- a/src/token/Eof.java +++ b/src/token/Eof.java @@ -3,8 +3,8 @@ package token; import java.util.function.Supplier; import file.FilePosition; -import sexpression.MalformedSExpressionException.EofEncounteredException; import sexpression.SExpression; +import token.ParseException.EofEncounteredException; public class Eof extends Token { diff --git a/src/sexpression/MalformedSExpressionException.java b/src/token/ParseException.java similarity index 63% rename from src/sexpression/MalformedSExpressionException.java rename to src/token/ParseException.java index e2966e2..7c765ce 100644 --- a/src/sexpression/MalformedSExpressionException.java +++ b/src/token/ParseException.java @@ -1,16 +1,15 @@ -package sexpression; +package token; import java.text.MessageFormat; import error.LispException; -import token.Token; -public abstract class MalformedSExpressionException extends LispException { +public abstract class ParseException extends LispException { private static final long serialVersionUID = 1L; private Token token; - public MalformedSExpressionException(Token token) { + public ParseException(Token token) { this.token = token; } @@ -27,7 +26,7 @@ public abstract class MalformedSExpressionException extends LispException { public abstract String getMessagePrefix(); - public static class EofEncounteredException extends MalformedSExpressionException { + public static class EofEncounteredException extends ParseException { private static final long serialVersionUID = 1L; @@ -40,7 +39,7 @@ public abstract class MalformedSExpressionException extends LispException { } } - public static class StartsWithRightParenthesisException extends MalformedSExpressionException { + public static class StartsWithRightParenthesisException extends ParseException { private static final long serialVersionUID = 1L; @@ -53,17 +52,4 @@ public abstract class MalformedSExpressionException extends LispException { } } - public static class UnrecognizedTokenException extends MalformedSExpressionException { - - private static final long serialVersionUID = 1L; - - public UnrecognizedTokenException(Token token) { - super(token); - } - - public String getMessagePrefix() { - return "Unrecognized token"; - } - } - } \ No newline at end of file diff --git a/src/token/RightParenthesis.java b/src/token/RightParenthesis.java index 135788c..fca5cc3 100644 --- a/src/token/RightParenthesis.java +++ b/src/token/RightParenthesis.java @@ -3,9 +3,9 @@ package token; import java.util.function.Supplier; import file.FilePosition; -import sexpression.MalformedSExpressionException.StartsWithRightParenthesisException; import sexpression.Nil; import sexpression.SExpression; +import token.ParseException.StartsWithRightParenthesisException; public class RightParenthesis extends Token { diff --git a/test/parser/LispParserTester.java b/test/parser/LispParserTester.java index 16f13ae..8d961dd 100644 --- a/test/parser/LispParserTester.java +++ b/test/parser/LispParserTester.java @@ -1,6 +1,7 @@ package parser; import static org.junit.Assert.*; +import static parser.SExpressionTypeAssertions.*; import static testutil.TestUtilities.createIOExceptionThrowingInputStream; import static testutil.TestUtilities.createInputStreamFromString; @@ -8,13 +9,12 @@ import java.io.InputStream; import org.junit.Test; +import error.ErrorManager; import error.LispException; import scanner.LispInputStream.UncheckedIOException; import scanner.LispScanner.UnterminatedStringException; -import sexpression.MalformedSExpressionException.EofEncounteredException; -import sexpression.MalformedSExpressionException.StartsWithRightParenthesisException; -import sexpression.Nil; -import sexpression.SExpression; +import token.ParseException.EofEncounteredException; +import token.ParseException.StartsWithRightParenthesisException; import token.TokenFactory.BadCharacterException; public class LispParserTester { @@ -29,64 +29,6 @@ public class LispParserTester { return new LispParser(stringInputStream, "testFile"); } - private void assertList(SExpression sExpression) { - assertFalse(sExpression.atomp()); - assertTrue(sExpression.consp()); - assertFalse(sExpression.functionp()); - assertTrue(sExpression.listp()); - assertFalse(sExpression.nullp()); - assertFalse(sExpression.numberp()); - assertFalse(sExpression.stringp()); - assertFalse(sExpression.symbolp()); - } - - private void assertNil(SExpression sExpression) { - assertEquals(sExpression, Nil.getUniqueInstance()); - - assertTrue(sExpression.atomp()); - assertFalse(sExpression.consp()); - assertFalse(sExpression.functionp()); - assertTrue(sExpression.listp()); - assertTrue(sExpression.nullp()); - assertFalse(sExpression.numberp()); - assertFalse(sExpression.stringp()); - assertTrue(sExpression.symbolp()); - - } - - private void assertNumber(SExpression sExpression) { - assertTrue(sExpression.atomp()); - assertFalse(sExpression.consp()); - assertFalse(sExpression.functionp()); - assertFalse(sExpression.listp()); - assertFalse(sExpression.nullp()); - assertTrue(sExpression.numberp()); - assertFalse(sExpression.stringp()); - assertFalse(sExpression.symbolp()); - } - - private void assertString(SExpression sExpression) { - assertTrue(sExpression.atomp()); - assertFalse(sExpression.consp()); - assertFalse(sExpression.functionp()); - assertFalse(sExpression.listp()); - assertFalse(sExpression.nullp()); - assertFalse(sExpression.numberp()); - assertTrue(sExpression.stringp()); - assertFalse(sExpression.symbolp()); - } - - private void assertSymbol(SExpression sExpression) { - assertTrue(sExpression.atomp()); - assertFalse(sExpression.consp()); - assertFalse(sExpression.functionp()); - assertFalse(sExpression.listp()); - assertFalse(sExpression.nullp()); - assertFalse(sExpression.numberp()); - assertFalse(sExpression.stringp()); - assertTrue(sExpression.symbolp()); - } - @Test public void testEofMethod_ReturnsTrueWithNoInput() { String input = ""; @@ -107,8 +49,8 @@ public class LispParserTester { public void testEofMethod_ReturnsTrueAfterSomeInput() { String input = "(yyz 9 9 9)"; LispParser parser = createLispParser(input); - parser.getNextSExpression(); + parser.getNextSExpression(); assertTrue(parser.isEof()); } @@ -116,6 +58,7 @@ public class LispParserTester { public void testEofMethod_ReturnsFalseAfterMultipleExpressions() { String input = "()()()"; LispParser parser = createLispParser(input); + assertFalse(parser.isEof()); parser.getNextSExpression(); assertFalse(parser.isEof()); @@ -127,6 +70,7 @@ public class LispParserTester { public void testEofMethod_ReturnsTrueAfterMultipleExpressions() { String input = "()()()"; LispParser parser = createLispParser(input); + assertFalse(parser.isEof()); parser.getNextSExpression(); assertFalse(parser.isEof()); @@ -238,6 +182,33 @@ public class LispParserTester { parser.getNextSExpression(); } + @Test + public void givenBadToken_ExceptionHasCorrectSeverity() { + String input = "["; + LispParser parser = createLispParser(input); + + try { + parser.getNextSExpression(); + } catch (BadCharacterException e) { + assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL); + } + } + + @Test + public void givenBadToken_ExceptionHasMessageText() { + String input = "["; + LispParser parser = createLispParser(input); + + try { + parser.getNextSExpression(); + } catch (BadCharacterException e) { + String message = e.getMessage(); + + assertNotNull(message); + assertTrue(message.length() > 0); + } + } + @Test(expected = UnterminatedStringException.class) public void givenUnterminatedString_ThrowsException() { String input = "\"string"; @@ -254,6 +225,33 @@ public class LispParserTester { parser.getNextSExpression(); } + @Test + public void givenUnterminatedList_ExceptionHasCorrectSeverity() { + String input = "(bad list"; + LispParser parser = createLispParser(input); + + try { + parser.getNextSExpression(); + } catch (EofEncounteredException e) { + assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL); + } + } + + @Test + public void givenUnterminatedList_ExceptionHasMessage() { + String input = "(bad list"; + LispParser parser = createLispParser(input); + + try { + parser.getNextSExpression(); + } catch (EofEncounteredException e) { + String message = e.getMessage(); + + assertNotNull(message); + assertTrue(message.length() > 0); + } + } + @Test(expected = StartsWithRightParenthesisException.class) public void givenUnmatchedRightParenthesis_ThrowsException() { String input = ")"; @@ -262,6 +260,33 @@ public class LispParserTester { parser.getNextSExpression(); } + @Test + public void givenUnmatchedRightParenthesis_ExceptionHasCorrectSeverity() { + String input = ")"; + LispParser parser = createLispParser(input); + + try { + parser.getNextSExpression(); + } catch (StartsWithRightParenthesisException e) { + assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL); + } + } + + @Test + public void givenUnmatchedRightParenthesis_ExceptionHasMessage() { + String input = ")"; + LispParser parser = createLispParser(input); + + try { + parser.getNextSExpression(); + } catch (StartsWithRightParenthesisException e) { + String message = e.getMessage(); + + assertNotNull(message); + assertTrue(message.length() > 0); + } + } + @Test(expected = BadCharacterException.class) public void givenBadCharacter_ThrowsExceptionAfterEofCalled() { String input = "["; diff --git a/test/parser/SExpressionTypeAssertions.java b/test/parser/SExpressionTypeAssertions.java new file mode 100644 index 0000000..a173b8f --- /dev/null +++ b/test/parser/SExpressionTypeAssertions.java @@ -0,0 +1,68 @@ +package parser; + +import static org.junit.Assert.*; + +import sexpression.Nil; +import sexpression.SExpression; + +public final class SExpressionTypeAssertions { + + public static void assertList(SExpression sExpression) { + assertFalse(sExpression.atomp()); + assertTrue(sExpression.consp()); + assertFalse(sExpression.functionp()); + assertTrue(sExpression.listp()); + assertFalse(sExpression.nullp()); + assertFalse(sExpression.numberp()); + assertFalse(sExpression.stringp()); + assertFalse(sExpression.symbolp()); + } + + public static void assertNil(SExpression sExpression) { + assertEquals(sExpression, Nil.getUniqueInstance()); + + assertTrue(sExpression.atomp()); + assertFalse(sExpression.consp()); + assertFalse(sExpression.functionp()); + assertTrue(sExpression.listp()); + assertTrue(sExpression.nullp()); + assertFalse(sExpression.numberp()); + assertFalse(sExpression.stringp()); + assertTrue(sExpression.symbolp()); + + } + + public static void assertNumber(SExpression sExpression) { + assertTrue(sExpression.atomp()); + assertFalse(sExpression.consp()); + assertFalse(sExpression.functionp()); + assertFalse(sExpression.listp()); + assertFalse(sExpression.nullp()); + assertTrue(sExpression.numberp()); + assertFalse(sExpression.stringp()); + assertFalse(sExpression.symbolp()); + } + + public static void assertString(SExpression sExpression) { + assertTrue(sExpression.atomp()); + assertFalse(sExpression.consp()); + assertFalse(sExpression.functionp()); + assertFalse(sExpression.listp()); + assertFalse(sExpression.nullp()); + assertFalse(sExpression.numberp()); + assertTrue(sExpression.stringp()); + assertFalse(sExpression.symbolp()); + } + + public static void assertSymbol(SExpression sExpression) { + assertTrue(sExpression.atomp()); + assertFalse(sExpression.consp()); + assertFalse(sExpression.functionp()); + assertFalse(sExpression.listp()); + assertFalse(sExpression.nullp()); + assertFalse(sExpression.numberp()); + assertFalse(sExpression.stringp()); + assertTrue(sExpression.symbolp()); + } + +} diff --git a/test/sexpression/SExpressionTester.java b/test/sexpression/SExpressionTester.java index 3216773..7f9cd9b 100644 --- a/test/sexpression/SExpressionTester.java +++ b/test/sexpression/SExpressionTester.java @@ -1,10 +1,13 @@ package sexpression; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; +import error.ErrorManager; +import sexpression.LispNumber.InvalidNumberException; + public class SExpressionTester { private void assertSExpressionMatchesString(String expected, SExpression sExpression) { @@ -67,4 +70,72 @@ public class SExpressionTester { assertSExpressionMatchesString(expected, list); } + @Test + public void testConsWithNonListCdrToString() { + String expected = "(A . B)"; + Cons list = new Cons(new Symbol("A"), new Symbol("B")); + + assertSExpressionMatchesString(expected, list); + } + + @Test + public void testCarOfNilIsNil() { + assertEquals(Nil.getUniqueInstance().getCar(), Nil.getUniqueInstance()); + } + + @Test + public void testCdrOfNilIsNil() { + assertEquals(Nil.getUniqueInstance().getCdr(), Nil.getUniqueInstance()); + } + + @Test + public void afterSettingCarOfNil_ShouldStillBeNil() { + Cons nil = Nil.getUniqueInstance(); + nil.setCar(new LispNumber(2)); + + assertEquals(nil.getCar(), Nil.getUniqueInstance()); + } + + @Test + public void afterSettingCdrOfNil_ShouldStillBeNil() { + Cons nil = Nil.getUniqueInstance(); + nil.setCdr(new LispNumber(2)); + + assertEquals(nil.getCdr(), Nil.getUniqueInstance()); + } + + @Test + public void testNumberValue() { + int value = 12; + LispNumber number = new LispNumber(String.valueOf(value)); + + assertEquals(number.getValue(), value); + } + + @Test(expected = InvalidNumberException.class) + public void testInvalidNumberText_ThrowsException() { + new LispNumber("a"); + } + + @Test + public void testInvalidNumberException_HasCorrectSeverity() { + try { + new LispNumber("a"); + } catch (InvalidNumberException e) { + assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL); + } + } + + @Test + public void testInvalidNumberException_HasMessageText() { + try { + new LispNumber("a"); + } catch (InvalidNumberException e) { + String message = e.getMessage(); + + assertNotNull(message); + assertTrue(message.length() > 0); + } + } + } diff --git a/test/testutil/TestUtilities.java b/test/testutil/TestUtilities.java index 97e4b27..4081007 100644 --- a/test/testutil/TestUtilities.java +++ b/test/testutil/TestUtilities.java @@ -2,7 +2,7 @@ package testutil; import java.io.*; -public class TestUtilities { +public final class TestUtilities { public static InputStream createInputStreamFromString(String string) { return new ByteArrayInputStream(string.getBytes());