Convert file position code to kotlin
This commit is contained in:
parent
3664a58989
commit
61adaffd3c
2
pom.xml
2
pom.xml
|
@ -10,7 +10,7 @@
|
|||
|
||||
<properties>
|
||||
<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>
|
||||
<skipTests>false</skipTests>
|
||||
</properties>
|
||||
|
|
|
@ -6,21 +6,21 @@ enum class Severity {
|
|||
|
||||
WARNING {
|
||||
override fun decorate(output: String, environment: RuntimeEnvironment): String =
|
||||
RuntimeEnvironment.decorateWarningOutput(output)!!
|
||||
RuntimeEnvironment.decorateWarningOutput(output)
|
||||
|
||||
override fun toDisplayString() = "warning"
|
||||
},
|
||||
|
||||
ERROR {
|
||||
override fun decorate(output: String, environment: RuntimeEnvironment): String =
|
||||
RuntimeEnvironment.decorateErrorOutput(output)!!
|
||||
RuntimeEnvironment.decorateErrorOutput(output)
|
||||
|
||||
override fun toDisplayString() = "error"
|
||||
},
|
||||
|
||||
CRITICAL {
|
||||
override fun decorate(output: String, environment: RuntimeEnvironment): String =
|
||||
RuntimeEnvironment.decorateCriticalOutput(output)!!
|
||||
RuntimeEnvironment.decorateCriticalOutput(output)
|
||||
|
||||
override fun toDisplayString() = "critical"
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package file
|
||||
|
||||
data class FilePosition(val fileName: String, val lineNumber: Int, val columnNumber: Int)
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -77,7 +77,7 @@ object LispInterpreterBuilder {
|
|||
this.languageFiles = ArrayList()
|
||||
|
||||
for (fileName in languageFiles)
|
||||
this.languageFiles!!.add(LanguageFile(classLoader.getResourceAsStream(fileName), fileName))
|
||||
this.languageFiles.add(LanguageFile(classLoader.getResourceAsStream(fileName), fileName))
|
||||
}
|
||||
|
||||
fun setTerminationFunction(terminationFunction: () -> Unit) {
|
||||
|
@ -135,7 +135,7 @@ object LispInterpreterBuilder {
|
|||
|
||||
private fun configurePath() {
|
||||
if (isFileBased)
|
||||
RuntimeEnvironment.path = Path.getPathPrefix(inputName!!)
|
||||
RuntimeEnvironment.path = Path.getPathPrefix(inputName)
|
||||
else
|
||||
RuntimeEnvironment.path = ""
|
||||
}
|
||||
|
|
|
@ -40,11 +40,11 @@ public class LispScanner {
|
|||
positionTracker.incrementLine();
|
||||
}
|
||||
|
||||
return tokenFactory.createEofToken(positionTracker.getCurrentPosition());
|
||||
return tokenFactory.createEofToken(positionTracker.currentPosition());
|
||||
}
|
||||
|
||||
private Token createTokenFromCharacter(char c) {
|
||||
FilePosition currentPosition = positionTracker.getCurrentPosition();
|
||||
FilePosition currentPosition = positionTracker.currentPosition();
|
||||
String tokenText = retrieveTokenText(c);
|
||||
|
||||
return tokenFactory.createToken(tokenText, currentPosition);
|
||||
|
@ -107,7 +107,7 @@ public class LispScanner {
|
|||
public ComplexTokenTextRetriever(char firstCharacter, Function<Character, Boolean> isPartOfToken) {
|
||||
this.isPartOfToken = isPartOfToken;
|
||||
this.text = new StringBuilder();
|
||||
this.position = positionTracker.getCurrentPosition();
|
||||
this.position = positionTracker.currentPosition();
|
||||
this.firstCharacter = firstCharacter;
|
||||
this.currentCharacter = firstCharacter;
|
||||
this.previousCharacter = firstCharacter;
|
||||
|
|
|
@ -89,7 +89,7 @@ class MainTest : SymbolAndFunctionCleaner() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun runWithBadFile() {
|
||||
fun `bad file displays correct error message`() {
|
||||
val expectedMessage = "[critical] bad.lisp (No such file or directory)"
|
||||
|
||||
exit.expectSystemExitWithStatus(1)
|
||||
|
@ -102,7 +102,7 @@ class MainTest : SymbolAndFunctionCleaner() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun runWithFile_PrintsDecoratedLastValueOnly() {
|
||||
fun `interpret file prints the decorated last value only`() {
|
||||
runInterpreterWithFile(FILE)
|
||||
|
||||
assertEquals("", systemErrLog())
|
||||
|
@ -110,7 +110,7 @@ class MainTest : SymbolAndFunctionCleaner() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun runInteractive() {
|
||||
fun `run interactive interpreter`() {
|
||||
val terminal = runInterpreterAndGetInteractor()
|
||||
|
||||
terminal.waitForPrompt()
|
||||
|
|
|
@ -90,7 +90,7 @@ class RuntimeEnvironmentTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `assing a prompt decorator`() {
|
||||
fun `assign a prompt decorator`() {
|
||||
RuntimeEnvironment.promptDecorator = { "[$it]" }
|
||||
|
||||
assertThat(RuntimeEnvironment.decoratePrompt("test")).isEqualTo("[test]")
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
|
||||
}
|
|
@ -16,52 +16,50 @@ import java.util.HashSet
|
|||
|
||||
class LispInterpreterTest {
|
||||
|
||||
private var indicatorSet: MutableSet<String>? = null
|
||||
private var outputStream: ByteArrayOutputStream? = null
|
||||
private var errorOutputStream: ByteArrayOutputStream? = null
|
||||
private val environment: RuntimeEnvironment
|
||||
private val builder: LispInterpreterBuilder
|
||||
|
||||
init {
|
||||
this.environment = RuntimeEnvironment
|
||||
this.builder = LispInterpreterBuilder
|
||||
companion object {
|
||||
private const val TERMINATED = "terminated"
|
||||
private val FILE = LispInterpreterTest::class.java.getResource("file.lisp").file
|
||||
}
|
||||
|
||||
private var indicatorSet = HashSet<String>()
|
||||
private var outputStream = ByteArrayOutputStream()
|
||||
private var errorOutputStream = ByteArrayOutputStream()
|
||||
|
||||
private fun setCommonFeatures() {
|
||||
builder.setOutput(PrintStream(outputStream!!))
|
||||
builder.setErrorOutput(PrintStream(errorOutputStream!!))
|
||||
builder.setTerminationFunction { }
|
||||
builder.setErrorTerminationFunction { indicatorSet!!.add(TERMINATED) }
|
||||
LispInterpreterBuilder.setOutput(PrintStream(outputStream))
|
||||
LispInterpreterBuilder.setErrorOutput(PrintStream(errorOutputStream))
|
||||
LispInterpreterBuilder.setTerminationFunction { }
|
||||
LispInterpreterBuilder.setErrorTerminationFunction { indicatorSet.add(TERMINATED) }
|
||||
}
|
||||
|
||||
private fun assertTerminated() {
|
||||
assertTrue(indicatorSet!!.contains(TERMINATED))
|
||||
assertTrue(indicatorSet.contains(TERMINATED))
|
||||
}
|
||||
|
||||
private fun assertErrorMessageWritten() {
|
||||
assertTrue(errorOutputStream!!.toByteArray().size > 0)
|
||||
assertTrue(errorOutputStream.toByteArray().isNotEmpty())
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
indicatorSet = HashSet()
|
||||
outputStream = ByteArrayOutputStream()
|
||||
errorOutputStream = ByteArrayOutputStream()
|
||||
environment.reset()
|
||||
builder.reset()
|
||||
indicatorSet.clear()
|
||||
outputStream.reset()
|
||||
errorOutputStream.reset()
|
||||
RuntimeEnvironment.reset()
|
||||
LispInterpreterBuilder.reset()
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
environment.reset()
|
||||
builder.reset()
|
||||
RuntimeEnvironment.reset()
|
||||
LispInterpreterBuilder.reset()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun buildInteractiveInterpreter() {
|
||||
setCommonFeatures()
|
||||
builder.setInput(System.`in`, "stdin")
|
||||
val interpreter = builder.build()
|
||||
LispInterpreterBuilder.setInput(System.`in`, "stdin")
|
||||
val interpreter = LispInterpreterBuilder.build()
|
||||
|
||||
assertTrue(interpreter is InteractiveLispInterpreter)
|
||||
}
|
||||
|
@ -69,9 +67,9 @@ class LispInterpreterTest {
|
|||
@Test
|
||||
fun buildNonInteractiveInterpreter() {
|
||||
setCommonFeatures()
|
||||
builder.setInput(System.`in`, "stdin")
|
||||
builder.setNotInteractive()
|
||||
val interpreter = builder.build()
|
||||
LispInterpreterBuilder.setInput(System.`in`, "stdin")
|
||||
LispInterpreterBuilder.setNotInteractive()
|
||||
val interpreter = LispInterpreterBuilder.build()
|
||||
|
||||
assertFalse(interpreter is InteractiveLispInterpreter)
|
||||
assertFalse(interpreter is FileLispInterpreter)
|
||||
|
@ -80,8 +78,8 @@ class LispInterpreterTest {
|
|||
@Test
|
||||
fun buildFileBasedInterpreter() {
|
||||
setCommonFeatures()
|
||||
builder.useFile(FILE)
|
||||
val interpreter = builder.build()
|
||||
LispInterpreterBuilder.useFile(FILE)
|
||||
val interpreter = LispInterpreterBuilder.build()
|
||||
|
||||
assertTrue(interpreter is FileLispInterpreter)
|
||||
}
|
||||
|
@ -89,8 +87,8 @@ class LispInterpreterTest {
|
|||
@Test
|
||||
fun attemptToBuildInterpreterOnBadFile() {
|
||||
setCommonFeatures()
|
||||
builder.useFile("does-not-exist.lisp")
|
||||
builder.build()
|
||||
LispInterpreterBuilder.useFile("does-not-exist.lisp")
|
||||
LispInterpreterBuilder.build()
|
||||
|
||||
assertErrorMessageWritten()
|
||||
assertTerminated()
|
||||
|
@ -98,65 +96,59 @@ class LispInterpreterTest {
|
|||
|
||||
@Test
|
||||
fun makeSureDecoratorsAreInitializedWithDefaults() {
|
||||
builder.build()
|
||||
LispInterpreterBuilder.build()
|
||||
|
||||
assertEquals("", environment.decoratePrompt(""))
|
||||
assertEquals("", environment.decorateValueOutput(""))
|
||||
assertEquals("", environment.decorateWarningOutput(""))
|
||||
assertEquals("", environment.decorateErrorOutput(""))
|
||||
assertEquals("", environment.decorateCriticalOutput(""))
|
||||
assertEquals("", RuntimeEnvironment.decoratePrompt(""))
|
||||
assertEquals("", RuntimeEnvironment.decorateValueOutput(""))
|
||||
assertEquals("", RuntimeEnvironment.decorateWarningOutput(""))
|
||||
assertEquals("", RuntimeEnvironment.decorateErrorOutput(""))
|
||||
assertEquals("", RuntimeEnvironment.decorateCriticalOutput(""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun makeSureDecoratorsAreSetCorrectly() {
|
||||
builder.setPromptDecorator { s -> "#$s#" }
|
||||
builder.setValueOutputDecorator { s -> "@$s@" }
|
||||
builder.setWarningOutputDecorator { s -> "%$s%" }
|
||||
builder.setErrorOutputDecorator { s -> "*$s*" }
|
||||
builder.setCriticalOutputDecorator { s -> "$$s$" }
|
||||
builder.build()
|
||||
LispInterpreterBuilder.setPromptDecorator { s -> "#$s#" }
|
||||
LispInterpreterBuilder.setValueOutputDecorator { s -> "@$s@" }
|
||||
LispInterpreterBuilder.setWarningOutputDecorator { s -> "%$s%" }
|
||||
LispInterpreterBuilder.setErrorOutputDecorator { s -> "*$s*" }
|
||||
LispInterpreterBuilder.setCriticalOutputDecorator { s -> "$$s$" }
|
||||
LispInterpreterBuilder.build()
|
||||
|
||||
assertEquals("#x#", environment.decoratePrompt("x"))
|
||||
assertEquals("@x@", environment.decorateValueOutput("x"))
|
||||
assertEquals("%x%", environment.decorateWarningOutput("x"))
|
||||
assertEquals("*x*", environment.decorateErrorOutput("x"))
|
||||
assertEquals("\$x$", environment.decorateCriticalOutput("x"))
|
||||
assertEquals("#x#", RuntimeEnvironment.decoratePrompt("x"))
|
||||
assertEquals("@x@", RuntimeEnvironment.decorateValueOutput("x"))
|
||||
assertEquals("%x%", RuntimeEnvironment.decorateWarningOutput("x"))
|
||||
assertEquals("*x*", RuntimeEnvironment.decorateErrorOutput("x"))
|
||||
assertEquals("\$x$", RuntimeEnvironment.decorateCriticalOutput("x"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fileBasedInterpreterWorks_PrintsLastValueOnly() {
|
||||
setCommonFeatures()
|
||||
builder.useFile(FILE)
|
||||
builder.build().interpret()
|
||||
LispInterpreterBuilder.useFile(FILE)
|
||||
LispInterpreterBuilder.build().interpret()
|
||||
|
||||
assertEquals("PICKLE\n\n", outputStream!!.toString())
|
||||
assertEquals("", errorOutputStream!!.toString())
|
||||
assertEquals("PICKLE\n\n", outputStream.toString())
|
||||
assertEquals("", errorOutputStream.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun interactiveInterpreterWorks() {
|
||||
setCommonFeatures()
|
||||
builder.setInput(createInputStreamFromString("'pickle"), "input")
|
||||
builder.build().interpret()
|
||||
LispInterpreterBuilder.setInput(createInputStreamFromString("'pickle"), "input")
|
||||
LispInterpreterBuilder.build().interpret()
|
||||
|
||||
assertEquals(format("{0}\n{1}\n{0}\n", PROMPT, "PICKLE"), outputStream!!.toString())
|
||||
assertEquals("", errorOutputStream!!.toString())
|
||||
assertEquals(format("{0}\n{1}\n{0}\n", PROMPT, "PICKLE"), outputStream.toString())
|
||||
assertEquals("", errorOutputStream.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun interpreterHandlesError() {
|
||||
setCommonFeatures()
|
||||
builder.setNotInteractive()
|
||||
builder.setInput(createInputStreamFromString("pickle"), "input")
|
||||
builder.build().interpret()
|
||||
LispInterpreterBuilder.setNotInteractive()
|
||||
LispInterpreterBuilder.setInput(createInputStreamFromString("pickle"), "input")
|
||||
LispInterpreterBuilder.build().interpret()
|
||||
|
||||
assertEquals("\n", outputStream!!.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
|
||||
assertEquals("\n", outputStream.toString())
|
||||
assertEquals("[error] symbol PICKLE has no value\n", errorOutputStream.toString())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,5 @@ abstract class SymbolAndFunctionCleaner {
|
|||
}
|
||||
|
||||
open fun additionalSetUp() {}
|
||||
|
||||
open fun additionalTearDown() {}
|
||||
}
|
||||
|
|
|
@ -21,9 +21,7 @@ public class TokenFactoryTest {
|
|||
@Before
|
||||
public void setUp() {
|
||||
tokenFactory = new TokenFactoryImpl();
|
||||
testPosition = new FilePosition("testFile");
|
||||
testPosition.setLineNumber(0);
|
||||
testPosition.setColumnNumber(0);
|
||||
testPosition = new FilePosition("testFile", 0, 0);
|
||||
}
|
||||
|
||||
private Token createToken(String text) {
|
||||
|
|
Loading…
Reference in New Issue