From 9064acd7edda36f7d2c40b9664e1fce10384fe90 Mon Sep 17 00:00:00 2001 From: Mike Cifelli Date: Sat, 5 May 2018 14:14:00 -0400 Subject: [PATCH] Convert lisp parser to kotlin --- src/main/kotlin/function/builtin/LOAD.java | 2 +- .../kotlin/interpreter/LispInterpreter.java | 4 +- src/main/kotlin/parser/LispParser.java | 63 --- src/main/kotlin/parser/LispParser.kt | 55 +++ .../kotlin/file/FilePositionTrackerTest.kt | 9 +- src/test/kotlin/parser/LispParserTest.java | 381 ------------------ src/test/kotlin/parser/LispParserTest.kt | 376 +++++++++++++++++ src/test/kotlin/testutil/TestUtilities.java | 2 +- src/test/kotlin/testutil/TypeAssertions.java | 129 ------ src/test/kotlin/testutil/TypeAssertions.kt | 136 +++++++ 10 files changed, 575 insertions(+), 582 deletions(-) delete mode 100644 src/main/kotlin/parser/LispParser.java create mode 100644 src/main/kotlin/parser/LispParser.kt delete mode 100644 src/test/kotlin/parser/LispParserTest.java create mode 100644 src/test/kotlin/parser/LispParserTest.kt delete mode 100644 src/test/kotlin/testutil/TypeAssertions.java create mode 100644 src/test/kotlin/testutil/TypeAssertions.kt diff --git a/src/main/kotlin/function/builtin/LOAD.java b/src/main/kotlin/function/builtin/LOAD.java index 2d080c0..c09d9bf 100644 --- a/src/main/kotlin/function/builtin/LOAD.java +++ b/src/main/kotlin/function/builtin/LOAD.java @@ -90,7 +90,7 @@ public class LOAD extends LispFunction { private boolean isSuccessfulEvaluation(LispParser parser) { while (!parser.isEof()) { try { - eval(parser.getNextSExpression()); + eval(parser.nextSExpression()); } catch (LispException e) { environment.getErrorManager().handle(e); return false; diff --git a/src/main/kotlin/interpreter/LispInterpreter.java b/src/main/kotlin/interpreter/LispInterpreter.java index a1eabb9..2534fdb 100644 --- a/src/main/kotlin/interpreter/LispInterpreter.java +++ b/src/main/kotlin/interpreter/LispInterpreter.java @@ -24,7 +24,7 @@ public class LispInterpreter { LispParser languageParser = new LispParser(file.getInputStream(), file.getName()); while (!languageParser.isEof()) - eval(languageParser.getNextSExpression()); + eval(languageParser.nextSExpression()); } } @@ -56,7 +56,7 @@ public class LispInterpreter { } protected SExpression evaluateNextSExpression() { - SExpression sExpression = parser.getNextSExpression(); + SExpression sExpression = parser.nextSExpression(); return eval(sExpression); } diff --git a/src/main/kotlin/parser/LispParser.java b/src/main/kotlin/parser/LispParser.java deleted file mode 100644 index 041584f..0000000 --- a/src/main/kotlin/parser/LispParser.java +++ /dev/null @@ -1,63 +0,0 @@ -package parser; - -import error.LispException; -import scanner.LispScanner; -import sexpression.SExpression; -import token.Eof; -import token.Token; - -import java.io.InputStream; - -/** - * Converts a stream of bytes into internal representations of Lisp s-expressions. - */ -public class LispParser { - - private LispScanner scanner; - private Token nextToken; - private LispException delayedException; - private boolean isNextTokenStored; - - public LispParser(InputStream inputStream, String fileName) { - scanner = new LispScanner(inputStream, fileName); - nextToken = null; - delayedException = null; - isNextTokenStored = false; - } - - public boolean isEof() { - if (!isNextTokenStored) - storeNextToken(); - - return nextToken instanceof Eof; - } - - private void storeNextToken() { - try { - nextToken = scanner.getNextToken(); - isNextTokenStored = true; - } catch (LispException e) { - delayedException = e; - } - } - - public SExpression getNextSExpression() { - throwDelayedExceptionIfNecessary(); - - if (!isNextTokenStored) - nextToken = scanner.getNextToken(); - - isNextTokenStored = false; - - return nextToken.parseSExpression(scanner::getNextToken); - } - - private void throwDelayedExceptionIfNecessary() { - if (delayedException != null) { - LispException exceptionToThrow = delayedException; - delayedException = null; - - throw exceptionToThrow; - } - } -} diff --git a/src/main/kotlin/parser/LispParser.kt b/src/main/kotlin/parser/LispParser.kt new file mode 100644 index 0000000..3caba9f --- /dev/null +++ b/src/main/kotlin/parser/LispParser.kt @@ -0,0 +1,55 @@ +package parser + +import error.LispException +import scanner.LispScanner +import sexpression.SExpression +import token.Eof +import token.Token +import java.io.InputStream + +/** + * Converts a stream of bytes into internal representations of Lisp s-expressions. + */ +class LispParser(inputStream: InputStream, fileName: String) { + + private val scanner: LispScanner = LispScanner(inputStream, fileName) + private var nextToken: Token? = null + private var delayedException: LispException? = null + private var isNextTokenStored: Boolean = false + + fun isEof(): Boolean { + if (!isNextTokenStored) + storeNextToken() + + return nextToken is Eof + } + + fun nextSExpression(): SExpression { + throwDelayedExceptionIfNecessary() + + if (!isNextTokenStored) + nextToken = scanner.nextToken + + isNextTokenStored = false + + return nextToken!!.parseSExpression({ scanner.nextToken }) + } + + private fun storeNextToken() { + try { + nextToken = scanner.nextToken + isNextTokenStored = true + } catch (e: LispException) { + delayedException = e + } + } + + private fun throwDelayedExceptionIfNecessary() { + if (delayedException != null) { + val exceptionToThrow = delayedException!! + delayedException = null + + throw exceptionToThrow + } + } +} diff --git a/src/test/kotlin/file/FilePositionTrackerTest.kt b/src/test/kotlin/file/FilePositionTrackerTest.kt index c4ddf5d..358ffe0 100644 --- a/src/test/kotlin/file/FilePositionTrackerTest.kt +++ b/src/test/kotlin/file/FilePositionTrackerTest.kt @@ -29,30 +29,29 @@ class FilePositionTrackerTest { } @Test - fun noMovement_ReturnsInitialPosition() { + fun `no movement returns the initial position`() { assertTrackerPositionEquals(createFilePosition(1, 0)) } @Test - fun advanceOneColumn_ReturnsCorrectPosition() { + fun `advancing one column returns correct position`() { trackerUnderTest.incrementColumn() assertTrackerPositionEquals(createFilePosition(1, 1)) } @Test - fun advanceOneLine_ReturnsCorrectPosition() { + fun `advancing one line returns correct position`() { trackerUnderTest.incrementLine() assertTrackerPositionEquals(createFilePosition(2, 0)) } @Test - fun advanceOneLine_ResetsColumn() { + fun `advancing one line resets column number`() { trackerUnderTest.incrementColumn() trackerUnderTest.incrementLine() assertTrackerPositionEquals(createFilePosition(2, 0)) } - } diff --git a/src/test/kotlin/parser/LispParserTest.java b/src/test/kotlin/parser/LispParserTest.java deleted file mode 100644 index af6b15a..0000000 --- a/src/test/kotlin/parser/LispParserTest.java +++ /dev/null @@ -1,381 +0,0 @@ -package parser; - -import error.LispException; -import org.junit.Test; -import scanner.LispScanner.UnterminatedStringException; -import stream.UncheckedIOException; -import token.Eof.EofEncounteredException; -import token.RightParenthesis.StartsWithRightParenthesisException; -import token.TokenFactory.BadCharacterException; - -import java.io.InputStream; - -import static error.Severity.ERROR; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static testutil.TestUtilities.createIOExceptionThrowingInputStream; -import static testutil.TestUtilities.createInputStreamFromString; -import static testutil.TypeAssertions.assertAtSignExpression; -import static testutil.TypeAssertions.assertBackTickExpression; -import static testutil.TypeAssertions.assertCommaExpression; -import static testutil.TypeAssertions.assertList; -import static testutil.TypeAssertions.assertNil; -import static testutil.TypeAssertions.assertNumber; -import static testutil.TypeAssertions.assertString; -import static testutil.TypeAssertions.assertSymbol; - -public class LispParserTest { - - private LispParser createLispParser(String input) { - InputStream stringInputStream = createInputStreamFromString(input); - return new LispParser(stringInputStream, "testFile"); - } - - private LispParser createIOExceptionThrowingLispParser() { - InputStream stringInputStream = createIOExceptionThrowingInputStream(); - return new LispParser(stringInputStream, "testFile"); - } - - @Test - public void eofMethod_ReturnsTrueWithNoInput() { - String input = ""; - LispParser parser = createLispParser(input); - - assertTrue(parser.isEof()); - } - - @Test - public void eofMethod_ReturnsFalseWithSomeInput() { - String input = "abc"; - LispParser parser = createLispParser(input); - - assertFalse(parser.isEof()); - } - - @Test - public void eofMethod_ReturnsTrueAfterSomeInput() { - String input = "(yyz 9 9 9)"; - LispParser parser = createLispParser(input); - - parser.getNextSExpression(); - assertTrue(parser.isEof()); - } - - @Test - public void eofMethod_ReturnsFalseAfterMultipleExpressions() { - String input = "()()()"; - LispParser parser = createLispParser(input); - - assertFalse(parser.isEof()); - parser.getNextSExpression(); - assertFalse(parser.isEof()); - parser.getNextSExpression(); - assertFalse(parser.isEof()); - } - - @Test - public void eofMethod_ReturnsTrueAfterMultipleExpressions() { - String input = "()()()"; - LispParser parser = createLispParser(input); - - assertFalse(parser.isEof()); - parser.getNextSExpression(); - assertFalse(parser.isEof()); - parser.getNextSExpression(); - assertFalse(parser.isEof()); - parser.getNextSExpression(); - assertTrue(parser.isEof()); - } - - @Test - public void givenNil_CreatesCorrectSExpression() { - String input = "()"; - LispParser parser = createLispParser(input); - - assertNil(parser.getNextSExpression()); - } - - @Test - public void givenNumber_CreatesCorrectSExpression() { - String input = "12"; - LispParser parser = createLispParser(input); - - assertNumber(parser.getNextSExpression()); - } - - @Test - public void givenIdentifier_CreatesCorrectSExpression() { - String input = "identifier1"; - LispParser parser = createLispParser(input); - - assertSymbol(parser.getNextSExpression()); - } - - @Test - public void givenString_CreatesCorrectSExpression() { - String input = "\"string\""; - LispParser parser = createLispParser(input); - - assertString(parser.getNextSExpression()); - } - - @Test - public void givenList_CreatesCorrectSExpression() { - String input = "(1 2)"; - LispParser parser = createLispParser(input); - - assertList(parser.getNextSExpression()); - } - - @Test - public void givenQuotedIdentifier_CreatesCorrectSExpression() { - String input = "'quoted"; - LispParser parser = createLispParser(input); - - assertList(parser.getNextSExpression()); - assertTrue(parser.isEof()); - } - - @Test - public void givenComplexList_CreatesCorrectSExpression() { - String input = "(defun f (x) \n (print \n (list \"x is \" x) \n ) \n )"; - LispParser parser = createLispParser(input); - - assertList(parser.getNextSExpression()); - assertTrue(parser.isEof()); - } - - @Test - public void givenMultipleComplexLists_CreatesCorrectSExpressions() { - String input = "(defun f (x) \n (print \n (list \"x is \" x) \n ) \n )" - + "(defun f (x) \n (print \n (list \"x is \" x) \n ) \n )"; - LispParser parser = createLispParser(input); - - assertList(parser.getNextSExpression()); - assertList(parser.getNextSExpression()); - assertTrue(parser.isEof()); - } - - @Test - public void givenMultipleExpressions_CreatesCorrectSExpressions() { - String input = "(setq x 2) x \"hi\" () 29"; - LispParser parser = createLispParser(input); - - assertList(parser.getNextSExpression()); - assertSymbol(parser.getNextSExpression()); - assertString(parser.getNextSExpression()); - assertNil(parser.getNextSExpression()); - assertNumber(parser.getNextSExpression()); - assertTrue(parser.isEof()); - } - - @Test - public void givenNil_CreatesCorrectSExpressionAfterEofCalls() { - String input = "()"; - LispParser parser = createLispParser(input); - - parser.isEof(); - parser.isEof(); - - assertNil(parser.getNextSExpression()); - assertTrue(parser.isEof()); - } - - @Test(expected = BadCharacterException.class) - public void givenBadToken_ThrowsException() { - String input = "["; - LispParser parser = createLispParser(input); - - parser.getNextSExpression(); - } - - @Test - public void givenBadToken_ExceptionHasCorrectAttributes() { - String input = "["; - LispParser parser = createLispParser(input); - - try { - parser.getNextSExpression(); - } catch (BadCharacterException e) { - String message = e.getMessage(); - - assertEquals(ERROR, e.getSeverity()); - assertNotNull(message); - assertTrue(message.length() > 0); - } - } - - @Test(expected = UnterminatedStringException.class) - public void givenUnterminatedString_ThrowsException() { - String input = "\"string"; - LispParser parser = createLispParser(input); - - parser.getNextSExpression(); - } - - @Test(expected = EofEncounteredException.class) - public void givenUnterminatedList_ThrowsException() { - String input = "(bad list"; - LispParser parser = createLispParser(input); - - parser.getNextSExpression(); - } - - @Test - public void givenUnterminatedList_ExceptionHasCorrectAttributes() { - String input = "(bad list"; - LispParser parser = createLispParser(input); - - try { - parser.getNextSExpression(); - } catch (EofEncounteredException e) { - String message = e.getMessage(); - - assertEquals(ERROR, e.getSeverity()); - assertNotNull(message); - assertTrue(message.length() > 0); - } - } - - @Test(expected = StartsWithRightParenthesisException.class) - public void givenUnmatchedRightParenthesis_ThrowsException() { - String input = ")"; - LispParser parser = createLispParser(input); - - parser.getNextSExpression(); - } - - @Test - public void givenUnmatchedRightParenthesis_ExceptionHasCorrectAttributes() { - String input = ")"; - LispParser parser = createLispParser(input); - - try { - parser.getNextSExpression(); - } catch (StartsWithRightParenthesisException e) { - String message = e.getMessage(); - - assertEquals(ERROR, e.getSeverity()); - assertNotNull(message); - assertTrue(message.length() > 0); - } - } - - @Test(expected = BadCharacterException.class) - public void givenBadCharacter_ThrowsExceptionAfterEofCalled() { - String input = "["; - LispParser parser = createLispParser(input); - - try { - parser.isEof(); - } catch (LispException e) { - fail("Exception thrown too early"); - } - - parser.getNextSExpression(); - } - - @Test(expected = BadCharacterException.class) - public void givenBadCharacterAfterValidToken_ThrowsExceptionAtTheCorrectTime() { - String input = "id["; - LispParser parser = createLispParser(input); - - try { - parser.getNextSExpression(); - parser.isEof(); - } catch (LispException e) { - fail("Exception thrown too early"); - } - - parser.getNextSExpression(); - } - - @Test - public void afterException_ReturnsEofCorrectly() { - String input = "id["; - LispParser parser = createLispParser(input); - parser.getNextSExpression(); - parser.isEof(); - - try { - parser.getNextSExpression(); - fail("Expected LispException"); - } catch (LispException e) { - } - - assertTrue(parser.isEof()); - } - - @Test(expected = UncheckedIOException.class) - public void handlesIOExceptionCorrectly() { - LispParser parser = createIOExceptionThrowingLispParser(); - - try { - parser.isEof(); - } catch (LispException e) { - fail("Exception thrown too early"); - } - - 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()); - } -} diff --git a/src/test/kotlin/parser/LispParserTest.kt b/src/test/kotlin/parser/LispParserTest.kt new file mode 100644 index 0000000..b0667d8 --- /dev/null +++ b/src/test/kotlin/parser/LispParserTest.kt @@ -0,0 +1,376 @@ +package parser + +import error.LispException +import error.Severity.ERROR +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Assertions.fail +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS +import scanner.LispScanner.UnterminatedStringException +import stream.UncheckedIOException +import testutil.TestUtilities.createIOExceptionThrowingInputStream +import testutil.TestUtilities.createInputStreamFromString +import testutil.TypeAssertions.assertAtSignExpression +import testutil.TypeAssertions.assertBackTickExpression +import testutil.TypeAssertions.assertCommaExpression +import testutil.TypeAssertions.assertList +import testutil.TypeAssertions.assertNil +import testutil.TypeAssertions.assertNumber +import testutil.TypeAssertions.assertString +import testutil.TypeAssertions.assertSymbol +import token.Eof.EofEncounteredException +import token.RightParenthesis.StartsWithRightParenthesisException +import token.TokenFactory.BadCharacterException + +@TestInstance(PER_CLASS) +class LispParserTest { + + private fun createLispParser(input: String): LispParser { + val stringInputStream = createInputStreamFromString(input) + return LispParser(stringInputStream, "testFile") + } + + private fun createIOExceptionThrowingLispParser(): LispParser { + val stringInputStream = createIOExceptionThrowingInputStream() + return LispParser(stringInputStream, "testFile") + } + + @Test + fun `EOF check is true with no input`() { + val input = "" + val parser = createLispParser(input) + + assertThat(parser.isEof()) + } + + @Test + fun `EOF check is false after some input`() { + val input = "abc" + val parser = createLispParser(input) + + assertThat(parser.isEof()).isFalse() + } + + @Test + fun `EOF check is true after some input`() { + val input = "(yyz 9 9 9)" + val parser = createLispParser(input) + + parser.nextSExpression() + assertThat(parser.isEof()) + } + + @Test + fun `EOF check is false after multiple expressions`() { + val input = "()()()" + val parser = createLispParser(input) + + assertThat(parser.isEof()).isFalse() + parser.nextSExpression() + assertThat(parser.isEof()).isFalse() + parser.nextSExpression() + assertThat(parser.isEof()).isFalse() + } + + @Test + fun `EOF check is true after multiple expressions`() { + val input = "()()()" + val parser = createLispParser(input) + + assertThat(parser.isEof()).isFalse() + parser.nextSExpression() + assertThat(parser.isEof()).isFalse() + parser.nextSExpression() + assertThat(parser.isEof()).isFalse() + parser.nextSExpression() + assertThat(parser.isEof()).isTrue() + } + + @Test + fun `nil is parsed correctly`() { + val input = "()" + val parser = createLispParser(input) + + assertNil(parser.nextSExpression()) + } + + @Test + fun `number is parsed correctly`() { + val input = "12" + val parser = createLispParser(input) + + assertNumber(parser.nextSExpression()) + } + + @Test + fun `identifier is parsed correctly`() { + val input = "identifier1" + val parser = createLispParser(input) + + assertSymbol(parser.nextSExpression()) + } + + @Test + fun `string is parsed correctly`() { + val input = "\"string\"" + val parser = createLispParser(input) + + assertString(parser.nextSExpression()) + } + + @Test + fun `list is parsed correctly`() { + val input = "(1 2)" + val parser = createLispParser(input) + + assertList(parser.nextSExpression()) + } + + @Test + fun `quoted identifier is parsed correctly`() { + val input = "'quoted" + val parser = createLispParser(input) + + assertList(parser.nextSExpression()) + assertThat(parser.isEof()).isTrue() + } + + @Test + fun `complex list is parsed correctly`() { + val input = "(defun f (x) \n (print \n (list \"x is \" x) \n ) \n )" + val parser = createLispParser(input) + + assertList(parser.nextSExpression()) + assertThat(parser.isEof()).isTrue() + } + + @Test + fun `multiple complex lists are parsed correctly`() { + val input = "(defun f (x) \n (print \n (list \"x is \" x) \n ) \n )" + + "(defun f (x) \n (print \n (list \"x is \" x) \n ) \n )" + val parser = createLispParser(input) + + assertList(parser.nextSExpression()) + assertList(parser.nextSExpression()) + assertThat(parser.isEof()).isTrue() + } + + @Test + fun `multiple expressions are parsed correctly`() { + val input = "(setq x 2) x \"hi\" () 29" + val parser = createLispParser(input) + + assertList(parser.nextSExpression()) + assertSymbol(parser.nextSExpression()) + assertString(parser.nextSExpression()) + assertNil(parser.nextSExpression()) + assertNumber(parser.nextSExpression()) + assertThat(parser.isEof()).isTrue() + } + + @Test + fun `nil is parsed correctly after EOF checks`() { + val input = "()" + val parser = createLispParser(input) + + parser.isEof() + parser.isEof() + + assertNil(parser.nextSExpression()) + assertThat(parser.isEof()).isTrue() + } + + @Test + fun `throws exception for a bad token`() { + val input = "[" + val parser = createLispParser(input) + + assertThrows(BadCharacterException::class.java) { parser.nextSExpression() } + } + + @Test + fun `bad token exception is cool`() { + val input = "[" + val parser = createLispParser(input) + + try { + parser.nextSExpression() + } catch (e: BadCharacterException) { + val message = e.message + + assertThat(message).isNotEmpty(); + assertThat(e.severity).isEqualTo(ERROR) + } + } + + @Test + fun `throws exception for unterminated string`() { + val input = "\"string" + val parser = createLispParser(input) + + assertThrows(UnterminatedStringException::class.java) { parser.nextSExpression() } + } + + @Test + fun `throws exception for unterminated list`() { + val input = "(bad list" + val parser = createLispParser(input) + + assertThrows(EofEncounteredException::class.java) { parser.nextSExpression() } + } + + @Test + fun `unterminated list exception is cool`() { + val input = "(bad list" + val parser = createLispParser(input) + + try { + parser.nextSExpression() + } catch (e: EofEncounteredException) { + val message = e.message + + assertThat(message).isNotEmpty() + assertThat(e.severity).isEqualTo(ERROR) + } + } + + @Test + fun `throws exception for unmatched right parenthesis`() { + val input = ")" + val parser = createLispParser(input) + + assertThrows(StartsWithRightParenthesisException::class.java) { parser.nextSExpression() } + } + + @Test + fun `unmatched right parenthesis exception is cool`() { + val input = ")" + val parser = createLispParser(input) + + try { + parser.nextSExpression() + } catch (e: StartsWithRightParenthesisException) { + val message = e.message + + assertThat(message).isNotEmpty() + assertThat(e.severity).isEqualTo(ERROR) + } + } + + @Test + fun `bad character throws exception after an EOF check`() { + val input = "[" + val parser = createLispParser(input) + + try { + parser.isEof() + } catch (e: LispException) { + fail("Exception thrown too early") + } + + assertThrows(BadCharacterException::class.java) { parser.nextSExpression() } + } + + @Test + fun `bad character throws exception at the correct time`() { + val input = "id[" + val parser = createLispParser(input) + + try { + parser.nextSExpression() + parser.isEof() + } catch (e: LispException) { + fail("Exception thrown too early") + } + + assertThrows(BadCharacterException::class.java) { parser.nextSExpression() } + } + + @Test + fun `EOF is returned after an exception`() { + val input = "id[" + val parser = createLispParser(input) + parser.nextSExpression() + parser.isEof() + + try { + parser.nextSExpression() + fail("Expected LispException") + } catch (e: LispException) { + } + + assertThat(parser.isEof()).isTrue() + } + + @Test + fun `IOException is handled correctly`() { + val parser = createIOExceptionThrowingLispParser() + + try { + parser.isEof() + } catch (e: LispException) { + fail("Exception thrown too early") + } + + assertThrows(UncheckedIOException::class.java) { parser.nextSExpression() } + } + + @Test + fun `back tick is parsed correctly`() { + val input = "`(list ,a ,@b)" + val parser = createLispParser(input) + + assertBackTickExpression(parser.nextSExpression()) + assertThat(parser.isEof()).isTrue() + } + + @Test + fun `comma is parsed correctly`() { + val input = ",a" + val parser = createLispParser(input) + + assertCommaExpression(parser.nextSExpression()) + assertThat(parser.isEof()).isTrue() + } + + @Test + fun `at sign is parsed correctly`() { + val input = "@b" + val parser = createLispParser(input) + + assertAtSignExpression(parser.nextSExpression()) + assertThat(parser.isEof()).isTrue() + } + + @Test + fun `back tick is not part of an identifier`() { + val input = "id`ab" + val parser = createLispParser(input) + + assertSymbol(parser.nextSExpression()) + assertBackTickExpression(parser.nextSExpression()) + assertThat(parser.isEof()).isTrue() + } + + @Test + fun `comma is not part of an identifier`() { + val input = "id,ab" + val parser = createLispParser(input) + + assertSymbol(parser.nextSExpression()) + assertCommaExpression(parser.nextSExpression()) + assertThat(parser.isEof()).isTrue() + } + + @Test + fun `at sign is not part of an identifier`() { + val input = "id@ab" + val parser = createLispParser(input) + + assertSymbol(parser.nextSExpression()) + assertAtSignExpression(parser.nextSExpression()) + assertThat(parser.isEof()).isTrue() + } +} diff --git a/src/test/kotlin/testutil/TestUtilities.java b/src/test/kotlin/testutil/TestUtilities.java index 2f8c3db..e5062bb 100644 --- a/src/test/kotlin/testutil/TestUtilities.java +++ b/src/test/kotlin/testutil/TestUtilities.java @@ -72,7 +72,7 @@ public final class TestUtilities { public static SExpression parseString(String input) { InputStream stringInputStream = TestUtilities.createInputStreamFromString(input); - return new LispParser(stringInputStream, "testFile").getNextSExpression(); + return new LispParser(stringInputStream, "testFile").nextSExpression(); } public static Cons makeList(SExpression... expressionList) { diff --git a/src/test/kotlin/testutil/TypeAssertions.java b/src/test/kotlin/testutil/TypeAssertions.java deleted file mode 100644 index 09975ab..0000000 --- a/src/test/kotlin/testutil/TypeAssertions.java +++ /dev/null @@ -1,129 +0,0 @@ -package testutil; - -import sexpression.SExpression; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static sexpression.Nil.NIL; -import static sexpression.Symbol.T; - -public final class TypeAssertions { - - public static void assertList(SExpression sExpression) { - assertThat(sExpression.isAtom(), is(false)); - assertThat(sExpression.isCons(), is(true)); - assertThat(sExpression.isFunction(), is(false)); - assertThat(sExpression.isList(), is(true)); - assertThat(sExpression.isNull(), is(false)); - assertThat(sExpression.isNumber(), is(false)); - assertThat(sExpression.isString(), is(false)); - assertThat(sExpression.isSymbol(), is(false)); - assertThat(sExpression.isBackquote(), is(false)); - assertThat(sExpression.isComma(), is(false)); - assertThat(sExpression.isAtSign(), is(false)); - } - - public static void assertNil(SExpression sExpression) { - assertThat(sExpression, is(NIL)); - - assertThat(sExpression.isAtom(), is(true)); - assertThat(sExpression.isCons(), is(false)); - assertThat(sExpression.isFunction(), is(false)); - assertThat(sExpression.isList(), is(true)); - assertThat(sExpression.isNull(), is(true)); - assertThat(sExpression.isNumber(), is(false)); - assertThat(sExpression.isString(), is(false)); - assertThat(sExpression.isSymbol(), is(true)); - assertThat(sExpression.isBackquote(), is(false)); - assertThat(sExpression.isComma(), is(false)); - assertThat(sExpression.isAtSign(), is(false)); - } - - public static void assertNumber(SExpression sExpression) { - assertThat(sExpression.isAtom(), is(true)); - assertThat(sExpression.isCons(), is(false)); - assertThat(sExpression.isFunction(), is(false)); - assertThat(sExpression.isList(), is(false)); - assertThat(sExpression.isNull(), is(false)); - assertThat(sExpression.isNumber(), is(true)); - assertThat(sExpression.isString(), is(false)); - assertThat(sExpression.isSymbol(), is(false)); - assertThat(sExpression.isBackquote(), is(false)); - assertThat(sExpression.isComma(), is(false)); - assertThat(sExpression.isAtSign(), is(false)); - } - - public static void assertString(SExpression sExpression) { - assertThat(sExpression.isAtom(), is(true)); - assertThat(sExpression.isCons(), is(false)); - assertThat(sExpression.isFunction(), is(false)); - assertThat(sExpression.isList(), is(false)); - assertThat(sExpression.isNull(), is(false)); - assertThat(sExpression.isNumber(), is(false)); - assertThat(sExpression.isString(), is(true)); - assertThat(sExpression.isSymbol(), is(false)); - assertThat(sExpression.isBackquote(), is(false)); - assertThat(sExpression.isComma(), is(false)); - assertThat(sExpression.isAtSign(), is(false)); - } - - public static void assertSymbol(SExpression sExpression) { - assertThat(sExpression.isAtom(), is(true)); - assertThat(sExpression.isCons(), is(false)); - assertThat(sExpression.isFunction(), is(false)); - assertThat(sExpression.isList(), is(false)); - assertThat(sExpression.isNull(), is(false)); - assertThat(sExpression.isNumber(), is(false)); - assertThat(sExpression.isString(), is(false)); - assertThat(sExpression.isSymbol(), is(true)); - assertThat(sExpression.isBackquote(), is(false)); - assertThat(sExpression.isComma(), is(false)); - assertThat(sExpression.isAtSign(), is(false)); - } - - public static void assertT(SExpression sExpression) { - assertThat(sExpression, is(T)); - } - - public static void assertBackTickExpression(SExpression sExpression) { - assertThat(sExpression.isAtom(), is(false)); - assertThat(sExpression.isCons(), is(false)); - assertThat(sExpression.isFunction(), is(false)); - assertThat(sExpression.isList(), is(false)); - assertThat(sExpression.isNull(), is(false)); - assertThat(sExpression.isNumber(), is(false)); - assertThat(sExpression.isString(), is(false)); - assertThat(sExpression.isSymbol(), is(false)); - assertThat(sExpression.isBackquote(), is(true)); - assertThat(sExpression.isComma(), is(false)); - assertThat(sExpression.isAtSign(), is(false)); - } - - public static void assertCommaExpression(SExpression sExpression) { - assertThat(sExpression.isAtom(), is(false)); - assertThat(sExpression.isCons(), is(false)); - assertThat(sExpression.isFunction(), is(false)); - assertThat(sExpression.isList(), is(false)); - assertThat(sExpression.isNull(), is(false)); - assertThat(sExpression.isNumber(), is(false)); - assertThat(sExpression.isString(), is(false)); - assertThat(sExpression.isSymbol(), is(false)); - assertThat(sExpression.isBackquote(), is(false)); - assertThat(sExpression.isComma(), is(true)); - assertThat(sExpression.isAtSign(), is(false)); - } - - public static void assertAtSignExpression(SExpression sExpression) { - assertThat(sExpression.isAtom(), is(false)); - assertThat(sExpression.isCons(), is(false)); - assertThat(sExpression.isFunction(), is(false)); - assertThat(sExpression.isList(), is(false)); - assertThat(sExpression.isNull(), is(false)); - assertThat(sExpression.isNumber(), is(false)); - assertThat(sExpression.isString(), is(false)); - assertThat(sExpression.isSymbol(), is(false)); - assertThat(sExpression.isBackquote(), is(false)); - assertThat(sExpression.isComma(), is(false)); - assertThat(sExpression.isAtSign(), is(true)); - } -} diff --git a/src/test/kotlin/testutil/TypeAssertions.kt b/src/test/kotlin/testutil/TypeAssertions.kt new file mode 100644 index 0000000..a313402 --- /dev/null +++ b/src/test/kotlin/testutil/TypeAssertions.kt @@ -0,0 +1,136 @@ +package testutil + +import org.assertj.core.api.Assertions.assertThat +import sexpression.Nil.NIL +import sexpression.SExpression +import sexpression.Symbol.T + +object TypeAssertions { + + @JvmStatic() + fun assertList(sExpression: SExpression) { + assertThat(sExpression.isAtom).isFalse() + assertThat(sExpression.isCons).isTrue() + assertThat(sExpression.isFunction).isFalse() + assertThat(sExpression.isList).isTrue() + assertThat(sExpression.isNull).isFalse() + assertThat(sExpression.isNumber).isFalse() + assertThat(sExpression.isString).isFalse() + assertThat(sExpression.isSymbol).isFalse() + assertThat(sExpression.isBackquote).isFalse() + assertThat(sExpression.isComma).isFalse() + assertThat(sExpression.isAtSign).isFalse() + } + + @JvmStatic() + fun assertNil(sExpression: SExpression) { + assertThat(sExpression).isEqualTo(NIL) + + assertThat(sExpression.isAtom).isTrue() + assertThat(sExpression.isCons).isFalse() + assertThat(sExpression.isFunction).isFalse() + assertThat(sExpression.isList).isTrue() + assertThat(sExpression.isNull).isTrue() + assertThat(sExpression.isNumber).isFalse() + assertThat(sExpression.isString).isFalse() + assertThat(sExpression.isSymbol).isTrue() + assertThat(sExpression.isBackquote).isFalse() + assertThat(sExpression.isComma).isFalse() + assertThat(sExpression.isAtSign).isFalse() + } + + @JvmStatic() + fun assertNumber(sExpression: SExpression) { + assertThat(sExpression.isAtom).isTrue() + assertThat(sExpression.isCons).isFalse() + assertThat(sExpression.isFunction).isFalse() + assertThat(sExpression.isList).isFalse() + assertThat(sExpression.isNull).isFalse() + assertThat(sExpression.isNumber).isTrue() + assertThat(sExpression.isString).isFalse() + assertThat(sExpression.isSymbol).isFalse() + assertThat(sExpression.isBackquote).isFalse() + assertThat(sExpression.isComma).isFalse() + assertThat(sExpression.isAtSign).isFalse() + } + + @JvmStatic() + fun assertString(sExpression: SExpression) { + assertThat(sExpression.isAtom).isTrue() + assertThat(sExpression.isCons).isFalse() + assertThat(sExpression.isFunction).isFalse() + assertThat(sExpression.isList).isFalse() + assertThat(sExpression.isNull).isFalse() + assertThat(sExpression.isNumber).isFalse() + assertThat(sExpression.isString).isTrue() + assertThat(sExpression.isSymbol).isFalse() + assertThat(sExpression.isBackquote).isFalse() + assertThat(sExpression.isComma).isFalse() + assertThat(sExpression.isAtSign).isFalse() + } + + @JvmStatic() + fun assertSymbol(sExpression: SExpression) { + assertThat(sExpression.isAtom).isTrue() + assertThat(sExpression.isCons).isFalse() + assertThat(sExpression.isFunction).isFalse() + assertThat(sExpression.isList).isFalse() + assertThat(sExpression.isNull).isFalse() + assertThat(sExpression.isNumber).isFalse() + assertThat(sExpression.isString).isFalse() + assertThat(sExpression.isSymbol).isTrue() + assertThat(sExpression.isBackquote).isFalse() + assertThat(sExpression.isComma).isFalse() + assertThat(sExpression.isAtSign).isFalse() + } + + @JvmStatic() + fun assertT(sExpression: SExpression) { + assertThat(sExpression).isEqualTo(T) + } + + @JvmStatic() + fun assertBackTickExpression(sExpression: SExpression) { + assertThat(sExpression.isAtom).isFalse() + assertThat(sExpression.isCons).isFalse() + assertThat(sExpression.isFunction).isFalse() + assertThat(sExpression.isList).isFalse() + assertThat(sExpression.isNull).isFalse() + assertThat(sExpression.isNumber).isFalse() + assertThat(sExpression.isString).isFalse() + assertThat(sExpression.isSymbol).isFalse() + assertThat(sExpression.isBackquote).isTrue() + assertThat(sExpression.isComma).isFalse() + assertThat(sExpression.isAtSign).isFalse() + } + + @JvmStatic() + fun assertCommaExpression(sExpression: SExpression) { + assertThat(sExpression.isAtom).isFalse() + assertThat(sExpression.isCons).isFalse() + assertThat(sExpression.isFunction).isFalse() + assertThat(sExpression.isList).isFalse() + assertThat(sExpression.isNull).isFalse() + assertThat(sExpression.isNumber).isFalse() + assertThat(sExpression.isString).isFalse() + assertThat(sExpression.isSymbol).isFalse() + assertThat(sExpression.isBackquote).isFalse() + assertThat(sExpression.isComma).isTrue() + assertThat(sExpression.isAtSign).isFalse() + } + + @JvmStatic() + fun assertAtSignExpression(sExpression: SExpression) { + assertThat(sExpression.isAtom).isFalse() + assertThat(sExpression.isCons).isFalse() + assertThat(sExpression.isFunction).isFalse() + assertThat(sExpression.isList).isFalse() + assertThat(sExpression.isNull).isFalse() + assertThat(sExpression.isNumber).isFalse() + assertThat(sExpression.isString).isFalse() + assertThat(sExpression.isSymbol).isFalse() + assertThat(sExpression.isBackquote).isFalse() + assertThat(sExpression.isComma).isFalse() + assertThat(sExpression.isAtSign).isTrue() + } +}