Backend refactoring: KjcEngine has been split up into separate classes for preprocessing, compiling, and running.

This commit is contained in:
dmose
2003-07-04 18:43:47 +00:00
parent 7d0526e28e
commit 01a3cc6eda
11 changed files with 1211 additions and 1302 deletions

View File

@@ -2,10 +2,10 @@
// while the kjc engine is in use. takes care of error handling.
public class KjcApplet extends BApplet {
KjcEngine engine;
PdeRuntime pdeRuntime;
public void setEngine(KjcEngine engine) {
this.engine = engine;
public void setRuntime(PdeRuntime pdeRuntime) {
this.pdeRuntime = pdeRuntime;
}
public void run() {
@@ -16,8 +16,8 @@ public class KjcApplet extends BApplet {
//System.out.println("ex found in run");
e.printStackTrace();
//engine.error(e);
engine.newMessage = true;
e.printStackTrace(engine.leechErr);
pdeRuntime.newMessage = true;
e.printStackTrace(pdeRuntime.leechErr);
}
}
}

File diff suppressed because it is too large Load Diff

85
app/PdeCompiler.java Normal file
View File

@@ -0,0 +1,85 @@
// -*- Mode: JDE; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
import java.io.*;
// This class will be the new default of Jikes.
//
public class PdeCompiler implements PdeMessageConsumer{
String buildPath;
String className;
PdeException exception;
PdeEditor editor;
public PdeCompiler(String buildPath, String className, PdeEditor editor) {
this.buildPath = buildPath;
this.className = className;
this.editor = editor;
}
public boolean compileJava(PrintStream leechErr) {
String command[] = new String[] {
"jikes",
"+E", // output errors in machine-parsable format
"-d", buildPath, // output the classes in the buildPath
buildPath + File.separator + className + ".java" // file to compile
};
int result;
// XXXdmose try/catch should be separate
try {
Process process = Runtime.getRuntime().exec(command);
// XXXdmose race condition?
new PdeMessageSiphon(process.getInputStream(),
process.getErrorStream(),
this);
result = process.waitFor();
} catch (Exception e) {
result = -1;
}
//System.err.println("result = " + result);
return result == 0 ? true : false;
}
// part of the PdeMessageConsumer interface
//
public void message(String s) {
// as in: lib\build\Temporary_5476_6442.java:88: caution:Assignment of an expression to itself [KOPI]
if (s.indexOf("caution") != -1) return;
// jikes always uses a forward slash character as its separator, so
// we need to replace any platform-specific separator characters before
// attemping to compare
//
String fullTempFilename = buildPath.replace(File.separatorChar, '/')
+ "/" + className + ".java";
if (s.indexOf(fullTempFilename) == 0) {
String s1 = s.substring(fullTempFilename.length() + 1);
int colon = s1.indexOf(':');
int lineNumber = Integer.parseInt(s1.substring(0, colon));
//System.out.println("pde / line number: " + lineNumber);
//String s2 = s1.substring(colon + 2);
int err = s1.indexOf("Error:");
if (err != -1) {
//err += "error:".length();
String description = s1.substring(err + "Error:".length());
description = description.trim();
System.out.println("description = " + description);
exception = new PdeException(description, lineNumber-1);
editor.error(exception);
} else {
System.err.println("i suck: " + s);
}
} else {
// this isn't the start of an error line, so don't attempt to parse
// a line number out of it
}
}
}

69
app/PdeCompilerKjc.java Normal file
View File

@@ -0,0 +1,69 @@
// -*- Mode: JDE; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// The existing Kjc code lives here, and can someday go away.
import java.io.*;
public class PdeCompilerKjc extends PdeCompiler {
public PdeCompilerKjc(String buildPath, String className, PdeEditor editor) {
super(buildPath, className, editor);
}
public boolean compileJava(PrintStream leechErr) {
System.setErr(leechErr); // redirect stderr to our leech filter
String args[] = new String[2];
args[0] = "-d" + buildPath;
args[1] = buildPath + File.separator + className + ".java";
//System.out.println("args = " + args[0] + " " + args[1]);
boolean success = at.dms.kjc.Main.compile(args);
System.setErr(PdeEditorConsole.consoleErr);
//System.err.println("success = " + success);
return success;
}
// part of the PdeMessageConsumer interface
//
public void message(String s) {
// as in: lib\build\Temporary_5476_6442.java:88: caution:Assignment of an expression to itself [KOPI]
if (s.indexOf("caution") != -1) return;
//
//System.out.println("leech2: " + new String(b, offset, length));
//String s = new String(b, offset, length);
//if (s.indexOf(tempFilename) == 0) {
String fullTempFilename = buildPath + File.separator + className + ".java";
if (s.indexOf(fullTempFilename) == 0) {
String s1 = s.substring(fullTempFilename.length() + 1);
int colon = s1.indexOf(':');
int lineNumber = Integer.parseInt(s1.substring(0, colon));
//System.out.println("pde / line number: " + lineNumber);
//String s2 = s1.substring(colon + 2);
int err = s1.indexOf("error:");
if (err != -1) {
//err += "error:".length();
String description = s1.substring(err + "error:".length());
description = description.trim();
//exception = new PdeException(description, lineNumber-2);
exception = new PdeException(description, lineNumber-1);
editor.error(exception);
} else {
System.err.println("i suck: " + s);
}
} else {
//System.err.println("don't understand: " + s);
exception = new PdeException("Error while compiling, " +
"please send code to bugs@proce55ing.net");
editor.error(exception);
}
}
}

View File

@@ -1,3 +1,5 @@
// -*- Mode: JDE; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
import java.awt.*;
import java.awt.event.*;
import java.io.*;
@@ -66,8 +68,6 @@ public class PdeEditor extends Panel {
//String lastFile;
//PdeRunner runner;
//KjcEngine engine;
PdeEngine engine;
Point appletLocation; //= new Point(0, 0);
Point presentLocation; // = new Point(0, 0);
@@ -76,6 +76,8 @@ public class PdeEditor extends Panel {
RunButtonWatcher watcher;
PdeRuntime pdeRuntime;
static final int GRID_SIZE = 33;
static final int INSET_SIZE = 5;
@@ -85,6 +87,13 @@ public class PdeEditor extends Panel {
PdeBase base;
PrintStream leechErr;
PdeMessageStream messageStream;
String buildPath;
static final String TEMP_CLASS = "Temporary";
// hack while fixing layout issues
//Component pain;
@@ -335,7 +344,7 @@ public class PdeEditor extends Panel {
//if (frame != null) frame.toFront(); // editor to front
try {
//System.out.println("moving to front");
engine.window.toFront();
pdeRuntime.window.toFront();
} catch (Exception ex) { }
}
});
@@ -645,27 +654,33 @@ public class PdeEditor extends Panel {
try {
if (presenting) {
presentationWindow.show();
presentationWindow.toFront();
//doRun(true);
presentationWindow.show();
presentationWindow.toFront();
//doRun(true);
}
String program = textarea.getText();
makeHistory(program, RUN);
//if (program.length() != 0) {
String buildPath = "lib" + File.separator + "build"; // TEMPORARY
//if (PdeBase.platform == PdeBase.MACOSX) {
//String pkg = "Proce55ing.app/Contents/Resources/Java/";
//buildPath = pkg + "build";
//String pkg = "Proce55ing.app/Contents/Resources/Java/";
//buildPath = pkg + "build";
//}
buildPath = "lib" + File.separator + "build";
File buildDir = new File(buildPath);
if (!buildDir.exists()) buildDir.mkdirs();
if (!buildDir.exists()) {
buildDir.mkdirs();
}
String dataPath =
sketchFile.getParent() + File.separator + "data";
//editor.sketchFile.getParent() + File.separator + "data";
String dataPath = sketchFile.getParent() + File.separator + "data";
if (dataPath != null) {
File dataDir = new File(dataPath);
if (dataDir.exists()) {
PdeEditor.copyDir(dataDir, buildDir);
}
}
/*
this needs to be reworked. there are three essential parts
@@ -690,21 +705,60 @@ this needs to be reworked. there are three essential parts
afterwards, some of these steps need a cleanup function
*/
int numero1 = (int) (Math.random() * 10000);
int numero2 = (int) (Math.random() * 10000);
String className = TEMP_CLASS + "_" + numero1 + "_" + numero2;
engine = new KjcEngine(this, program, buildPath, dataPath);
//engine.start();
//engine.start(presenting ? presentLocation : appletLocation);
engine.start(presenting ? presentLocation : appletLocation);
//System.out.println("done iwth engine.start()");
//}
// do the preprocessing and write a .java file
//
// in an advanced program, the returned classname could be different,
// which is why we need to set className based on the return value
//
PdePreprocessor preprocessorOro =
new PdePreprocessorOro(program, buildPath);
className = preprocessorOro.writeJava(className, true);
watcher = new RunButtonWatcher();
// compile the program
//
PdeCompiler compilerKjc = new PdeCompilerKjc(buildPath, className, this);
// this will catch and parse errors during compilation
messageStream = new PdeMessageStream(this, compilerKjc);
leechErr = new PrintStream(messageStream);
boolean result = compilerKjc.compileJava(leechErr);
// if the compilation worked, run the applet
//
if (result) {
// create a runtime object
pdeRuntime = new PdeRuntime(this, className);
// use the runtime object to consume the errors now
messageStream.setMessageConsumer(pdeRuntime);
// start the applet
pdeRuntime.start(presenting ? presentLocation : appletLocation,
leechErr);
// spawn a thread to update PDE GUI state
watcher = new RunButtonWatcher();
} else {
cleanTempFiles(buildPath);
}
} catch (PdeException e) {
//state = RUNNER_ERROR;
//forceStop = false;
//this.stop();
engine.stop();
// if we made it to the runtime stage, stop that thread
//
if (pdeRuntime != null) {
pdeRuntime.stop();
}
cleanTempFiles(buildPath);
e.printStackTrace();
//editor.error(e);
error(e);
@@ -712,8 +766,16 @@ afterwards, some of these steps need a cleanup function
} catch (Exception e) {
e.printStackTrace();
//this.stop();
engine.stop();
// if we made it to the runtime stage, stop that thread
//
if (pdeRuntime != null) {
pdeRuntime.stop();
}
cleanTempFiles(buildPath);
}
//engine = null;
//System.out.println("out of doRun()");
// required so that key events go to the panel and <key> works
@@ -730,16 +792,15 @@ afterwards, some of these steps need a cleanup function
public void run() {
while (Thread.currentThread() == thread) {
KjcEngine eng = (KjcEngine)engine;
if ((engine != null) && (eng.applet != null)) {
//System.out.println(eng.applet.finished);
buttons.running(!eng.applet.finished);
//} else {
//System.out.println("still pooping");
}
try {
Thread.sleep(250);
} catch (InterruptedException e) { }
if ((pdeRuntime != null) && (pdeRuntime.applet != null)) {
//System.out.println(pdeRuntime.applet.finished);
buttons.running(!pdeRuntime.applet.finished);
//} else {
//System.out.println("still pooping");
}
try {
Thread.sleep(250);
} catch (InterruptedException e) { }
}
}
@@ -758,8 +819,7 @@ afterwards, some of these steps need a cleanup function
}
*/
//System.out.println("stop1");
if (engine != null) engine.stop();
if (pdeRuntime != null) pdeRuntime.stop();
if (watcher != null) watcher.stop();
//System.out.println("stop2");
message(EMPTY);
@@ -784,7 +844,7 @@ afterwards, some of these steps need a cleanup function
} else {
try {
appletLocation = engine.window.getLocation();
appletLocation = pdeRuntime.window.getLocation();
} catch (NullPointerException e) { }
}
//System.out.println("doclose2");
@@ -796,12 +856,17 @@ afterwards, some of these steps need a cleanup function
//System.out.println("doclose3");
try {
engine.close(); // kills the window
engine = null; // will this help?
if (pdeRuntime != null) {
pdeRuntime.close(); // kills the window
pdeRuntime = null; // will this help?
}
} catch (Exception e) { }
//System.out.println("doclose4");
//buttons.clear(); // done by doStop
if (buildPath != null) {
cleanTempFiles(buildPath);
}
}
@@ -1364,19 +1429,28 @@ afterwards, some of these steps need a cleanup function
// create the project directory
// pass null for datapath because the files shouldn't be
// copied to the build dir.. that's only for the temp stuff
KjcEngine ex_engine = new KjcEngine(this, program,
appletDir.getPath(), null);
//dataDir.getPath(), this);
//File projectDir = new File(appletDir, projectName);
//projectDir.mkdirs();
appletDir.mkdirs();
// projectName will be updated with actual class name
exportSketchName = ex_engine.writeJava(exportSketchName, false);
if (!ex_engine.compileJava()) {
//throw new Exception("error while compiling, couldn't export");
// message() will already have error message in this case
return;
// preprocess the program
//
PdePreprocessor preprocessorOro =
new PdePreprocessorOro(program, appletDir.getPath());
exportSketchName = preprocessorOro.writeJava(exportSketchName, false);
PdeCompiler compilerKjc = new PdeCompilerKjc(appletDir.getPath(),
exportSketchName, this);
// this will catch and parse errors during compilation
messageStream = new PdeMessageStream(this, compilerKjc);
leechErr = new PrintStream(messageStream);
if (!compilerKjc.compileJava(leechErr)) {
//throw new Exception("error while compiling, couldn't export");
// message() will already have error message in this case
return;
}
// not necessary, now compiles into applet dir
@@ -1387,7 +1461,7 @@ afterwards, some of these steps need a cleanup function
//copyFile(new File(javaName), new File(appletDir, javaName));
// remove temporary .java and .class files
//ex_engine.cleanup();
// cleanTempFiles(buildPath);
int wide = BApplet.DEFAULT_WIDTH;
int high = BApplet.DEFAULT_HEIGHT;
@@ -1850,8 +1924,8 @@ afterwards, some of these steps need a cleanup function
}
if (end == -1) end = len;
// sometimes KJC claims that the line it found an error in is
// the last line in the file + 1. Just highlight the last line
// sometimes KJC claims that the line it found an error in is
// the last line in the file + 1. Just highlight the last line
// in this case. [dmose]
if (st == -1) st = len;
@@ -1971,21 +2045,50 @@ afterwards, some of these steps need a cleanup function
}
}
static protected void removeDir(File dir) {
//System.out.println("removing " + dir);
// cleanup temp files
//
static protected void cleanTempFiles(String buildPath) {
// if the java runtime is holding onto any files in the build dir, we
// won't be able to delete them, so we need to force a gc here
//
System.gc();
File dirObject = new File(buildPath);
// note that we can't remove the builddir itself, otherwise
// the next time we start up, internal runs using PdeRuntime won't
// work because the build dir won't exist at startup, so the classloader
// will ignore the fact that that dir is in the CLASSPATH in run.sh
//
if (dirObject.exists()) {
removeDescendants(dirObject);
}
}
// remove all files in a directory
//
static protected void removeDescendants(File dir) {
String files[] = dir.list();
for (int i = 0; i < files.length; i++) {
if (files[i].equals(".") || files[i].equals("..")) continue;
File dead = new File(dir, files[i]);
if (!dead.isDirectory()) {
if (!dead.delete()) System.err.println("couldn't delete " + dead);
if (!dead.delete())
System.err.println("couldn't delete " + dead);
} else {
removeDir(dead);
//dead.delete();
removeDir(dead);
//dead.delete();
}
}
}
// remove all files in a directory and the dir itself
//
static protected void removeDir(File dir) {
//System.out.println("removing " + dir);
removeDescendants(dir);
dir.delete();
}
}

View File

@@ -0,0 +1,15 @@
// -*- Mode: JDE; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// Different instances of PdeMessageStream need to do different things with
// messages. In particular, a stream instance used for parsing output from
// the compiler compiler has to interpret its messages differently than one
// parsing output from the runtime.
//
// Classes which consume messages and do something with them should implement
// this interface.
//
public interface PdeMessageConsumer {
public void message(String s);
}

57
app/PdeMessageSiphon.java Normal file
View File

@@ -0,0 +1,57 @@
/// -*- Mode: JDE; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
import java.io.*;
// XXXdmose error checking
class PdeMessageSiphon implements Runnable {
BufferedReader stdout, stderr;
PrintStream leechErr;
Thread thread;
PdeMessageConsumer consumer;
public PdeMessageSiphon(InputStream stdout, InputStream stderr,
PdeMessageConsumer consumer) {
// XXXdmose error checking here
this.stdout = new BufferedReader(new InputStreamReader(stdout));
this.stderr = new BufferedReader(new InputStreamReader(stderr));
this.consumer = consumer;
thread = new Thread(this);
thread.start();
}
public void run() {
String currentLine;
while (Thread.currentThread() == thread) {
// XXX put back while loops
try {
if (stderr.ready()) {
currentLine = stderr.readLine();
System.err.println(currentLine);
consumer.message(currentLine);
}
if (stdout.ready()) {
currentLine = stdout.readLine();
System.err.println(currentLine);
consumer.message(currentLine);
}
Thread.sleep(10);
} catch (Exception e) {
System.err.println("PdeMessageSiphon err " + e);
thread.stop();
thread = null;
}
}
}
}

36
app/PdeMessageStream.java Normal file
View File

@@ -0,0 +1,36 @@
// -*- Mode: JDE; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
import java.io.*;
class PdeMessageStream extends OutputStream {
PdeEditor editor;
PdeMessageConsumer messageConsumer;
public PdeMessageStream(PdeEditor editor,
PdeMessageConsumer messageConsumer) {
this.editor = editor;
this.messageConsumer = messageConsumer;
}
public void setMessageConsumer(PdeMessageConsumer messageConsumer) {
this.messageConsumer = messageConsumer;
}
public void close() { }
public void flush() { }
public void write(byte b[]) {
System.out.println("leech1: " + new String(b));
}
public void write(byte b[], int offset, int length) {
//System.out.println("leech2: " + new String(b));
this.messageConsumer.message(new String(b, offset, length));
}
public void write(int b) {
System.out.println("leech3: '" + ((char)b) + "'");
}
}

11
app/PdePreprocessor.java Normal file
View File

@@ -0,0 +1,11 @@
// This class will be the new CUP-generated preprocessor.
//
public class PdePreprocessor {
public PdePreprocessor() {
}
public String writeJava(String name, boolean kjc) {
return "This class is currently purely abstract";
}
}

441
app/PdePreprocessorOro.java Normal file
View File

@@ -0,0 +1,441 @@
// -*- Mode: JDE; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// The existing ORO-based preprocessing code lives here, and can
// someday go away.
import com.oroinc.text.regex.*;
import java.io.*;
public class PdePreprocessorOro extends PdePreprocessor {
static final String EXTENDS = "extends BApplet ";
static final String EXTENDS_KJC = "extends KjcApplet ";
static final String applet_imports[] = {
"java.applet", "java.awt", "java.awt.image", "java.awt.event",
"java.io", "java.net", "java.text", "java.util", "java.util.zip"
};
static final String application_imports[] = {
"java.applet", "java.awt", "java.awt.image", "java.awt.event",
"java.io", "java.net", "java.text", "java.util", "java.util.zip",
"javax.comm",
// if jdk14 defined, jdk13 will be as well
#ifdef JDK13
"javax.sound.midi", "javax.sound.midi.spi",
"javax.sound.sampled", "javax.sound.sampled.spi",
#endif
#ifdef JDK14
"javax.xml.parsers", "javax.xml.transform",
"javax.xml.transform.dom", "javax.xml.transform.sax",
"javax.xml.transform.stream", "org.xml.sax",
"org.xml.sax.ext", "org.xml.sax.helpers"
#endif
};
String program;
String buildPath;
boolean usingExternal; // use an external process to display the applet?
String tempClass;
String tempFilename;
String tempClassFilename;
public PdePreprocessorOro(String program, String buildPath) {
this.program = program;
this.buildPath = buildPath;
usingExternal = PdeBase.getBoolean("play.external", false);
}
static final int BEGINNER = 0;
static final int INTERMEDIATE = 1;
static final int ADVANCED = 2;
// writes .java file into buildPath
public String writeJava(String name, boolean kjc) {
//System.out.println("writing java");
try {
int programType = BEGINNER;
// remove (encode) comments temporarily
program = commentsCodec(program /*, true*/);
// insert 'f' for all floats
// shouldn't substitute f's for: "Univers76.vlw.gz";
if (PdeBase.getBoolean("compiler.substitute_f", true)) {
/*
a = 0.2 * 3
(3.)
(.3 * 6)
(.30*7)
float f = 0.3;
fill(0.3, 0.2, 0.1);
next to white space \s or math ops +-/*()
or , on either side,
followed by ; (might as well on either side)
// allow 3. to work (also allows x.x too)
program = substipoot(program, "(\\d+\\.\\d*)(\\D)", "$1f$2");
program = substipoot(program, "(\\d+\\.\\d*)ff", "$1f");
// allow .3 to work (also allows x.x)
program = substipoot(program, "(\\d*\\.\\d+)(\\D)", "$1f$2");
program = substipoot(program, "(\\d*\\.\\d+)ff", "$1f");
*/
program = substipoot(program, "([\\s\\,\\;\\+\\-\\/\\*\\(\\)])(\\d+\\.\\d*)([\\s\\,\\;\\+\\-\\/\\*\\(\\)])", "$1$2f$3");
program = substipoot(program, "([\\s\\,\\;\\+\\-\\/\\*\\(\\)])(\\d*\\.\\d+)([\\s\\,\\;\\+\\-\\/\\*\\(\\)])", "$1$2f$3");
}
// allow int(3.75) instead of just (int)3.75
if (PdeBase.getBoolean("compiler.enhanced_casting", true)) {
program = substipoot(program, "([^A-Za-z0-9_])byte\\((.*)\\)", "$1(byte)($2)");
program = substipoot(program, "([^A-Za-z0-9_])char\\((.*)\\)", "$1(char)($2)");
program = substipoot(program, "([^A-Za-z0-9_])int\\((.*)\\)", "$1(int)($2)");
program = substipoot(program, "([^A-Za-z0-9_])float\\((.*)\\)", "$1(float)($2)");
}
if (PdeBase.getBoolean("compiler.color_datatype", true)) {
// so that regexp works correctly in this strange edge case
if (program.indexOf("color") == 0) program = " " + program;
// swap 'color' with 'int' when used as a datatype
program = substipoot(program,
"([;\\s\\(])color([\\s\\[])", "$1int$2");
// had to add ( at beginning for addPixel(color c...)
//"([;\\s])color([\\s\\[])", "$1int$2");
// had to add [ to that guy for color[] stuff
//"([;\\s])color([\\s])", "$1int$2");
//"([^A-Za-z0-9_.])color([^A-Za-z0-9_\\(.])", "$1int$2");
// color(something) like int() and the rest is no good
// because there is already a function called 'color' in BGraphics
//program = substipoot(program, "([^A-Za-z0-9_])color\\((.*)\\)", "$1(int)($2)");
}
if (PdeBase.getBoolean("compiler.inline_web_colors", true)) {
// convert "= #cc9988" into "= 0xffcc9988"
//program = substipoot(program, "(=\\s*)\\#([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])", "$1 0xff$2$3$4");
//program = substipoot(program, "#([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([;\\s])", "0xff$1$2$3$4");
//program = substipoot(program, "#([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([;\\s])", "0x$4$1$2$3$5");
program = substipoot(program, "#([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])", "0xff$1$2$3");
}
if ((program.indexOf("void setup()") != -1) ||
(program.indexOf("void loop()") != -1) ||
(program.indexOf("void draw()") != -1)) {
programType = INTERMEDIATE;
}
int index = program.indexOf("public class");
if (index != -1) {
programType = ADVANCED;
// kjc will get pissed off if i call the .java file
// something besides the name of the class.. so here goes
String s = program.substring(index + "public class".length()).trim();
index = s.indexOf(' ');
name = s.substring(0, index);
tempClass = name;
// and we're running inside
if (kjc) { // if running inside processing...
index = program.indexOf(EXTENDS); // ...and extends BApplet
if (index != -1) { // just extends object
String left = program.substring(0, index);
String right = program.substring(index + EXTENDS.length());
// replace with 'extends KjcApplet'
program = left + ((usingExternal) ? EXTENDS : EXTENDS_KJC) + right;
}
}
}
tempFilename = name + ".java";
tempClassFilename = name + ".class";
PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(buildPath + File.separator + tempFilename))));
//String eol = System.getProperties().getProperty("line.separator");
if (programType < ADVANCED) {
// spew out a bunch of java imports
if (kjc) { // if running in environment, or exporting an app
for (int i = 0; i < application_imports.length; i++) {
writer.print("import " + application_imports[i] + ".*; ");
}
} else { // exporting an applet
for (int i = 0; i < applet_imports.length; i++) {
writer.println("import " + applet_imports[i] + ".*; ");
//writer.print("import " + applet_imports[i] + ".*; ");
//if (!kjc) writer.println();
}
}
// add serial if running inside pde
//if (kjc) writer.print("import javax.comm.*;");
if (!kjc) writer.println();
writer.print("public class " + name + " extends " +
((kjc && !usingExternal) ?
"KjcApplet" : "BApplet") + " {");
}
if (programType == BEGINNER) {
if (!kjc) writer.println();
// hack so that the regexp below works
//if (program.indexOf("size(") == 0) program = " " + program;
if ((program.indexOf("size(") == 0) ||
(program.indexOf("background(") == 0)) {
program = " " + program;
}
PatternMatcher matcher = null;
PatternCompiler compiler = null;
Pattern pattern = null;
Perl5Substitution subst = null;
PatternMatcherInput input = null;
///////// grab (first) reference to size()
matcher = new Perl5Matcher();
compiler = new Perl5Compiler();
try {
pattern =
compiler.compile("[\\s\\;](size\\(\\s*\\d+,\\s*\\d+\\s*\\);)");
//compiler.compile("^([^A-Za-z0-9_]+)(size\\(\\s*\\d+,\\s*\\d+\\s*\\);)");
} catch (MalformedPatternException e){
e.printStackTrace();
//System.err.println("Bad pattern.");
//System.err.println(e.getMessage());
System.exit(1);
}
String sizeInfo = null;
input = new PatternMatcherInput(program);
if (matcher.contains(input, pattern)) {
MatchResult result = matcher.getMatch();
//int wide = Integer.parseInt(result.group(1).toString());
//int high = Integer.parseInt(result.group(2).toString());
//sizeInfo = "void setup() { " + result.group(0) + " } ";
sizeInfo = result.group(0);
} else {
// no size() defined, make it default
sizeInfo = "size(" + BApplet.DEFAULT_WIDTH + ", " +
BApplet.DEFAULT_HEIGHT + "); ";
}
// remove references to size()
// this winds up removing every reference to size()
// not really intended, but will help things work
subst = new Perl5Substitution("", Perl5Substitution.INTERPOLATE_ALL);
//subst = new Perl5Substitution("$1", Perl5Substitution.INTERPOLATE_ALL);
program = Util.substitute(matcher, pattern, subst, program,
Util.SUBSTITUTE_ALL);
/////////// grab (first) reference to background()
matcher = new Perl5Matcher();
compiler = new Perl5Compiler();
try {
pattern =
compiler.compile("[\\s\\;](background\\(.*\\);)");
//[\\s\\;]
//compiler.compile("([^A-Za-z0-9_]+)(background\\(.*\\);)");
} catch (MalformedPatternException e){
//System.err.println("Bad pattern.");
//System.err.println(e.getMessage());
e.printStackTrace();
System.exit(1);
}
String backgroundInfo = "";
input = new PatternMatcherInput(program);
if (matcher.contains(input, pattern)) {
MatchResult result = matcher.getMatch();
//int wide = Integer.parseInt(result.group(1).toString());
//int high = Integer.parseInt(result.group(2).toString());
//sizeInfo = "void setup() { " + result.group(0) + " } ";
backgroundInfo = result.group(0);
//} else {
// no size() defined, make it default
//sizeInfo = "size(" + BApplet.DEFAULT_WIDTH + ", " +
//BApplet.DEFAULT_HEIGHT + "); ";
}
// remove references to size()
// this winds up removing every reference to size()
// not really intended, but will help things work
subst = new Perl5Substitution("", Perl5Substitution.INTERPOLATE_ALL);
//subst = new Perl5Substitution("$1", Perl5Substitution.INTERPOLATE_ALL);
program = Util.substitute(matcher, pattern, subst, program,
Util.SUBSTITUTE_ALL);
//////// spew out the size and background info
writer.print("void setup() { ");
writer.print(sizeInfo);
writer.print(backgroundInfo);
writer.print("} ");
writer.print("void draw() {");
}
// decode comments to bring them back
program = commentsCodec(program /*, false*/);
// spew the actual program
// this should really add extra indents,
// especially when not in kjc mode (!kjc == export)
// things will be one line off if there's an error in the code
if (!kjc) writer.println();
writer.println(program);
//System.out.println(program);
if (programType == BEGINNER) {
writer.println("}");
}
if (programType < ADVANCED) {
writer.print("}");
}
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
return name;
}
protected String commentsCodec(String program /*, boolean encode*/) {
// need to preprocess class to remove comments
// so tthat they don't fool this crappy parsing below
char p[] = program.toCharArray();
char lastp = 0;
boolean insideComment = false;
boolean eolComment = false;
boolean slash = false;
boolean insideQuote = false;
for (int i = 0; i < p.length; i++) {
if (insideComment) {
if (eolComment &&
((p[i] == '\r') || (p[i] == '\n'))) {
insideComment = false;
slash = false;
} else if (!eolComment &&
(p[i] == '*') &&
(i != (p.length-1)) &&
(p[i+1] == '/')) {
insideComment = false;
slash = false;
} else {
//if ((p[i] > 32) && (p[i] < 127)) {
if ((p[i] >= 48) && (p[i] < 128)) {
p[i] = rotateTable[p[i]];
//p[i] = encode ? encodeTable[p[i]] : decodeTable[p[i]];
}
//p[i] = ' ';
}
} else { // not yet inside a comment
if (insideQuote) {
if ((p[i] == '\"') && (lastp != '\\')) {
insideQuote = !insideQuote;
} else {
if ((p[i] >= 48) && (p[i] < 128)) {
p[i] = rotateTable[p[i]];
//p[i] = encode ? encodeTable[p[i]] : decodeTable[p[i]];
}
}
} else { // not inside a quote
if (p[i] == '/') {
if (slash) {
insideComment = true;
eolComment = true;
} else {
slash = true;
}
} else if (p[i] == '\"') {
if (lastp != '\\') insideQuote = !insideQuote;
} else if (p[i] == '*') {
if (slash) {
insideComment = true;
eolComment = false;
}
} else {
slash = false;
}
}
}
lastp = p[i];
}
//System.out.println(new String(p));
return new String(p);
}
protected String substipoot(String what, String incoming, String outgoing) {
PatternMatcher matcher = new Perl5Matcher();
PatternCompiler compiler = new Perl5Compiler();
Pattern pattern = null;
try {
pattern = compiler.compile(incoming);
} catch (MalformedPatternException e){
System.err.println("Bad pattern.");
System.err.println(e.getMessage());
System.exit(1);
}
Perl5Substitution subst =
new Perl5Substitution(outgoing, Perl5Substitution.INTERPOLATE_ALL);
return Util.substitute(matcher, pattern, subst, what,
Util.SUBSTITUTE_ALL);
}
//static char encodeTable[] = new char[127];
//static char decodeTable[] = new char[127];
static char rotateTable[] = new char[128];
static {
for (int i = 0; i < 80; i++) {
rotateTable[i+48] = (char) (48 + ((i + 40) % 80));
}
/*
int rot = (123 - 65) / 2;
for (int i = 65; i < 123; i++) {
rotateTable[i] = (char) (((i - 65 + rot) % (rot*2)) + 65); // : (char)i;
}
*/
//for (int i = 33; i < 127; i++) {
//rotateTable[i] = //Character.isAlpha((char)i) ?
//(char) (((i - 33 + rot) % 94) + 33) : (char)i;
//encodeTable[i] = (char) (i+1);
//decodeTable[i] = (char) (i-1);
//encodeTable[i] = (char) (((i - 33 + rot) % 94) + 33);
//decodeTable[i] = encodeTable[i];
//encodeTable[i] = (char) (((i - 33 + rot) % 94) + 33);
//decodeTable[i] = (char) (((i + 33 + rot) % 94) + 33);
//System.out.println((int) decodeTable[i]);
//}
}
}

332
app/PdeRuntime.java Normal file
View File

@@ -0,0 +1,332 @@
// -*- Mode: JDE; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
import java.awt.*; // for window
import java.awt.event.*; // also for window
import java.io.*;
// Runs the compiled java applet.
//
public class PdeRuntime implements PdeMessageConsumer {
Process process;
KjcApplet applet;
PdeException exception;
Window window;
PdeEditor editor;
PrintStream leechErr;
String className;
boolean newMessage;
int messageLineCount;
public PdeRuntime(PdeEditor editor, String className) {
this.editor = editor;
this.className = className;
}
public void start(Point windowLocation, PrintStream leechErr)
throws PdeException {
this.leechErr = leechErr;
Frame frame = PdeBase.frame;
Point parentLoc = frame.getLocation();
Insets parentInsets = frame.getInsets();
int x1 = parentLoc.x - 20;
int y1 = parentLoc.y;
try {
if (PdeBase.getBoolean("play.external", false)) {
String cmd = PdeBase.get("play.external.command");
// "cmd /c " + not helpful?
process = Runtime.getRuntime().exec(cmd + " " + className +
" " + className +
" " + x1 + " " + y1);
new PdeMessageSiphon(process.getInputStream(),
process.getErrorStream(),
this);
} else {
// temporarily disabled
//KjcClassLoader loader = new KjcClassLoader(buildPath);
//Class c = loader.loadClass(tempClass);
Class c = Class.forName(className);
//try {
applet = (KjcApplet) c.newInstance();
//} catch (Exception e) {
// e.printStackTrace();
//}
//System.out.println(c + " " + applet);
//((KjcApplet)applet).errStream = leechErr;
applet.setRuntime(this);
// has to be before init
applet.serialProperties(PdeBase.properties);
applet.init();
if (applet.exception != null) {
if (applet.exception instanceof javax.comm.PortInUseException) {
throw new PdeException("Another program is already " +
"using the serial port.");
} else {
throw new PdeException(applet.exception.getMessage());
}
}
applet.start();
if (editor.presenting) {
window = new Window(new Frame());
window.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
//System.out.println("window got " + e);
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
//editor.doClose();
//new DelayedClose(editor);
stop();
editor.doClose();
}
}
});
/*
window.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent e) {
System.out.println(e);
window.toFront();
}});
*/
} else {
window = new Frame(editor.sketchName); // gonna use ugly windows instead
((Frame)window).setResizable(false);
if (PdeBase.icon != null) ((Frame)window).setIconImage(PdeBase.icon);
window.pack(); // to get a peer, size set later, need for insets
window.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
stop();
editor.doClose();
//new DelayedClose(editor);
//editor.doClose();
//editor.doStop();
}
});
}
if (!(window instanceof Frame)) y1 += parentInsets.top;
window.add(applet);
applet.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
//System.out.println("applet got " + e);
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
stop();
editor.doClose();
//new DelayedClose(editor);
//editor.doClose();
}
}
});
// @#$((* java 1.3
// removed because didn't seem to be needed anymore
// also, was causing offset troubles
/*
window.addMouseListener(applet);
window.addMouseMotionListener(applet);
window.addKeyListener(applet);
*/
Dimension screen =
Toolkit.getDefaultToolkit().getScreenSize();
//System.out.println(SystemColor.windowBorder.toString());
//window.setLayout(new BorderLayout());
window.setLayout(null);
if (editor.presenting) {
window.setBounds((screen.width - applet.width) / 2,
(screen.height - applet.height) / 2,
applet.width, applet.height);
applet.setBounds(0, 0, applet.width, applet.height);
} else {
Insets insets = window.getInsets();
//System.out.println(insets);
int mw = PdeBase.getInteger("run.window.width.minimum", 120);
int mh = PdeBase.getInteger("run.window.height.minimum", 120);
int ww = Math.max(applet.width, mw) + insets.left + insets.right;
int wh = Math.max(applet.height, mh) + insets.top + insets.bottom;
if (x1 - ww > 10) { // if it fits to the left of the window
window.setBounds(x1 - ww, y1, ww, wh);
} else { // if it fits inside the editor window
x1 = parentLoc.x + PdeEditor.GRID_SIZE * 2;
y1 = parentLoc.y + PdeEditor.GRID_SIZE * 2;
if ((x1 + ww > screen.width - PdeEditor.GRID_SIZE) ||
(y1 + wh > screen.height - PdeEditor.GRID_SIZE)) {
// otherwise center on screen
x1 = (screen.width - ww) / 2;
y1 = (screen.height - wh) / 2;
}
window.setBounds(x1, y1, ww, wh);
}
Color windowBgColor =
PdeBase.getColor("run.window.bgcolor", SystemColor.control);
//new Color(102, 102, 102));
window.setBackground(windowBgColor);
//window.setBackground(SystemColor.windowBorder);
//window.setBackground(SystemColor.control);
applet.setBounds((ww - applet.width)/2,
insets.top + ((wh-insets.top-insets.bottom) -
applet.height)/2, ww, wh);
}
applet.setVisible(true); // no effect
if (windowLocation != null) {
window.setLocation(windowLocation);
}
window.show();
applet.requestFocus(); // necessary for key events
}
//System.out.println("KJC RUNNING");
//need to parse this code to give a decent error message
//internal error
//java.lang.NullPointerException
// at ProcessingApplet.colorMode(ProcessingApplet.java:652)
// at Temporary_203_1176.setup(Temporary_203_1176.java:3)
} catch (Exception e) {
// this will pass through to the first part of message
// this handles errors that happen inside setup()
newMessage = true;
e.printStackTrace(this.leechErr);
//if (exception != null) throw exception;
}
}
public void stop() {
//System.out.println();
//System.out.println("* stopping");
// in case stop is called during compilation
if (applet != null) applet.stop();
//if (window != null) window.hide();
// above avoids NullPointerExceptions
// but still threading is too complex, and so
// some boogers are being left behind
applet = null;
//window = null;
if (process != null) { // running externally
//System.out.println("killing external process");
//process.destroy();
//System.out.println("cutting umbilical cord");
try {
FileOutputStream fos = new FileOutputStream("die");
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
//try {
//umbilical.close();
//umbilical = null;
//} catch (IOException e) {
//e.printStackTrace();w
//}
}
//System.gc();
//System.out.println("* stopped");
//System.out.println("thread count: " + Thread.activeCount());
//System.out.println();
//System.out.println();
}
public void close() {
//if (window != null) window.hide();
if (window != null) {
//System.err.println("disposing window");
window.dispose();
window = null;
}
}
public void message(String s) {
//if (s.indexOf("MAKE WAY") != -1) {
//System.out.println("new message coming");
//newMessage = true;
//} else {
if (newMessage) {
//System.out.println("making msg of " + s);
exception = new PdeException(s); // type of java ex
//System.out.println("setting ex type to " + s);
newMessage = false;
messageLineCount = 0;
} else {
messageLineCount++;
int index = s.indexOf(className + ".java");
//System.out.println("> " + index + " " + s);
if (index != -1) {
int len = (className + ".java").length();
String lineNumberStr = s.substring(index + len + 1);
index = lineNumberStr.indexOf(')');
lineNumberStr = lineNumberStr.substring(0, index);
//System.err.println("error line is: " + lineNumberStr);
try {
exception.line = Integer.parseInt(lineNumberStr) - 1; //2;
//System.out.println("exception in RUNNING");
editor.error(exception);
} catch (NumberFormatException e) {
e.printStackTrace();
}
} else if ((index = s.indexOf(className + ".class")) != -1) {
// code to check for:
// at Temporary_484_3845.loop(Compiled Code)
// would also probably get:
// at Temporary_484_3845.loop
// which (i believe) is used by the mac and/or jview
String functionStr = s.substring(index +
(className + ".class").length() + 1);
index = functionStr.indexOf('(');
if (index != -1) {
functionStr = functionStr.substring(0, index);
}
exception = new PdeException(//"inside \"" + functionStr + "()\": " +
exception.getMessage() +
" inside " + functionStr + "() " +
"[add Compiler.disable() to setup()]");
editor.error(exception);
// this will fall through in tihs example:
// at Temporary_4636_9696.pootie(Compiled Code)
// at Temporary_4636_9696.loop(Temporary_4636_9696.java:24)
// because pootie() (re)sets the exception title
// and throws it, but then the line number gets set
// because of the line that comes after
} else if (messageLineCount > 5) {
// this means the class name may not be mentioned
// in the stack trace.. this is just a general purpose
// error, but needs to make it through anyway.
// so if five lines have gone past, might as well signal
//System.out.println("signalling");
messageLineCount = -100;
exception = new PdeException(exception.getMessage());
editor.error(exception);
}
//System.out.println("got it " + s);
}
}
}