Refactor Repl: use sealed class to represent line result

This commit is contained in:
Pavel V. Talanov
2016-05-31 17:42:46 +03:00
parent 17832e10e7
commit 8ac40d7401
3 changed files with 43 additions and 92 deletions
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.cli.jvm.repl
import com.intellij.openapi.Disposable
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.cli.common.KotlinVersion
import org.jetbrains.kotlin.cli.jvm.repl.LineResult.*
import org.jetbrains.kotlin.cli.jvm.repl.messages.unescapeLineBreaks
import org.jetbrains.kotlin.config.CompilerConfiguration
import java.io.File
@@ -84,8 +85,8 @@ class ReplFromTerminal(
return if (notQuit) WhatNextAfterOneLine.READ_LINE else WhatNextAfterOneLine.QUIT
}
val lineResultType = eval(line)
if (lineResultType === ReplInterpreter.LineResultType.INCOMPLETE) {
val lineResult = eval(line)
if (lineResult is LineResult.Incomplete) {
return WhatNextAfterOneLine.INCOMPLETE
}
else {
@@ -93,27 +94,20 @@ class ReplFromTerminal(
}
}
private fun eval(line: String): ReplInterpreter.LineResultType {
private fun eval(line: String): LineResult {
val lineResult = replInterpreter.eval(line)
if (lineResult.type === ReplInterpreter.LineResultType.SUCCESS) {
writer.notifyCommandSuccess()
if (!lineResult.isUnit) {
writer.outputCommandResult(lineResult.value)
when (lineResult) {
is ValueResult, UnitResult -> {
writer.notifyCommandSuccess()
if (lineResult is ValueResult) {
writer.outputCommandResult(lineResult.value)
}
}
Incomplete -> writer.notifyIncomplete()
is Error.CompileTime -> writer.outputCompileError(lineResult.errorText)
is Error.Runtime -> writer.outputRuntimeError(lineResult.errorText)
}
else if (lineResult.type === ReplInterpreter.LineResultType.INCOMPLETE) {
writer.notifyIncomplete()
}
else if (lineResult.type === ReplInterpreter.LineResultType.COMPILE_ERROR) {
writer.outputCompileError(lineResult.errorText!!)
}
else if (lineResult.type === ReplInterpreter.LineResultType.RUNTIME_ERROR) {
writer.outputRuntimeError(lineResult.errorText!!)
}
else {
throw IllegalStateException("unknown line result type: " + lineResult)
}
return lineResult.type
return lineResult
}
@Throws(Exception::class)
@@ -70,66 +70,6 @@ class ReplInterpreter(
private val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl
private val analyzerEngine = CliReplAnalyzerEngine(environment)
enum class LineResultType {
SUCCESS,
COMPILE_ERROR,
RUNTIME_ERROR,
INCOMPLETE
}
class LineResult private constructor(
private val resultingValue: Any?,
private val unit: Boolean,
val errorText: String?,
val type: LineResultType
) {
val value: Any?
get() {
checkSuccessful()
return resultingValue
}
val isUnit: Boolean
get() {
checkSuccessful()
return unit
}
private fun checkSuccessful() {
if (type != LineResultType.SUCCESS) {
error("it is error")
}
}
companion object {
private fun error(errorText: String, errorType: LineResultType): LineResult {
val resultingErrorText = when {
errorText.isEmpty() -> "<unknown error>"
!errorText.endsWith("\n") -> errorText + "\n"
else -> errorText
}
return LineResult(null, false, resultingErrorText, errorType)
}
fun successful(value: Any?, unit: Boolean): LineResult {
return LineResult(value, unit, null, LineResultType.SUCCESS)
}
fun compileError(errorText: String): LineResult {
return error(errorText, LineResultType.COMPILE_ERROR)
}
fun runtimeError(errorText: String): LineResult {
return error(errorText, LineResultType.RUNTIME_ERROR)
}
fun incomplete(): LineResult {
return LineResult(null, false, null, LineResultType.INCOMPLETE)
}
}
}
fun eval(line: String): LineResult {
++lineNumber
@@ -149,23 +89,23 @@ class ReplInterpreter(
if (syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof) {
return if (allowIncompleteLines) {
previousIncompleteLines.add(line)
LineResult.incomplete()
LineResult.Incomplete
}
else {
LineResult.compileError(errorHolder.renderedDiagnostics)
LineResult.Error.CompileTime(errorHolder.renderedDiagnostics)
}
}
previousIncompleteLines.clear()
if (syntaxErrorReport.isHasErrors) {
return LineResult.compileError(errorHolder.renderedDiagnostics)
return LineResult.Error.CompileTime(errorHolder.renderedDiagnostics)
}
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, lineNumber)
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder, false)
val scriptDescriptor = when (analysisResult) {
is WithErrors -> return LineResult.compileError(errorHolder.renderedDiagnostics)
is WithErrors -> return LineResult.Error.CompileTime(errorHolder.renderedDiagnostics)
is Successful -> analysisResult.scriptDescriptor
else -> error("Unexpected result ${analysisResult.javaClass}")
}
@@ -196,7 +136,7 @@ class ReplInterpreter(
scriptInstanceConstructor.newInstance(*constructorArgs)
}
catch (e: Throwable) {
return LineResult.runtimeError(renderStackTrace(e.cause!!))
return LineResult.Error.Runtime(renderStackTrace(e.cause!!))
}
finally {
onUserCodeExecuting(false)
@@ -207,7 +147,10 @@ class ReplInterpreter(
earlierLines.add(EarlierLine(line, scriptDescriptor, scriptClass, scriptInstance))
return LineResult.successful(rv, !state.replSpecific.hasResult)
if (!state.replSpecific.hasResult) {
return LineResult.UnitResult
}
return LineResult.ValueResult(rv)
}
catch (e: Throwable) {
val writer = PrintWriter(System.err)
@@ -275,3 +218,15 @@ class ReplInterpreter(
}
}
}
sealed class LineResult {
class ValueResult(val value: Any?): LineResult()
object UnitResult: LineResult()
object Incomplete : LineResult()
sealed class Error(val errorText: String): LineResult() {
class Runtime(errorText: String): Error(errorText)
class CompileTime(errorText: String): Error(errorText)
}
}
@@ -17,6 +17,8 @@
package org.jetbrains.kotlin.repl
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.cli.jvm.repl.ConsoleReplConfiguration
import org.jetbrains.kotlin.cli.jvm.repl.LineResult
import org.jetbrains.kotlin.cli.jvm.repl.ReplInterpreter
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils
@@ -81,7 +83,7 @@ abstract class AbstractReplInterpreterTest : KtUsefulTestCase() {
protected fun doTest(path: String) {
val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK)
val repl = ReplInterpreter(testRootDisposable, configuration, false, null)
val repl = ReplInterpreter(testRootDisposable, configuration, ConsoleReplConfiguration())
for ((code, expected) in loadLines(File(path))) {
val lineResult = repl.eval(code)
@@ -90,11 +92,11 @@ abstract class AbstractReplInterpreterTest : KtUsefulTestCase() {
repl.dumpClasses(PrintWriter(System.out))
}
val actual = when (lineResult.type) {
ReplInterpreter.LineResultType.SUCCESS -> if (!lineResult.isUnit) "${lineResult.value}" else ""
ReplInterpreter.LineResultType.RUNTIME_ERROR,
ReplInterpreter.LineResultType.COMPILE_ERROR -> lineResult.errorText!!
ReplInterpreter.LineResultType.INCOMPLETE -> INCOMPLETE_LINE_MESSAGE
val actual = when (lineResult) {
is LineResult.ValueResult -> "${lineResult.value}"
is LineResult.Error -> lineResult.errorText
LineResult.Incomplete -> INCOMPLETE_LINE_MESSAGE
LineResult.UnitResult -> ""
}
Assert.assertEquals(