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 {
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());
}
+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);
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");
}
}