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