/* -*- 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 "(/\\*[^*]*(?:\\*(?!/)[^*]*)*\\*/)" + "|" + // 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 "(/\\*[^*]*(?:\\*(?!/)[^*]*)*\\*/)" + "|" + // 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(); } public 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)); out.print("#include \"WProgram.h\"\n"); // 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 {} /** * 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; } }