Arduino/app/preproc/PdePreprocessor.java

386 lines
12 KiB
Java
Executable File

/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
PdePreprocessor - wrapper for default ANTLR-generated parser
Part of the Wiring project - http://wiring.org.co
Copyright (c) 2004-05 Hernando Barragan
Processing version Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
ANTLR-generated parser and several supporting classes written
by Dan Mosedale via funding from the Interaction Institute IVREA.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app.preproc;
import processing.app.*;
import processing.core.*;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import antlr.*;
import antlr.collections.*;
import antlr.collections.impl.*;
import com.oroinc.text.regex.*;
public class PdePreprocessor {
static final int JDK11 = 0;
static final int JDK13 = 1;
static final int JDK14 = 2;
//static String defaultImports[][] = new String[3][];
// these ones have the .* at the end, since a class name
// might be at the end instead of .* whcih would make trouble
// other classes using this can lop of the . and anything after
// it to produce a package name consistently.
public String extraImports[];
static public final int STATIC = 0; // formerly BEGINNER
static public final int ACTIVE = 1; // formerly INTERMEDIATE
static public final int JAVA = 2; // formerly ADVANCED
// static to make it easier for the antlr preproc to get at it
static public int programType = -1;
Reader programReader;
String buildPath;
// stores number of built user-defined function prototypes
public int prototypeCount = 0;
// stores number of included library headers written
public int headerCount = 0;
/**
* These may change in-between (if the prefs panel adds this option)
* so grab them here on construction.
*/
public PdePreprocessor() {}
/**
* Used by PdeEmitter.dumpHiddenTokens()
*/
//public static TokenStreamCopyingHiddenTokenFilter filter;
/**
* Returns the index of the first character that's not whitespace, a comment
* or a pre-processor directive.
*/
public int firstStatement(String in) {
PatternMatcherInput input = new PatternMatcherInput(in);
PatternCompiler compiler = new Perl5Compiler();
PatternMatcher matcher = new Perl5Matcher();
Pattern pattern = null;
try {
pattern = compiler.compile(
// XXX: doesn't properly handle special single-quoted characters
// whitespace
"\\s+" + "|" +
// multi-line comment
"(/\\*(?:.|\\n)*?\\*/)" + "|" +
// single-line comment
"(//.*?$)" + "|" +
// pre-processor directive
"(#(?:\\\\\\n|.)*)",
Perl5Compiler.MULTILINE_MASK);
} catch (MalformedPatternException e) {
throw new RuntimeException("Internal error in firstStatement()", e);
}
int i = 0;
while (matcher.matchesPrefix(input, pattern)) {
i = matcher.getMatch().endOffset(0);
input.setCurrentOffset(i);
}
return i;
}
/**
* Strips comments, pre-processor directives, single- and double-quoted
* strings from a string.
* @param in the String to strip
* @return the stripped String
*/
public String strip(String in) throws MalformedPatternException {
PatternCompiler compiler = new Perl5Compiler();
PatternMatcher matcher = new Perl5Matcher();
Pattern pattern = compiler.compile(
// XXX: doesn't properly handle special single-quoted characters
// single-quoted character
"('.')" + "|" +
// double-quoted string
"(\"(?:[^\"\\\\]|\\\\.)*\")" + "|" +
// multi-line comment
"(/\\*(?:.|\\n)*?\\*/)" + "|" +
// single-line comment
"(//.*?$)" + "|" +
// pre-processor directive
"(^\\s*#.*?$)",
Perl5Compiler.MULTILINE_MASK);
while (matcher.contains(in, pattern)) {
MatchResult result = matcher.getMatch();
// XXX: should preserve newlines in the result so that line numbers of
// the stripped string correspond to those in the original source.
in = in.substring(0, result.beginOffset(0)) + " " + in.substring(result.endOffset(0));
}
return in;
}
/**
* Removes the contents of all top-level curly brace pairs {}.
* @param in the String to collapse
* @return the collapsed String
*/
private String collapseBraces(String in) {
StringBuffer buffer = new StringBuffer();
int nesting = 0;
int start = 0;
// XXX: need to keep newlines inside braces so we can determine the line
// number of a prototype
for (int i = 0; i < in.length(); i++) {
if (in.charAt(i) == '{') {
if (nesting == 0) {
buffer.append(in.substring(start, i + 1)); // include the '{'
}
nesting++;
}
if (in.charAt(i) == '}') {
nesting--;
if (nesting == 0) {
start = i; // include the '}'
}
}
}
buffer.append(in.substring(start));
return buffer.toString();
}
private List prototypes(String in) throws MalformedPatternException {
in = collapseBraces(strip(in));
PatternMatcherInput input = new PatternMatcherInput(in);
PatternCompiler compiler = new Perl5Compiler();
PatternMatcher matcher = new Perl5Matcher();
// XXX: doesn't handle ... varargs
// XXX: doesn't handle function pointers
Pattern pattern = compiler.compile(
"[\\w\\[\\]\\*]+\\s+[\\[\\]\\*\\w\\s]+\\([,\\[\\]\\*\\w\\s]*\\)(?=\\s*\\{)");
List matches = new ArrayList();
while (matcher.contains(input, pattern)) {
matches.add(matcher.getMatch().group(0) + ";");
}
return matches;
}
/**
* preprocesses a pde file and write out a java file
* @param pretty true if should also space out/indent lines
* @return the classname of the exported Java
*/
//public String write(String program, String buildPath, String name,
// String extraImports[]) throws java.lang.Exception {
public String write(String program, String buildPath,
String name, String codeFolderPackages[],
Target target)
throws java.lang.Exception {
// if the program ends with no CR or LF an OutOfMemoryError will happen.
// not gonna track down the bug now, so here's a hack for it:
// bug filed at http://dev.processing.org/bugs/show_bug.cgi?id=5
//if ((program.length() > 0) &&
//program.charAt(program.length()-1) != '\n') {
program += "\n";
//}
// if the program ends with an unterminated multiline comment,
// an OutOfMemoryError or NullPointerException will happen.
// again, not gonna bother tracking this down, but here's a hack.
// http://dev.processing.org/bugs/show_bug.cgi?id=16
Sketch.scrubComments(program);
// this returns the scrubbed version, but more important for this
// function, it'll check to see if there are errors with the comments.
if (Preferences.getBoolean("preproc.substitute_unicode")) {
// check for non-ascii chars (these will be/must be in unicode format)
char p[] = program.toCharArray();
int unicodeCount = 0;
for (int i = 0; i < p.length; i++) {
if (p[i] > 127) unicodeCount++;
}
// if non-ascii chars are in there, convert to unicode escapes
if (unicodeCount != 0) {
// add unicodeCount * 5.. replacing each unicode char
// with six digit uXXXX sequence (xxxx is in hex)
// (except for nbsp chars which will be a replaced with a space)
int index = 0;
char p2[] = new char[p.length + unicodeCount*5];
for (int i = 0; i < p.length; i++) {
if (p[i] < 128) {
p2[index++] = p[i];
} else if (p[i] == 160) { // unicode for non-breaking space
p2[index++] = ' ';
} else {
int c = p[i];
p2[index++] = '\\';
p2[index++] = 'u';
char str[] = Integer.toHexString(c).toCharArray();
// add leading zeros, so that the length is 4
//for (int i = 0; i < 4 - str.length; i++) p2[index++] = '0';
for (int m = 0; m < 4 - str.length; m++) p2[index++] = '0';
System.arraycopy(str, 0, p2, index, str.length);
index += str.length;
}
}
program = new String(p2, 0, index);
}
}
// if this guy has his own imports, need to remove them
// just in case it's not an advanced mode sketch
PatternMatcher matcher = new Perl5Matcher();
PatternCompiler compiler = new Perl5Compiler();
//String mess = "^\\s*(import\\s+\\S+\\s*;)";
//String mess = "^\\s*(import\\s+)(\\S+)(\\s*;)";
String mess = "^\\s*#include\\s+[<\"](\\S+)[\">]";
java.util.Vector imports = new java.util.Vector();
Pattern pattern = null;
try {
pattern = compiler.compile(mess);
} catch (MalformedPatternException e) {
e.printStackTrace();
return null;
}
PatternMatcherInput input = new PatternMatcherInput(program);
while (matcher.contains(input, pattern)) {
imports.add(matcher.getMatch().group(1));
}
extraImports = new String[imports.size()];
imports.copyInto(extraImports);
// do this after the program gets re-combobulated
this.programReader = new StringReader(program);
this.buildPath = buildPath;
List prototypes = prototypes(program);
// store # of prototypes so that line number reporting can be adjusted
prototypeCount = prototypes.size();
if (name == null) return null;
// output the code
File streamFile = new File(buildPath, name + ".cpp");
PrintStream stream = new PrintStream(new FileOutputStream(streamFile));
writeHeader(stream);
//added to write the pde code to the cpp file
writeProgram(stream, program, prototypes);
writeFooter(stream, target);
stream.close();
return name;
}
// Write the pde program to the cpp file
void writeProgram(PrintStream out, String program, List prototypes) {
int prototypeInsertionPoint = firstStatement(program);
out.print(program.substring(0, prototypeInsertionPoint));
// print user defined prototypes
for (int i = 0; i < prototypes.size(); i++) {
out.print(prototypes.get(i) + "\n");
}
out.print(program.substring(prototypeInsertionPoint));
}
/**
* Write any required header material (eg imports, class decl stuff)
*
* @param out PrintStream to write it to.
*/
void writeHeader(PrintStream out)
throws IOException {
out.print("#include \"WProgram.h\"\n");
}
/**
* Write any necessary closing text.
*
* @param out PrintStream to write it to.
*/
void writeFooter(PrintStream out, Target target) throws java.lang.Exception {
// Open the file main.cxx and copy its entire contents to the bottom of the
// generated sketch .cpp file...
String mainFileName = target.getPath() + File.separator + "main.cxx";
FileReader reader = null;
reader = new FileReader(mainFileName);
LineNumberReader mainfile = new LineNumberReader(reader);
String line;
while ((line = mainfile.readLine()) != null) {
out.print(line + "\n");
}
mainfile.close();
}
static String advClassName = "";
/**
* Find the first CLASS_DEF node in the tree, and return the name of the
* class in question.
*
* XXXdmose right now, we're using a little hack to the grammar to get
* this info. In fact, we should be descending the AST passed in.
*/
String getFirstClassName(AST ast) {
String t = advClassName;
advClassName = "";
return t;
}
}