/*
* Copyright 2010 Jon S Akhtar (Sylvanaar)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sylvanaar.idea.Lua;
import jsyntaxpane.lexers.LuaLexer;
import se.krka.kahlua.converter.KahluaConverterManager;
import se.krka.kahlua.integration.LuaCaller;
import se.krka.kahlua.integration.LuaReturn;
import se.krka.kahlua.integration.expose.LuaJavaClassExposer;
import se.krka.kahlua.j2se.interpreter.History;
import se.krka.kahlua.j2se.interpreter.InputTerminal;
import se.krka.kahlua.j2se.interpreter.OutputTerminal;
import se.krka.kahlua.j2se.interpreter.autocomplete.AutoComplete;
import se.krka.kahlua.j2se.interpreter.jsyntax.JSyntaxUtil;
import se.krka.kahlua.j2se.interpreter.jsyntax.KahluaKit;
import se.krka.kahlua.luaj.compiler.LuaCompiler;
import se.krka.kahlua.vm.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* Created by IntelliJ IDEA.
* User: Jon S Akhtar
* Date: Sep 19, 2010
* Time: 4:29:54 PM
*/
public class KahluaInterpreter extends JPanel {
private final KahluaThread thread;
private final OutputTerminal terminal;
private final JLabel status = new JLabel("");
private final History history = new History();
private final ExecutorService executors = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory());
private Future<?> future;
final KahluaConverterManager manager = new KahluaConverterManager();
final LuaCaller caller = new LuaCaller(manager);
final LuaJavaClassExposer exposer;
public KahluaInterpreter(Platform platform, KahluaTable env) {
super(new BorderLayout());
JSyntaxUtil.setup();
exposer = new LuaJavaClassExposer(manager, platform, env);
exposer.exposeGlobalFunctions(this);
final InputTerminal input = new InputTerminal(Color.BLACK);
final KahluaKit kit = new KahluaKit(new LuaLexer());
JSyntaxUtil.installSyntax(input, true, kit);
new AutoComplete(input, platform, env);
terminal = new OutputTerminal(Color.BLACK, input.getFont(), input);
terminal.setPreferredSize(new Dimension(800, 400));
terminal.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
if (e.getKeyChar() != 0) {
input.requestFocus();
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(e);
}
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
});
add(status, BorderLayout.SOUTH);
add(terminal, BorderLayout.CENTER);
input.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent keyEvent) {
}
@Override
public void keyPressed(KeyEvent keyEvent) {
}
@Override
public void keyReleased(KeyEvent keyEvent) {
if (isControl(keyEvent)) {
if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER) {
if (isDone()) {
String text = input.getText();
history.add(text);
terminal.appendLua(withNewline(text));
input.setText("");
execute(text);
}
keyEvent.consume();
}
if (keyEvent.getKeyCode() == KeyEvent.VK_UP) {
history.moveBack(input);
keyEvent.consume();
}
if (keyEvent.getKeyCode() == KeyEvent.VK_DOWN) {
history.moveForward(input);
keyEvent.consume();
}
}
}
});
this.addComponentListener(new ComponentListener() {
@Override
public void componentResized(ComponentEvent componentEvent) {
}
@Override
public void componentMoved(ComponentEvent componentEvent) {
}
@Override
public void componentShown(ComponentEvent componentEvent) {
input.requestFocus();
}
@Override
public void componentHidden(ComponentEvent componentEvent) {
}
});
thread = new KahluaThread(terminal.getPrintStream(), platform, env);
}
private String withNewline(String text) {
if (text.endsWith("\n")) {
return text;
}
return text + "\n";
}
private boolean isControl(KeyEvent keyEvent) {
return (keyEvent.getModifiers() & KeyEvent.CTRL_MASK) != 0;
}
public Runnable getRunnableExecution(final String text) {
return new Runnable() {
@Override
public void run() {
status.setText("[running...]");
try {
LuaClosure luaClosure = smartCompile(text);
LuaReturn result = caller.protectedCall(thread, luaClosure);
if (result.isSuccess()) {
for (Object o : result) {
terminal.appendOutput(KahluaUtil.tostring(o, thread)+"\n");
}
} else {
terminal.appendError(result.getErrorString()+"\n");
terminal.appendError(result.getLuaStackTrace()+"\n");
result.getJavaException().printStackTrace(System.err);
}
} catch (IOException e) {
e.printStackTrace(terminal.getPrintStream());
} catch (RuntimeException e) {
terminal.appendError(e.getMessage()+"\n");
}
status.setText("");
}
};
}
public void execute(final String text) {
future = executors.submit(getRunnableExecution(text));
}
private LuaClosure smartCompile(String text) throws IOException {
LuaClosure luaClosure;
try {
luaClosure = LuaCompiler.loadstring("return " + text, "interpreter", thread.getEnvironment());
} catch (KahluaException e) {
// Ignore it and try without "return "
luaClosure = LuaCompiler.loadstring(text, "interpreter", thread.getEnvironment());
}
return luaClosure;
}
public boolean isDone() {
return future == null || future.isDone();
}
public KahluaThread getThread() {
return thread;
}
public OutputTerminal getTerminal() {
return terminal;
}
}