Convert file position code to kotlin

This commit is contained in:
Mike Cifelli 2018-05-05 12:02:43 -04:00
parent 3664a58989
commit 61adaffd3c
15 changed files with 152 additions and 212 deletions

View File

@ -10,7 +10,7 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.version>1.2.31</kotlin.version> <kotlin.version>1.2.41</kotlin.version>
<junit5.version>5.1.0</junit5.version> <junit5.version>5.1.0</junit5.version>
<skipTests>false</skipTests> <skipTests>false</skipTests>
</properties> </properties>

View File

@ -6,21 +6,21 @@ enum class Severity {
WARNING { WARNING {
override fun decorate(output: String, environment: RuntimeEnvironment): String = override fun decorate(output: String, environment: RuntimeEnvironment): String =
RuntimeEnvironment.decorateWarningOutput(output)!! RuntimeEnvironment.decorateWarningOutput(output)
override fun toDisplayString() = "warning" override fun toDisplayString() = "warning"
}, },
ERROR { ERROR {
override fun decorate(output: String, environment: RuntimeEnvironment): String = override fun decorate(output: String, environment: RuntimeEnvironment): String =
RuntimeEnvironment.decorateErrorOutput(output)!! RuntimeEnvironment.decorateErrorOutput(output)
override fun toDisplayString() = "error" override fun toDisplayString() = "error"
}, },
CRITICAL { CRITICAL {
override fun decorate(output: String, environment: RuntimeEnvironment): String = override fun decorate(output: String, environment: RuntimeEnvironment): String =
RuntimeEnvironment.decorateCriticalOutput(output)!! RuntimeEnvironment.decorateCriticalOutput(output)
override fun toDisplayString() = "critical" override fun toDisplayString() = "critical"
}; };

View File

@ -1,32 +0,0 @@
package file;
public class FilePosition {
private String fileName;
private int lineNumber;
private int columnNumber;
public FilePosition(String fileName) {
this.fileName = fileName;
}
public String getFileName() {
return fileName;
}
public int getLineNumber() {
return lineNumber;
}
public void setLineNumber(int lineNumber) {
this.lineNumber = lineNumber;
}
public int getColumnNumber() {
return columnNumber;
}
public void setColumnNumber(int columnNumber) {
this.columnNumber = columnNumber;
}
}

View File

@ -0,0 +1,3 @@
package file
data class FilePosition(val fileName: String, val lineNumber: Int, val columnNumber: Int)

View File

@ -1,31 +0,0 @@
package file;
public class FilePositionTracker {
private String fileName;
private int lineNumber;
private int columnNumber;
public FilePositionTracker(String fileName) {
this.fileName = fileName;
this.lineNumber = 1;
this.columnNumber = 0;
}
public FilePosition getCurrentPosition() {
FilePosition currentPosition = new FilePosition(fileName);
currentPosition.setLineNumber(lineNumber);
currentPosition.setColumnNumber(columnNumber);
return currentPosition;
}
public void incrementColumn() {
columnNumber++;
}
public void incrementLine() {
lineNumber++;
columnNumber = 0;
}
}

View File

@ -0,0 +1,18 @@
package file
class FilePositionTracker(private val fileName: String) {
private var lineNumber = 1
private var columnNumber = 0
fun currentPosition() = FilePosition(fileName = fileName, lineNumber = lineNumber, columnNumber = columnNumber)
fun incrementColumn() {
columnNumber++
}
fun incrementLine() {
lineNumber++
columnNumber = 0
}
}

View File

@ -77,7 +77,7 @@ object LispInterpreterBuilder {
this.languageFiles = ArrayList() this.languageFiles = ArrayList()
for (fileName in languageFiles) for (fileName in languageFiles)
this.languageFiles!!.add(LanguageFile(classLoader.getResourceAsStream(fileName), fileName)) this.languageFiles.add(LanguageFile(classLoader.getResourceAsStream(fileName), fileName))
} }
fun setTerminationFunction(terminationFunction: () -> Unit) { fun setTerminationFunction(terminationFunction: () -> Unit) {
@ -135,7 +135,7 @@ object LispInterpreterBuilder {
private fun configurePath() { private fun configurePath() {
if (isFileBased) if (isFileBased)
RuntimeEnvironment.path = Path.getPathPrefix(inputName!!) RuntimeEnvironment.path = Path.getPathPrefix(inputName)
else else
RuntimeEnvironment.path = "" RuntimeEnvironment.path = ""
} }

View File

@ -40,11 +40,11 @@ public class LispScanner {
positionTracker.incrementLine(); positionTracker.incrementLine();
} }
return tokenFactory.createEofToken(positionTracker.getCurrentPosition()); return tokenFactory.createEofToken(positionTracker.currentPosition());
} }
private Token createTokenFromCharacter(char c) { private Token createTokenFromCharacter(char c) {
FilePosition currentPosition = positionTracker.getCurrentPosition(); FilePosition currentPosition = positionTracker.currentPosition();
String tokenText = retrieveTokenText(c); String tokenText = retrieveTokenText(c);
return tokenFactory.createToken(tokenText, currentPosition); return tokenFactory.createToken(tokenText, currentPosition);
@ -107,7 +107,7 @@ public class LispScanner {
public ComplexTokenTextRetriever(char firstCharacter, Function<Character, Boolean> isPartOfToken) { public ComplexTokenTextRetriever(char firstCharacter, Function<Character, Boolean> isPartOfToken) {
this.isPartOfToken = isPartOfToken; this.isPartOfToken = isPartOfToken;
this.text = new StringBuilder(); this.text = new StringBuilder();
this.position = positionTracker.getCurrentPosition(); this.position = positionTracker.currentPosition();
this.firstCharacter = firstCharacter; this.firstCharacter = firstCharacter;
this.currentCharacter = firstCharacter; this.currentCharacter = firstCharacter;
this.previousCharacter = firstCharacter; this.previousCharacter = firstCharacter;

View File

@ -89,7 +89,7 @@ class MainTest : SymbolAndFunctionCleaner() {
} }
@Test @Test
fun runWithBadFile() { fun `bad file displays correct error message`() {
val expectedMessage = "[critical] bad.lisp (No such file or directory)" val expectedMessage = "[critical] bad.lisp (No such file or directory)"
exit.expectSystemExitWithStatus(1) exit.expectSystemExitWithStatus(1)
@ -102,7 +102,7 @@ class MainTest : SymbolAndFunctionCleaner() {
} }
@Test @Test
fun runWithFile_PrintsDecoratedLastValueOnly() { fun `interpret file prints the decorated last value only`() {
runInterpreterWithFile(FILE) runInterpreterWithFile(FILE)
assertEquals("", systemErrLog()) assertEquals("", systemErrLog())
@ -110,7 +110,7 @@ class MainTest : SymbolAndFunctionCleaner() {
} }
@Test @Test
fun runInteractive() { fun `run interactive interpreter`() {
val terminal = runInterpreterAndGetInteractor() val terminal = runInterpreterAndGetInteractor()
terminal.waitForPrompt() terminal.waitForPrompt()

View File

@ -90,7 +90,7 @@ class RuntimeEnvironmentTest {
} }
@Test @Test
fun `assing a prompt decorator`() { fun `assign a prompt decorator`() {
RuntimeEnvironment.promptDecorator = { "[$it]" } RuntimeEnvironment.promptDecorator = { "[$it]" }
assertThat(RuntimeEnvironment.decoratePrompt("test")).isEqualTo("[test]") assertThat(RuntimeEnvironment.decoratePrompt("test")).isEqualTo("[test]")

View File

@ -1,65 +0,0 @@
package file;
import org.junit.Before;
import org.junit.Test;
import java.util.Objects;
import static org.junit.Assert.assertTrue;
public class FilePositionTrackerTest {
public static final String FILE_NAME = "testFile";
private FilePositionTracker trackerUnderTest;
private FilePosition createFilePosition(int lineNumber, int columnNumber) {
FilePosition position = new FilePosition(FILE_NAME);
position.setLineNumber(lineNumber);
position.setColumnNumber(columnNumber);
return position;
}
private void assertTrackerPositionEquals(FilePosition expectedPosition) {
assertTrue(arePositionsEqual(expectedPosition, trackerUnderTest.getCurrentPosition()));
}
private boolean arePositionsEqual(FilePosition position1, FilePosition position2) {
return Objects.equals(position1.getFileName(), position2.getFileName())
&& Objects.equals(position1.getLineNumber(), position2.getLineNumber())
&& Objects.equals(position1.getColumnNumber(), position2.getColumnNumber());
}
@Before
public void setUp() {
trackerUnderTest = new FilePositionTracker(FILE_NAME);
}
@Test
public void noMovement_ReturnsInitialPosition() {
assertTrackerPositionEquals(createFilePosition(1, 0));
}
@Test
public void advanceOneColumn_ReturnsCorrectPosition() {
trackerUnderTest.incrementColumn();
assertTrackerPositionEquals(createFilePosition(1, 1));
}
@Test
public void advanceOneLine_ReturnsCorrectPosition() {
trackerUnderTest.incrementLine();
assertTrackerPositionEquals(createFilePosition(2, 0));
}
@Test
public void advanceOneLine_ResetsColumn() {
trackerUnderTest.incrementColumn();
trackerUnderTest.incrementLine();
assertTrackerPositionEquals(createFilePosition(2, 0));
}
}

View File

@ -0,0 +1,58 @@
package file
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
@TestInstance(PER_CLASS)
class FilePositionTrackerTest {
companion object {
const val FILE_NAME = "testFile"
}
private lateinit var trackerUnderTest: FilePositionTracker
private fun createFilePosition(lineNumber: Int, columnNumber: Int): FilePosition {
return FilePosition(FILE_NAME, lineNumber, columnNumber)
}
private fun assertTrackerPositionEquals(expectedPosition: FilePosition) {
assertThat(trackerUnderTest.currentPosition()).isEqualTo(expectedPosition)
}
@BeforeEach
fun setUp() {
trackerUnderTest = FilePositionTracker(FILE_NAME)
}
@Test
fun noMovement_ReturnsInitialPosition() {
assertTrackerPositionEquals(createFilePosition(1, 0))
}
@Test
fun advanceOneColumn_ReturnsCorrectPosition() {
trackerUnderTest.incrementColumn()
assertTrackerPositionEquals(createFilePosition(1, 1))
}
@Test
fun advanceOneLine_ReturnsCorrectPosition() {
trackerUnderTest.incrementLine()
assertTrackerPositionEquals(createFilePosition(2, 0))
}
@Test
fun advanceOneLine_ResetsColumn() {
trackerUnderTest.incrementColumn()
trackerUnderTest.incrementLine()
assertTrackerPositionEquals(createFilePosition(2, 0))
}
}

View File

@ -16,52 +16,50 @@ import java.util.HashSet
class LispInterpreterTest { class LispInterpreterTest {
private var indicatorSet: MutableSet<String>? = null companion object {
private var outputStream: ByteArrayOutputStream? = null private const val TERMINATED = "terminated"
private var errorOutputStream: ByteArrayOutputStream? = null private val FILE = LispInterpreterTest::class.java.getResource("file.lisp").file
private val environment: RuntimeEnvironment
private val builder: LispInterpreterBuilder
init {
this.environment = RuntimeEnvironment
this.builder = LispInterpreterBuilder
} }
private var indicatorSet = HashSet<String>()
private var outputStream = ByteArrayOutputStream()
private var errorOutputStream = ByteArrayOutputStream()
private fun setCommonFeatures() { private fun setCommonFeatures() {
builder.setOutput(PrintStream(outputStream!!)) LispInterpreterBuilder.setOutput(PrintStream(outputStream))
builder.setErrorOutput(PrintStream(errorOutputStream!!)) LispInterpreterBuilder.setErrorOutput(PrintStream(errorOutputStream))
builder.setTerminationFunction { } LispInterpreterBuilder.setTerminationFunction { }
builder.setErrorTerminationFunction { indicatorSet!!.add(TERMINATED) } LispInterpreterBuilder.setErrorTerminationFunction { indicatorSet.add(TERMINATED) }
} }
private fun assertTerminated() { private fun assertTerminated() {
assertTrue(indicatorSet!!.contains(TERMINATED)) assertTrue(indicatorSet.contains(TERMINATED))
} }
private fun assertErrorMessageWritten() { private fun assertErrorMessageWritten() {
assertTrue(errorOutputStream!!.toByteArray().size > 0) assertTrue(errorOutputStream.toByteArray().isNotEmpty())
} }
@Before @Before
fun setUp() { fun setUp() {
indicatorSet = HashSet() indicatorSet.clear()
outputStream = ByteArrayOutputStream() outputStream.reset()
errorOutputStream = ByteArrayOutputStream() errorOutputStream.reset()
environment.reset() RuntimeEnvironment.reset()
builder.reset() LispInterpreterBuilder.reset()
} }
@After @After
fun tearDown() { fun tearDown() {
environment.reset() RuntimeEnvironment.reset()
builder.reset() LispInterpreterBuilder.reset()
} }
@Test @Test
fun buildInteractiveInterpreter() { fun buildInteractiveInterpreter() {
setCommonFeatures() setCommonFeatures()
builder.setInput(System.`in`, "stdin") LispInterpreterBuilder.setInput(System.`in`, "stdin")
val interpreter = builder.build() val interpreter = LispInterpreterBuilder.build()
assertTrue(interpreter is InteractiveLispInterpreter) assertTrue(interpreter is InteractiveLispInterpreter)
} }
@ -69,9 +67,9 @@ class LispInterpreterTest {
@Test @Test
fun buildNonInteractiveInterpreter() { fun buildNonInteractiveInterpreter() {
setCommonFeatures() setCommonFeatures()
builder.setInput(System.`in`, "stdin") LispInterpreterBuilder.setInput(System.`in`, "stdin")
builder.setNotInteractive() LispInterpreterBuilder.setNotInteractive()
val interpreter = builder.build() val interpreter = LispInterpreterBuilder.build()
assertFalse(interpreter is InteractiveLispInterpreter) assertFalse(interpreter is InteractiveLispInterpreter)
assertFalse(interpreter is FileLispInterpreter) assertFalse(interpreter is FileLispInterpreter)
@ -80,8 +78,8 @@ class LispInterpreterTest {
@Test @Test
fun buildFileBasedInterpreter() { fun buildFileBasedInterpreter() {
setCommonFeatures() setCommonFeatures()
builder.useFile(FILE) LispInterpreterBuilder.useFile(FILE)
val interpreter = builder.build() val interpreter = LispInterpreterBuilder.build()
assertTrue(interpreter is FileLispInterpreter) assertTrue(interpreter is FileLispInterpreter)
} }
@ -89,8 +87,8 @@ class LispInterpreterTest {
@Test @Test
fun attemptToBuildInterpreterOnBadFile() { fun attemptToBuildInterpreterOnBadFile() {
setCommonFeatures() setCommonFeatures()
builder.useFile("does-not-exist.lisp") LispInterpreterBuilder.useFile("does-not-exist.lisp")
builder.build() LispInterpreterBuilder.build()
assertErrorMessageWritten() assertErrorMessageWritten()
assertTerminated() assertTerminated()
@ -98,65 +96,59 @@ class LispInterpreterTest {
@Test @Test
fun makeSureDecoratorsAreInitializedWithDefaults() { fun makeSureDecoratorsAreInitializedWithDefaults() {
builder.build() LispInterpreterBuilder.build()
assertEquals("", environment.decoratePrompt("")) assertEquals("", RuntimeEnvironment.decoratePrompt(""))
assertEquals("", environment.decorateValueOutput("")) assertEquals("", RuntimeEnvironment.decorateValueOutput(""))
assertEquals("", environment.decorateWarningOutput("")) assertEquals("", RuntimeEnvironment.decorateWarningOutput(""))
assertEquals("", environment.decorateErrorOutput("")) assertEquals("", RuntimeEnvironment.decorateErrorOutput(""))
assertEquals("", environment.decorateCriticalOutput("")) assertEquals("", RuntimeEnvironment.decorateCriticalOutput(""))
} }
@Test @Test
fun makeSureDecoratorsAreSetCorrectly() { fun makeSureDecoratorsAreSetCorrectly() {
builder.setPromptDecorator { s -> "#$s#" } LispInterpreterBuilder.setPromptDecorator { s -> "#$s#" }
builder.setValueOutputDecorator { s -> "@$s@" } LispInterpreterBuilder.setValueOutputDecorator { s -> "@$s@" }
builder.setWarningOutputDecorator { s -> "%$s%" } LispInterpreterBuilder.setWarningOutputDecorator { s -> "%$s%" }
builder.setErrorOutputDecorator { s -> "*$s*" } LispInterpreterBuilder.setErrorOutputDecorator { s -> "*$s*" }
builder.setCriticalOutputDecorator { s -> "$$s$" } LispInterpreterBuilder.setCriticalOutputDecorator { s -> "$$s$" }
builder.build() LispInterpreterBuilder.build()
assertEquals("#x#", environment.decoratePrompt("x")) assertEquals("#x#", RuntimeEnvironment.decoratePrompt("x"))
assertEquals("@x@", environment.decorateValueOutput("x")) assertEquals("@x@", RuntimeEnvironment.decorateValueOutput("x"))
assertEquals("%x%", environment.decorateWarningOutput("x")) assertEquals("%x%", RuntimeEnvironment.decorateWarningOutput("x"))
assertEquals("*x*", environment.decorateErrorOutput("x")) assertEquals("*x*", RuntimeEnvironment.decorateErrorOutput("x"))
assertEquals("\$x$", environment.decorateCriticalOutput("x")) assertEquals("\$x$", RuntimeEnvironment.decorateCriticalOutput("x"))
} }
@Test @Test
fun fileBasedInterpreterWorks_PrintsLastValueOnly() { fun fileBasedInterpreterWorks_PrintsLastValueOnly() {
setCommonFeatures() setCommonFeatures()
builder.useFile(FILE) LispInterpreterBuilder.useFile(FILE)
builder.build().interpret() LispInterpreterBuilder.build().interpret()
assertEquals("PICKLE\n\n", outputStream!!.toString()) assertEquals("PICKLE\n\n", outputStream.toString())
assertEquals("", errorOutputStream!!.toString()) assertEquals("", errorOutputStream.toString())
} }
@Test @Test
fun interactiveInterpreterWorks() { fun interactiveInterpreterWorks() {
setCommonFeatures() setCommonFeatures()
builder.setInput(createInputStreamFromString("'pickle"), "input") LispInterpreterBuilder.setInput(createInputStreamFromString("'pickle"), "input")
builder.build().interpret() LispInterpreterBuilder.build().interpret()
assertEquals(format("{0}\n{1}\n{0}\n", PROMPT, "PICKLE"), outputStream!!.toString()) assertEquals(format("{0}\n{1}\n{0}\n", PROMPT, "PICKLE"), outputStream.toString())
assertEquals("", errorOutputStream!!.toString()) assertEquals("", errorOutputStream.toString())
} }
@Test @Test
fun interpreterHandlesError() { fun interpreterHandlesError() {
setCommonFeatures() setCommonFeatures()
builder.setNotInteractive() LispInterpreterBuilder.setNotInteractive()
builder.setInput(createInputStreamFromString("pickle"), "input") LispInterpreterBuilder.setInput(createInputStreamFromString("pickle"), "input")
builder.build().interpret() LispInterpreterBuilder.build().interpret()
assertEquals("\n", outputStream!!.toString()) assertEquals("\n", outputStream.toString())
assertEquals("[error] symbol PICKLE has no value\n", errorOutputStream!!.toString()) assertEquals("[error] symbol PICKLE has no value\n", errorOutputStream.toString())
}
companion object {
private val TERMINATED = "terminated"
private val FILE = LispInterpreterTest::class.java.getResource("file.lisp").file
} }
} }

View File

@ -29,6 +29,5 @@ abstract class SymbolAndFunctionCleaner {
} }
open fun additionalSetUp() {} open fun additionalSetUp() {}
open fun additionalTearDown() {} open fun additionalTearDown() {}
} }

View File

@ -21,9 +21,7 @@ public class TokenFactoryTest {
@Before @Before
public void setUp() { public void setUp() {
tokenFactory = new TokenFactoryImpl(); tokenFactory = new TokenFactoryImpl();
testPosition = new FilePosition("testFile"); testPosition = new FilePosition("testFile", 0, 0);
testPosition.setLineNumber(0);
testPosition.setColumnNumber(0);
} }
private Token createToken(String text) { private Token createToken(String text) {