Merge pull request #3227 from galsasson/master

TweakMode fix to issue #3208
This commit is contained in:
Ben Fry
2015-04-28 16:16:55 -04:00
3 changed files with 221 additions and 113 deletions

View File

@@ -2739,10 +2739,11 @@ public class JavaEditor extends Editor {
* Replace all numbers with variables and add code to initialize
* these variables and handle update messages.
*/
//public boolean automateSketch(Sketch sketch, ArrayList<Handle> handles[])
protected boolean automateSketch(Sketch sketch, List<List<Handle>> handles) {
protected boolean automateSketch(Sketch sketch, SketchParser parser) {
SketchCode[] code = sketch.getCode();
List<List<Handle>> handles = parser.allHandles;
if (code.length < 1) {
return false;
}
@@ -2751,8 +2752,8 @@ public class JavaEditor extends Editor {
return false;
}
int setupStartPos = SketchParser.getSetupStart(baseCode[0]);
if (setupStartPos < 0) {
int setupEndPos = SketchParser.getSetupEnd(baseCode[0]);
if (setupEndPos < 0) {
return false;
}
@@ -2794,7 +2795,7 @@ public class JavaEditor extends Editor {
}
code[tab].setProgram(c);
}
// add the main header to the code in the first tab
String c = code[0].getProgram();
@@ -2849,8 +2850,8 @@ public class JavaEditor extends Editor {
" tweakmode_initAllVars();\n"+
" tweakmode_initCommunication();\n\n";
setupStartPos = SketchParser.getSetupStart(c);
c = replaceString(c, setupStartPos, setupStartPos, addToSetup);
setupEndPos = SketchParser.getSetupEnd(c);
c = replaceString(c, setupEndPos, setupEndPos, addToSetup);
code[0].setProgram(header + c);

View File

@@ -167,7 +167,6 @@ public class JavaMode extends Mode {
RunnerListener listener,
final boolean present) throws SketchException {
final JavaEditor editor = (JavaEditor)listener;
boolean launchInteractive = false;
if (isSketchModified(sketch)) {
editor.deactivateRun();
@@ -193,7 +192,7 @@ public class JavaMode extends Mode {
final SketchParser parser = new SketchParser(editor.baseCode, requiresTweak);
// add our code to the sketch
launchInteractive = editor.automateSketch(sketch, parser.allHandles);
final boolean launchInteractive = editor.automateSketch(sketch, parser);
build = new JavaBuild(sketch);
appletClassName = build.build(false);
@@ -204,8 +203,10 @@ public class JavaMode extends Mode {
public void run() {
runtime.launch(present); // this blocks until finished
// next lines are executed when the sketch quits
editor.initEditorCode(parser.allHandles, false);
editor.stopInteractiveMode(parser.allHandles);
if (launchInteractive) {
editor.initEditorCode(parser.allHandles, false);
editor.stopInteractiveMode(parser.allHandles);
}
}
}).start();

View File

@@ -38,17 +38,37 @@ public class SketchParser {
ArrayList<ColorMode> colorModes;
List<List<Range>> scientificNotations;
Range setupFunction;
List<List<Range>> commentBlocks;
List<int[]> curlyScopes;
public SketchParser(String[] codeTabs, boolean requiresComment) {
this.codeTabs = codeTabs;
this.requiresComment = requiresComment;
intVarCount=0;
floatVarCount=0;
// get all comment blocks
commentBlocks = new ArrayList<>();
for (String code : codeTabs) {
commentBlocks.add(getCommentBlocks(code));
}
// get setup function range (to ignore all numbers there)
setupFunction = new Range(getSetupStart(codeTabs[0]), getSetupEnd(codeTabs[0]));
// build curly scope for every character in the code
curlyScopes = new ArrayList<>();
for (String code : codeTabs) {
curlyScopes.add(getCurlyScopes(code));
}
// get all scientific notation (to ignore them)
scientificNotations = getAllScientificNotations();
// find, add, and sort all tweakable numbers in the sketch
// find, add, and sort all tweak-able numbers in the sketch
addAllNumbers();
// handle colors
@@ -65,11 +85,11 @@ public class SketchParser {
public void addAllNumbers() {
//allHandles = new ArrayList[codeTabs.length]; // moved inside addAllDecimalNumbers
allHandles = new ArrayList<>();
addAllDecimalNumbers();
addAllHexNumbers();
addAllWebColorNumbers();
//for (int i=0; i<codeTabs.length; i++) {
for (List<Handle> handle : allHandles) {
//Collections.sort(allHandles[i], new HandleComparator());
Collections.sort(handle, new HandleComparator());
@@ -83,14 +103,11 @@ public class SketchParser {
* list of all numbers in the sketch (excluding hexadecimals)
*/
private void addAllDecimalNumbers() {
allHandles = new ArrayList<>();
// for every number found:
// save its type (int/float), name, value and position in code.
Pattern p = Pattern.compile("[\\[\\{<>(),\\t\\s\\+\\-\\/\\*^%!|&=?:~]\\d+\\.?\\d*");
for (int i = 0; i < codeTabs.length; i++) {
//allHandles[i] = new ArrayList<Handle>();
List<Handle> handles = new ArrayList<Handle>();
allHandles.add(handles);
@@ -102,10 +119,15 @@ public class SketchParser {
int start = m.start()+1;
int end = m.end();
if (isInComment(start, codeTabs[i])) {
if (isInRangeList(start, commentBlocks.get(i))) {
// ignore comments
continue;
}
if (setupFunction.contains(start)) {
// ignore numbers in setup
continue;
}
if (requiresComment) {
// only add numbers that have the "// tweak" comment in their line
@@ -150,7 +172,7 @@ public class SketchParser {
continue;
// beware of the global assignment (bug from 26.07.2013)
if (isGlobal(m.start(), c))
if (isGlobal(m.start(), i))
continue;
int line = countLines(c.substring(0, start)) - 1; // zero based
@@ -189,10 +211,15 @@ public class SketchParser {
int start = m.start()+1;
int end = m.end();
if (isInComment(start, codeTabs[i])) {
if (isInRangeList(start, commentBlocks.get(i))) {
// ignore comments
continue;
}
if (setupFunction.contains(start)) {
// ignore number in setup
continue;
}
if (requiresComment) {
// only add numbers that have the "// tweak" comment in their line
@@ -207,7 +234,7 @@ public class SketchParser {
}
// beware of the global assignment (bug from 26.07.2013)
if (isGlobal(m.start(), c)) {
if (isGlobal(m.start(), i)) {
continue;
}
@@ -245,10 +272,15 @@ public class SketchParser {
int start = m.start();
int end = m.end();
if (isInComment(start, codeTabs[i])) {
if (isInRangeList(start, commentBlocks.get(i))) {
// ignore comments
continue;
}
if (setupFunction.contains(start)) {
// ignore number in setup
continue;
}
if (requiresComment) {
// only add numbers that have the "// tweak" comment in their line
@@ -263,7 +295,7 @@ public class SketchParser {
}
// beware of the global assignment (bug from 26.07.2013)
if (isGlobal(m.start(), c)) {
if (isGlobal(m.start(), i)) {
continue;
}
@@ -288,13 +320,14 @@ public class SketchParser {
private ArrayList<ColorMode> findAllColorModes() {
ArrayList<ColorMode> modes = new ArrayList<ColorMode>();
for (String tab : codeTabs) {
for (int i=0; i<codeTabs.length; i++) {
String tab = codeTabs[i];
int index = -1;
// search for a call to colorMode function
while ((index = tab.indexOf("colorMode", index+1)) > -1) {
// found colorMode at index
if (isInComment(index, tab)) {
if (isInRangeList(index, commentBlocks.get(i))) {
// ignore comments
continue;
}
@@ -326,7 +359,6 @@ public class SketchParser {
Pattern p = Pattern.compile("color\\(|color\\s\\(|fill[\\(\\s]|stroke[\\(\\s]|background[\\(\\s]|tint[\\(\\s]");
for (int i = 0; i < codeTabs.length; i++) {
//colorBoxes[i] = new ArrayList<ColorControlBox>();
List<ColorControlBox> colorBox = new ArrayList<ColorControlBox>();
colorBoxes.add(colorBox);
@@ -344,10 +376,15 @@ public class SketchParser {
continue;
}
if (isInComment(m.start(), tab)) {
if (isInRangeList(m.start(), commentBlocks.get(i))) {
// ignore colors in a comment
continue;
}
if (setupFunction.contains(m.start())) {
// ignore number in setup
continue;
}
// look for handles inside the parenthesis
for (Handle handle : allHandles.get(i)) {
@@ -360,7 +397,7 @@ public class SketchParser {
if (colorHandles.size() > 0) {
/* make sure there is no other stuff between '()' like variables.
* substract all handle values from string inside parenthesis and
* subtract all handle values from string inside parenthesis and
* check there is no garbage left
*/
String insidePar = tab.substring(openPar+1, closePar);
@@ -423,10 +460,15 @@ public class SketchParser {
continue;
}
if (isInComment(m.start(), tab)) {
if (isInRangeList(m.start(), commentBlocks.get(i))) {
// ignore colors in a comment
continue;
}
if (setupFunction.contains(m.start())) {
// ignore number in setup
continue;
}
// put 'colorParamsEnd' after three parameters inside the parenthesis or at the close
int colorParamsEnd = openPar;
@@ -450,7 +492,7 @@ public class SketchParser {
if (colorHandles.size() > 0) {
/* make sure there is no other stuff between '()' like variables.
* substract all handle values from string inside parenthesis and
* subtract all handle values from string inside parenthesis and
* check there is no garbage left
*/
String insidePar = tab.substring(openPar+1, colorParamsEnd);
@@ -542,18 +584,13 @@ public class SketchParser {
private List<List<Range>> getAllScientificNotations() {
//ArrayList<Range> notations[] = new ArrayList[codeTabs.length];
List<List<Range>> notations = new ArrayList<>();
Pattern p = Pattern.compile("[+\\-]?(?:0|[1-9]\\d*)(?:\\.\\d*)?[eE][+\\-]?\\d+");
//for (int i = 0; i < codeTabs.length; i++) {
for (String code : codeTabs) {
List<Range> notation = new ArrayList<Range>();
//notations[i] = new ArrayList<Range>();
//Matcher m = p.matcher(codeTabs[i]);
Matcher m = p.matcher(code);
while (m.find()) {
//notations[i].add(new Range(m.start(), m.end()));
notation.add(new Range(m.start(), m.end()));
}
notations.add(notation);
@@ -646,92 +683,128 @@ public class SketchParser {
return false;
}
/**
* Builds an int array for every tab that represents the scope depth at each character
*
* @return
*/
static private int[] getCurlyScopes(String code)
{
List<Range> comments = getCommentBlocks(code);
int[] scopes = new int[code.length()];
int curlyScope = 0;
boolean arrayAssignmentMaybeCommingFlag = false;
int arrayAssignmentCurlyScope = 0;
for (int pos=0; pos<code.length(); pos++) {
scopes[pos] = curlyScope;
if (isInRangeList(pos, comments)) {
// we are inside a comment, ignore and move on
continue;
}
if (code.charAt(pos) == '{') {
if (arrayAssignmentMaybeCommingFlag ||
arrayAssignmentCurlyScope>0) {
// this is an array assignment
arrayAssignmentCurlyScope++;
arrayAssignmentMaybeCommingFlag = false;
}
else {
curlyScope++;
}
}
else if (code.charAt(pos) == '}') {
if (arrayAssignmentCurlyScope>0) {
arrayAssignmentCurlyScope--;
}
else {
curlyScope--;
}
}
else if (code.charAt(pos) == '=') {
arrayAssignmentMaybeCommingFlag = true;
}
else if (!isWhiteSpace(code.charAt(pos))) {
arrayAssignmentMaybeCommingFlag = false;
}
}
return scopes;
}
static private boolean isWhiteSpace(char c) {
if (c == ' ' ||
c == '\t' ||
c == '\n' ||
c == '\r') {
return true;
}
int[][] a = {{1,2},{3,4}};
return false;
}
/**
* Is this a global position?
* @param pos position
* @param code code
* @param codeTabIndex index of the code in codeTabs
* @return
* true if the position 'pos' is in global scope in the code 'code'
* true if the position 'pos' is in global scope in the code 'codeTabs[codeTabIndex]'
*
*/
static private boolean isGlobal(int pos, String code) {
int curlyScope = 0; // count '{-}'
for (int c=pos; c>=0; c--)
{
if (code.charAt(c) == '{') {
// check if a function or an array assignment
for (int cc=c; cc>=0; cc--) {
if (code.charAt(cc)==')') {
curlyScope++;
break;
}
else if (code.charAt(cc)==']') {
break;
}
else if (code.charAt(cc)==';') {
break;
}
}
}
else if (code.charAt(c) == '}') {
// check if a function or an array assignment
for (int cc=c; cc>=0; cc--) {
if (code.charAt(cc)==')') {
curlyScope--;
break;
}
else if (code.charAt(cc)==']') {
break;
}
else if (code.charAt(cc)==';') {
break;
}
}
}
}
if (curlyScope == 0) {
// it is a global position
return true;
}
return false;
private boolean isGlobal(int pos, int codeTabIndex) {
return (curlyScopes.get(codeTabIndex)[pos]==0);
};
static private boolean isInComment(int pos, String code) {
// look for one line comment
int lineStart = getStartOfLine(pos, code);
if (lineStart < 0) {
return false;
public static List<Range> getCommentBlocks(String code) {
List<Range> commentBlocks = new ArrayList<Range>();
int lastBlockStart=0;
boolean lookForEnd = false;
for (int pos=0; pos<code.length()-1; pos++) {
if (lookForEnd) {
// we have a start, look for the end
if (code.charAt(pos) == '*' && code.charAt(pos+1) == '/') {
commentBlocks.add(new Range(lastBlockStart, pos+1));
lookForEnd = false;
}
}
else {
if (code.charAt(pos) == '/' && code.charAt(pos+1) == '*') {
// we found a block start
lastBlockStart = pos;
lookForEnd = true;
}
else if (code.charAt(pos) == '/' && code.charAt(pos+1) == '/') {
// we found a line comment
commentBlocks.add(new Range(pos, getEndOfLine(pos, code)));
}
}
}
if (code.substring(lineStart, pos).indexOf("//") != -1) {
return true;
return commentBlocks;
}
private static boolean isInRangeList(int pos, List<Range> rangeList) {
for (Range r : rangeList) {
if (r.contains(pos)) {
return true;
}
}
// TODO: look for block comments
return false;
}
static private int getEndOfLine(int pos, String code) {
return code.indexOf("\n", pos);
}
static private int getStartOfLine(int pos, String code) {
while (pos >= 0) {
if (code.charAt(pos) == '\n') {
return pos+1;
}
pos--;
}
return 0;
}
/** returns the object of the function starting at 'pos'
/** returns the object name (what comes before the '.') of the function starting at 'pos'
*
* @param pos
* @param code
@@ -762,7 +835,7 @@ public class SketchParser {
}
static public int getSetupStart(String code) {
public static int getSetupStart(String code) {
Pattern p = Pattern.compile("void[\\s\\t\\r\\n]*setup[\\s\\t]*\\(\\)[\\s\\t\\r\\n]*\\{");
Matcher m = p.matcher(code);
@@ -772,11 +845,44 @@ public class SketchParser {
return -1;
}
// private String replaceString(String str, int start, int end, String put) {
// return str.substring(0, start) + put + str.substring(end, str.length());
// }
public static int getSetupEnd(String code) {
List<Range> comments = getCommentBlocks(code);
int setupStart = getSetupStart(code);
if (setupStart == -1) {
return -1;
}
System.out.println("setup start = " + setupStart);
// count brackets to look for setup end
int bracketCount=1;
int pos = setupStart;
while (bracketCount>0 && pos<code.length()) {
if (isInRangeList(pos, comments)) {
// in a comment, ignore and move on
pos++;
continue;
}
if (code.charAt(pos) == '{') {
bracketCount++;
}
else if (code.charAt(pos) == '}') {
bracketCount--;
}
pos++;
}
if (bracketCount == 0) {
return pos-1;
}
return -1;
}
static class Range {