Convert lisp parser to kotlin
This commit is contained in:
parent
61adaffd3c
commit
9064acd7ed
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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<Unit>("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<Unit>("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<Unit>("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<Unit>("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()
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue