Convert main class to kotlin

This commit is contained in:
Mike Cifelli 2018-03-30 20:54:48 -04:00
parent d7a751ad39
commit 3664a58989
3 changed files with 189 additions and 252 deletions

View File

@ -1,135 +1,98 @@
package application package application
import com.googlecode.lanterna.terminal.DefaultTerminalFactory import com.googlecode.lanterna.terminal.DefaultTerminalFactory
import com.googlecode.lanterna.terminal.IOSafeTerminal
import com.googlecode.lanterna.terminal.IOSafeTerminalAdapter.createRuntimeExceptionConvertingAdapter import com.googlecode.lanterna.terminal.IOSafeTerminalAdapter.createRuntimeExceptionConvertingAdapter
import com.googlecode.lanterna.terminal.Terminal
import interpreter.LispInterpreter import interpreter.LispInterpreter
import interpreter.LispInterpreterBuilder import interpreter.LispInterpreterBuilder
import stream.UncheckedIOException
import terminal.LispTerminal import terminal.LispTerminal
import terminal.LispTerminal.END_OF_SEGMENT import terminal.LispTerminal.END_OF_SEGMENT
import terminal.TerminalConfiguration import terminal.TerminalConfiguration
import java.io.IOException
import java.io.PipedInputStream import java.io.PipedInputStream
import java.io.PipedOutputStream import java.io.PipedOutputStream
import java.io.PrintStream import java.io.PrintStream
import java.text.MessageFormat.format import java.text.MessageFormat.format
class LispMain { class LispMain @JvmOverloads constructor(var configuration: TerminalConfiguration = TerminalConfiguration()) {
private var builder: LispInterpreterBuilder? = null init {
private var inputReader: PipedInputStream? = null if (configuration.terminal == null) {
private var output: PrintStream? = null configuration.setInputPair(PipedOutputStream(), PipedInputStream())
private var lispTerminal: LispTerminal? = null configuration.setOutputPair(PipedOutputStream(), PipedInputStream())
private var configuration: TerminalConfiguration? = null configuration.terminal = createIOSafeTerminal()
}
private val version = LispMain::class.java.`package`.implementationVersion
constructor() {
this.builder = LispInterpreterBuilder
} }
constructor(configuration: TerminalConfiguration) { private var inputReader: PipedInputStream = configuration.inputReader
this.builder = LispInterpreterBuilder private var output: PrintStream = PrintStream(configuration.outputWriter)
this.configuration = configuration private var lispTerminal: LispTerminal = LispTerminal(configuration)
} private val version = javaClass.`package`.implementationVersion
fun runInteractive() { fun runInteractive() {
initializeTerminal()
printGreeting() printGreeting()
lispTerminal!!.start() lispTerminal.start()
buildInteractiveInterpreter().interpret() buildInteractiveInterpreter().interpret()
shutdownTerminal() 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) { fun runWithFile(fileName: String) {
buildFileInterpreter(fileName).interpret() buildFileInterpreter(fileName).interpret()
} }
private fun buildFileInterpreter(fileName: String): LispInterpreter { private fun createIOSafeTerminal() = createRuntimeExceptionConvertingAdapter(createDefaultTerminal())
builder!!.useFile(fileName) private fun createDefaultTerminal() = DefaultTerminalFactory().createTerminal()
builder!!.setOutput(System.out) private fun makeColorDecorator(color: String): (String) -> String = { color + it + ANSI_RESET }
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() private fun printGreeting() {
output.println(format(GREETING, (version ?: "null")))
output.println()
}
private fun buildInteractiveInterpreter(): LispInterpreter {
LispInterpreterBuilder.setInput(inputReader, "terminal")
LispInterpreterBuilder.setOutput(output)
LispInterpreterBuilder.setErrorOutput(output)
LispInterpreterBuilder.setTerminationFunction { this.shutdownTerminal() }
LispInterpreterBuilder.setErrorTerminationFunction { this.shutdownTerminal() }
LispInterpreterBuilder.setLanguageFileNames(*LANGUAGE_FILE_NAMES)
LispInterpreterBuilder.setPromptDecorator { it + END_OF_SEGMENT }
LispInterpreterBuilder.setValueOutputDecorator(makeColorDecorator(ANSI_GREEN))
LispInterpreterBuilder.setWarningOutputDecorator(makeColorDecorator(ANSI_YELLOW))
LispInterpreterBuilder.setErrorOutputDecorator(makeColorDecorator(ANSI_RED))
LispInterpreterBuilder.setCriticalOutputDecorator(makeColorDecorator(ANSI_PURPLE))
return LispInterpreterBuilder.build()
}
private fun buildFileInterpreter(fileName: String): LispInterpreter {
LispInterpreterBuilder.useFile(fileName)
LispInterpreterBuilder.setOutput(System.out)
LispInterpreterBuilder.setErrorOutput(System.err)
LispInterpreterBuilder.setTerminationFunction { System.exit(0) }
LispInterpreterBuilder.setErrorTerminationFunction { System.exit(1) }
LispInterpreterBuilder.setLanguageFileNames(*LANGUAGE_FILE_NAMES)
LispInterpreterBuilder.setValueOutputDecorator(makeColorDecorator(ANSI_GREEN))
LispInterpreterBuilder.setWarningOutputDecorator(makeColorDecorator(ANSI_YELLOW))
LispInterpreterBuilder.setErrorOutputDecorator(makeColorDecorator(ANSI_RED))
LispInterpreterBuilder.setCriticalOutputDecorator(makeColorDecorator(ANSI_PURPLE))
return LispInterpreterBuilder.build()
}
private fun shutdownTerminal() {
output.print(END_OF_SEGMENT)
lispTerminal.stop()
output.close()
} }
companion object { companion object {
val GREETING = "Transcendental Lisp - Version {0}" const val GREETING = "Transcendental Lisp - Version {0}"
val ANSI_RESET = "\u001B[0m" const val ANSI_RESET = "\u001B[0m"
val ANSI_RED = "\u001B[31m" const val ANSI_RED = "\u001B[31m"
val ANSI_GREEN = "\u001B[32m" const val ANSI_GREEN = "\u001B[32m"
val ANSI_YELLOW = "\u001B[33m" const val ANSI_YELLOW = "\u001B[33m"
val ANSI_PURPLE = "\u001B[35m" const val ANSI_PURPLE = "\u001B[35m"
val LANGUAGE_FILE_NAMES = arrayOf("functions.lisp", "dlambda.lisp") val LANGUAGE_FILE_NAMES = arrayOf("functions.lisp", "dlambda.lisp")
@ -137,7 +100,7 @@ class LispMain {
fun main(arguments: Array<String>) { fun main(arguments: Array<String>) {
val lispMain = LispMain() val lispMain = LispMain()
if (arguments.size == 0) if (arguments.isEmpty())
lispMain.runInteractive() lispMain.runInteractive()
else else
lispMain.runWithFile(arguments[0]) lispMain.runWithFile(arguments[0])

View File

@ -1,153 +0,0 @@
package application;
import com.googlecode.lanterna.terminal.virtual.DefaultVirtualTerminal;
import environment.RuntimeEnvironment;
import interpreter.LispInterpreterBuilder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.ExpectedSystemExit;
import org.junit.contrib.java.lang.system.SystemErrRule;
import org.junit.contrib.java.lang.system.SystemOutRule;
import terminal.TerminalConfiguration;
import terminal.VirtualTerminalInteractor;
import testutil.SymbolAndFunctionCleaner;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.CountDownLatch;
import static com.googlecode.lanterna.input.KeyType.Enter;
import static java.text.MessageFormat.format;
import static org.junit.Assert.assertEquals;
public class MainTest extends SymbolAndFunctionCleaner {
private static final String FILE = MainTest.class.getResource("file.lisp").getFile();
private RuntimeEnvironment environment;
private CountDownLatch latch;
public MainTest() {
this.environment = RuntimeEnvironment.INSTANCE;
}
private void runInterpreterWithFile(String fileName) {
TerminalConfiguration configuration = new TerminalConfiguration();
configuration.setInputPair(new PipedOutputStream(), new PipedInputStream());
configuration.setOutputPair(new PipedOutputStream(), new PipedInputStream());
configuration.setTerminal(new DefaultVirtualTerminal());
LispMain main = new LispMain(configuration);
main.runWithFile(fileName);
}
private VirtualTerminalInteractor runInterpreterAndGetInteractor() {
VirtualTerminalInteractor terminal = new VirtualTerminalInteractor();
latch = new CountDownLatch(1);
new Thread(() -> {
try {
LispMain main = new LispMain(terminal.getConfiguration());
main.runInteractive();
} finally {
latch.countDown();
}
}).start();
return terminal;
}
private void waitForInterpreterToShutdown() {
try {
latch.await();
} catch (InterruptedException ignored) {
}
}
private String getSystemOutLog() {
return systemOutRule.getLogWithNormalizedLineSeparator();
}
private String getSystemErrLog() {
return systemErrRule.getLogWithNormalizedLineSeparator();
}
private String getExpectedGreeting() {
return format(LispMain.Companion.getGREETING(), getClass().getPackage().getImplementationVersion());
}
@Rule
public ExpectedSystemExit exit = ExpectedSystemExit.none();
@Rule
public SystemErrRule systemErrRule = new SystemErrRule().enableLog().mute();
@Rule
public SystemOutRule systemOutRule = new SystemOutRule().enableLog().mute();
@Override
public void additionalSetUp() {
environment.reset();
LispInterpreterBuilder.INSTANCE.reset();
}
@Override
public void additionalTearDown() {
environment.reset();
LispInterpreterBuilder.INSTANCE.reset();
}
@Test
public void runWithBadFile() {
String expectedMessage = "[critical] bad.lisp (No such file or directory)";
exit.expectSystemExitWithStatus(1);
exit.checkAssertionAfterwards(() -> {
assertEquals(format("{0}{1}{2}\n",
LispMain.Companion.getANSI_PURPLE(),
expectedMessage,
LispMain.Companion.getANSI_RESET()), getSystemErrLog());
assertEquals("", getSystemOutLog());
});
runInterpreterWithFile("bad.lisp");
}
@Test
public void runWithFile_PrintsDecoratedLastValueOnly() {
runInterpreterWithFile(FILE);
assertEquals("", getSystemErrLog());
assertEquals(format("{0}{1}{2}\n\n",
LispMain.Companion.getANSI_GREEN(),
"RADISH",
LispMain.Companion.getANSI_RESET()), getSystemOutLog());
}
@Test
public void runInteractive() {
VirtualTerminalInteractor terminal = runInterpreterAndGetInteractor();
terminal.waitForPrompt();
terminal.enterCharacters("'hi");
terminal.pressKey(Enter);
terminal.waitForPrompt();
terminal.assertCursorPosition(2, 5);
terminal.assertScreenText(getExpectedGreeting(), " ", "~ 'hi ", " ", "HI ", "~ ");
terminal.enterControlCharacter('d');
terminal.assertInputStreamClosed();
waitForInterpreterToShutdown();
}
@Test
public void runMain() {
LispMain.Companion.main(new String[] { FILE });
assertEquals("", getSystemErrLog());
assertEquals(format("{0}{1}{2}\n\n",
LispMain.Companion.getANSI_GREEN(),
"RADISH",
LispMain.Companion.getANSI_RESET()), getSystemOutLog());
}
}

View File

@ -0,0 +1,127 @@
package application
import application.LispMain.Companion.ANSI_GREEN
import application.LispMain.Companion.ANSI_PURPLE
import application.LispMain.Companion.ANSI_RESET
import com.googlecode.lanterna.input.KeyType.Enter
import com.googlecode.lanterna.terminal.virtual.DefaultVirtualTerminal
import environment.RuntimeEnvironment
import interpreter.LispInterpreterBuilder
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.junit.contrib.java.lang.system.ExpectedSystemExit
import org.junit.contrib.java.lang.system.SystemErrRule
import org.junit.contrib.java.lang.system.SystemOutRule
import terminal.TerminalConfiguration
import terminal.VirtualTerminalInteractor
import testutil.SymbolAndFunctionCleaner
import java.io.PipedInputStream
import java.io.PipedOutputStream
import java.text.MessageFormat.format
import java.util.concurrent.CountDownLatch
class MainTest : SymbolAndFunctionCleaner() {
companion object {
private val FILE = MainTest::class.java.getResource("file.lisp").file
}
private lateinit var latch: CountDownLatch
private fun systemOutLog() = systemOutRule.logWithNormalizedLineSeparator
private fun systemErrLog() = systemErrRule.logWithNormalizedLineSeparator
private fun expectedGreeting() = format(LispMain.GREETING, javaClass.`package`.implementationVersion)
@Rule
@JvmField
val exit: ExpectedSystemExit = ExpectedSystemExit.none()
@Rule
@JvmField
val systemErrRule: SystemErrRule = SystemErrRule().enableLog().mute()
@Rule
@JvmField
val systemOutRule: SystemOutRule = SystemOutRule().enableLog().mute()
private fun runInterpreterWithFile(fileName: String) {
val configuration = TerminalConfiguration()
configuration.setInputPair(PipedOutputStream(), PipedInputStream())
configuration.setOutputPair(PipedOutputStream(), PipedInputStream())
configuration.terminal = DefaultVirtualTerminal()
val main = LispMain(configuration)
main.runWithFile(fileName)
}
private fun runInterpreterAndGetInteractor(): VirtualTerminalInteractor {
val terminal = VirtualTerminalInteractor()
latch = CountDownLatch(1)
Thread {
try {
val main = LispMain(terminal.configuration)
main.runInteractive()
} finally {
latch.countDown()
}
}.start()
return terminal
}
private fun waitForInterpreterToShutdown() {
try {
latch.await()
} catch (ignored: InterruptedException) {
}
}
override fun additionalSetUp() {
RuntimeEnvironment.reset()
LispInterpreterBuilder.reset()
}
override fun additionalTearDown() {
RuntimeEnvironment.reset()
LispInterpreterBuilder.reset()
}
@Test
fun runWithBadFile() {
val expectedMessage = "[critical] bad.lisp (No such file or directory)"
exit.expectSystemExitWithStatus(1)
exit.checkAssertionAfterwards {
assertEquals(format("{0}{1}{2}\n", ANSI_PURPLE, expectedMessage, ANSI_RESET), systemErrLog())
assertEquals("", systemOutLog())
}
runInterpreterWithFile("bad.lisp")
}
@Test
fun runWithFile_PrintsDecoratedLastValueOnly() {
runInterpreterWithFile(FILE)
assertEquals("", systemErrLog())
assertEquals(format("{0}{1}{2}\n\n", ANSI_GREEN, "RADISH", ANSI_RESET), systemOutLog())
}
@Test
fun runInteractive() {
val terminal = runInterpreterAndGetInteractor()
terminal.waitForPrompt()
terminal.enterCharacters("'hi")
terminal.pressKey(Enter)
terminal.waitForPrompt()
terminal.assertCursorPosition(2, 5)
terminal.assertScreenText(expectedGreeting(), " ", "~ 'hi ", " ", "HI ", "~ ")
terminal.enterControlCharacter('d')
terminal.assertInputStreamClosed()
waitForInterpreterToShutdown()
}
}