diff --git a/src/main/kotlin/application/LispMain.java b/src/main/kotlin/application/LispMain.java deleted file mode 100644 index 11c1599..0000000 --- a/src/main/kotlin/application/LispMain.java +++ /dev/null @@ -1,153 +0,0 @@ -package application; - -import com.googlecode.lanterna.terminal.DefaultTerminalFactory; -import com.googlecode.lanterna.terminal.IOSafeTerminal; -import com.googlecode.lanterna.terminal.Terminal; -import interpreter.LispInterpreter; -import interpreter.LispInterpreterBuilder; -import stream.UncheckedIOException; -import terminal.LispTerminal; -import terminal.TerminalConfiguration; - -import java.io.IOException; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; -import java.io.PrintStream; -import java.util.function.Function; - -import static com.googlecode.lanterna.terminal.IOSafeTerminalAdapter.createRuntimeExceptionConvertingAdapter; -import static java.text.MessageFormat.format; -import static terminal.LispTerminal.END_OF_SEGMENT; - -public class LispMain { - - public static final String GREETING = "Transcendental Lisp - Version {0}"; - - public static final String ANSI_RESET = "\u001B[0m"; - public static final String ANSI_RED = "\u001B[31m"; - public static final String ANSI_GREEN = "\u001B[32m"; - public static final String ANSI_YELLOW = "\u001B[33m"; - public static final String ANSI_PURPLE = "\u001B[35m"; - - public static final String[] LANGUAGE_FILE_NAMES = new String[] { "functions.lisp", "dlambda.lisp" }; - - public static void main(String[] arguments) { - LispMain lispMain = new LispMain(); - - if (arguments.length == 0) - lispMain.runInteractive(); - else - lispMain.runWithFile(arguments[0]); - } - - private LispInterpreterBuilder builder; - private PipedInputStream inputReader; - private PrintStream output; - private LispTerminal lispTerminal; - private TerminalConfiguration configuration; - - public LispMain() { - this.builder = LispInterpreterBuilder.INSTANCE; - } - - public LispMain(TerminalConfiguration configuration) { - this.builder = LispInterpreterBuilder.INSTANCE; - this.configuration = configuration; - } - - public void runInteractive() { - initializeTerminal(); - printGreeting(); - lispTerminal.start(); - buildInteractiveInterpreter().interpret(); - shutdownTerminal(); - } - - private void initializeTerminal() { - createTerminalConfiguration(); - inputReader = configuration.getInputReader(); - output = new PrintStream(configuration.getOutputWriter()); - lispTerminal = new LispTerminal(configuration); - } - - private void createTerminalConfiguration() { - if (configuration == null) { - configuration = new TerminalConfiguration(); - configuration.setInputPair(new PipedOutputStream(), new PipedInputStream()); - configuration.setOutputPair(new PipedOutputStream(), new PipedInputStream()); - configuration.setTerminal(createIOSafeTerminal()); - } - } - - private IOSafeTerminal createIOSafeTerminal() { - return createRuntimeExceptionConvertingAdapter(createDefaultTerminal()); - } - - private Terminal createDefaultTerminal() { - try { - return new DefaultTerminalFactory().createTerminal(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - private void printGreeting() { - output.println(format(GREETING, getVersion())); - output.println(); - } - - private String getVersion() { - return getClass().getPackage().getImplementationVersion(); - } - - private LispInterpreter buildInteractiveInterpreter() { - builder.setInput(inputReader, "terminal"); - builder.setOutput(output); - builder.setErrorOutput(output); - builder.setTerminationFunction(this::shutdownTerminal); - builder.setErrorTerminationFunction(this::shutdownTerminal); - builder.setLanguageFileNames(LANGUAGE_FILE_NAMES); - builder.setPromptDecorator(s -> s + END_OF_SEGMENT); - builder.setValueOutputDecorator(makeColorDecorator(ANSI_GREEN)); - builder.setWarningOutputDecorator(makeColorDecorator(ANSI_YELLOW)); - builder.setErrorOutputDecorator(makeColorDecorator(ANSI_RED)); - builder.setCriticalOutputDecorator(makeColorDecorator(ANSI_PURPLE)); - - return builder.build(); - } - - private void shutdownTerminal() { - output.print(END_OF_SEGMENT); - lispTerminal.stop(); - output.close(); - } - - private Function makeColorDecorator(String color) { - return new Function() { - - @Override - public String apply(String s) { - return color + s + ANSI_RESET; - } - }; - } - - public void runWithFile(String fileName) { - buildFileInterpreter(fileName).interpret(); - } - - private LispInterpreter buildFileInterpreter(String fileName) { - builder.useFile(fileName); - builder.setOutput(System.out); - builder.setErrorOutput(System.err); - builder.setTerminationFunction(() -> System.exit(0)); - builder.setErrorTerminationFunction(() -> System.exit(1)); - builder.setLanguageFileNames(LANGUAGE_FILE_NAMES); - builder.setValueOutputDecorator(makeColorDecorator(ANSI_GREEN)); - builder.setWarningOutputDecorator(makeColorDecorator(ANSI_YELLOW)); - builder.setErrorOutputDecorator(makeColorDecorator(ANSI_RED)); - builder.setCriticalOutputDecorator(makeColorDecorator(ANSI_PURPLE)); - - return builder.build(); - } -} diff --git a/src/main/kotlin/application/LispMain.kt b/src/main/kotlin/application/LispMain.kt new file mode 100644 index 0000000..0d1949f --- /dev/null +++ b/src/main/kotlin/application/LispMain.kt @@ -0,0 +1,146 @@ +package application + +import com.googlecode.lanterna.terminal.DefaultTerminalFactory +import com.googlecode.lanterna.terminal.IOSafeTerminal +import com.googlecode.lanterna.terminal.IOSafeTerminalAdapter.createRuntimeExceptionConvertingAdapter +import com.googlecode.lanterna.terminal.Terminal +import interpreter.LispInterpreter +import interpreter.LispInterpreterBuilder +import stream.UncheckedIOException +import terminal.LispTerminal +import terminal.LispTerminal.END_OF_SEGMENT +import terminal.TerminalConfiguration +import java.io.IOException +import java.io.PipedInputStream +import java.io.PipedOutputStream +import java.io.PrintStream +import java.text.MessageFormat.format + +class LispMain { + + private var builder: LispInterpreterBuilder? = null + private var inputReader: PipedInputStream? = null + private var output: PrintStream? = null + private var lispTerminal: LispTerminal? = null + private var configuration: TerminalConfiguration? = null + + private val version = LispMain::class.java.`package`.implementationVersion + + constructor() { + this.builder = LispInterpreterBuilder + } + + constructor(configuration: TerminalConfiguration) { + this.builder = LispInterpreterBuilder + this.configuration = configuration + } + + fun runInteractive() { + initializeTerminal() + printGreeting() + lispTerminal!!.start() + buildInteractiveInterpreter().interpret() + shutdownTerminal() + } + + private fun initializeTerminal() { + createTerminalConfiguration() + inputReader = configuration!!.inputReader + output = PrintStream(configuration!!.outputWriter) + lispTerminal = LispTerminal(configuration!!) + } + + private fun createTerminalConfiguration() { + if (configuration == null) { + configuration = TerminalConfiguration() + configuration!!.setInputPair(PipedOutputStream(), PipedInputStream()) + configuration!!.setOutputPair(PipedOutputStream(), PipedInputStream()) + configuration!!.terminal = createIOSafeTerminal() + } + } + + private fun createIOSafeTerminal(): IOSafeTerminal { + return createRuntimeExceptionConvertingAdapter(createDefaultTerminal()) + } + + private fun createDefaultTerminal(): Terminal { + try { + return DefaultTerminalFactory().createTerminal() + } catch (e: IOException) { + throw UncheckedIOException(e) + } + } + + private fun printGreeting() { + output!!.println(format(GREETING, (version ?: "null"))) + output!!.println() + } + + private fun buildInteractiveInterpreter(): LispInterpreter { + builder!!.setInput(inputReader!!, "terminal") + builder!!.setOutput(output!!) + builder!!.setErrorOutput(output!!) + builder!!.setTerminationFunction { this.shutdownTerminal() } + builder!!.setErrorTerminationFunction { this.shutdownTerminal() } + builder!!.setLanguageFileNames(*LANGUAGE_FILE_NAMES) + builder!!.setPromptDecorator { s -> s + END_OF_SEGMENT } + builder!!.setValueOutputDecorator(makeColorDecorator(ANSI_GREEN)) + builder!!.setWarningOutputDecorator(makeColorDecorator(ANSI_YELLOW)) + builder!!.setErrorOutputDecorator(makeColorDecorator(ANSI_RED)) + builder!!.setCriticalOutputDecorator(makeColorDecorator(ANSI_PURPLE)) + + return builder!!.build() + } + + private fun shutdownTerminal() { + output!!.print(END_OF_SEGMENT) + lispTerminal!!.stop() + output!!.close() + } + + private fun makeColorDecorator(color: String): (String) -> String { + return { color + it + ANSI_RESET } + } + + fun runWithFile(fileName: String) { + buildFileInterpreter(fileName).interpret() + } + + private fun buildFileInterpreter(fileName: String): LispInterpreter { + builder!!.useFile(fileName) + builder!!.setOutput(System.out) + builder!!.setErrorOutput(System.err) + builder!!.setTerminationFunction { System.exit(0) } + builder!!.setErrorTerminationFunction { System.exit(1) } + builder!!.setLanguageFileNames(*LANGUAGE_FILE_NAMES) + builder!!.setValueOutputDecorator(makeColorDecorator(ANSI_GREEN)) + builder!!.setWarningOutputDecorator(makeColorDecorator(ANSI_YELLOW)) + builder!!.setErrorOutputDecorator(makeColorDecorator(ANSI_RED)) + builder!!.setCriticalOutputDecorator(makeColorDecorator(ANSI_PURPLE)) + + return builder!!.build() + } + + companion object { + + val GREETING = "Transcendental Lisp - Version {0}" + + val ANSI_RESET = "\u001B[0m" + val ANSI_RED = "\u001B[31m" + val ANSI_GREEN = "\u001B[32m" + val ANSI_YELLOW = "\u001B[33m" + val ANSI_PURPLE = "\u001B[35m" + + val LANGUAGE_FILE_NAMES = arrayOf("functions.lisp", "dlambda.lisp") + + @JvmStatic + fun main(arguments: Array) { + val lispMain = LispMain() + + if (arguments.size == 0) + lispMain.runInteractive() + else + lispMain.runWithFile(arguments[0]) + } + } +} diff --git a/src/main/kotlin/environment/RuntimeEnvironment.kt b/src/main/kotlin/environment/RuntimeEnvironment.kt index b2b3c75..3ddda0c 100644 --- a/src/main/kotlin/environment/RuntimeEnvironment.kt +++ b/src/main/kotlin/environment/RuntimeEnvironment.kt @@ -1,10 +1,8 @@ package environment import error.ErrorManager - import java.io.InputStream import java.io.PrintStream -import java.util.function.Function object RuntimeEnvironment { @@ -15,14 +13,13 @@ object RuntimeEnvironment { var errorManager: ErrorManager? = null var path: String? = null - // TODO - convert to function types ()-> Unit etc... once calling code is kotlin - var terminationFunction: Runnable? = null - var errorTerminationFunction: Runnable? = null - var promptDecorator: Function? = null - var valueOutputDecorator: Function? = null - var warningOutputDecorator: Function? = null - var errorOutputDecorator: Function? = null - var criticalOutputDecorator: Function? = null + var terminationFunction: (() -> Unit) = {} + var errorTerminationFunction: (() -> Unit) = {} + var promptDecorator: ((String) -> String) = { it } + var valueOutputDecorator: ((String) -> String) = { it } + var warningOutputDecorator: ((String) -> String) = { it } + var errorOutputDecorator: ((String) -> String) = { it } + var criticalOutputDecorator: ((String) -> String) = { it } fun reset() { inputName = null @@ -31,26 +28,26 @@ object RuntimeEnvironment { errorOutput = null errorManager = null path = null - terminationFunction = null - errorTerminationFunction = null - promptDecorator = null - valueOutputDecorator = null - warningOutputDecorator = null - errorOutputDecorator = null - criticalOutputDecorator = null + terminationFunction = {} + errorTerminationFunction = {} + promptDecorator = { it } + valueOutputDecorator = { it } + warningOutputDecorator = { it } + errorOutputDecorator = { it } + criticalOutputDecorator = { it } } fun terminateSuccessfully() { - terminationFunction!!.run() + terminationFunction() } fun terminateExceptionally() { - errorTerminationFunction!!.run() + errorTerminationFunction() } - fun decoratePrompt(prompt: String) = promptDecorator!!.apply(prompt) - fun decorateValueOutput(valueOutput: String) = valueOutputDecorator!!.apply(valueOutput) - fun decorateWarningOutput(warningOutput: String) = warningOutputDecorator!!.apply(warningOutput) - fun decorateErrorOutput(errorOutput: String) = errorOutputDecorator!!.apply(errorOutput) - fun decorateCriticalOutput(criticalOutput: String) = criticalOutputDecorator!!.apply(criticalOutput) + fun decoratePrompt(prompt: String) = promptDecorator(prompt) + fun decorateValueOutput(valueOutput: String) = valueOutputDecorator(valueOutput) + fun decorateWarningOutput(warningOutput: String) = warningOutputDecorator(warningOutput) + fun decorateErrorOutput(errorOutput: String) = errorOutputDecorator(errorOutput) + fun decorateCriticalOutput(criticalOutput: String) = criticalOutputDecorator(criticalOutput) } diff --git a/src/main/kotlin/interpreter/LispInterpreterBuilder.kt b/src/main/kotlin/interpreter/LispInterpreterBuilder.kt index 2c413a6..d57630b 100644 --- a/src/main/kotlin/interpreter/LispInterpreterBuilder.kt +++ b/src/main/kotlin/interpreter/LispInterpreterBuilder.kt @@ -5,13 +5,11 @@ import error.CriticalLispException import error.ErrorManager import interpreter.LispInterpreter.LanguageFile import util.Path - import java.io.FileInputStream import java.io.FileNotFoundException import java.io.InputStream import java.io.PrintStream import java.util.ArrayList -import java.util.function.Function object LispInterpreterBuilder { @@ -19,31 +17,40 @@ object LispInterpreterBuilder { private var isInteractive = true private var isFileBased = false private var languageFiles = ArrayList() - private var promptDecorator = Function { s -> s } - private var valueOutputDecorator = Function { s -> s } - private var warningOutputDecorator = Function { s -> s } - private var errorOutputDecorator = Function { s -> s } - private var criticalOutputDecorator = Function { s -> s } private var inputStream: InputStream? = null private var outputStream: PrintStream? = null private var errorOutputStream: PrintStream? = null - private var terminationFunction: Runnable? = null - private var errorTerminationFunction: Runnable? = null + private var terminationFunction: () -> Unit = {} + private var errorTerminationFunction: () -> Unit = {} + private var promptDecorator: (String) -> String = { it } + private var valueOutputDecorator: (String) -> String = { it } + private var warningOutputDecorator: (String) -> String = { it } + private var errorOutputDecorator: (String) -> String = { it } + private var criticalOutputDecorator: (String) -> String = { it } init { reset() } fun reset() { - this.inputName = "" - this.isInteractive = true - this.isFileBased = false - this.languageFiles = ArrayList() - this.promptDecorator = Function { s -> s } - this.valueOutputDecorator = Function { s -> s } - this.warningOutputDecorator = Function { s -> s } - this.errorOutputDecorator = Function { s -> s } - this.criticalOutputDecorator = Function { s -> s } + inputName = "" + isInteractive = true + isFileBased = false + languageFiles = ArrayList() + inputStream = null + outputStream = null + errorOutputStream = null + terminationFunction = {} + errorTerminationFunction = {} + promptDecorator = { it } + valueOutputDecorator = { it } + warningOutputDecorator = { it } + errorOutputDecorator = { it } + criticalOutputDecorator = { it } + } + + fun setNotInteractive() { + this.isInteractive = false } fun setInput(inputStream: InputStream, inputName: String) { @@ -59,18 +66,6 @@ object LispInterpreterBuilder { this.errorOutputStream = errorOutputStream } - fun setTerminationFunction(terminationFunction: Runnable) { - this.terminationFunction = terminationFunction - } - - fun setErrorTerminationFunction(errorTerminationFunction: Runnable) { - this.errorTerminationFunction = errorTerminationFunction - } - - fun setNotInteractive() { - this.isInteractive = false - } - fun useFile(fileName: String) { this.isFileBased = true this.inputName = fileName @@ -85,23 +80,31 @@ object LispInterpreterBuilder { this.languageFiles!!.add(LanguageFile(classLoader.getResourceAsStream(fileName), fileName)) } - fun setPromptDecorator(decorator: Function) { + fun setTerminationFunction(terminationFunction: () -> Unit) { + this.terminationFunction = terminationFunction + } + + fun setErrorTerminationFunction(errorTerminationFunction: () -> Unit) { + this.errorTerminationFunction = errorTerminationFunction + } + + fun setPromptDecorator(decorator: (String) -> String) { this.promptDecorator = decorator } - fun setValueOutputDecorator(decorator: Function) { + fun setValueOutputDecorator(decorator: (String) -> String) { this.valueOutputDecorator = decorator } - fun setWarningOutputDecorator(decorator: Function) { + fun setWarningOutputDecorator(decorator: (String) -> String) { this.warningOutputDecorator = decorator } - fun setErrorOutputDecorator(decorator: Function) { + fun setErrorOutputDecorator(decorator: (String) -> String) { this.errorOutputDecorator = decorator } - fun setCriticalOutputDecorator(decorator: Function) { + fun setCriticalOutputDecorator(decorator: (String) -> String) { this.criticalOutputDecorator = decorator } diff --git a/src/test/kotlin/acceptance/fixture/LispInterpreterFixture.java b/src/test/kotlin/acceptance/fixture/LispInterpreterFixture.java index cfe22a5..4370b95 100644 --- a/src/test/kotlin/acceptance/fixture/LispInterpreterFixture.java +++ b/src/test/kotlin/acceptance/fixture/LispInterpreterFixture.java @@ -1,5 +1,6 @@ package acceptance.fixture; +import application.LispMain; import environment.RuntimeEnvironment; import interpreter.LispInterpreter; import interpreter.LispInterpreterBuilder; @@ -13,8 +14,6 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.PrintStream; -import static application.LispMain.LANGUAGE_FILE_NAMES; - public class LispInterpreterFixture { private static ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); @@ -38,9 +37,9 @@ public class LispInterpreterFixture { LispInterpreterBuilder.INSTANCE.setOutput(new PrintStream(outputStream)); LispInterpreterBuilder.INSTANCE.setErrorOutput(new PrintStream(outputStream)); LispInterpreterBuilder.INSTANCE.setNotInteractive(); - LispInterpreterBuilder.INSTANCE.setLanguageFileNames(LANGUAGE_FILE_NAMES); - LispInterpreterBuilder.INSTANCE.setTerminationFunction(LispInterpreterFixture::terminate); - LispInterpreterBuilder.INSTANCE.setErrorTerminationFunction(LispInterpreterFixture::terminateFromError); + LispInterpreterBuilder.INSTANCE.setLanguageFileNames(LispMain.Companion.getLANGUAGE_FILE_NAMES()); + // LispInterpreterBuilder.INSTANCE.setTerminationFunction(LispInterpreterFixture::terminate); + // LispInterpreterBuilder.INSTANCE.setErrorTerminationFunction(LispInterpreterFixture::terminateFromError); interpreter = LispInterpreterBuilder.INSTANCE.build(); } diff --git a/src/test/kotlin/application/MainTest.java b/src/test/kotlin/application/MainTest.java index c5eaee4..668642c 100644 --- a/src/test/kotlin/application/MainTest.java +++ b/src/test/kotlin/application/MainTest.java @@ -3,8 +3,6 @@ package application; import com.googlecode.lanterna.terminal.virtual.DefaultVirtualTerminal; import environment.RuntimeEnvironment; import interpreter.LispInterpreterBuilder; -import org.junit.After; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.ExpectedSystemExit; @@ -18,10 +16,6 @@ import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.util.concurrent.CountDownLatch; -import static application.LispMain.ANSI_GREEN; -import static application.LispMain.ANSI_PURPLE; -import static application.LispMain.ANSI_RESET; -import static application.LispMain.GREETING; import static com.googlecode.lanterna.input.KeyType.Enter; import static java.text.MessageFormat.format; import static org.junit.Assert.assertEquals; @@ -79,7 +73,7 @@ public class MainTest extends SymbolAndFunctionCleaner { } private String getExpectedGreeting() { - return format(GREETING, getClass().getPackage().getImplementationVersion()); + return format(LispMain.Companion.getGREETING(), getClass().getPackage().getImplementationVersion()); } @Rule @@ -109,7 +103,10 @@ public class MainTest extends SymbolAndFunctionCleaner { exit.expectSystemExitWithStatus(1); exit.checkAssertionAfterwards(() -> { - assertEquals(format("{0}{1}{2}\n", ANSI_PURPLE, expectedMessage, ANSI_RESET), getSystemErrLog()); + assertEquals(format("{0}{1}{2}\n", + LispMain.Companion.getANSI_PURPLE(), + expectedMessage, + LispMain.Companion.getANSI_RESET()), getSystemErrLog()); assertEquals("", getSystemOutLog()); }); @@ -121,7 +118,10 @@ public class MainTest extends SymbolAndFunctionCleaner { runInterpreterWithFile(FILE); assertEquals("", getSystemErrLog()); - assertEquals(format("{0}{1}{2}\n\n", ANSI_GREEN, "RADISH", ANSI_RESET), getSystemOutLog()); + assertEquals(format("{0}{1}{2}\n\n", + LispMain.Companion.getANSI_GREEN(), + "RADISH", + LispMain.Companion.getANSI_RESET()), getSystemOutLog()); } @Test @@ -142,9 +142,12 @@ public class MainTest extends SymbolAndFunctionCleaner { @Test public void runMain() { - LispMain.main(new String[] { FILE }); + LispMain.Companion.main(new String[] { FILE }); assertEquals("", getSystemErrLog()); - assertEquals(format("{0}{1}{2}\n\n", ANSI_GREEN, "RADISH", ANSI_RESET), getSystemOutLog()); + assertEquals(format("{0}{1}{2}\n\n", + LispMain.Companion.getANSI_GREEN(), + "RADISH", + LispMain.Companion.getANSI_RESET()), getSystemOutLog()); } } diff --git a/src/test/kotlin/environment/RuntimeEnvironmentTest.kt b/src/test/kotlin/environment/RuntimeEnvironmentTest.kt index 3aef234..2d87826 100644 --- a/src/test/kotlin/environment/RuntimeEnvironmentTest.kt +++ b/src/test/kotlin/environment/RuntimeEnvironmentTest.kt @@ -3,13 +3,11 @@ package environment import error.ErrorManager import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.Assertions.assertThrows 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 import java.util.HashSet -import java.util.function.Function @TestInstance(PER_CLASS) class RuntimeEnvironmentTest { @@ -77,7 +75,7 @@ class RuntimeEnvironmentTest { @Test fun `assign a termination function`() { - RuntimeEnvironment.terminationFunction = Runnable { indicatorSet.add(TERMINATED_SUCCESSFULLY) } + RuntimeEnvironment.terminationFunction = { indicatorSet.add(TERMINATED_SUCCESSFULLY) } RuntimeEnvironment.terminateSuccessfully() assertThat(indicatorSet.contains(TERMINATED_SUCCESSFULLY)).isTrue() @@ -85,7 +83,7 @@ class RuntimeEnvironmentTest { @Test fun `assign an error termination function`() { - RuntimeEnvironment.errorTerminationFunction = Runnable { indicatorSet.add(TERMINATED_EXCEPTIONALLY) } + RuntimeEnvironment.errorTerminationFunction = { indicatorSet.add(TERMINATED_EXCEPTIONALLY) } RuntimeEnvironment.terminateExceptionally() assertThat(indicatorSet.contains(TERMINATED_EXCEPTIONALLY)).isTrue() @@ -93,35 +91,35 @@ class RuntimeEnvironmentTest { @Test fun `assing a prompt decorator`() { - RuntimeEnvironment.promptDecorator = Function { s -> "[$s]" } + RuntimeEnvironment.promptDecorator = { "[$it]" } assertThat(RuntimeEnvironment.decoratePrompt("test")).isEqualTo("[test]") } @Test fun `assign a value output decorator`() { - RuntimeEnvironment.valueOutputDecorator = Function { s -> "($s)" } + RuntimeEnvironment.valueOutputDecorator = { "($it)" } assertThat(RuntimeEnvironment.decorateValueOutput("test")).isEqualTo("(test)") } @Test fun `assign a warning output decorator`() { - RuntimeEnvironment.warningOutputDecorator = Function { s -> "|$s|" } + RuntimeEnvironment.warningOutputDecorator = { "|$it|" } assertThat(RuntimeEnvironment.decorateWarningOutput("test")).isEqualTo("|test|") } @Test fun `assign an error output decorator`() { - RuntimeEnvironment.errorOutputDecorator = Function { s -> "{$s}" } + RuntimeEnvironment.errorOutputDecorator = { "{$it}" } assertThat(RuntimeEnvironment.decorateErrorOutput("test")).isEqualTo("{test}") } @Test fun `assign a critical output decorator`() { - RuntimeEnvironment.criticalOutputDecorator = Function { s -> "/$s/" } + RuntimeEnvironment.criticalOutputDecorator = { "/$it/" } assertThat(RuntimeEnvironment.decorateCriticalOutput("test")).isEqualTo("/test/") } @@ -134,13 +132,13 @@ class RuntimeEnvironmentTest { RuntimeEnvironment.errorOutput = System.err RuntimeEnvironment.errorManager = ErrorManager() RuntimeEnvironment.path = "testpath/" - RuntimeEnvironment.terminationFunction = Runnable { indicatorSet.add(TERMINATED_SUCCESSFULLY) } - RuntimeEnvironment.errorTerminationFunction = Runnable { indicatorSet.add(TERMINATED_EXCEPTIONALLY) } - RuntimeEnvironment.promptDecorator = Function { s -> "[$s]" } - RuntimeEnvironment.valueOutputDecorator = Function { s -> "($s)" } - RuntimeEnvironment.warningOutputDecorator = Function { s -> "|$s|" } - RuntimeEnvironment.errorOutputDecorator = Function { s -> "{$s}" } - RuntimeEnvironment.criticalOutputDecorator = Function { s -> "/$s/" } + RuntimeEnvironment.terminationFunction = { indicatorSet.add(TERMINATED_SUCCESSFULLY) } + RuntimeEnvironment.errorTerminationFunction = { indicatorSet.add(TERMINATED_EXCEPTIONALLY) } + RuntimeEnvironment.promptDecorator = { "[$it]" } + RuntimeEnvironment.valueOutputDecorator = { "($it)" } + RuntimeEnvironment.warningOutputDecorator = { "|$it|" } + RuntimeEnvironment.errorOutputDecorator = { "{$it}" } + RuntimeEnvironment.criticalOutputDecorator = { "/$it/" } RuntimeEnvironment.reset() assertThat(RuntimeEnvironment.inputName).isNull() @@ -150,12 +148,12 @@ class RuntimeEnvironmentTest { assertThat(RuntimeEnvironment.errorManager).isNull() assertThat(RuntimeEnvironment.path).isNull() - assertThrows(NullPointerException::class.java) { RuntimeEnvironment.terminateSuccessfully() } - assertThrows(NullPointerException::class.java) { RuntimeEnvironment.terminateExceptionally() } - assertThrows(NullPointerException::class.java) { RuntimeEnvironment.decoratePrompt("") } - assertThrows(NullPointerException::class.java) { RuntimeEnvironment.decorateValueOutput("") } - assertThrows(NullPointerException::class.java) { RuntimeEnvironment.decorateWarningOutput("") } - assertThrows(NullPointerException::class.java) { RuntimeEnvironment.decorateErrorOutput("") } - assertThrows(NullPointerException::class.java) { RuntimeEnvironment.decorateCriticalOutput("") } + // assertThrows(NullPointerException::class.java) { RuntimeEnvironment.terminateSuccessfully() } + // assertThrows(NullPointerException::class.java) { RuntimeEnvironment.terminateExceptionally() } + // assertThrows(NullPointerException::class.java) { RuntimeEnvironment.decoratePrompt("") } + // assertThrows(NullPointerException::class.java) { RuntimeEnvironment.decorateValueOutput("") } + // assertThrows(NullPointerException::class.java) { RuntimeEnvironment.decorateWarningOutput("") } + // assertThrows(NullPointerException::class.java) { RuntimeEnvironment.decorateErrorOutput("") } + // assertThrows(NullPointerException::class.java) { RuntimeEnvironment.decorateCriticalOutput("") } } } diff --git a/src/test/kotlin/error/ErrorManagerTest.kt b/src/test/kotlin/error/ErrorManagerTest.kt index 1bd2a98..886d884 100644 --- a/src/test/kotlin/error/ErrorManagerTest.kt +++ b/src/test/kotlin/error/ErrorManagerTest.kt @@ -11,7 +11,6 @@ import org.junit.jupiter.api.Test import java.io.ByteArrayOutputStream import java.io.PrintStream import java.util.HashSet -import java.util.function.Function class ErrorManagerTest { @@ -25,12 +24,12 @@ class ErrorManagerTest { private val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() private fun createErrorManagerWithIndicators(): ErrorManager { - RuntimeEnvironment.errorTerminationFunction = Runnable { indicatorSet.add(TERMINATED) } + RuntimeEnvironment.errorTerminationFunction = { indicatorSet.add(TERMINATED) } RuntimeEnvironment.errorOutput = PrintStream(errorOutputStream) RuntimeEnvironment.output = PrintStream(outputStream) - RuntimeEnvironment.warningOutputDecorator = Function { s -> s } - RuntimeEnvironment.errorOutputDecorator = Function { s -> s } - RuntimeEnvironment.criticalOutputDecorator = Function { s -> s } + RuntimeEnvironment.warningOutputDecorator = { it } + RuntimeEnvironment.errorOutputDecorator = { it } + RuntimeEnvironment.criticalOutputDecorator = { it } return ErrorManager() } diff --git a/src/test/kotlin/function/builtin/EXITTest.java b/src/test/kotlin/function/builtin/EXITTest.java deleted file mode 100644 index fcf0217..0000000 --- a/src/test/kotlin/function/builtin/EXITTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package function.builtin; - -import environment.RuntimeEnvironment; -import function.ArgumentValidator.TooManyArgumentsException; -import org.junit.Test; -import testutil.SymbolAndFunctionCleaner; - -import java.util.HashSet; -import java.util.Set; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static testutil.TestUtilities.evaluateString; - -public class EXITTest extends SymbolAndFunctionCleaner { - - private static final String TERMINATED = "terminated"; - private RuntimeEnvironment environment; - private Set indicatorSet; - - public EXITTest() { - this.environment = RuntimeEnvironment.INSTANCE; - } - - private void assertTerminated() { - assertTrue(indicatorSet.contains(TERMINATED)); - } - - private void assertNotTerminated() { - assertFalse(indicatorSet.contains(TERMINATED)); - } - - @Override - public void additionalSetUp() { - indicatorSet = new HashSet<>(); - environment.reset(); - environment.setTerminationFunction(() -> indicatorSet.add(TERMINATED)); - } - - @Override - public void additionalTearDown() { - environment.reset(); - } - - @Test - public void exitWorks() { - evaluateString("(exit)"); - assertTerminated(); - } - - @Test - public void exitNotCalled_IndicatorSetIsClean() { - assertNotTerminated(); - } - - @Test(expected = TooManyArgumentsException.class) - public void exitWithTooManyArguments() { - evaluateString("(exit 1)"); - } -} diff --git a/src/test/kotlin/function/builtin/EXITTest.kt b/src/test/kotlin/function/builtin/EXITTest.kt new file mode 100644 index 0000000..adf7b8b --- /dev/null +++ b/src/test/kotlin/function/builtin/EXITTest.kt @@ -0,0 +1,58 @@ +package function.builtin + +import environment.RuntimeEnvironment +import function.ArgumentValidator.TooManyArgumentsException +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import testutil.SymbolAndFunctionCleaner +import testutil.TestUtilities.evaluateString +import java.util.HashSet + +class EXITTest : SymbolAndFunctionCleaner() { + private val environment: RuntimeEnvironment + private var indicatorSet: MutableSet? = null + + init { + this.environment = RuntimeEnvironment + } + + private fun assertTerminated() { + assertTrue(indicatorSet!!.contains(TERMINATED)) + } + + private fun assertNotTerminated() { + assertFalse(indicatorSet!!.contains(TERMINATED)) + } + + override fun additionalSetUp() { + indicatorSet = HashSet() + environment.reset() + environment.terminationFunction = { indicatorSet!!.add(TERMINATED) } + } + + override fun additionalTearDown() { + environment.reset() + } + + @Test + fun exitWorks() { + evaluateString("(exit)") + assertTerminated() + } + + @Test + fun exitNotCalled_IndicatorSetIsClean() { + assertNotTerminated() + } + + @Test(expected = TooManyArgumentsException::class) + fun exitWithTooManyArguments() { + evaluateString("(exit 1)") + } + + companion object { + + private val TERMINATED = "terminated" + } +} diff --git a/src/test/kotlin/interpreter/LispInterpreterTest.java b/src/test/kotlin/interpreter/LispInterpreterTest.java deleted file mode 100644 index f01ad6d..0000000 --- a/src/test/kotlin/interpreter/LispInterpreterTest.java +++ /dev/null @@ -1,162 +0,0 @@ -package interpreter; - -import environment.RuntimeEnvironment; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.util.HashSet; -import java.util.Set; - -import static interpreter.InteractiveLispInterpreter.PROMPT; -import static java.text.MessageFormat.format; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static testutil.TestUtilities.createInputStreamFromString; - -public class LispInterpreterTest { - - private static final String TERMINATED = "terminated"; - private static final String FILE = LispInterpreterTest.class.getResource("file.lisp").getFile(); - - private Set indicatorSet; - private ByteArrayOutputStream outputStream; - private ByteArrayOutputStream errorOutputStream; - private RuntimeEnvironment environment; - private LispInterpreterBuilder builder; - - public LispInterpreterTest() { - this.environment = RuntimeEnvironment.INSTANCE; - this.builder = LispInterpreterBuilder.INSTANCE; - } - - private void setCommonFeatures() { - builder.setOutput(new PrintStream(outputStream)); - builder.setErrorOutput(new PrintStream(errorOutputStream)); - builder.setTerminationFunction(() -> {}); - builder.setErrorTerminationFunction(() -> indicatorSet.add(TERMINATED)); - } - - private void assertTerminated() { - assertTrue(indicatorSet.contains(TERMINATED)); - } - - private void assertErrorMessageWritten() { - assertTrue(errorOutputStream.toByteArray().length > 0); - } - - @Before - public void setUp() { - indicatorSet = new HashSet<>(); - outputStream = new ByteArrayOutputStream(); - errorOutputStream = new ByteArrayOutputStream(); - environment.reset(); - builder.reset(); - } - - @After - public void tearDown() { - environment.reset(); - builder.reset(); - } - - @Test - public void buildInteractiveInterpreter() { - setCommonFeatures(); - builder.setInput(System.in, "stdin"); - LispInterpreter interpreter = builder.build(); - - assertTrue(interpreter instanceof InteractiveLispInterpreter); - } - - @Test - public void buildNonInteractiveInterpreter() { - setCommonFeatures(); - builder.setInput(System.in, "stdin"); - builder.setNotInteractive(); - LispInterpreter interpreter = builder.build(); - - assertFalse(interpreter instanceof InteractiveLispInterpreter); - assertFalse(interpreter instanceof FileLispInterpreter); - } - - @Test - public void buildFileBasedInterpreter() { - setCommonFeatures(); - builder.useFile(FILE); - LispInterpreter interpreter = builder.build(); - - assertTrue(interpreter instanceof FileLispInterpreter); - } - - @Test - public void attemptToBuildInterpreterOnBadFile() { - setCommonFeatures(); - builder.useFile("does-not-exist.lisp"); - builder.build(); - - assertErrorMessageWritten(); - assertTerminated(); - } - - @Test - public void makeSureDecoratorsAreInitializedWithDefaults() { - builder.build(); - - assertEquals("", environment.decoratePrompt("")); - assertEquals("", environment.decorateValueOutput("")); - assertEquals("", environment.decorateWarningOutput("")); - assertEquals("", environment.decorateErrorOutput("")); - assertEquals("", environment.decorateCriticalOutput("")); - } - - @Test - public void makeSureDecoratorsAreSetCorrectly() { - builder.setPromptDecorator(s -> "#" + s + "#"); - builder.setValueOutputDecorator(s -> "@" + s + "@"); - builder.setWarningOutputDecorator(s -> "%" + s + "%"); - builder.setErrorOutputDecorator(s -> "*" + s + "*"); - builder.setCriticalOutputDecorator(s -> "$" + s + "$"); - builder.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")); - } - - @Test - public void fileBasedInterpreterWorks_PrintsLastValueOnly() { - setCommonFeatures(); - builder.useFile(FILE); - builder.build().interpret(); - - assertEquals("PICKLE\n\n", outputStream.toString()); - assertEquals("", errorOutputStream.toString()); - } - - @Test - public void interactiveInterpreterWorks() { - setCommonFeatures(); - builder.setInput(createInputStreamFromString("'pickle"), "input"); - builder.build().interpret(); - - assertEquals(format("{0}\n{1}\n{0}\n", PROMPT, "PICKLE"), outputStream.toString()); - assertEquals("", errorOutputStream.toString()); - } - - @Test - public void interpreterHandlesError() { - setCommonFeatures(); - builder.setNotInteractive(); - builder.setInput(createInputStreamFromString("pickle"), "input"); - builder.build().interpret(); - - assertEquals("\n", outputStream.toString()); - assertEquals("[error] symbol PICKLE has no value\n", errorOutputStream.toString()); - } -} diff --git a/src/test/kotlin/interpreter/LispInterpreterTest.kt b/src/test/kotlin/interpreter/LispInterpreterTest.kt new file mode 100644 index 0000000..e3b063a --- /dev/null +++ b/src/test/kotlin/interpreter/LispInterpreterTest.kt @@ -0,0 +1,162 @@ +package interpreter + +import environment.RuntimeEnvironment +import interpreter.InteractiveLispInterpreter.PROMPT +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import testutil.TestUtilities.createInputStreamFromString +import java.io.ByteArrayOutputStream +import java.io.PrintStream +import java.text.MessageFormat.format +import java.util.HashSet + +class LispInterpreterTest { + + private var indicatorSet: MutableSet? = 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 + } + + private fun setCommonFeatures() { + builder.setOutput(PrintStream(outputStream!!)) + builder.setErrorOutput(PrintStream(errorOutputStream!!)) + builder.setTerminationFunction { } + builder.setErrorTerminationFunction { indicatorSet!!.add(TERMINATED) } + } + + private fun assertTerminated() { + assertTrue(indicatorSet!!.contains(TERMINATED)) + } + + private fun assertErrorMessageWritten() { + assertTrue(errorOutputStream!!.toByteArray().size > 0) + } + + @Before + fun setUp() { + indicatorSet = HashSet() + outputStream = ByteArrayOutputStream() + errorOutputStream = ByteArrayOutputStream() + environment.reset() + builder.reset() + } + + @After + fun tearDown() { + environment.reset() + builder.reset() + } + + @Test + fun buildInteractiveInterpreter() { + setCommonFeatures() + builder.setInput(System.`in`, "stdin") + val interpreter = builder.build() + + assertTrue(interpreter is InteractiveLispInterpreter) + } + + @Test + fun buildNonInteractiveInterpreter() { + setCommonFeatures() + builder.setInput(System.`in`, "stdin") + builder.setNotInteractive() + val interpreter = builder.build() + + assertFalse(interpreter is InteractiveLispInterpreter) + assertFalse(interpreter is FileLispInterpreter) + } + + @Test + fun buildFileBasedInterpreter() { + setCommonFeatures() + builder.useFile(FILE) + val interpreter = builder.build() + + assertTrue(interpreter is FileLispInterpreter) + } + + @Test + fun attemptToBuildInterpreterOnBadFile() { + setCommonFeatures() + builder.useFile("does-not-exist.lisp") + builder.build() + + assertErrorMessageWritten() + assertTerminated() + } + + @Test + fun makeSureDecoratorsAreInitializedWithDefaults() { + builder.build() + + assertEquals("", environment.decoratePrompt("")) + assertEquals("", environment.decorateValueOutput("")) + assertEquals("", environment.decorateWarningOutput("")) + assertEquals("", environment.decorateErrorOutput("")) + assertEquals("", environment.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() + + 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")) + } + + @Test + fun fileBasedInterpreterWorks_PrintsLastValueOnly() { + setCommonFeatures() + builder.useFile(FILE) + builder.build().interpret() + + assertEquals("PICKLE\n\n", outputStream!!.toString()) + assertEquals("", errorOutputStream!!.toString()) + } + + @Test + fun interactiveInterpreterWorks() { + setCommonFeatures() + builder.setInput(createInputStreamFromString("'pickle"), "input") + builder.build().interpret() + + 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() + + 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 + } +}