mirror of
https://github.com/processing/processing4.git
synced 2026-02-13 10:30:44 +01:00
moving things back off the branch, and into full disaster mode
This commit is contained in:
9
app/src/processing/mode/java/preproc/.cvsignore
Normal file
9
app/src/processing/mode/java/preproc/.cvsignore
Normal file
@@ -0,0 +1,9 @@
|
||||
*Lexer.java
|
||||
*Recognizer.java
|
||||
*TokenTypes.java
|
||||
*TokenTypes.txt
|
||||
*TreeParser.java
|
||||
*TreeParserTokenTypes.java
|
||||
*TreeParserTokenTypes.txt
|
||||
expanded*.g
|
||||
|
||||
766
app/src/processing/mode/java/preproc/PdeEmitter.java
Normal file
766
app/src/processing/mode/java/preproc/PdeEmitter.java
Normal file
@@ -0,0 +1,766 @@
|
||||
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
package processing.mode.java.preproc;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.BitSet;
|
||||
import java.util.Stack;
|
||||
import processing.app.Preferences;
|
||||
import processing.app.SketchException;
|
||||
import processing.app.antlr.PdeTokenTypes;
|
||||
import antlr.CommonASTWithHiddenTokens;
|
||||
import antlr.CommonHiddenStreamToken;
|
||||
import antlr.collections.AST;
|
||||
|
||||
/* Based on original code copyright (c) 2003 Andy Tripp <atripp@comcast.net>.
|
||||
* shipped under GPL with permission.
|
||||
*/
|
||||
|
||||
/**
|
||||
* PDEEmitter: A class that can take an ANTLR Java AST and produce
|
||||
* reasonably formatted Java code from it. To use it, create a
|
||||
* PDEEmitter object, call setOut() if you want to print to something
|
||||
* other than System.out, and then call print(), passing the
|
||||
* AST. Typically, the AST node that you pass would be the root of a
|
||||
* tree - the ROOT_ID node that represents a Java file.
|
||||
*
|
||||
* Modified March 2010 to support Java 5 type arguments and for loops by
|
||||
* @author Jonathan Feinberg <jdf@pobox.com>
|
||||
*/
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class PdeEmitter implements PdeTokenTypes {
|
||||
private final PdePreprocessor pdePreprocessor;
|
||||
private final PrintWriter out;
|
||||
private final PrintStream debug = System.err;
|
||||
|
||||
private final Stack<AST> stack = new Stack<AST>();
|
||||
private final static int ROOT_ID = 0;
|
||||
|
||||
public PdeEmitter(final PdePreprocessor pdePreprocessor, final PrintWriter out) {
|
||||
this.pdePreprocessor = pdePreprocessor;
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a child of the given AST that has the given type
|
||||
* @returns a child AST of the given type. If it can't find a child of the
|
||||
* given type, return null.
|
||||
*/
|
||||
private AST getChild(final AST ast, final int childType) {
|
||||
AST child = ast.getFirstChild();
|
||||
while (child != null) {
|
||||
if (child.getType() == childType) {
|
||||
// debug.println("getChild: found:" + name(ast));
|
||||
return child;
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the list of hidden tokens linked to after the AST node passed in.
|
||||
* Most hidden tokens are dumped from this function.
|
||||
*/
|
||||
private void dumpHiddenAfter(final AST ast) {
|
||||
dumpHiddenTokens(((CommonASTWithHiddenTokens) ast).getHiddenAfter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the list of hidden tokens linked to before the AST node passed in.
|
||||
* The only time hidden tokens need to be dumped with this function is when
|
||||
* dealing parts of the tree where automatic tree construction was
|
||||
* turned off with the ! operator in the grammar file and the nodes were
|
||||
* manually constructed in such a way that the usual tokens don't have the
|
||||
* necessary hiddenAfter links.
|
||||
*/
|
||||
private void dumpHiddenBefore(final AST ast) {
|
||||
|
||||
antlr.CommonHiddenStreamToken child = null, parent = ((CommonASTWithHiddenTokens) ast)
|
||||
.getHiddenBefore();
|
||||
|
||||
// if there aren't any hidden tokens here, quietly return
|
||||
//
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// traverse back to the head of the list of tokens before this node
|
||||
do {
|
||||
child = parent;
|
||||
parent = child.getHiddenBefore();
|
||||
} while (parent != null);
|
||||
|
||||
// dump that list
|
||||
dumpHiddenTokens(child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the list of hidden tokens linked to from the token passed in.
|
||||
*/
|
||||
private void dumpHiddenTokens(CommonHiddenStreamToken t) {
|
||||
for (; t != null; t = pdePreprocessor.getHiddenAfter(t)) {
|
||||
out.print(t.getText());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the children of the given AST
|
||||
* @param ast The AST to print
|
||||
* @returns true iff anything was printed
|
||||
*/
|
||||
private boolean printChildren(final AST ast) throws SketchException {
|
||||
boolean ret = false;
|
||||
AST child = ast.getFirstChild();
|
||||
while (child != null) {
|
||||
ret = true;
|
||||
print(child);
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether an AST has any children or not.
|
||||
* @return true iff the AST has at least one child
|
||||
*/
|
||||
private boolean hasChildren(final AST ast) {
|
||||
return (ast.getFirstChild() != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the best node in the subtree for printing. This really means
|
||||
* the next node which could potentially have hiddenBefore data. It's
|
||||
* usually the first printable leaf, but not always.
|
||||
*
|
||||
* @param includeThisNode Should this node be included in the search?
|
||||
* If false, only descendants are searched.
|
||||
*
|
||||
* @return the first printable leaf node in an AST
|
||||
*/
|
||||
private AST getBestPrintableNode(final AST ast, final boolean includeThisNode) {
|
||||
AST child;
|
||||
|
||||
if (includeThisNode) {
|
||||
child = ast;
|
||||
} else {
|
||||
child = ast.getFirstChild();
|
||||
}
|
||||
|
||||
if (child != null) {
|
||||
|
||||
switch (child.getType()) {
|
||||
|
||||
// the following node types are printing nodes that print before
|
||||
// any children, but then also recurse over children. So they
|
||||
// may have hiddenBefore chains that need to be printed first. Many
|
||||
// statements and all unary expression types qualify. Return these
|
||||
// nodes directly
|
||||
case CLASS_DEF:
|
||||
case LITERAL_if:
|
||||
case LITERAL_new:
|
||||
case LITERAL_for:
|
||||
case LITERAL_while:
|
||||
case LITERAL_do:
|
||||
case LITERAL_break:
|
||||
case LITERAL_continue:
|
||||
case LITERAL_return:
|
||||
case LITERAL_switch:
|
||||
case LITERAL_try:
|
||||
case LITERAL_throw:
|
||||
case LITERAL_synchronized:
|
||||
case LITERAL_assert:
|
||||
case BNOT:
|
||||
case LNOT:
|
||||
case INC:
|
||||
case DEC:
|
||||
case UNARY_MINUS:
|
||||
case UNARY_PLUS:
|
||||
return child;
|
||||
|
||||
// Some non-terminal node types (at the moment, I only know of
|
||||
// MODIFIERS, but there may be other such types), can be
|
||||
// leaves in the tree but not have any children. If this is
|
||||
// such a node, move on to the next sibling.
|
||||
case MODIFIERS:
|
||||
if (child.getFirstChild() == null) {
|
||||
return getBestPrintableNode(child.getNextSibling(), false);
|
||||
}
|
||||
// new jikes doesn't like fallthrough, so just duplicated here:
|
||||
return getBestPrintableNode(child, false);
|
||||
|
||||
default:
|
||||
return getBestPrintableNode(child, false);
|
||||
}
|
||||
}
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
// Because the meanings of <, >, >>, and >>> are overloaded to support
|
||||
// type arguments and type parameters, we have to treat them
|
||||
// as copyable to hidden text (or else the following syntax,
|
||||
// such as (); and what not gets lost under certain circumstances
|
||||
//
|
||||
// Since they are copied to the hidden stream, you don't want
|
||||
// to print them explicitly; they come out in the dumpHiddenXXX methods.
|
||||
// -- jdf
|
||||
private static final BitSet OTHER_COPIED_TOKENS = new BitSet() {
|
||||
{
|
||||
set(LT);
|
||||
set(GT);
|
||||
set(SR);
|
||||
set(BSR);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Prints a binary operator
|
||||
*/
|
||||
private void printBinaryOperator(final AST ast) throws SketchException {
|
||||
print(ast.getFirstChild());
|
||||
if (!OTHER_COPIED_TOKENS.get(ast.getType())) {
|
||||
out.print(ast.getText());
|
||||
dumpHiddenAfter(ast);
|
||||
}
|
||||
print(ast.getFirstChild().getNextSibling());
|
||||
}
|
||||
|
||||
private void printMethodDef(final AST ast) throws SketchException {
|
||||
final AST modifiers = ast.getFirstChild();
|
||||
final AST typeParameters, type;
|
||||
if (modifiers.getNextSibling().getType() == TYPE_PARAMETERS) {
|
||||
typeParameters = modifiers.getNextSibling();
|
||||
type = typeParameters.getNextSibling();
|
||||
} else {
|
||||
typeParameters = null;
|
||||
type = modifiers.getNextSibling();
|
||||
}
|
||||
final AST methodName = type.getNextSibling();
|
||||
if (methodName.getText().equals("main")) {
|
||||
pdePreprocessor.setFoundMain(true);
|
||||
}
|
||||
printChildren(ast);
|
||||
}
|
||||
|
||||
private void printIfThenElse(final AST literalIf) throws SketchException {
|
||||
out.print(literalIf.getText());
|
||||
dumpHiddenAfter(literalIf);
|
||||
|
||||
final AST condition = literalIf.getFirstChild();
|
||||
print(condition); // the "if" condition: an EXPR
|
||||
|
||||
// the "then" clause is either an SLIST or an EXPR
|
||||
final AST thenPath = condition.getNextSibling();
|
||||
print(thenPath);
|
||||
|
||||
// optional "else" clause: an SLIST or an EXPR
|
||||
// what could be simpler?
|
||||
final AST elsePath = thenPath.getNextSibling();
|
||||
if (elsePath != null) {
|
||||
out.print("else");
|
||||
final AST bestPrintableNode = getBestPrintableNode(elsePath, true);
|
||||
dumpHiddenBefore(bestPrintableNode);
|
||||
final CommonHiddenStreamToken hiddenBefore =
|
||||
((CommonASTWithHiddenTokens) elsePath).getHiddenBefore();
|
||||
if (elsePath.getType() == PdeTokenTypes.SLIST && elsePath.getNumberOfChildren() == 0 &&
|
||||
hiddenBefore == null) {
|
||||
out.print("{");
|
||||
final CommonHiddenStreamToken hiddenAfter =
|
||||
((CommonASTWithHiddenTokens) elsePath).getHiddenAfter();
|
||||
if (hiddenAfter == null) {
|
||||
out.print("}");
|
||||
} else {
|
||||
dumpHiddenTokens(hiddenAfter);
|
||||
}
|
||||
} else {
|
||||
print(elsePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the given AST. Call this function to print your PDE code.
|
||||
*
|
||||
* It works by making recursive calls to print children.
|
||||
* So the code below is one big "switch" statement on the passed AST type.
|
||||
*/
|
||||
public void print(final AST ast) throws SketchException {
|
||||
if (ast == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
stack.push(ast);
|
||||
|
||||
final AST child1 = ast.getFirstChild();
|
||||
AST child2 = null;
|
||||
AST child3 = null;
|
||||
if (child1 != null) {
|
||||
child2 = child1.getNextSibling();
|
||||
if (child2 != null) {
|
||||
child3 = child2.getNextSibling();
|
||||
}
|
||||
}
|
||||
|
||||
switch (ast.getType()) {
|
||||
// The top of the tree looks like this:
|
||||
// ROOT_ID "Whatever.java"
|
||||
// package
|
||||
// imports
|
||||
// class definition
|
||||
case ROOT_ID:
|
||||
dumpHiddenTokens(pdePreprocessor.getInitialHiddenToken());
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
// supporting a "package" statement in a PDE program has
|
||||
// a bunch of issues with it that need to dealt in the compilation
|
||||
// code too, so this isn't actually tested.
|
||||
case PACKAGE_DEF:
|
||||
out.print("package");
|
||||
dumpHiddenAfter(ast);
|
||||
print(ast.getFirstChild());
|
||||
break;
|
||||
|
||||
// IMPORT has exactly one child
|
||||
case IMPORT:
|
||||
out.print("import");
|
||||
dumpHiddenAfter(ast);
|
||||
print(ast.getFirstChild());
|
||||
break;
|
||||
|
||||
case STATIC_IMPORT:
|
||||
out.print("import static");
|
||||
dumpHiddenAfter(ast);
|
||||
print(ast.getFirstChild());
|
||||
break;
|
||||
|
||||
case CLASS_DEF:
|
||||
case INTERFACE_DEF:
|
||||
print(getChild(ast, MODIFIERS));
|
||||
if (ast.getType() == CLASS_DEF) {
|
||||
out.print("class");
|
||||
} else {
|
||||
out.print("interface");
|
||||
}
|
||||
dumpHiddenBefore(getChild(ast, IDENT));
|
||||
print(getChild(ast, IDENT));
|
||||
print(getChild(ast, TYPE_PARAMETERS));
|
||||
print(getChild(ast, EXTENDS_CLAUSE));
|
||||
print(getChild(ast, IMPLEMENTS_CLAUSE));
|
||||
print(getChild(ast, OBJBLOCK));
|
||||
break;
|
||||
|
||||
case EXTENDS_CLAUSE:
|
||||
if (hasChildren(ast)) {
|
||||
out.print("extends");
|
||||
dumpHiddenBefore(getBestPrintableNode(ast, false));
|
||||
printChildren(ast);
|
||||
}
|
||||
break;
|
||||
|
||||
case IMPLEMENTS_CLAUSE:
|
||||
if (hasChildren(ast)) {
|
||||
out.print("implements");
|
||||
dumpHiddenBefore(getBestPrintableNode(ast, false));
|
||||
printChildren(ast);
|
||||
}
|
||||
break;
|
||||
|
||||
// DOT always has exactly two children.
|
||||
case DOT:
|
||||
print(child1);
|
||||
out.print(".");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child2);
|
||||
break;
|
||||
|
||||
case MODIFIERS:
|
||||
case OBJBLOCK:
|
||||
case CTOR_DEF:
|
||||
//case METHOD_DEF:
|
||||
case PARAMETERS:
|
||||
case PARAMETER_DEF:
|
||||
case VARIABLE_PARAMETER_DEF:
|
||||
case VARIABLE_DEF:
|
||||
case TYPE:
|
||||
case SLIST:
|
||||
case ELIST:
|
||||
case ARRAY_DECLARATOR:
|
||||
case TYPECAST:
|
||||
case EXPR:
|
||||
case ARRAY_INIT:
|
||||
case FOR_INIT:
|
||||
case FOR_CONDITION:
|
||||
case FOR_ITERATOR:
|
||||
case METHOD_CALL:
|
||||
case INSTANCE_INIT:
|
||||
case INDEX_OP:
|
||||
case SUPER_CTOR_CALL:
|
||||
case CTOR_CALL:
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case METHOD_DEF:
|
||||
printMethodDef(ast);
|
||||
break;
|
||||
|
||||
// if we have two children, it's of the form "a=0"
|
||||
// if just one child, it's of the form "=0" (where the
|
||||
// lhs is above this AST).
|
||||
case ASSIGN:
|
||||
if (child2 != null) {
|
||||
print(child1);
|
||||
out.print("=");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child2);
|
||||
} else {
|
||||
out.print("=");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child1);
|
||||
}
|
||||
break;
|
||||
|
||||
// binary operators:
|
||||
case PLUS:
|
||||
case MINUS:
|
||||
case DIV:
|
||||
case MOD:
|
||||
case NOT_EQUAL:
|
||||
case EQUAL:
|
||||
case LE:
|
||||
case GE:
|
||||
case LOR:
|
||||
case LAND:
|
||||
case BOR:
|
||||
case BXOR:
|
||||
case BAND:
|
||||
case SL:
|
||||
case SR:
|
||||
case BSR:
|
||||
case LITERAL_instanceof:
|
||||
case PLUS_ASSIGN:
|
||||
case MINUS_ASSIGN:
|
||||
case STAR_ASSIGN:
|
||||
case DIV_ASSIGN:
|
||||
case MOD_ASSIGN:
|
||||
case SR_ASSIGN:
|
||||
case BSR_ASSIGN:
|
||||
case SL_ASSIGN:
|
||||
case BAND_ASSIGN:
|
||||
case BXOR_ASSIGN:
|
||||
case BOR_ASSIGN:
|
||||
|
||||
case LT:
|
||||
case GT:
|
||||
printBinaryOperator(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_for:
|
||||
out.print(ast.getText());
|
||||
dumpHiddenAfter(ast);
|
||||
if (child1.getType() == FOR_EACH_CLAUSE) {
|
||||
printChildren(child1);
|
||||
print(child2);
|
||||
} else {
|
||||
printChildren(ast);
|
||||
}
|
||||
break;
|
||||
|
||||
case POST_INC:
|
||||
case POST_DEC:
|
||||
print(child1);
|
||||
out.print(ast.getText());
|
||||
dumpHiddenAfter(ast);
|
||||
break;
|
||||
|
||||
// unary operators:
|
||||
case BNOT:
|
||||
case LNOT:
|
||||
case INC:
|
||||
case DEC:
|
||||
case UNARY_MINUS:
|
||||
case UNARY_PLUS:
|
||||
out.print(ast.getText());
|
||||
dumpHiddenAfter(ast);
|
||||
print(child1);
|
||||
break;
|
||||
|
||||
case LITERAL_new:
|
||||
out.print("new");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_return:
|
||||
out.print("return");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child1);
|
||||
break;
|
||||
|
||||
case STATIC_INIT:
|
||||
out.print("static");
|
||||
dumpHiddenBefore(getBestPrintableNode(ast, false));
|
||||
print(child1);
|
||||
break;
|
||||
|
||||
case LITERAL_switch:
|
||||
out.print("switch");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case LABELED_STAT:
|
||||
case CASE_GROUP:
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_case:
|
||||
out.print("case");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_default:
|
||||
out.print("default");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case NUM_INT:
|
||||
case CHAR_LITERAL:
|
||||
case STRING_LITERAL:
|
||||
case NUM_FLOAT:
|
||||
case NUM_LONG:
|
||||
out.print(ast.getText());
|
||||
dumpHiddenAfter(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_synchronized: // 0137 to fix bug #136
|
||||
case LITERAL_assert:
|
||||
out.print(ast.getText());
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_private:
|
||||
case LITERAL_public:
|
||||
case LITERAL_protected:
|
||||
case LITERAL_static:
|
||||
case LITERAL_transient:
|
||||
case LITERAL_native:
|
||||
case LITERAL_threadsafe:
|
||||
//case LITERAL_synchronized: // 0137 to fix bug #136
|
||||
case LITERAL_volatile:
|
||||
case LITERAL_class: // 0176 to fix bug #1466
|
||||
case FINAL:
|
||||
case ABSTRACT:
|
||||
case LITERAL_package:
|
||||
case LITERAL_void:
|
||||
case LITERAL_boolean:
|
||||
case LITERAL_byte:
|
||||
case LITERAL_char:
|
||||
case LITERAL_short:
|
||||
case LITERAL_int:
|
||||
case LITERAL_float:
|
||||
case LITERAL_long:
|
||||
case LITERAL_double:
|
||||
case LITERAL_true:
|
||||
case LITERAL_false:
|
||||
case LITERAL_null:
|
||||
case SEMI:
|
||||
case LITERAL_this:
|
||||
case LITERAL_super:
|
||||
out.print(ast.getText());
|
||||
dumpHiddenAfter(ast);
|
||||
break;
|
||||
|
||||
case EMPTY_STAT:
|
||||
case EMPTY_FIELD:
|
||||
break;
|
||||
|
||||
case LITERAL_continue:
|
||||
case LITERAL_break:
|
||||
out.print(ast.getText());
|
||||
dumpHiddenAfter(ast);
|
||||
if (child1 != null) {// maybe label
|
||||
print(child1);
|
||||
}
|
||||
break;
|
||||
|
||||
// yuck: Distinguish between "import x.y.*" and "x = 1 * 3"
|
||||
case STAR:
|
||||
if (hasChildren(ast)) { // the binary mult. operator
|
||||
printBinaryOperator(ast);
|
||||
} else { // the special "*" in import:
|
||||
out.print("*");
|
||||
dumpHiddenAfter(ast);
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_throws:
|
||||
out.print("throws");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_if:
|
||||
printIfThenElse(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_while:
|
||||
out.print("while");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_do:
|
||||
out.print("do");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child1); // an SLIST
|
||||
out.print("while");
|
||||
dumpHiddenBefore(getBestPrintableNode(child2, false));
|
||||
print(child2); // an EXPR
|
||||
break;
|
||||
|
||||
case LITERAL_try:
|
||||
out.print("try");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_catch:
|
||||
out.print("catch");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
// the first child is the "try" and the second is the SLIST
|
||||
case LITERAL_finally:
|
||||
out.print("finally");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_throw:
|
||||
out.print("throw");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child1);
|
||||
break;
|
||||
|
||||
// the dreaded trinary operator
|
||||
case QUESTION:
|
||||
print(child1);
|
||||
out.print("?");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child2);
|
||||
print(child3);
|
||||
break;
|
||||
|
||||
// pde specific or modified tokens start here
|
||||
|
||||
// Image -> BImage, Font -> BFont as appropriate
|
||||
case IDENT:
|
||||
/*
|
||||
if (ast.getText().equals("Image") &&
|
||||
Preferences.getBoolean("preproc.substitute_image")) { //, true)) {
|
||||
out.print("BImage");
|
||||
} else if (ast.getText().equals("Font") &&
|
||||
Preferences.getBoolean("preproc.substitute_font")) { //, true)) {
|
||||
out.print("BFont");
|
||||
} else {
|
||||
*/
|
||||
out.print(ast.getText());
|
||||
//}
|
||||
dumpHiddenAfter(ast);
|
||||
break;
|
||||
|
||||
// the color datatype is just an alias for int
|
||||
case LITERAL_color:
|
||||
out.print("int");
|
||||
dumpHiddenAfter(ast);
|
||||
break;
|
||||
|
||||
case WEBCOLOR_LITERAL:
|
||||
if (ast.getText().length() != 6) {
|
||||
System.err.println("Internal error: incorrect length of webcolor "
|
||||
+ "literal should have been detected sooner.");
|
||||
break;
|
||||
}
|
||||
out.print("0xff" + ast.getText());
|
||||
dumpHiddenAfter(ast);
|
||||
break;
|
||||
|
||||
// allow for stuff like int(43.2).
|
||||
case CONSTRUCTOR_CAST:
|
||||
final AST terminalTypeNode = child1.getFirstChild();
|
||||
final AST exprToCast = child2;
|
||||
final String pooType = terminalTypeNode.getText();
|
||||
out.print("PApplet.parse" + Character.toUpperCase(pooType.charAt(0))
|
||||
+ pooType.substring(1));
|
||||
dumpHiddenAfter(terminalTypeNode); // the left paren
|
||||
print(exprToCast);
|
||||
break;
|
||||
|
||||
// making floating point literals default to floats, not doubles
|
||||
case NUM_DOUBLE:
|
||||
final String literalDouble = ast.getText().toLowerCase();
|
||||
out.print(literalDouble);
|
||||
if (Preferences.getBoolean("preproc.substitute_floats")
|
||||
&& literalDouble.indexOf('d') == -1) { // permit literal doubles
|
||||
out.print("f");
|
||||
}
|
||||
dumpHiddenAfter(ast);
|
||||
break;
|
||||
|
||||
case TYPE_ARGUMENTS:
|
||||
case TYPE_PARAMETERS:
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case TYPE_ARGUMENT:
|
||||
case TYPE_PARAMETER:
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case WILDCARD_TYPE:
|
||||
out.print(ast.getText());
|
||||
dumpHiddenAfter(ast);
|
||||
print(ast.getFirstChild());
|
||||
break;
|
||||
|
||||
case TYPE_LOWER_BOUNDS:
|
||||
case TYPE_UPPER_BOUNDS:
|
||||
out.print(ast.getType() == TYPE_LOWER_BOUNDS ? "super" : "extends");
|
||||
dumpHiddenBefore(getBestPrintableNode(ast, false));
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case ANNOTATION:
|
||||
out.print("@");
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case ANNOTATION_ARRAY_INIT:
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case ANNOTATION_MEMBER_VALUE_PAIR:
|
||||
print(ast.getFirstChild());
|
||||
out.print("=");
|
||||
dumpHiddenBefore(getBestPrintableNode(ast.getFirstChild().getNextSibling(), false));
|
||||
print(ast.getFirstChild().getNextSibling());
|
||||
break;
|
||||
|
||||
default:
|
||||
debug.println("Unrecognized type:" + ast.getType() + " ("
|
||||
+ TokenUtil.nameOf(ast) + ")");
|
||||
break;
|
||||
}
|
||||
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
}
|
||||
755
app/src/processing/mode/java/preproc/PdePreprocessor.java
Normal file
755
app/src/processing/mode/java/preproc/PdePreprocessor.java
Normal file
@@ -0,0 +1,755 @@
|
||||
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
PdePreprocessor - wrapper for default ANTLR-generated parser
|
||||
Part of the Processing project - http://processing.org
|
||||
|
||||
Copyright (c) 2004-10 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.mode.java.preproc;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import processing.app.Preferences;
|
||||
import processing.app.SketchException;
|
||||
import processing.app.antlr.PdeLexer;
|
||||
import processing.app.antlr.PdeRecognizer;
|
||||
import processing.app.antlr.PdeTokenTypes;
|
||||
import processing.core.PApplet;
|
||||
import antlr.*;
|
||||
import antlr.collections.AST;
|
||||
|
||||
/**
|
||||
* Class that orchestrates preprocessing p5 syntax into straight Java.
|
||||
* <P/>
|
||||
* <B>Current Preprocessor Subsitutions:</B>
|
||||
* <UL>
|
||||
* <LI>any function not specified as being protected or private will
|
||||
* be made 'public'. this means that <TT>void setup()</TT> becomes
|
||||
* <TT>public void setup()</TT>. This is important to note when
|
||||
* coding with core.jar outside of the PDE.
|
||||
* <LI><TT>compiler.substitute_floats</TT> (currently "substitute_f")
|
||||
* treat doubles as floats, i.e. 12.3 becomes 12.3f so that people
|
||||
* don't have to add f after their numbers all the time since it's
|
||||
* confusing for beginners.
|
||||
* <LI><TT>compiler.enhanced_casting</TT> byte(), char(), int(), float()
|
||||
* works for casting. this is basic in the current implementation, but
|
||||
* should be expanded as described above. color() works similarly to int(),
|
||||
* however there is also a *function* called color(r, g, b) in p5.
|
||||
* <LI><TT>compiler.color_datatype</TT> 'color' is aliased to 'int'
|
||||
* as a datatype to represent ARGB packed into a single int, commonly
|
||||
* used in p5 for pixels[] and other color operations. this is just a
|
||||
* search/replace type thing, and it can be used interchangeably with int.
|
||||
* <LI><TT>compiler.web_colors</TT> (currently "inline_web_colors")
|
||||
* color c = #cc0080; should unpack to 0xffcc0080 (the ff at the top is
|
||||
* so that the color is opaque), which is just an int.
|
||||
* </UL>
|
||||
* <B>Other preprocessor functionality</B>
|
||||
* <UL>
|
||||
* <LI>detects what 'mode' the program is in: static (no function
|
||||
* brackets at all, just assumes everything is in draw), active
|
||||
* (setup plus draw or loop), and java mode (full java support).
|
||||
* http://processing.org/reference/environment/
|
||||
* </UL>
|
||||
* <P/>
|
||||
* The PDE Preprocessor is based on the Java Grammar that comes with
|
||||
* ANTLR 2.7.2. Moving it forward to a new version of the grammar
|
||||
* shouldn't be too difficult.
|
||||
* <P/>
|
||||
* Here's some info about the various files in this directory:
|
||||
* <P/>
|
||||
* <TT>java.g:</TT> this is the ANTLR grammar for Java 1.3/1.4 from the
|
||||
* ANTLR distribution. It is in the public domain. The only change to
|
||||
* this file from the original this file is the uncommenting of the
|
||||
* clauses required to support assert().
|
||||
* <P/>
|
||||
* <TT>java.tree.g:</TT> this describes the Abstract Syntax Tree (AST)
|
||||
* generated by java.g. It is only here as a reference for coders hacking
|
||||
* on the preprocessor, it is not built or used at all. Note that pde.g
|
||||
* overrides some of the java.g rules so that in PDE ASTs, there are a
|
||||
* few minor differences. Also in the public domain.
|
||||
* <P/>
|
||||
* <TT>pde.g:</TT> this is the grammar and lexer for the PDE language
|
||||
* itself. It subclasses the java.g grammar and lexer. There are a couple
|
||||
* of overrides to java.g that I hope to convince the ANTLR folks to fold
|
||||
* back into their grammar, but most of this file is highly specific to
|
||||
* PDE itself.
|
||||
* <TT>PdeEmitter.java:</TT> this class traverses the AST generated by
|
||||
* the PDE Recognizer, and emits it as Java code, doing any necessary
|
||||
* transformations along the way. It is based on JavaEmitter.java,
|
||||
* available from antlr.org, written by Andy Tripp <atripp@comcast.net>,
|
||||
* who has given permission for it to be distributed under the GPL.
|
||||
* <P/>
|
||||
* <TT>ExtendedCommonASTWithHiddenTokens.java:</TT> this adds a necessary
|
||||
* initialize() method, as well as a number of methods to allow for XML
|
||||
* serialization of the parse tree in a such a way that the hidden tokens
|
||||
* are visible. Much of the code is taken from the original
|
||||
* CommonASTWithHiddenTokens class. I hope to convince the ANTLR folks
|
||||
* to fold these changes back into that class so that this file will be
|
||||
* unnecessary.
|
||||
* <P/>
|
||||
* <TT>TokenStreamCopyingHiddenTokenFilter.java:</TT> this class provides
|
||||
* TokenStreamHiddenTokenFilters with the concept of tokens which can be
|
||||
* copied so that they are seen by both the hidden token stream as well
|
||||
* as the parser itself. This is useful when one wants to use an
|
||||
* existing parser (like the Java parser included with ANTLR) that throws
|
||||
* away some tokens to create a parse tree which can be used to spit out
|
||||
* a copy of the code with only minor modifications. Partially derived
|
||||
* from ANTLR code. I hope to convince the ANTLR folks to fold this
|
||||
* functionality back into ANTLR proper as well.
|
||||
* <P/>
|
||||
* <TT>whitespace_test.pde:</TT> a torture test to ensure that the
|
||||
* preprocessor is correctly preserving whitespace, comments, and other
|
||||
* hidden tokens correctly. See the comments in the code for details about
|
||||
* how to run the test.
|
||||
* <P/>
|
||||
* All other files in this directory are generated at build time by ANTLR
|
||||
* itself. The ANTLR manual goes into a fair amount of detail about the
|
||||
* what each type of file is for.
|
||||
* <P/>
|
||||
*
|
||||
* Hacked to death in 2010 by
|
||||
* @author Jonathan Feinberg <jdf@pobox.com>
|
||||
*/
|
||||
public class PdePreprocessor {
|
||||
|
||||
// used for calling the ASTFactory to get the root node
|
||||
private static final int ROOT_ID = 0;
|
||||
|
||||
protected final String indent;
|
||||
private final String name;
|
||||
|
||||
public static enum Mode {
|
||||
STATIC, ACTIVE, JAVA
|
||||
}
|
||||
|
||||
private TokenStreamCopyingHiddenTokenFilter filter;
|
||||
|
||||
private boolean foundMain;
|
||||
|
||||
public void setFoundMain(boolean foundMain) {
|
||||
this.foundMain = foundMain;
|
||||
}
|
||||
|
||||
public boolean getFoundMain() {
|
||||
return foundMain;
|
||||
}
|
||||
|
||||
private String advClassName = "";
|
||||
|
||||
public void setAdvClassName(final String advClassName) {
|
||||
this.advClassName = advClassName;
|
||||
}
|
||||
|
||||
protected Mode mode;
|
||||
|
||||
public void setMode(final Mode mode) {
|
||||
// System.err.println("Setting mode to " + mode);
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public PdePreprocessor(final String sketchName) {
|
||||
this(sketchName, Preferences.getInteger("editor.tabs.size"));
|
||||
}
|
||||
|
||||
public PdePreprocessor(final String sketchName, final int tabSize) {
|
||||
this.name = sketchName;
|
||||
final char[] indentChars = new char[tabSize];
|
||||
Arrays.fill(indentChars, ' ');
|
||||
indent = new String(indentChars);
|
||||
}
|
||||
|
||||
CommonHiddenStreamToken getHiddenAfter(final CommonHiddenStreamToken t) {
|
||||
return filter.getHiddenAfter(t);
|
||||
}
|
||||
|
||||
CommonHiddenStreamToken getInitialHiddenToken() {
|
||||
return filter.getInitialHiddenToken();
|
||||
}
|
||||
|
||||
private static int countNewlines(final String s) {
|
||||
int count = 0;
|
||||
for (int pos = s.indexOf('\n', 0); pos >= 0; pos = s.indexOf('\n', pos + 1))
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
private static void checkForUnterminatedMultilineComment(final String program)
|
||||
throws SketchException {
|
||||
final int length = program.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
// for any double slash comments, ignore until the end of the line
|
||||
if ((program.charAt(i) == '/') && (i < length - 1)
|
||||
&& (program.charAt(i + 1) == '/')) {
|
||||
i += 2;
|
||||
while ((i < length) && (program.charAt(i) != '\n')) {
|
||||
i++;
|
||||
}
|
||||
// check to see if this is the start of a new multiline comment.
|
||||
// if it is, then make sure it's actually terminated somewhere.
|
||||
} else if ((program.charAt(i) == '/') && (i < length - 1)
|
||||
&& (program.charAt(i + 1) == '*')) {
|
||||
final int startOfComment = i;
|
||||
i += 2;
|
||||
boolean terminated = false;
|
||||
while (i < length - 1) {
|
||||
if ((program.charAt(i) == '*') && (program.charAt(i + 1) == '/')) {
|
||||
i += 2;
|
||||
terminated = true;
|
||||
break;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (!terminated) {
|
||||
throw new SketchException("Unclosed /* comment */", 0,
|
||||
countNewlines(program.substring(0,
|
||||
startOfComment)));
|
||||
}
|
||||
} else if (program.charAt(i) == '"') {
|
||||
final int stringStart = i;
|
||||
boolean terminated = false;
|
||||
for (i++; i < length; i++) {
|
||||
final char c = program.charAt(i);
|
||||
if (c == '"') {
|
||||
terminated = true;
|
||||
break;
|
||||
} else if (c == '\\') {
|
||||
if (i == length - 1) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
} else if (c == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!terminated) {
|
||||
throw new SketchException("Unterminated string constant", 0,
|
||||
countNewlines(program.substring(0,
|
||||
stringStart)));
|
||||
}
|
||||
} else if (program.charAt(i) == '\'') {
|
||||
i++;
|
||||
if (i >= length) {
|
||||
throw new SketchException("Unterminated character constant", 0,
|
||||
countNewlines(program.substring(0, i)));
|
||||
}
|
||||
if (program.charAt(i) == '\\') {
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
if (i >= length) {
|
||||
throw new SketchException("Unterminated character constant", 0,
|
||||
countNewlines(program.substring(0, i)));
|
||||
}
|
||||
if (program.charAt(i) != '\'') {
|
||||
throw new SketchException("Badly formed character constant", 0,
|
||||
countNewlines(program.substring(0, i)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public PreprocessorResult write(final Writer out, String program)
|
||||
throws SketchException, RecognitionException, TokenStreamException {
|
||||
return write(out, program, null);
|
||||
}
|
||||
|
||||
public PreprocessorResult write(Writer out, String program,
|
||||
String codeFolderPackages[])
|
||||
throws SketchException, RecognitionException, TokenStreamException {
|
||||
|
||||
// these ones have the .* at the end, since a class name might be at the end
|
||||
// instead of .* which would make trouble other classes using this can lop
|
||||
// off the . and anything after it to produce a package name consistently.
|
||||
final ArrayList<String> programImports = new ArrayList<String>();
|
||||
|
||||
// imports just from the code folder, treated differently
|
||||
// than the others, since the imports are auto-generated.
|
||||
final ArrayList<String> codeFolderImports = new ArrayList<String>();
|
||||
|
||||
// need to reset whether or not this has a main()
|
||||
foundMain = false;
|
||||
|
||||
// bug #5
|
||||
if (!program.endsWith("\n"))
|
||||
program += "\n";
|
||||
|
||||
checkForUnterminatedMultilineComment(program);
|
||||
|
||||
if (Preferences.getBoolean("preproc.substitute_unicode")) {
|
||||
program = substituteUnicode(program);
|
||||
}
|
||||
|
||||
final String importRegexp = "(?:^|;)\\s*(import\\s+)((?:static\\s+)?\\S+)(\\s*;)";
|
||||
|
||||
do {
|
||||
String[] pieces = PApplet.match(program, importRegexp);
|
||||
// Stop the loop if we've removed all the importy lines
|
||||
if (pieces == null)
|
||||
break;
|
||||
|
||||
String piece = pieces[1] + pieces[2] + pieces[3];
|
||||
int len = piece.length(); // how much to trim out
|
||||
|
||||
programImports.add(pieces[2]); // the package name
|
||||
|
||||
// find index of this import in the program
|
||||
int idx = program.indexOf(piece);
|
||||
|
||||
// Remove the import from the main program
|
||||
program = program.substring(0, idx) + program.substring(idx + len);
|
||||
} while (true);
|
||||
|
||||
if (codeFolderPackages != null) {
|
||||
for (String item : codeFolderPackages) {
|
||||
codeFolderImports.add(item + ".*");
|
||||
}
|
||||
}
|
||||
|
||||
final PrintWriter stream = new PrintWriter(out);
|
||||
final int headerOffset = writeImports(stream, programImports,
|
||||
codeFolderImports);
|
||||
return new PreprocessorResult(mode, headerOffset + 2, write(program, stream),
|
||||
programImports);
|
||||
}
|
||||
|
||||
static String substituteUnicode(String program) {
|
||||
// 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 (unicodeCount == 0)
|
||||
return program;
|
||||
// if non-ascii chars are in there, convert to unicode escapes
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
return new String(p2, 0, index);
|
||||
}
|
||||
|
||||
private static final Pattern PUBLIC_CLASS =
|
||||
Pattern.compile("(^|;)\\s*public\\s+class", Pattern.MULTILINE);
|
||||
|
||||
private static final Pattern FUNCTION_DECL =
|
||||
Pattern.compile("(^|;)\\s*((public|private|protected|final|static)\\s+)*" +
|
||||
"(void|int|float|double|String|char|byte)" +
|
||||
"(\\s*\\[\\s*\\])?\\s+[a-zA-Z0-9]+\\s*\\(",
|
||||
Pattern.MULTILINE);
|
||||
|
||||
/**
|
||||
* preprocesses a pde file and writes out a java file
|
||||
* @return the class name of the exported Java
|
||||
*/
|
||||
private String write(final String program, final PrintWriter stream)
|
||||
throws SketchException, RecognitionException, TokenStreamException {
|
||||
|
||||
PdeRecognizer parser = createParser(program);
|
||||
if (PUBLIC_CLASS.matcher(program).find()) {
|
||||
try {
|
||||
final PrintStream saved = System.err;
|
||||
try {
|
||||
// throw away stderr for this tentative parse
|
||||
System.setErr(new PrintStream(new ByteArrayOutputStream()));
|
||||
parser.javaProgram();
|
||||
} finally {
|
||||
System.setErr(saved);
|
||||
}
|
||||
setMode(Mode.JAVA);
|
||||
} catch (Exception e) {
|
||||
// I can't figure out any other way of resetting the parser.
|
||||
parser = createParser(program);
|
||||
parser.pdeProgram();
|
||||
}
|
||||
} else if (FUNCTION_DECL.matcher(program).find()) {
|
||||
setMode(Mode.ACTIVE);
|
||||
parser.activeProgram();
|
||||
} else {
|
||||
parser.pdeProgram();
|
||||
}
|
||||
|
||||
// set up the AST for traversal by PdeEmitter
|
||||
//
|
||||
ASTFactory factory = new ASTFactory();
|
||||
AST parserAST = parser.getAST();
|
||||
AST rootNode = factory.create(ROOT_ID, "AST ROOT");
|
||||
rootNode.setFirstChild(parserAST);
|
||||
|
||||
makeSimpleMethodsPublic(rootNode);
|
||||
|
||||
// unclear if this actually works, but it's worth a shot
|
||||
//
|
||||
//((CommonAST)parserAST).setVerboseStringConversion(
|
||||
// true, parser.getTokenNames());
|
||||
// (made to use the static version because of jikes 1.22 warning)
|
||||
CommonAST.setVerboseStringConversion(true, parser.getTokenNames());
|
||||
|
||||
final String className;
|
||||
if (mode == Mode.JAVA) {
|
||||
// if this is an advanced program, the classname is already defined.
|
||||
className = getFirstClassName(parserAST);
|
||||
} else {
|
||||
className = this.name;
|
||||
}
|
||||
|
||||
// if 'null' was passed in for the name, but this isn't
|
||||
// a 'java' mode class, then there's a problem, so punt.
|
||||
//
|
||||
if (className == null)
|
||||
return null;
|
||||
|
||||
// debug
|
||||
if (false) {
|
||||
final StringWriter buf = new StringWriter();
|
||||
final PrintWriter bufout = new PrintWriter(buf);
|
||||
writeDeclaration(bufout, className);
|
||||
new PdeEmitter(this, bufout).print(rootNode);
|
||||
writeFooter(bufout, className);
|
||||
debugAST(rootNode, true);
|
||||
System.err.println(buf.toString());
|
||||
}
|
||||
|
||||
writeDeclaration(stream, className);
|
||||
new PdeEmitter(this, stream).print(rootNode);
|
||||
writeFooter(stream, className);
|
||||
|
||||
// if desired, serialize the parse tree to an XML file. can
|
||||
// be viewed usefully with Mozilla or IE
|
||||
if (Preferences.getBoolean("preproc.output_parse_tree")) {
|
||||
writeParseTree("parseTree.xml", parserAST);
|
||||
}
|
||||
|
||||
return className;
|
||||
}
|
||||
|
||||
private PdeRecognizer createParser(final String program) {
|
||||
// create a lexer with the stream reader, and tell it to handle
|
||||
// hidden tokens (eg whitespace, comments) since we want to pass these
|
||||
// through so that the line numbers when the compiler reports errors
|
||||
// match those that will be highlighted in the PDE IDE
|
||||
//
|
||||
PdeLexer lexer = new PdeLexer(new StringReader(program));
|
||||
lexer.setTokenObjectClass("antlr.CommonHiddenStreamToken");
|
||||
|
||||
// create the filter for hidden tokens and specify which tokens to
|
||||
// hide and which to copy to the hidden text
|
||||
//
|
||||
filter = new TokenStreamCopyingHiddenTokenFilter(lexer);
|
||||
filter.hide(PdeRecognizer.SL_COMMENT);
|
||||
filter.hide(PdeRecognizer.ML_COMMENT);
|
||||
filter.hide(PdeRecognizer.WS);
|
||||
filter.copy(PdeRecognizer.SEMI);
|
||||
filter.copy(PdeRecognizer.LPAREN);
|
||||
filter.copy(PdeRecognizer.RPAREN);
|
||||
filter.copy(PdeRecognizer.LCURLY);
|
||||
filter.copy(PdeRecognizer.RCURLY);
|
||||
filter.copy(PdeRecognizer.COMMA);
|
||||
filter.copy(PdeRecognizer.RBRACK);
|
||||
filter.copy(PdeRecognizer.LBRACK);
|
||||
filter.copy(PdeRecognizer.COLON);
|
||||
filter.copy(PdeRecognizer.TRIPLE_DOT);
|
||||
|
||||
// Because the meanings of < and > are overloaded to support
|
||||
// type arguments and type parameters, we have to treat them
|
||||
// as copyable to hidden text (or else the following syntax,
|
||||
// such as (); and what not gets lost under certain circumstances)
|
||||
// -- jdf
|
||||
filter.copy(PdeRecognizer.LT);
|
||||
filter.copy(PdeRecognizer.GT);
|
||||
filter.copy(PdeRecognizer.SR);
|
||||
filter.copy(PdeRecognizer.BSR);
|
||||
|
||||
// create a parser and set what sort of AST should be generated
|
||||
//
|
||||
final PdeRecognizer parser = new PdeRecognizer(this, filter);
|
||||
|
||||
// use our extended AST class
|
||||
//
|
||||
parser.setASTNodeClass("antlr.ExtendedCommonASTWithHiddenTokens");
|
||||
return parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk the tree looking for METHOD_DEFs. Any simple METHOD_DEF (one
|
||||
* without TYPE_PARAMETERS) lacking an
|
||||
* access specifier is given public access.
|
||||
* @param node
|
||||
*/
|
||||
private void makeSimpleMethodsPublic(final AST node) {
|
||||
if (node.getType() == PdeTokenTypes.METHOD_DEF) {
|
||||
final AST mods = node.getFirstChild();
|
||||
final AST oldFirstMod = mods.getFirstChild();
|
||||
for (AST mod = oldFirstMod; mod != null; mod = mod.getNextSibling()) {
|
||||
final int t = mod.getType();
|
||||
if (t == PdeTokenTypes.LITERAL_private ||
|
||||
t == PdeTokenTypes.LITERAL_protected ||
|
||||
t == PdeTokenTypes.LITERAL_public) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mods.getNextSibling().getType() == PdeTokenTypes.TYPE_PARAMETERS) {
|
||||
return;
|
||||
}
|
||||
final CommonHiddenStreamToken publicToken =
|
||||
new CommonHiddenStreamToken(PdeTokenTypes.LITERAL_public, "public") {
|
||||
{
|
||||
setHiddenAfter(new CommonHiddenStreamToken(PdeTokenTypes.WS, " "));
|
||||
}
|
||||
};
|
||||
final AST publicNode = new CommonASTWithHiddenTokens(publicToken);
|
||||
publicNode.setNextSibling(oldFirstMod);
|
||||
mods.setFirstChild(publicNode);
|
||||
} else {
|
||||
for (AST kid = node.getFirstChild(); kid != null; kid = kid
|
||||
.getNextSibling())
|
||||
makeSimpleMethodsPublic(kid);
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeParseTree(String filename, AST ast) {
|
||||
try {
|
||||
PrintStream stream = new PrintStream(new FileOutputStream(filename));
|
||||
stream.println("<?xml version=\"1.0\"?>");
|
||||
stream.println("<document>");
|
||||
OutputStreamWriter writer = new OutputStreamWriter(stream);
|
||||
if (ast != null) {
|
||||
((CommonAST) ast).xmlSerialize(writer);
|
||||
}
|
||||
writer.flush();
|
||||
stream.println("</document>");
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param out
|
||||
* @param programImports
|
||||
* @param codeFolderImports
|
||||
* @return the header offset
|
||||
*/
|
||||
protected int writeImports(final PrintWriter out,
|
||||
final List<String> programImports,
|
||||
final List<String> codeFolderImports) {
|
||||
int count = writeImportList(out, getCoreImports());
|
||||
count += writeImportList(out, programImports);
|
||||
count += writeImportList(out, codeFolderImports);
|
||||
count += writeImportList(out, getDefaultImports());
|
||||
return count;
|
||||
}
|
||||
|
||||
protected int writeImportList(PrintWriter out, List<String> imports) {
|
||||
return writeImportList(out, (String[]) imports.toArray(new String[0]));
|
||||
}
|
||||
|
||||
protected int writeImportList(PrintWriter out, String[] imports) {
|
||||
int count = 0;
|
||||
if (imports != null && imports.length != 0) {
|
||||
for (String item : imports) {
|
||||
out.println("import " + item + "; ");
|
||||
count++;
|
||||
}
|
||||
out.println();
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write any required header material (eg imports, class decl stuff)
|
||||
*
|
||||
* @param out PrintStream to write it to.
|
||||
* @param exporting Is this being exported from PDE?
|
||||
* @param className Name of the class being created.
|
||||
*/
|
||||
protected void writeDeclaration(PrintWriter out, String className) {
|
||||
if (mode == Mode.JAVA) {
|
||||
// Print two blank lines so that the offset doesn't change
|
||||
out.println();
|
||||
out.println();
|
||||
|
||||
} else if (mode == Mode.ACTIVE) {
|
||||
// Print an extra blank line so the offset is identical to the others
|
||||
out.println("public class " + className + " extends PApplet {");
|
||||
out.println();
|
||||
|
||||
} else if (mode == Mode.STATIC) {
|
||||
out.println("public class " + className + " extends PApplet {");
|
||||
out.println(indent + "public void setup() {");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write any necessary closing text.
|
||||
*
|
||||
* @param out PrintStream to write it to.
|
||||
*/
|
||||
protected void writeFooter(PrintWriter out, String className) {
|
||||
if (mode == Mode.STATIC) {
|
||||
// close off draw() definition
|
||||
out.println(indent + "noLoop();");
|
||||
out.println("} ");
|
||||
}
|
||||
|
||||
if ((mode == Mode.STATIC) || (mode == Mode.ACTIVE)) {
|
||||
if (!foundMain) {
|
||||
out.println(indent + "static public void main(String args[]) {");
|
||||
out.print(indent + indent + "PApplet.main(new String[] { ");
|
||||
|
||||
if (Preferences.getBoolean("export.application.fullscreen")) {
|
||||
out.print("\"" + PApplet.ARGS_PRESENT + "\", ");
|
||||
|
||||
String farbe = Preferences.get("run.present.bgcolor");
|
||||
out.print("\"" + PApplet.ARGS_BGCOLOR + "=" + farbe + "\", ");
|
||||
|
||||
if (Preferences.getBoolean("export.application.stop")) {
|
||||
farbe = Preferences.get("run.present.stop.color");
|
||||
out.print("\"" + PApplet.ARGS_STOP_COLOR + "=" + farbe + "\", ");
|
||||
} else {
|
||||
out.print("\"" + PApplet.ARGS_HIDE_STOP + "\", ");
|
||||
}
|
||||
} else {
|
||||
String farbe = Preferences.get("run.window.bgcolor");
|
||||
out.print("\"" + PApplet.ARGS_BGCOLOR + "=" + farbe + "\", ");
|
||||
}
|
||||
out.println("\"" + className + "\" });");
|
||||
out.println(indent + "}");
|
||||
}
|
||||
|
||||
// close off the class definition
|
||||
out.println("}");
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getCoreImports() {
|
||||
return new String[] { "processing.core.*", "processing.xml.*" };
|
||||
}
|
||||
|
||||
public String[] getDefaultImports() {
|
||||
// These may change in-between (if the prefs panel adds this option)
|
||||
String prefsLine = Preferences.get("preproc.imports.list");
|
||||
return PApplet.splitTokens(prefsLine, ", ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first CLASS_DEF node in the tree, and return the name of the
|
||||
* class in question.
|
||||
*
|
||||
* TODO [dmose] 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;
|
||||
}
|
||||
|
||||
public void debugAST(final AST ast, final boolean includeHidden) {
|
||||
System.err.println("------------------");
|
||||
debugAST(ast, includeHidden, 0);
|
||||
}
|
||||
|
||||
private void debugAST(final AST ast, final boolean includeHidden,
|
||||
final int indent) {
|
||||
for (int i = 0; i < indent; i++)
|
||||
System.err.print(" ");
|
||||
if (includeHidden) {
|
||||
System.err.print(debugHiddenBefore(ast));
|
||||
}
|
||||
if (ast.getType() > 0 && !ast.getText().equals(TokenUtil.nameOf(ast))) {
|
||||
System.err.print(TokenUtil.nameOf(ast) + "/");
|
||||
}
|
||||
System.err.print(ast.getText().replace("\n", "\\n"));
|
||||
if (includeHidden) {
|
||||
System.err.print(debugHiddenAfter(ast));
|
||||
}
|
||||
System.err.println();
|
||||
for (AST kid = ast.getFirstChild(); kid != null; kid = kid.getNextSibling())
|
||||
debugAST(kid, includeHidden, indent + 1);
|
||||
}
|
||||
|
||||
private String debugHiddenAfter(AST ast) {
|
||||
if (!(ast instanceof antlr.CommonASTWithHiddenTokens))
|
||||
return "";
|
||||
return debugHiddenTokens(((antlr.CommonASTWithHiddenTokens) ast)
|
||||
.getHiddenAfter());
|
||||
}
|
||||
|
||||
private String debugHiddenBefore(AST ast) {
|
||||
if (!(ast instanceof antlr.CommonASTWithHiddenTokens))
|
||||
return "";
|
||||
antlr.CommonHiddenStreamToken child = null, parent = ((antlr.CommonASTWithHiddenTokens) ast)
|
||||
.getHiddenBefore();
|
||||
|
||||
if (parent == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
do {
|
||||
child = parent;
|
||||
parent = child.getHiddenBefore();
|
||||
} while (parent != null);
|
||||
|
||||
return debugHiddenTokens(child);
|
||||
}
|
||||
|
||||
private String debugHiddenTokens(antlr.CommonHiddenStreamToken t) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (; t != null; t = filter.getHiddenAfter(t)) {
|
||||
if (sb.length() == 0)
|
||||
sb.append("[");
|
||||
sb.append(t.getText().replace("\n", "\\n"));
|
||||
}
|
||||
if (sb.length() > 0)
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
32
app/src/processing/mode/java/preproc/PreprocessorResult.java
Normal file
32
app/src/processing/mode/java/preproc/PreprocessorResult.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package processing.mode.java.preproc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import processing.app.SketchException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jonathan Feinberg <jdf@pobox.com>
|
||||
*
|
||||
*/
|
||||
public class PreprocessorResult {
|
||||
public final int headerOffset;
|
||||
public final String className;
|
||||
public final List<String> extraImports;
|
||||
public final PdePreprocessor.Mode programType;
|
||||
|
||||
public PreprocessorResult(PdePreprocessor.Mode programType,
|
||||
int headerOffset, String className,
|
||||
final List<String> extraImports) throws SketchException {
|
||||
if (className == null) {
|
||||
throw new SketchException("Could not find main class");
|
||||
}
|
||||
this.headerOffset = headerOffset;
|
||||
this.className = className;
|
||||
this.extraImports = Collections.unmodifiableList(new ArrayList<String>(extraImports));
|
||||
this.programType = programType;
|
||||
}
|
||||
|
||||
}
|
||||
30
app/src/processing/mode/java/preproc/TokenUtil.java
Normal file
30
app/src/processing/mode/java/preproc/TokenUtil.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package processing.mode.java.preproc;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import antlr.collections.AST;
|
||||
import processing.app.antlr.PdeTokenTypes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jonathan Feinberg <jdf@pobox.com>
|
||||
*
|
||||
*/
|
||||
public class TokenUtil {
|
||||
private static final String[] tokenNames= new String[200];
|
||||
static {
|
||||
for (int i = 0; i < tokenNames.length; i++) {
|
||||
tokenNames[i] = "ERROR:" + i;
|
||||
}
|
||||
for (final Field f : PdeTokenTypes.class.getDeclaredFields()) {
|
||||
try {
|
||||
tokenNames[f.getInt(null)] = f.getName();
|
||||
} catch (Exception unexpected) {
|
||||
throw new RuntimeException(unexpected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String nameOf(final AST node) {
|
||||
return tokenNames[node.getType()];
|
||||
}
|
||||
}
|
||||
1832
app/src/processing/mode/java/preproc/java15.g
Normal file
1832
app/src/processing/mode/java/preproc/java15.g
Normal file
File diff suppressed because it is too large
Load Diff
393
app/src/processing/mode/java/preproc/pde.g
Normal file
393
app/src/processing/mode/java/preproc/pde.g
Normal file
@@ -0,0 +1,393 @@
|
||||
/* -*- mode: antlr; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
header {
|
||||
package processing.mode.java.preproc;
|
||||
}
|
||||
|
||||
class PdeRecognizer extends JavaRecognizer;
|
||||
|
||||
options {
|
||||
importVocab = Java;
|
||||
exportVocab = PdePartial;
|
||||
|
||||
//codeGenMakeSwitchThreshold=10; // this is set high for debugging
|
||||
//codeGenBitsetTestThreshold=10; // this is set high for debugging
|
||||
|
||||
// developers may to want to set this to true for better
|
||||
// debugging messages, however, doing so disables highlighting errors
|
||||
// in the editor.
|
||||
defaultErrorHandler = false; //true;
|
||||
}
|
||||
|
||||
tokens {
|
||||
CONSTRUCTOR_CAST; EMPTY_FIELD;
|
||||
}
|
||||
|
||||
{
|
||||
// this clause copied from java15.g! ANTLR does not copy this
|
||||
// section from the super grammar.
|
||||
/**
|
||||
* Counts the number of LT seen in the typeArguments production.
|
||||
* It is used in semantic predicates to ensure we have seen
|
||||
* enough closing '>' characters; which actually may have been
|
||||
* either GT, SR or BSR tokens.
|
||||
*/
|
||||
private int ltCounter = 0;
|
||||
|
||||
private PdePreprocessor pp;
|
||||
public PdeRecognizer(final PdePreprocessor pp, final TokenStream ts) {
|
||||
this(ts);
|
||||
this.pp = pp;
|
||||
}
|
||||
|
||||
private void mixed() throws RecognitionException, TokenStreamException {
|
||||
throw new RecognitionException("It looks like you're mixing \"active\" and \"static\" modes.",
|
||||
getFilename(), LT(1).getLine(), LT(1).getColumn());
|
||||
}
|
||||
}
|
||||
|
||||
pdeProgram
|
||||
:
|
||||
// Some programs can be equally well interpreted as STATIC or ACTIVE;
|
||||
// this forces the parser to prefer the STATIC interpretation.
|
||||
(staticProgram) => staticProgram
|
||||
{ pp.setMode(PdePreprocessor.Mode.STATIC); }
|
||||
|
||||
| (activeProgram) => activeProgram
|
||||
{ pp.setMode(PdePreprocessor.Mode.ACTIVE); }
|
||||
|
||||
| staticProgram
|
||||
{ pp.setMode(PdePreprocessor.Mode.STATIC); }
|
||||
;
|
||||
|
||||
// advanced mode is really just a normal java file
|
||||
javaProgram
|
||||
: compilationUnit
|
||||
;
|
||||
|
||||
activeProgram
|
||||
: (
|
||||
(IDENT LPAREN) => IDENT LPAREN { mixed(); }
|
||||
| possiblyEmptyField
|
||||
)+ EOF!
|
||||
;
|
||||
|
||||
staticProgram
|
||||
: (
|
||||
statement
|
||||
)* EOF!
|
||||
;
|
||||
|
||||
// copy of the java.g rule with WEBCOLOR_LITERAL added
|
||||
constant
|
||||
: NUM_INT
|
||||
| CHAR_LITERAL
|
||||
| STRING_LITERAL
|
||||
| NUM_FLOAT
|
||||
| NUM_LONG
|
||||
| NUM_DOUBLE
|
||||
| webcolor_literal
|
||||
;
|
||||
|
||||
// fix bug http://dev.processing.org/bugs/show_bug.cgi?id=1519
|
||||
// by altering a syntactic predicate whose sole purpose is to
|
||||
// emit a useless error with no line numbers.
|
||||
// These are from Java15.g, with a few lines edited to make nice errors.
|
||||
|
||||
// Type arguments to a class or interface type
|
||||
typeArguments
|
||||
{int currentLtLevel = 0;}
|
||||
:
|
||||
{currentLtLevel = ltCounter;}
|
||||
LT! {ltCounter++;}
|
||||
typeArgument
|
||||
(options{greedy=true;}: // match as many as possible
|
||||
{if (! (inputState.guessing !=0 || ltCounter == currentLtLevel + 1)) {
|
||||
throw new RecognitionException("Maybe too many > characters?",
|
||||
getFilename(), LT(1).getLine(), LT(1).getColumn());
|
||||
}}
|
||||
COMMA! typeArgument
|
||||
)*
|
||||
|
||||
( // turn warning off since Antlr generates the right code,
|
||||
// plus we have our semantic predicate below
|
||||
options{generateAmbigWarnings=false;}:
|
||||
typeArgumentsOrParametersEnd
|
||||
)?
|
||||
|
||||
// make sure we have gobbled up enough '>' characters
|
||||
// if we are at the "top level" of nested typeArgument productions
|
||||
{if (! ((currentLtLevel != 0) || ltCounter == currentLtLevel)) {
|
||||
throw new RecognitionException("Maybe too many > characters?",
|
||||
getFilename(), LT(1).getLine(), LT(1).getColumn());
|
||||
}}
|
||||
|
||||
{#typeArguments = #(#[TYPE_ARGUMENTS, "TYPE_ARGUMENTS"], #typeArguments);}
|
||||
;
|
||||
|
||||
typeParameters
|
||||
{int currentLtLevel = 0;}
|
||||
:
|
||||
{currentLtLevel = ltCounter;}
|
||||
LT! {ltCounter++;}
|
||||
typeParameter (COMMA! typeParameter)*
|
||||
(typeArgumentsOrParametersEnd)?
|
||||
|
||||
// make sure we have gobbled up enough '>' characters
|
||||
// if we are at the "top level" of nested typeArgument productions
|
||||
{if (! ((currentLtLevel != 0) || ltCounter == currentLtLevel)) {
|
||||
throw new RecognitionException("Maybe too many > characters?",
|
||||
getFilename(), LT(1).getLine(), LT(1).getColumn());
|
||||
}}
|
||||
|
||||
{#typeParameters = #(#[TYPE_PARAMETERS, "TYPE_PARAMETERS"], #typeParameters);}
|
||||
;
|
||||
|
||||
|
||||
// this gobbles up *some* amount of '>' characters, and counts how many
|
||||
// it gobbled.
|
||||
protected typeArgumentsOrParametersEnd
|
||||
: GT! {ltCounter-=1;}
|
||||
| SR! {ltCounter-=2;}
|
||||
| BSR! {ltCounter-=3;}
|
||||
;
|
||||
|
||||
// of the form #cc008f in PDE
|
||||
webcolor_literal
|
||||
: w:WEBCOLOR_LITERAL
|
||||
{ if (! (processing.app.Preferences.getBoolean("preproc.web_colors")
|
||||
&&
|
||||
w.getText().length() == 6)) {
|
||||
throw new RecognitionException("Web colors must be exactly 6 hex digits. This looks like " + w.getText().length() + ".",
|
||||
getFilename(), LT(1).getLine(), LT(1).getColumn());
|
||||
}} // must be exactly 6 hex digits
|
||||
;
|
||||
|
||||
// copy of the java.g builtInType rule
|
||||
builtInConsCastType
|
||||
: "void"
|
||||
| "boolean"
|
||||
| "byte"
|
||||
| "char"
|
||||
| "short"
|
||||
| "int"
|
||||
| "float"
|
||||
| "long"
|
||||
| "double"
|
||||
;
|
||||
|
||||
// our types include the java types and "color". this is separated into two
|
||||
// rules so that constructor casts can just use the original typelist, since
|
||||
// we don't want to support the color type as a constructor cast.
|
||||
//
|
||||
builtInType
|
||||
: builtInConsCastType
|
||||
| "color" // aliased to an int in PDE
|
||||
{ processing.app.Preferences.getBoolean("preproc.color_datatype") }?
|
||||
;
|
||||
|
||||
// constructor style casts.
|
||||
constructorCast!
|
||||
: t:consCastTypeSpec[true]
|
||||
LPAREN!
|
||||
e:expression
|
||||
RPAREN!
|
||||
// if this is a string literal, make sure the type we're trying to cast
|
||||
// to is one of the supported ones
|
||||
//
|
||||
{ #e.getType() != STRING_LITERAL ||
|
||||
( #t.getType() == LITERAL_byte ||
|
||||
#t.getType() == LITERAL_double ||
|
||||
#t.getType() == LITERAL_float ||
|
||||
#t.getType() == LITERAL_int ||
|
||||
#t.getType() == LITERAL_long ||
|
||||
#t.getType() == LITERAL_short ) }?
|
||||
// create the node
|
||||
//
|
||||
{#constructorCast = #(#[CONSTRUCTOR_CAST,"CONSTRUCTOR_CAST"], t, e);}
|
||||
;
|
||||
|
||||
// A list of types that be used as the destination type in a constructor-style
|
||||
// cast. Ideally, this would include all class types, not just "String".
|
||||
// Unfortunately, it's not possible to tell whether Foo(5) is supposed to be
|
||||
// a method call or a constructor cast without have a table of all valid
|
||||
// types or methods, which requires semantic analysis (eg processing of import
|
||||
// statements). So we accept the set of built-in types plus "String".
|
||||
//
|
||||
consCastTypeSpec[boolean addImagNode]
|
||||
// : stringTypeSpec[addImagNode]
|
||||
// | builtInConsCastTypeSpec[addImagNode]
|
||||
: builtInConsCastTypeSpec[addImagNode]
|
||||
// trying to remove String() cast [fry]
|
||||
;
|
||||
|
||||
//stringTypeSpec[boolean addImagNode]
|
||||
// : id:IDENT { #id.getText().equals("String") }?
|
||||
// {
|
||||
// if ( addImagNode ) {
|
||||
// #stringTypeSpec = #(#[TYPE,"TYPE"],
|
||||
// #stringTypeSpec);
|
||||
// }
|
||||
// }
|
||||
// ;
|
||||
|
||||
builtInConsCastTypeSpec[boolean addImagNode]
|
||||
: builtInConsCastType
|
||||
{
|
||||
if ( addImagNode ) {
|
||||
#builtInConsCastTypeSpec = #(#[TYPE,"TYPE"],
|
||||
#builtInConsCastTypeSpec);
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
// Since "color" tokens are lexed as LITERAL_color now, we need to have a rule
|
||||
// that can generate a method call from an expression that starts with this
|
||||
// token
|
||||
//
|
||||
colorMethodCall
|
||||
: c:"color" {#c.setType(IDENT);} // this would default to LITERAL_color
|
||||
lp:LPAREN^ {#lp.setType(METHOD_CALL);}
|
||||
argList
|
||||
RPAREN!
|
||||
;
|
||||
|
||||
// copy of the java.g rule with added constructorCast and colorMethodCall
|
||||
// alternatives
|
||||
primaryExpression
|
||||
: (consCastTypeSpec[false] LPAREN) => constructorCast
|
||||
{ processing.app.Preferences.getBoolean("preproc.enhanced_casting") }?
|
||||
| identPrimary ( options {greedy=true;} : DOT^ "class" )?
|
||||
| constant
|
||||
| "true"
|
||||
| "false"
|
||||
| "null"
|
||||
| newExpression
|
||||
| "this"
|
||||
| "super"
|
||||
| LPAREN! assignmentExpression RPAREN!
|
||||
| colorMethodCall
|
||||
// look for int.class and int[].class
|
||||
| builtInType
|
||||
( lbt:LBRACK^ {#lbt.setType(ARRAY_DECLARATOR);} RBRACK! )*
|
||||
DOT^ "class"
|
||||
;
|
||||
|
||||
// the below variable rule hacks are needed so that it's possible for the
|
||||
// emitter to correctly output variable declarations of the form "float a, b"
|
||||
// from the AST. This means that our AST has a somewhat different form in
|
||||
// these rules than the java one does, and this new form may have its own
|
||||
// semantic issues. But it seems to fix the comma declaration issues.
|
||||
//
|
||||
variableDefinitions![AST mods, AST t]
|
||||
: vd:variableDeclarator[getASTFactory().dupTree(mods),
|
||||
getASTFactory().dupTree(t)]
|
||||
{#variableDefinitions = #(#[VARIABLE_DEF,"VARIABLE_DEF"], mods,
|
||||
t, vd);}
|
||||
;
|
||||
variableDeclarator[AST mods, AST t]
|
||||
: ( id:IDENT (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)*
|
||||
v:varInitializer (COMMA!)? )+
|
||||
;
|
||||
|
||||
// java.g builds syntax trees with an inconsistent structure. override one of
|
||||
// the rules there to fix this.
|
||||
//
|
||||
explicitConstructorInvocation!
|
||||
: (typeArguments)?
|
||||
t:"this" LPAREN a1:argList RPAREN SEMI
|
||||
{#explicitConstructorInvocation = #(#[CTOR_CALL, "CTOR_CALL"],
|
||||
#t, #a1);}
|
||||
| s:"super" LPAREN a2:argList RPAREN SEMI
|
||||
{#explicitConstructorInvocation = #(#[SUPER_CTOR_CALL,
|
||||
"SUPER_CTOR_CALL"],
|
||||
#s, #a2);}
|
||||
;
|
||||
|
||||
// quick-n-dirty hack to the get the advanced class name. we should
|
||||
// really be getting it from the AST and not forking this rule from
|
||||
// the java.g copy at all. Since this is a recursive descent parser, we get
|
||||
// the last class name in the file so that we don't end up with the classname
|
||||
// of an inner class. If there is more than one "outer" class in a file,
|
||||
// this heuristic will fail.
|
||||
//
|
||||
classDefinition![AST modifiers]
|
||||
: "class" i:IDENT
|
||||
// it _might_ have type paramaters
|
||||
(tp:typeParameters)?
|
||||
// it _might_ have a superclass...
|
||||
sc:superClassClause
|
||||
// it might implement some interfaces...
|
||||
ic:implementsClause
|
||||
// now parse the body of the class
|
||||
cb:classBlock
|
||||
{#classDefinition = #(#[CLASS_DEF,"CLASS_DEF"],
|
||||
modifiers,i,tp,sc,ic,cb);
|
||||
pp.setAdvClassName(i.getText());}
|
||||
;
|
||||
|
||||
possiblyEmptyField
|
||||
: classField
|
||||
| s:SEMI {#s.setType(EMPTY_FIELD);}
|
||||
;
|
||||
|
||||
class PdeLexer extends JavaLexer;
|
||||
|
||||
options {
|
||||
importVocab=PdePartial;
|
||||
exportVocab=Pde;
|
||||
}
|
||||
|
||||
// We need to preserve whitespace and commentary instead of ignoring
|
||||
// like the supergrammar does. Otherwise Jikes won't be able to give
|
||||
// us error messages that point to the equivalent PDE code.
|
||||
|
||||
// WS, SL_COMMENT, ML_COMMENT are copies of the original productions,
|
||||
// but with the SKIP assigment removed.
|
||||
|
||||
WS : ( ' '
|
||||
| '\t'
|
||||
| '\f'
|
||||
// handle newlines
|
||||
| ( options {generateAmbigWarnings=false;}
|
||||
: "\r\n" // Evil DOS
|
||||
| '\r' // Macintosh
|
||||
| '\n' // Unix (the right way)
|
||||
)
|
||||
{ newline(); }
|
||||
)+
|
||||
;
|
||||
|
||||
// Single-line comments
|
||||
SL_COMMENT
|
||||
: "//"
|
||||
(~('\n'|'\r'))* ('\n'|'\r'('\n')?)
|
||||
{newline();}
|
||||
;
|
||||
|
||||
// multiple-line comments
|
||||
ML_COMMENT
|
||||
: "/*"
|
||||
( /* '\r' '\n' can be matched in one alternative or by matching
|
||||
'\r' in one iteration and '\n' in another. I am trying to
|
||||
handle any flavor of newline that comes in, but the language
|
||||
that allows both "\r\n" and "\r" and "\n" to all be valid
|
||||
newline is ambiguous. Consequently, the resulting grammar
|
||||
must be ambiguous. I'm shutting this warning off.
|
||||
*/
|
||||
options {
|
||||
generateAmbigWarnings=false;
|
||||
}
|
||||
:
|
||||
{ LA(2)!='/' }? '*'
|
||||
| '\r' '\n' {newline();}
|
||||
| '\r' {newline();}
|
||||
| '\n' {newline();}
|
||||
| ~('*'|'\n'|'\r')
|
||||
)*
|
||||
"*/"
|
||||
;
|
||||
|
||||
WEBCOLOR_LITERAL
|
||||
: '#'! (HEX_DIGIT)+
|
||||
;
|
||||
|
||||
Reference in New Issue
Block a user