001 /* 002 * Copyright 2010-2015 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.jetbrains.kotlin.cli.jvm.repl; 018 019 import com.intellij.openapi.Disposable; 020 import com.intellij.openapi.util.io.FileUtil; 021 import jline.console.ConsoleReader; 022 import jline.console.history.FileHistory; 023 import org.jetbrains.annotations.NotNull; 024 import org.jetbrains.kotlin.cli.common.KotlinVersion; 025 import org.jetbrains.kotlin.config.CompilerConfiguration; 026 import org.jetbrains.kotlin.utils.UtilsPackage; 027 028 import java.io.File; 029 import java.io.PrintWriter; 030 import java.util.Arrays; 031 import java.util.List; 032 033 public class ReplFromTerminal { 034 035 private ReplInterpreter replInterpreter; 036 private Throwable replInitializationFailed; 037 private final Object waitRepl = new Object(); 038 039 private final ConsoleReader consoleReader; 040 041 public ReplFromTerminal( 042 @NotNull final Disposable disposable, 043 @NotNull final CompilerConfiguration compilerConfiguration) { 044 new Thread("initialize-repl") { 045 @Override 046 public void run() { 047 try { 048 replInterpreter = new ReplInterpreter(disposable, compilerConfiguration); 049 } 050 catch (Throwable e) { 051 replInitializationFailed = e; 052 } 053 synchronized (waitRepl) { 054 waitRepl.notifyAll(); 055 } 056 } 057 }.start(); 058 059 try { 060 consoleReader = new ConsoleReader("kotlin", System.in, System.out, null); 061 consoleReader.setHistoryEnabled(true); 062 consoleReader.setExpandEvents(false); 063 consoleReader.setHistory(new FileHistory(new File(new File(System.getProperty("user.home")), ".kotlin_history"))); 064 } 065 catch (Exception e) { 066 throw UtilsPackage.rethrow(e); 067 } 068 } 069 070 private ReplInterpreter getReplInterpreter() { 071 if (replInterpreter != null) { 072 return replInterpreter; 073 } 074 synchronized (waitRepl) { 075 while (replInterpreter == null && replInitializationFailed == null) { 076 try { 077 waitRepl.wait(); 078 } 079 catch (Throwable e) { 080 throw UtilsPackage.rethrow(e); 081 } 082 } 083 if (replInterpreter != null) { 084 return replInterpreter; 085 } 086 throw UtilsPackage.rethrow(replInitializationFailed); 087 } 088 } 089 090 private void doRun() { 091 try { 092 System.out.println("Welcome to Kotlin version " + KotlinVersion.VERSION + 093 " (JRE " + System.getProperty("java.runtime.version") + ")"); 094 System.out.println("Type :help for help, :quit for quit"); 095 WhatNextAfterOneLine next = WhatNextAfterOneLine.READ_LINE; 096 while (true) { 097 next = one(next); 098 if (next == WhatNextAfterOneLine.QUIT) { 099 break; 100 } 101 } 102 } 103 catch (Exception e) { 104 throw UtilsPackage.rethrow(e); 105 } 106 finally { 107 try { 108 ((FileHistory) consoleReader.getHistory()).flush(); 109 } 110 catch (Exception e) { 111 System.err.println("failed to flush history: " + e); 112 } 113 } 114 } 115 116 private enum WhatNextAfterOneLine { 117 READ_LINE, 118 INCOMPLETE, 119 QUIT, 120 } 121 122 @NotNull 123 private WhatNextAfterOneLine one(@NotNull WhatNextAfterOneLine next) { 124 try { 125 String line = consoleReader.readLine(next == WhatNextAfterOneLine.INCOMPLETE ? "... " : ">>> "); 126 if (line == null) { 127 return WhatNextAfterOneLine.QUIT; 128 } 129 130 if (line.startsWith(":") && (line.length() == 1 || line.charAt(1) != ':')) { 131 boolean notQuit = oneCommand(line.substring(1)); 132 return notQuit ? WhatNextAfterOneLine.READ_LINE : WhatNextAfterOneLine.QUIT; 133 } 134 135 ReplInterpreter.LineResultType lineResultType = eval(line); 136 if (lineResultType == ReplInterpreter.LineResultType.INCOMPLETE) { 137 return WhatNextAfterOneLine.INCOMPLETE; 138 } 139 else { 140 return WhatNextAfterOneLine.READ_LINE; 141 } 142 } 143 catch (Exception e) { 144 throw UtilsPackage.rethrow(e); 145 } 146 } 147 148 @NotNull 149 private ReplInterpreter.LineResultType eval(@NotNull String line) { 150 ReplInterpreter.LineResult lineResult = getReplInterpreter().eval(line); 151 if (lineResult.getType() == ReplInterpreter.LineResultType.SUCCESS) { 152 if (!lineResult.isUnit()) { 153 System.out.println(lineResult.getValue()); 154 } 155 } 156 else if (lineResult.getType() == ReplInterpreter.LineResultType.INCOMPLETE) { 157 } 158 else if (lineResult.getType() == ReplInterpreter.LineResultType.ERROR) { 159 System.out.print(lineResult.getErrorText()); 160 } 161 else { 162 throw new IllegalStateException("unknown line result type: " + lineResult); 163 } 164 return lineResult.getType(); 165 } 166 167 private boolean oneCommand(@NotNull String command) throws Exception { 168 List<String> split = splitCommand(command); 169 if (split.size() >= 1 && command.equals("help")) { 170 System.out.println("Available commands:"); 171 System.out.println(":help show this help"); 172 System.out.println(":quit exit the interpreter"); 173 System.out.println(":dump bytecode dump classes to terminal"); 174 System.out.println(":load <file> load script from specified file"); 175 return true; 176 } 177 else if (split.size() >= 2 && split.get(0).equals("dump") && split.get(1).equals("bytecode")) { 178 getReplInterpreter().dumpClasses(new PrintWriter(System.out)); 179 return true; 180 } 181 else if (split.size() >= 1 && split.get(0).equals("quit")) { 182 return false; 183 } 184 else if (split.size() >= 2 && split.get(0).equals("load")) { 185 String fileName = split.get(1); 186 String scriptText = FileUtil.loadFile(new File(fileName)); 187 eval(scriptText); 188 return true; 189 } 190 else { 191 System.out.println("Unknown command"); 192 System.out.println("Type :help for help"); 193 return true; 194 } 195 } 196 197 private static List<String> splitCommand(@NotNull String command) { 198 return Arrays.asList(command.split(" ")); 199 } 200 201 public static void run(@NotNull Disposable disposable, @NotNull CompilerConfiguration configuration) { 202 new ReplFromTerminal(disposable, configuration).doRun(); 203 } 204 205 }