361 lines
10 KiB
Kotlin
361 lines
10 KiB
Kotlin
package parser
|
|
|
|
import error.LispException
|
|
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.LispIOException
|
|
import testutil.TestUtilities.assertIsErrorWithMessage
|
|
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(createInputStreamFromString(input), "testFile")
|
|
private fun createIOExceptionThrowingLispParser() = LispParser(createIOExceptionThrowingInputStream(), "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) {
|
|
assertIsErrorWithMessage(e)
|
|
}
|
|
}
|
|
|
|
@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) {
|
|
assertIsErrorWithMessage(e)
|
|
}
|
|
}
|
|
|
|
@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) {
|
|
assertIsErrorWithMessage(e)
|
|
}
|
|
}
|
|
|
|
@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(LispIOException::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()
|
|
}
|
|
}
|