repl: multiline
This commit is contained in:
+24
-2
@@ -123,13 +123,35 @@ public final class AnalyzerWithCompilerReport {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean reportSyntaxErrors(@NotNull PsiElement file, @NotNull final MessageCollector messageCollector) {
|
||||
public static class SyntaxErrorReport {
|
||||
private final boolean hasErrors;
|
||||
private final boolean onlyErrorAtEof;
|
||||
|
||||
public SyntaxErrorReport(boolean hasErrors, boolean onlyErrorAtEof) {
|
||||
this.hasErrors = hasErrors;
|
||||
this.onlyErrorAtEof = onlyErrorAtEof;
|
||||
}
|
||||
|
||||
public boolean isHasErrors() {
|
||||
return hasErrors;
|
||||
}
|
||||
|
||||
public boolean isOnlyErrorAtEof() {
|
||||
return onlyErrorAtEof;
|
||||
}
|
||||
}
|
||||
|
||||
public static SyntaxErrorReport reportSyntaxErrors(@NotNull final PsiElement file, @NotNull final MessageCollector messageCollector) {
|
||||
class ErrorReportingVisitor extends AnalyzingUtils.PsiErrorElementVisitor {
|
||||
boolean hasErrors = false;
|
||||
boolean onlyErrorAtEof = false;
|
||||
|
||||
private <E extends PsiElement> void reportDiagnostic(E element, SimpleDiagnosticFactory<E> factory, String message) {
|
||||
MyDiagnostic<?> diagnostic = new MyDiagnostic<E>(element, factory, message);
|
||||
AnalyzerWithCompilerReport.reportDiagnostic(diagnostic, messageCollector);
|
||||
if (element.getTextRange().getStartOffset() == file.getTextRange().getEndOffset()) {
|
||||
onlyErrorAtEof = !hasErrors;
|
||||
}
|
||||
hasErrors = true;
|
||||
}
|
||||
|
||||
@@ -150,7 +172,7 @@ public final class AnalyzerWithCompilerReport {
|
||||
|
||||
file.accept(visitor);
|
||||
|
||||
return visitor.hasErrors;
|
||||
return new SyntaxErrorReport(visitor.hasErrors, visitor.onlyErrorAtEof);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -89,9 +89,10 @@ public class ReplFromTerminal {
|
||||
try {
|
||||
System.out.println("Kotlin interactive shell");
|
||||
System.out.println("Type :help for help, :quit for quit");
|
||||
WhatNextAfterOneLine next = WhatNextAfterOneLine.READ_LINE;
|
||||
while (true) {
|
||||
boolean next = one();
|
||||
if (!next) {
|
||||
next = one(next);
|
||||
if (next == WhatNextAfterOneLine.QUIT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -106,33 +107,55 @@ public class ReplFromTerminal {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean one() {
|
||||
private enum WhatNextAfterOneLine {
|
||||
READ_LINE,
|
||||
INCOMPLETE,
|
||||
QUIT,
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private WhatNextAfterOneLine one(@NotNull WhatNextAfterOneLine next) {
|
||||
try {
|
||||
String line = consoleReader.readLine(">>> ");
|
||||
String line = consoleReader.readLine(next == WhatNextAfterOneLine.INCOMPLETE ? "... " : ">>> ");
|
||||
if (line == null) {
|
||||
return false;
|
||||
return WhatNextAfterOneLine.QUIT;
|
||||
}
|
||||
|
||||
if (line.startsWith(":")) {
|
||||
return oneCommand(line.substring(1));
|
||||
boolean notQuit = oneCommand(line.substring(1));
|
||||
return notQuit ? WhatNextAfterOneLine.READ_LINE : WhatNextAfterOneLine.QUIT;
|
||||
}
|
||||
|
||||
eval(line);
|
||||
return true;
|
||||
ReplInterpreter.LineResultType lineResultType = eval(line);
|
||||
if (lineResultType == ReplInterpreter.LineResultType.INCOMPLETE) {
|
||||
return WhatNextAfterOneLine.INCOMPLETE;
|
||||
}
|
||||
else {
|
||||
return WhatNextAfterOneLine.READ_LINE;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw ExceptionUtils.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void eval(@NotNull String line) {
|
||||
@NotNull
|
||||
private ReplInterpreter.LineResultType eval(@NotNull String line) {
|
||||
ReplInterpreter.LineResult lineResult = getReplInterpreter().eval(line);
|
||||
if (!lineResult.isSuccessful()) {
|
||||
if (lineResult.getType() == ReplInterpreter.LineResultType.SUCCESS) {
|
||||
if (!lineResult.isUnit()) {
|
||||
System.out.println(lineResult.getValue());
|
||||
}
|
||||
}
|
||||
else if (lineResult.getType() == ReplInterpreter.LineResultType.INCOMPLETE) {
|
||||
}
|
||||
else if (lineResult.getType() == ReplInterpreter.LineResultType.ERROR) {
|
||||
System.out.print(lineResult.getErrorText());
|
||||
}
|
||||
else if (!lineResult.isUnit()) {
|
||||
System.out.println(lineResult.getValue());
|
||||
else {
|
||||
throw new IllegalStateException("unknown line result type: " + lineResult);
|
||||
}
|
||||
return lineResult.getType();
|
||||
}
|
||||
|
||||
private boolean oneCommand(@NotNull String command) throws Exception {
|
||||
|
||||
@@ -79,6 +79,7 @@ public class ReplInterpreter {
|
||||
@Nullable
|
||||
private JetScope lastLineScope;
|
||||
private List<EarlierLine> earlierLines = Lists.newArrayList();
|
||||
private List<String> previousIncompleteLines = Lists.newArrayList();
|
||||
private final ReplClassLoader classLoader;
|
||||
|
||||
@NotNull
|
||||
@@ -120,23 +121,34 @@ public class ReplInterpreter {
|
||||
classLoader = new ReplClassLoader(new URLClassLoader(classpath.toArray(new URL[0])));
|
||||
}
|
||||
|
||||
public enum LineResultType {
|
||||
SUCCESS,
|
||||
ERROR,
|
||||
INCOMPLETE,
|
||||
}
|
||||
|
||||
public static class LineResult {
|
||||
|
||||
private final Object value;
|
||||
private final boolean unit;
|
||||
private final String errorText;
|
||||
@NotNull
|
||||
private final LineResultType type;
|
||||
|
||||
private LineResult(Object value, boolean unit, String errorText) {
|
||||
private LineResult(Object value, boolean unit, String errorText, @NotNull LineResultType type) {
|
||||
this.value = value;
|
||||
this.unit = unit;
|
||||
this.errorText = errorText;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public boolean isSuccessful() {
|
||||
return errorText == null;
|
||||
@NotNull
|
||||
public LineResultType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
private void checkSuccessful() {
|
||||
if (!isSuccessful()) {
|
||||
if (!(getType() == LineResultType.SUCCESS)) {
|
||||
throw new IllegalStateException("it is error");
|
||||
}
|
||||
}
|
||||
@@ -157,7 +169,7 @@ public class ReplInterpreter {
|
||||
}
|
||||
|
||||
public static LineResult successful(Object value, boolean unit) {
|
||||
return new LineResult(value, unit, null);
|
||||
return new LineResult(value, unit, null, LineResultType.SUCCESS);
|
||||
}
|
||||
|
||||
public static LineResult error(@NotNull String errorText) {
|
||||
@@ -167,7 +179,11 @@ public class ReplInterpreter {
|
||||
else if (!errorText.endsWith("\n")) {
|
||||
errorText = errorText + "\n";
|
||||
}
|
||||
return new LineResult(null, false, errorText);
|
||||
return new LineResult(null, false, errorText, LineResultType.ERROR);
|
||||
}
|
||||
|
||||
public static LineResult incomplete() {
|
||||
return new LineResult(null, false, null, LineResultType.INCOMPLETE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,14 +193,29 @@ public class ReplInterpreter {
|
||||
|
||||
JvmClassName scriptClassName = JvmClassName.byInternalName("Line" + lineNumber);
|
||||
|
||||
LightVirtualFile virtualFile = new LightVirtualFile("line" + lineNumber + ".ktscript", JetLanguage.INSTANCE, line);
|
||||
StringBuilder fullText = new StringBuilder();
|
||||
for (String prevLine : previousIncompleteLines) {
|
||||
fullText.append(prevLine + "\n");
|
||||
}
|
||||
fullText.append(line);
|
||||
|
||||
LightVirtualFile virtualFile = new LightVirtualFile("line" + lineNumber + ".ktscript", JetLanguage.INSTANCE, fullText.toString());
|
||||
virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET);
|
||||
JetFile psiFile = (JetFile) ((PsiFileFactoryImpl) PsiFileFactory.getInstance(jetCoreEnvironment.getProject())).trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false);
|
||||
|
||||
MessageCollectorToString errorCollector = new MessageCollectorToString();
|
||||
|
||||
boolean hasErrors = AnalyzerWithCompilerReport.reportSyntaxErrors(psiFile, errorCollector);
|
||||
if (hasErrors) {
|
||||
AnalyzerWithCompilerReport.SyntaxErrorReport syntaxErrorReport =
|
||||
AnalyzerWithCompilerReport.reportSyntaxErrors(psiFile, errorCollector);
|
||||
|
||||
if (syntaxErrorReport.isOnlyErrorAtEof()) {
|
||||
previousIncompleteLines.add(line);
|
||||
return LineResult.incomplete();
|
||||
}
|
||||
|
||||
previousIncompleteLines.clear();
|
||||
|
||||
if (syntaxErrorReport.isHasErrors()) {
|
||||
return LineResult.error(errorCollector.getString());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
>>> fun clock() =
|
||||
incomplete
|
||||
>>> 12
|
||||
null
|
||||
>>> clock()
|
||||
12
|
||||
@@ -0,0 +1,10 @@
|
||||
>>> fun get7() =
|
||||
incomplete
|
||||
>>> 4 +
|
||||
incomplete
|
||||
>>> 2 +
|
||||
incomplete
|
||||
>>> 1
|
||||
null
|
||||
>>> get7()
|
||||
7
|
||||
@@ -64,9 +64,12 @@ public class ReplInterpreterTest {
|
||||
|
||||
ReplInterpreter.LineResult lineResult = repl.eval(code);
|
||||
Object actual;
|
||||
if (lineResult.isSuccessful()) {
|
||||
if (lineResult.getType() == ReplInterpreter.LineResultType.SUCCESS) {
|
||||
actual = lineResult.getValue();
|
||||
}
|
||||
else if (lineResult.getType() == ReplInterpreter.LineResultType.INCOMPLETE) {
|
||||
actual = "incomplete";
|
||||
}
|
||||
else {
|
||||
actual = lineResult.getErrorText();
|
||||
}
|
||||
@@ -143,5 +146,14 @@ public class ReplInterpreterTest {
|
||||
testFile("evaluationErrors.repl");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multiline() {
|
||||
testFile("multiline.repl");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multiline3() {
|
||||
testFile("multiline3.repl");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user