repl: multiline

This commit is contained in:
Stepan Koltsov
2012-06-28 19:38:44 +04:00
parent dbc0d3c4e0
commit cd1211fcb3
6 changed files with 128 additions and 24 deletions
@@ -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());
} }
+6
View File
@@ -0,0 +1,6 @@
>>> fun clock() =
incomplete
>>> 12
null
>>> clock()
12
+10
View File
@@ -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");
}
} }