cvs cleanup

This commit is contained in:
benfry
2005-04-09 02:30:37 +00:00
parent 587a8b3a98
commit 82ee726e8d
31 changed files with 9803 additions and 139 deletions

View File

@@ -1,123 +0,0 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
PdeCompilerJavac - compiler interface to kjc.. someday this will go away
Part of the Processing project - http://processing.org
Except where noted, code is written by Ben Fry and
Copyright (c) 2001-03 Massachusetts Institute of Technology
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
*/
/*
import java.io.*;
// this class is incomplete. it was added in an attempt to support
// mac os 9, but the javac for jdk 1.1 doesn't seem to like the
// file format of the classes produced by jikes, which is revision
// 57 code instead of rev 55.
// using jikes -target 1.1 doesn't seem to help. the next attempt
// was to use javac from the 1.1 jdk, which i attempted to hack
// into the bagel build scripts, but javac actually crashed (not
// just and exception, but an application crash) whenever used
// (especially if called on long paths/from another directory).
// there are plenty of ways to address this, but for the time being,
// it's not worth the time so we'll just suspend macos9 support.
public class PdeCompilerJavac extends PdeCompiler {
public PdeCompilerJavac(String buildPath, String className,
File includeFolder, PdeEditor editor) {
super(buildPath, className, includeFolder, editor);
}
public boolean compileJava(PrintStream leechErr) {
// probably not needed with javac
//System.setErr(leechErr); // redirect stderr to our leech filter
int argc = 0;
String args[] = new String[4];
args[argc++] = "-d";
args[argc++] = buildPath;
args[argc++] = "-nowarn";
args[argc++] = buildPath + File.separator + className + ".java";
//System.out.println("args = " + args[0] + " " + args[1]);
System.out.println("compiling with javac");
return new sun.tools.javac.Main(leechErr, "javac").compile(args);
// probably not needed with javac
//System.setErr(PdeEditorConsole.consoleErr);
//System.err.println("success = " + success);
//return success;
}
// part of the PdeMessageConsumer interface
//
public void message(String s) {
System.out.println("javac msg: " + s);
// as in: lib\build\Temporary_5476_6442.java:88: caution:Assignment of an expression to itself [KOPI]
if (s.indexOf("caution") != -1) return;
//
//System.out.println("leech2: " + new String(b, offset, length));
//String s = new String(b, offset, length);
//if (s.indexOf(tempFilename) == 0) {
String fullTempFilename = buildPath + File.separator + className + ".java";
if (s.indexOf(fullTempFilename) == 0) {
String s1 = s.substring(fullTempFilename.length() + 1);
int colon = s1.indexOf(':');
int lineNumber = Integer.parseInt(s1.substring(0, colon));
//System.out.println("pde / line number: " + lineNumber);
//String s2 = s1.substring(colon + 2);
int err = s1.indexOf("error:");
if (err != -1) {
//err += "error:".length();
String description = s1.substring(err + "error:".length());
description = description.trim();
// as in: ...error:Constructor setup must be named Temporary_5362_2548 [JL1 8.6]
if(description.indexOf("Constructor setup must be named") != -1) {
description = "Missing function return type, or constructor does not match class name";
}
//exception = new PdeException(description, lineNumber-2);
exception = new PdeException(description, lineNumber-1);
editor.error(exception);
} else {
System.err.println("i suck: " + s);
}
} else {
//System.err.println("don't understand: " + s);
exception = new PdeException(SUPER_BADNESS);
editor.error(exception);
}
}
}
*/

9
app/preproc/.cvsignore Normal file
View File

@@ -0,0 +1,9 @@
*Lexer.java
*Recognizer.java
*TokenTypes.java
*TokenTypes.txt
*TreeParser.java
*TreeParserTokenTypes.java
*TreeParserTokenTypes.txt
expanded*.g

View File

@@ -0,0 +1,132 @@
package antlr;
/* ANTLR Translator Generator
* Project led by Terence Parr at http://www.jGuru.com
* Software rights: http://www.antlr.org/RIGHTS.html
*
* $Id$
*/
import java.io.*;
import antlr.*;
import antlr.collections.*;
import antlr.collections.impl.*;
/** A CommonAST whose initialization copies hidden token
* information from the Token used to create a node.
*/
public class ExtendedCommonASTWithHiddenTokens
extends CommonASTWithHiddenTokens {
public ExtendedCommonASTWithHiddenTokens() {
super();
}
public ExtendedCommonASTWithHiddenTokens(Token tok) {
super(tok);
}
public void initialize(AST ast) {
ExtendedCommonASTWithHiddenTokens a =
(ExtendedCommonASTWithHiddenTokens)ast;
super.initialize(a);
hiddenBefore = a.getHiddenBefore();
hiddenAfter = a.getHiddenAfter();
}
public String getHiddenAfterString() {
CommonHiddenStreamToken t;
StringBuffer hiddenAfterString = new StringBuffer(100);
for ( t = hiddenAfter ; t != null ; t = t.getHiddenAfter() ) {
hiddenAfterString.append(t.getText());
}
return hiddenAfterString.toString();
}
public String getHiddenBeforeString() {
antlr.CommonHiddenStreamToken
child = null,
parent = hiddenBefore;
// 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
StringBuffer hiddenBeforeString = new StringBuffer(100);
for ( CommonHiddenStreamToken t = child; t != null ;
t = t.getHiddenAfter() ) {
hiddenBeforeString.append(t.getText());
}
return hiddenBeforeString.toString();
}
public void xmlSerializeNode(Writer out)
throws IOException {
StringBuffer buf = new StringBuffer(100);
buf.append("<");
buf.append(getClass().getName() + " ");
buf.append("hiddenBeforeString=\"" +
encode(getHiddenBeforeString()) +
"\" text=\"" + encode(getText()) + "\" type=\"" +
getType() + "\" hiddenAfterString=\"" +
encode(getHiddenAfterString()) + "\"/>");
out.write(buf.toString());
}
public void xmlSerializeRootOpen(Writer out)
throws IOException {
StringBuffer buf = new StringBuffer(100);
buf.append("<");
buf.append(getClass().getName() + " ");
buf.append("hiddenBeforeString=\"" +
encode(getHiddenBeforeString()) +
"\" text=\"" + encode(getText()) + "\" type=\"" +
getType() + "\" hiddenAfterString=\"" +
encode(getHiddenAfterString()) + "\">\n");
out.write(buf.toString());
}
public void xmlSerializeRootClose(Writer out)
throws IOException {
out.write("</" + getClass().getName() + ">\n");
}
public void xmlSerialize(Writer out) throws IOException {
// print out this node and all siblings
for (AST node = this;
node != null;
node = node.getNextSibling()) {
if (node.getFirstChild() == null) {
// print guts (class name, attributes)
((BaseAST)node).xmlSerializeNode(out);
}
else {
((BaseAST)node).xmlSerializeRootOpen(out);
// print children
((BaseAST)node.getFirstChild()).xmlSerialize(out);
// print end tag
((BaseAST)node).xmlSerializeRootClose(out);
}
}
}
}

922
app/preproc/PdeEmitter.java Normal file
View File

@@ -0,0 +1,922 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
package processing.app.preproc;
import processing.app.*;
/* Based on original code copyright (c) 2003 Andy Tripp <atripp@comcast.net>.
* shipped under GPL with permission.
*/
import antlr.*;
import antlr.collections.*;
import antlr.collections.impl.*;
import java.io.*;
import java.util.*;
/**
* 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.
*/
public class PdeEmitter implements PdeTokenTypes
{
private PrintStream out = System.out;
private PrintStream debug = System.err;
private static int ALL = -1;
private java.util.Stack stack = new java.util.Stack();
private static String[] tokenNames;
private final static int ROOT_ID = 0;
static {
setupTokenNames();
}
/*
private static Hashtable publicMethods;
private static final String publicMethodList[] = {
"setup", "draw", //"loop",
"mousePressed", "mouseReleased", "mouseClicked",
"mouseEntered", "mouseExited",
"mouseMoved", "mouseDragged",
"keyPressed", "keyReleased", "keyTyped"
};
static {
publicMethods = new Hashtable();
for (int i = 0; i < publicMethodList.length; i++) {
publicMethods.put(publicMethodList[i], new Object());
}
}
*/
// Map each AST token type to a String
private static void setupTokenNames() {
tokenNames = new String[200];
for (int i=0; i<tokenNames.length; i++) {
tokenNames[i] = "ERROR:" + i;
}
tokenNames[POST_INC]="++";
tokenNames[POST_DEC]="--";
tokenNames[UNARY_MINUS]="-";
tokenNames[UNARY_PLUS]="+";
tokenNames[STAR]="*";
tokenNames[ASSIGN]="=";
tokenNames[PLUS_ASSIGN]="+=";
tokenNames[MINUS_ASSIGN]="-=";
tokenNames[STAR_ASSIGN]="*=";
tokenNames[DIV_ASSIGN]="/=";
tokenNames[MOD_ASSIGN]="%=";
tokenNames[SR_ASSIGN]=">>=";
tokenNames[BSR_ASSIGN]=">>>=";
tokenNames[SL_ASSIGN]="<<=";
tokenNames[BAND_ASSIGN]="&=";
tokenNames[BXOR_ASSIGN]="^=";
tokenNames[BOR_ASSIGN]="|=";
tokenNames[QUESTION]="?";
tokenNames[LOR]="||";
tokenNames[LAND]="&&";
tokenNames[BOR]="|";
tokenNames[BXOR]="^";
tokenNames[BAND]="&";
tokenNames[NOT_EQUAL]="!=";
tokenNames[EQUAL]="==";
tokenNames[LT]="<";
tokenNames[GT]=">";
tokenNames[LE]="<=";
tokenNames[GE]=">=";
tokenNames[SL]="<<";
tokenNames[SR]=">>";
tokenNames[BSR]=">>>";
tokenNames[PLUS]="+";
tokenNames[MINUS]="-";
tokenNames[DIV]="/";
tokenNames[MOD]="%";
tokenNames[INC]="++";
tokenNames[DEC]="--";
tokenNames[BNOT]="~";
tokenNames[LNOT]="!";
tokenNames[FINAL]="final";
tokenNames[ABSTRACT]="abstract";
tokenNames[LITERAL_package]="package";
tokenNames[LITERAL_import]="import";
tokenNames[LITERAL_void]="void";
tokenNames[LITERAL_boolean]="boolean";
tokenNames[LITERAL_byte]="byte";
tokenNames[LITERAL_char]="char";
tokenNames[LITERAL_short]="short";
tokenNames[LITERAL_int]="int";
tokenNames[LITERAL_float]="float";
tokenNames[LITERAL_long]="long";
tokenNames[LITERAL_double]="double";
tokenNames[LITERAL_private]="private";
tokenNames[LITERAL_public]="public";
tokenNames[LITERAL_protected]="protected";
tokenNames[LITERAL_static]="static";
tokenNames[LITERAL_transient]="transient";
tokenNames[LITERAL_native]="native";
tokenNames[LITERAL_threadsafe]="threadsafe";
tokenNames[LITERAL_synchronized]="synchronized";
tokenNames[LITERAL_volatile]="volatile";
tokenNames[LITERAL_class]="class";
tokenNames[LITERAL_extends]="extends";
tokenNames[LITERAL_interface]="interface";
tokenNames[LITERAL_implements]="implements";
tokenNames[LITERAL_throws]="throws";
tokenNames[LITERAL_if]="if";
tokenNames[LITERAL_else]="else";
tokenNames[LITERAL_for]="for";
tokenNames[LITERAL_while]="while";
tokenNames[LITERAL_do]="do";
tokenNames[LITERAL_break]="break";
tokenNames[LITERAL_continue]="continue";
tokenNames[LITERAL_return]="return";
tokenNames[LITERAL_switch]="switch";
tokenNames[LITERAL_throw]="throw";
tokenNames[LITERAL_case]="case";
tokenNames[LITERAL_default]="default";
tokenNames[LITERAL_try]="try";
tokenNames[LITERAL_finally]="finally";
tokenNames[LITERAL_catch]="catch";
tokenNames[LITERAL_instanceof]="instanceof";
tokenNames[LITERAL_this]="this";
tokenNames[LITERAL_super]="super";
tokenNames[LITERAL_true]="true";
tokenNames[LITERAL_false]="false";
tokenNames[LITERAL_null]="null";
tokenNames[LITERAL_new]="new";
tokenNames[LITERAL_color]="int"; // PDE specific alias
}
/**
* Specify a PrintStream to print to. System.out is the default.
* @param out the PrintStream to print to
*/
public void setOut(PrintStream out) {
this.out = out;
}
private String name(AST ast) {
return tokenNames[ast.getType()];
}
private String name(int type) {
return tokenNames[type];
}
/**
* 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(AST ast, 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(AST ast) {
dumpHiddenTokens(((antlr.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(AST ast) {
antlr.CommonHiddenStreamToken
child = null,
parent = ((antlr.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(antlr.CommonHiddenStreamToken t) {
for ( ; t != null ; t=PdePreprocessor.filter.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(AST ast) throws PdeException {
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(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(AST ast, 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_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;
}
/**
* Prints a binary operator
*/
private void printBinaryOperator(AST ast) throws PdeException {
print(ast.getFirstChild());
out.print(name(ast));
dumpHiddenAfter(ast);
print(ast.getFirstChild().getNextSibling());
}
/**
* 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 (AST ast) throws PdeException {
if (ast == null) {
return;
}
AST parent = null;
if (!stack.isEmpty()) {
parent = (AST) stack.peek();
}
stack.push(ast);
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.filter.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 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, 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_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:
// kids seem to be: MODIFIERS TYPE setup PARAMETERS
//AST parent = (AST) stack.peek();
AST modifiersChild = ast.getFirstChild();
AST typeChild = modifiersChild.getNextSibling();
AST methodNameChild = typeChild.getNextSibling();
AST parametersChild = methodNameChild.getNextSibling();
// to output, use print(child) on each of the four
/*
// 1. figure out if this is setup, draw, or loop
String methodName = methodNameChild.getText();
if (publicMethods.get(methodName) != null) {
// make sure this feller is public
boolean foundPublic = false;
AST child = modifiersChild.getFirstChild();
while (child != null) {
if (child.getText().equals("public")) {
foundPublic = true;
child = null;
} else {
//out.print("." + child.getText() + ".");
child = child.getNextSibling();
}
}
if (!foundPublic) {
out.print("public ");
}
*/
// if this method doesn't have a specifier, make it public
// (useful for setup/keyPressed/etc)
boolean foundSpecifier = false;
AST child = modifiersChild.getFirstChild();
while (child != null) {
String childText = child.getText();
if (childText.equals("public") ||
childText.equals("protected") ||
childText.equals("private")) {
foundSpecifier = true;
child = null;
} else {
//out.print("." + child.getText() + ".");
child = child.getNextSibling();
}
}
if (!foundSpecifier) {
out.print("public ");
}
printChildren(ast); // everything is fine
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 LT:
case GT:
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:
printBinaryOperator(ast);
break;
case LITERAL_for:
out.print(name(ast));
dumpHiddenAfter(ast);
printChildren(ast);
break;
case POST_INC:
case POST_DEC:
print(child1);
out.print(name(ast));
dumpHiddenAfter(ast);
break;
// unary operators:
case BNOT:
case LNOT:
case INC:
case DEC:
case UNARY_MINUS:
case UNARY_PLUS:
out.print(name(ast));
dumpHiddenAfter(ast);
print(child1);
break;
case LITERAL_new:
out.print("new");
dumpHiddenAfter(ast);
print(child1);
print(child2);
// "new String[] {...}": the stuff in {} is child3
if (child3 != null) {
print(child3);
}
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 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:
out.print(ast.getText());
dumpHiddenAfter(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:
case LITERAL_volatile:
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:
case LITERAL_continue:
case LITERAL_break:
out.print(name(ast));
dumpHiddenAfter(ast);
break;
case EMPTY_STAT:
case EMPTY_FIELD:
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:
out.print("if");
dumpHiddenAfter(ast);
print(child1); // the "if" condition: an EXPR
print(child2); // the "then" clause is an SLIST
if (child3 != null) {
out.print("else");
dumpHiddenBefore(getBestPrintableNode(child3, true));
print(child3); // optional "else" clause: an SLIST
}
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") &&
PdePreferences.getBoolean("preproc.substitute_image")) { //, true)) {
out.print("BImage");
} else if (ast.getText().equals("Font") &&
PdePreferences.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:
AST nonTerminalTypeNode = child1;
AST terminalTypeNode = child1.getFirstChild();
AST exprToCast = child2;
/*
// if this is a string type, add .valueOf()
if (nonTerminalTypeNode.getType() == PdeRecognizer.TYPE &&
terminalTypeNode.getText().equals("String")) {
out.print(terminalTypeNode.getText() + ".valueOf");
dumpHiddenAfter(terminalTypeNode);
print(exprToCast);
// if the expresion to be cast is a string literal, try and parse it.
//
// ideally, we'd be able to do this for all expressions with a
// string type, not just string literals. however, the parser
// doesn't currently track expression type, and for full
// functionality, we'd need to do semantic analysis to handle
// imports so that we could know the return types of method calls.
//
} else if (exprToCast.getFirstChild().getType() == STRING_LITERAL ) {
switch (terminalTypeNode.getType()) {
case PdeRecognizer.LITERAL_byte:
out.print("Byte.parseByte");
dumpHiddenAfter(terminalTypeNode);
print(exprToCast);
break;
case PdeRecognizer.LITERAL_double:
out.print("(new Double");
dumpHiddenAfter(terminalTypeNode);
out.print(exprToCast.getFirstChild().getText() + ").doubleValue()");
dumpHiddenAfter(exprToCast.getFirstChild());
break;
case PdeRecognizer.LITERAL_float:
out.print("(new Float");
dumpHiddenAfter(terminalTypeNode);
out.print(exprToCast.getFirstChild().getText() + ").floatValue()");
dumpHiddenAfter(exprToCast.getFirstChild());
break;
case PdeRecognizer.LITERAL_int:
case PdeRecognizer.LITERAL_color:
out.print("Integer.parseInt");
dumpHiddenAfter(terminalTypeNode);
print(exprToCast);
break;
case PdeRecognizer.LITERAL_long:
out.print("Long.parseLong");
break;
case PdeRecognizer.LITERAL_short:
out.print("Short.parseShort");
break;
default:
throw new PdeException(PdeCompiler.SUPER_BADNESS);
}
// for builtin types, use regular casting syntax
} else {
*/
// result of below is (int)(4.0
//out.print("(");
//out.print(terminalTypeNode.getText() + ")"); // typename
//dumpHiddenAfter(terminalTypeNode);
//print(exprToCast);
//}
//out.print("(");
String pooType = terminalTypeNode.getText();
out.print("PApplet.to" +
Character.toUpperCase(pooType.charAt(0)) +
pooType.substring(1));
dumpHiddenAfter(terminalTypeNode); // the left paren
print(exprToCast);
//out.print("x)");
break;
// making floating point literals default to floats, not doubles
case NUM_DOUBLE:
out.print(ast.getText());
if (PdePreferences.getBoolean("preproc.substitute_floats")) { //, true) ) {
out.print("f");
}
dumpHiddenAfter(ast);
break;
default:
debug.println("Invalid type:" + ast.getType());
break;
/* The following are tokens, but I don't think JavaRecognizer
ever produces an AST with one of these types:
case COMMA:
case LITERAL_implements:
case LITERAL_class:
case LITERAL_extends:
case EOF:
case NULL_TREE_LOOKAHEAD:
case BLOCK:
case LABELED_STAT: // refuse to implement on moral grounds :)
case LITERAL_import:
case LBRACK:
case RBRACK:
case LCURLY:
case RCURLY:
case LPAREN:
case RPAREN:
case LITERAL_else: // else is a child of "if" AST
case COLON: // part of the trinary operator
case WS: // whitespace
case ESC:
case HEX_DIGIT:
case VOCAB:
case EXPONENT: // exponents and float suffixes are left in the NUM_FLOAT
case FLOAT_SUFFIX
*/
}
stack.pop();
}
}

View File

@@ -4,8 +4,8 @@
PdePreprocessor - wrapper for default ANTLR-generated parser
Part of the Processing project - http://processing.org
Except where noted, code is written by Ben Fry and
Copyright (c) 2001-03 Massachusetts Institute of Technology
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.
@@ -25,6 +25,9 @@
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app.preproc;
import processing.app.*;
import processing.core.*;
import java.io.*;
@@ -48,17 +51,17 @@ public class PdePreprocessor {
// 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.
String extraImports[];
public String extraImports[];
// imports just from the code folder, treated differently
// than the others, since the imports are auto-generated.
String codeFolderImports[];
public String codeFolderImports[];
static final int STATIC = 0; // formerly BEGINNER
static final int ACTIVE = 1; // formerly INTERMEDIATE
static final int JAVA = 2; // formerly ADVANCED
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 int programType = -1;
static public int programType = -1;
Reader programReader;
String buildPath;

106
app/preproc/README.txt Normal file
View File

@@ -0,0 +1,106 @@
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.
Here's some info about the various files in this directory:
java.g: 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().
java.tree.g: 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.
pde.g: 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.
PdeEmitter.java: 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.
ExtendedCommonASTWithHiddenTokens.java: 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.
TokenStreamCopyingHiddenTokenFilter.java: 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.
whitespace_test.pde: 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.
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.
....
Current Preprocessor Subsitutions:
"compiler.substitute_floats" (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. this is
confusing for beginners.
"compiler.enhanced_casting"
- 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. will this cause trouble?
"compiler.color_datattype"
- '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.
"compiler.web_colors" (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.
Other preprocessor functionality
- 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://proce55ing.net/reference/environment/index.html
- size and background are pulled from draw mode programs and placed
into setup(). this has a problem if size() is based on a variable,
which we try to avoid people doing, but would like to be able to
support it (perhaps by requiring the size() to be final?)
- currently does a godawful scrambling of the comments so that the
substitution doesn't try to run on them. this also causes lots of
bizarro bugs.
Possible?
- would be nice to just type code wherever, mixing a 'static' style
app with a few functions. would be simpler for starting out. but it
seems that the declarations would have to be pulled out, but that
all seems problematic. or maybe it could all be inside a static { }
block. but that wouldn't seem to work either.

View File

@@ -0,0 +1,221 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
package antlr;
//package processing.app.preproc;
import antlr.*;
import antlr.collections.impl.BitSet;
/**
* 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.
*
* This code is partially derived from the public domain ANLTR TokenStream
*/
public class TokenStreamCopyingHiddenTokenFilter
extends TokenStreamHiddenTokenFilter
implements TokenStream {
protected BitSet copyMask;
CommonHiddenStreamToken hiddenCopy = null;
public TokenStreamCopyingHiddenTokenFilter(TokenStream input) {
super(input);
copyMask = new BitSet();
}
/**
* Indicate that all tokens of type tokenType should be copied. The copy
* is put in the stream of hidden tokens, and the original is returned in the
* stream of normal tokens.
*
* @param tokenType integer representing the token type to copied
*/
public void copy(int tokenType) {
copyMask.add(tokenType);
}
/**
* Create a clone of the important parts of the given token. Note that this
* does NOT copy the hiddenBefore and hiddenAfter fields.
*
* @param t token to partially clone
* @return newly created partial clone
*/
public CommonHiddenStreamToken partialCloneToken(CommonHiddenStreamToken t) {
CommonHiddenStreamToken u = new CommonHiddenStreamToken(t.getType(),
t.getText());
u.setColumn(t.getColumn());
u.setLine(t.getLine());
u.setFilename(t.getFilename());
return u;
}
public void linkAndCopyToken(CommonHiddenStreamToken prev,
CommonHiddenStreamToken monitored) {
// create a copy of the token in the lookahead for use as hidden token
hiddenCopy = partialCloneToken(LA(1));
// attach copy to the previous token, whether hidden or monitored
prev.setHiddenAfter(hiddenCopy);
// if previous token was hidden, set the hiddenBefore pointer of the
// copy to point back to it
if (prev != monitored) {
hiddenCopy.setHiddenBefore(prev);
}
// we don't want the non-hidden copy to link back to the hidden
// copy on the next pass through this function, so we leave
// lastHiddenToken alone
//System.err.println("hidden copy: " + hiddenCopy.toString());
return;
}
private void consumeFirst() throws TokenStreamException {
consume(); // get first token of input stream
// Handle situation where hidden or discarded tokens
// appear first in input stream
CommonHiddenStreamToken p=null;
// while hidden, copied, or discarded scarf tokens
while ( hideMask.member(LA(1).getType()) ||
discardMask.member(LA(1).getType()) ||
copyMask.member(LA(1).getType()) ) {
// if we've hit one of the tokens that needs to be copied, we copy it
// and then break out of the loop, because the parser needs to see it
// too
//
if (copyMask.member(LA(1).getType())) {
// copy the token in the lookahead
hiddenCopy = partialCloneToken(LA(1));
// if there's an existing token before this, link that and the
// copy together
if (p != null) {
p.setHiddenAfter(hiddenCopy);
hiddenCopy.setHiddenBefore(p); // double-link
}
lastHiddenToken = hiddenCopy;
if (firstHidden == null) {
firstHidden = hiddenCopy;
}
// we don't want to consume this token, because it also needs to
// be passed through to the parser, so break out of the while look
// entirely
//
break;
} else if (hideMask.member(LA(1).getType())) {
if (p != null) {
p.setHiddenAfter(LA(1));
LA(1).setHiddenBefore(p); // double-link
}
p = LA(1);
lastHiddenToken = p;
if (firstHidden == null) {
firstHidden = p; // record hidden token if first
}
}
consume();
}
}
/** Return the next monitored token.
* Test the token following the monitored token.
* If following is another monitored token, save it
* for the next invocation of nextToken (like a single
* lookahead token) and return it then.
* If following is unmonitored, nondiscarded (hidden)
* channel token, add it to the monitored token.
*
* Note: EOF must be a monitored Token.
*/
public Token nextToken() throws TokenStreamException {
// handle an initial condition; don't want to get lookahead
// token of this splitter until first call to nextToken
if (LA(1) == null) {
consumeFirst();
}
//System.err.println();
// we always consume hidden tokens after monitored, thus,
// upon entry LA(1) is a monitored token.
CommonHiddenStreamToken monitored = LA(1);
// point to hidden tokens found during last invocation
monitored.setHiddenBefore(lastHiddenToken);
lastHiddenToken = null;
// Look for hidden tokens, hook them into list emanating
// from the monitored tokens.
consume();
CommonHiddenStreamToken prev = monitored;
// deal with as many not-purely-monitored tokens as possible
while ( hideMask.member(LA(1).getType()) ||
discardMask.member(LA(1).getType()) ||
copyMask.member(LA(1).getType()) ) {
if (copyMask.member(LA(1).getType())) {
// copy the token and link it backwards
if (hiddenCopy != null) {
linkAndCopyToken(hiddenCopy, monitored);
} else {
linkAndCopyToken(prev, monitored);
}
// we now need to parse it as a monitored token, so we return, which
// avoids the consume() call at the end of this loop. the next call
// will parse it as a monitored token.
//System.err.println("returned: " + monitored.toString());
return monitored;
} else if (hideMask.member(LA(1).getType())) {
// attach the hidden token to the monitored in a chain
// link forwards
prev.setHiddenAfter(LA(1));
// link backwards
if (prev != monitored) { //hidden cannot point to monitored tokens
LA(1).setHiddenBefore(prev);
} else if (hiddenCopy != null) {
hiddenCopy.setHiddenAfter(LA(1));
LA(1).setHiddenBefore(hiddenCopy);
hiddenCopy = null;
}
//System.err.println("hidden: " + prev.getHiddenAfter().toString() + "\" after: " + prev.toString());
prev = lastHiddenToken = LA(1);
}
consume();
}
// remember the last hidden token for next time around
if (hiddenCopy != null) {
lastHiddenToken = hiddenCopy;
hiddenCopy = null;
}
//System.err.println("returned: " + monitored.toString());
return monitored;
}
}

11
app/preproc/clean.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
rm -f *Lexer.java
rm -f *Recognizer.java
rm -f *TokenTypes.java
rm -f *TokenTypes.txt
rm -f *TreeParser.java
rm -f *TreeParserTokenTypes.java
rm -f *TreeParserTokenTypes.txt
rm -f expanded*.g

1276
app/preproc/java.g Normal file

File diff suppressed because it is too large Load Diff

326
app/preproc/java.tree.g Normal file
View File

@@ -0,0 +1,326 @@
package antlr;
/** Java 1.3 AST Recognizer Grammar
*
* Author: (see java.g preamble)
*
* This grammar is in the PUBLIC DOMAIN
*/
class JavaTreeParser extends TreeParser;
options {
importVocab = Java;
}
compilationUnit
: (packageDefinition)?
(importDefinition)*
(typeDefinition)*
;
packageDefinition
: #( PACKAGE_DEF identifier )
;
importDefinition
: #( IMPORT identifierStar )
;
typeDefinition
: #(CLASS_DEF modifiers IDENT extendsClause implementsClause objBlock )
| #(INTERFACE_DEF modifiers IDENT extendsClause interfaceBlock )
;
typeSpec
: #(TYPE typeSpecArray)
;
typeSpecArray
: #( ARRAY_DECLARATOR typeSpecArray )
| type
;
type: identifier
| builtInType
;
builtInType
: "void"
| "boolean"
| "byte"
| "char"
| "short"
| "int"
| "float"
| "long"
| "double"
;
modifiers
: #( MODIFIERS (modifier)* )
;
modifier
: "private"
| "public"
| "protected"
| "static"
| "transient"
| "final"
| "abstract"
| "native"
| "threadsafe"
| "synchronized"
| "const"
| "volatile"
| "strictfp"
;
extendsClause
: #(EXTENDS_CLAUSE (identifier)* )
;
implementsClause
: #(IMPLEMENTS_CLAUSE (identifier)* )
;
interfaceBlock
: #( OBJBLOCK
( methodDecl
| variableDef
)*
)
;
objBlock
: #( OBJBLOCK
( ctorDef
| methodDef
| variableDef
| typeDefinition
| #(STATIC_INIT slist)
| #(INSTANCE_INIT slist)
)*
)
;
ctorDef
: #(CTOR_DEF modifiers methodHead (slist)?)
;
methodDecl
: #(METHOD_DEF modifiers typeSpec methodHead)
;
methodDef
: #(METHOD_DEF modifiers typeSpec methodHead (slist)?)
;
variableDef
: #(VARIABLE_DEF modifiers typeSpec variableDeclarator varInitializer)
;
parameterDef
: #(PARAMETER_DEF modifiers typeSpec IDENT )
;
objectinitializer
: #(INSTANCE_INIT slist)
;
variableDeclarator
: IDENT
| LBRACK variableDeclarator
;
varInitializer
: #(ASSIGN initializer)
|
;
initializer
: expression
| arrayInitializer
;
arrayInitializer
: #(ARRAY_INIT (initializer)*)
;
methodHead
: IDENT #( PARAMETERS (parameterDef)* ) (throwsClause)?
;
throwsClause
: #( "throws" (identifier)* )
;
identifier
: IDENT
| #( DOT identifier IDENT )
;
identifierStar
: IDENT
| #( DOT identifier (STAR|IDENT) )
;
slist
: #( SLIST (stat)* )
;
stat: typeDefinition
| variableDef
| expression
| #(LABELED_STAT IDENT stat)
| #("if" expression stat (stat)? )
| #( "for"
#(FOR_INIT (variableDef | elist)?)
#(FOR_CONDITION (expression)?)
#(FOR_ITERATOR (elist)?)
stat
)
| #("while" expression stat)
| #("do" stat expression)
| #("break" (IDENT)? )
| #("continue" (IDENT)? )
| #("return" (expression)? )
| #("switch" expression (caseGroup)*)
| #("throw" expression)
| #("synchronized" expression stat)
| tryBlock
| slist // nested SLIST
// uncomment to make assert JDK 1.4 stuff work
| #("assert" expression (expression)?)
| EMPTY_STAT
;
caseGroup
: #(CASE_GROUP (#("case" expression) | "default")+ slist)
;
tryBlock
: #( "try" slist (handler)* (#("finally" slist))? )
;
handler
: #( "catch" parameterDef slist )
;
elist
: #( ELIST (expression)* )
;
expression
: #(EXPR expr)
;
expr: #(QUESTION expr expr expr) // trinary operator
| #(ASSIGN expr expr) // binary operators...
| #(PLUS_ASSIGN expr expr)
| #(MINUS_ASSIGN expr expr)
| #(STAR_ASSIGN expr expr)
| #(DIV_ASSIGN expr expr)
| #(MOD_ASSIGN expr expr)
| #(SR_ASSIGN expr expr)
| #(BSR_ASSIGN expr expr)
| #(SL_ASSIGN expr expr)
| #(BAND_ASSIGN expr expr)
| #(BXOR_ASSIGN expr expr)
| #(BOR_ASSIGN expr expr)
| #(LOR expr expr)
| #(LAND expr expr)
| #(BOR expr expr)
| #(BXOR expr expr)
| #(BAND expr expr)
| #(NOT_EQUAL expr expr)
| #(EQUAL expr expr)
| #(LT expr expr)
| #(GT expr expr)
| #(LE expr expr)
| #(GE expr expr)
| #(SL expr expr)
| #(SR expr expr)
| #(BSR expr expr)
| #(PLUS expr expr)
| #(MINUS expr expr)
| #(DIV expr expr)
| #(MOD expr expr)
| #(STAR expr expr)
| #(INC expr)
| #(DEC expr)
| #(POST_INC expr)
| #(POST_DEC expr)
| #(BNOT expr)
| #(LNOT expr)
| #("instanceof" expr expr)
| #(UNARY_MINUS expr)
| #(UNARY_PLUS expr)
| primaryExpression
;
primaryExpression
: IDENT
| #( DOT
( expr
( IDENT
| arrayIndex
| "this"
| "class"
| #( "new" IDENT elist )
| "super"
)
| #(ARRAY_DECLARATOR typeSpecArray)
| builtInType ("class")?
)
)
| arrayIndex
| #(METHOD_CALL primaryExpression elist)
| ctorCall
| #(TYPECAST typeSpec expr)
| newExpression
| constant
| "super"
| "true"
| "false"
| "this"
| "null"
| typeSpec // type name used with instanceof
;
ctorCall
: #( CTOR_CALL elist )
| #( SUPER_CTOR_CALL
( elist
| primaryExpression elist
)
)
;
arrayIndex
: #(INDEX_OP expr expression)
;
constant
: NUM_INT
| CHAR_LITERAL
| STRING_LITERAL
| NUM_FLOAT
| NUM_DOUBLE
| NUM_LONG
;
newExpression
: #( "new" type
( newArrayDeclarator (arrayInitializer)?
| elist (objBlock)?
)
)
;
newArrayDeclarator
: #( ARRAY_DECLARATOR (newArrayDeclarator)? (expression)? )
;

3
app/preproc/make.sh Executable file
View File

@@ -0,0 +1,3 @@
java -cp ../../build/macosx/work/lib/antlr.jar antlr.Tool java.g
# now build the pde stuff that extends the java classes
java -cp ../../build/macosx/work/lib/antlr.jar antlr.Tool -glib java.g pde.g

304
app/preproc/pde.g Normal file
View File

@@ -0,0 +1,304 @@
/* -*- mode: antlr; c-basic-offset: 4; indent-tabs-mode: nil -*- */
header {
package processing.app.preproc;
import processing.app.*;
}
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;
}
pdeProgram
// only java mode programs will have their own public classes or
// imports (and they must have at least one)
: ( "public" "class" | "import" ) => javaProgram
{ PdePreprocessor.programType = PdePreprocessor.JAVA; }
// the syntactic predicate here looks for any minimal (thus
// the non-greedy qualifier) number of fields, followed by
// the tokens that represent the definition of loop() or
// some other member function. java mode programs may have such
// definitions, but they won't reach this point, having already been
// selected in the previous alternative. static mode programs
// don't have member functions.
//
| ( ( options {greedy=false;}: possiblyEmptyField)* "void" IDENT LPAREN )
=> activeProgram
{ PdePreprocessor.programType = PdePreprocessor.ACTIVE; }
| staticProgram
{ PdePreprocessor.programType = PdePreprocessor.STATIC; }
;
// advanced mode is really just a normal java file
javaProgram
: compilationUnit
;
activeProgram
: (possiblyEmptyField)+
;
staticProgram
: (statement)*
;
// 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
;
// of the form #cc008f in PDE
webcolor_literal
: w:WEBCOLOR_LITERAL
{ PdePreferences.getBoolean("preproc.web_colors") &&
w.getText().length() == 6 }? // 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
{ PdePreferences.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
{ PdePreferences.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!
: 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 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,sc,ic,cb);
PdePreprocessor.advClassName = i.getText();}
;
possiblyEmptyField
: field
| 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)+
;

View File

@@ -0,0 +1,150 @@
// this is a whitespace and other invisible token torture test for the ANTLR-based
// preprocessor. edit pde.properties and set "editor.save_build_files" to true.
// then, build this in processing. next, use
//
// diff -u --strip-trailing-cr \
// work/sketchbook/default/whitespace_test/whitespace_test.pde \
// work/lib/build/MyDemo.java
//
// to compare the files before and after preprocessing. There should not be
// any differences.
import // comment test
java.io.*;
// comment 1
public class // post-class comment
// comment2
MyDemo extends BApplet implements // foo
java.lang. // bar
Cloneable {
//argh
public // foo
String // bar
fff = /*rheet */ "stuff";
static /*a*/ {
/*foo*/
/*bar*/
six = 6;
} /* b*/
static /*a*/ final /*b*/ int six;
void setup()
{
size(200, 200);
background(255);
this . fff = /* ook */ (String)/*foo*/"yo";
rectMode(CENTER_DIAMETER); // comment 1a
noStroke();
fill(255, 204, 0);
int q = /*a*/ - /*b*/ 1;
boolean c = /*a*/ ! /*b*/ true;
}
int foo() /*a*/ throws /*b*/ java.lang.Exception /*c*/
{
int b = 7;
switch /*a*/ ( /*b*/ b /*c*/ ) {
case /*d*/ 1 /*e*/: /*f*/
int c=9;
/*g*/
break; /*h*/
default /*i*/ :
int d=9;
break;
}
try { /* qq */
loop(); /* rr */
} catch /*ss*/ (java.lang.Exception ex) /*tt*/ {
b = 8; /*tut*/
throw /*utu*/ ex;
} /*uu*/ finally /*vv*/ {
b = 9;
} /*ww*/
b /*aaa*/ = /*bbb*/ true /*ccc*/ ? /*ddd*/ 0 /*eee*/
: /* fff*/ 1 /*ggg*/;
return /*a*/ 5 /*b*/;
}
// comment 2
void loop()
{
int arr1 /* VVV */ [ /* XXX */] /*YYY*/ ;
int[] arr2 = { /*a*/ 2, 3 /*b*/ } /*c*/ ;
for /*a*/ (/*b*/ int j=0 /*c*/; /*d*/ j<2/*e*/ ; /*f*/ j++ /*g*/)
/*h*/
arr2[1] = 6;
/*foo*/
;
/*bar*/
rect(width-mouseX, height-mouseY, 50, 50);
rect(mouseX, mouseY, 50, 50);
if (/*a*/ arr2[1] == 6/*b*/) {
/*c*/
int d=7;
} /*d*/else /*e*/{
int e=8;
/*f*/
}
int f;
if (/*aa*/ arr2[1] ==6 /*bb*/ )
/*cc*/
f=8; /*dd*/
else /*ee*/
f=10; /*ff*/
while ( /*aaa*/ f < 15) /*bbb*/ {
f ++;
} /*ggg*/
do /* aaaa */ {
f++;
} /*bbbb*/ while /*cccc*/ ( /*a*/ - /*b*/ 20 > f) /*dddd*/;
f = 2 * 3 + 4;
f = ( 2 * 3 ) + 4 + /*aa*/ -/*bb*/1;
f = 2 * ( 3 + 4 ) ;
fff = /*a*/ new /*b*/ String(/*c*/"foo"/*d*/) /*e*/;
int arr3[] = /*a*/ new /*b*/ int/*c*/[/*d*/] /*e*/ {1/*f*/,2};
int arr4[][] = new /*a*/int/*b*/[1][2]/*c*/;
}
class Shoe
{
Shoe(String brand)
{
println(brand);
}
}
class NikeAir extends Shoe
{
NikeAir()
{
/*a*/ super /*b*/ ( /*c*/ "Nike" /*d*/ ) /*e*/ ;
/*aa*/ ( /*bb*/ new /*cc*/ MyDemo /*dd*/ (/*ee*/)/*ff*/)/*gg*/./*hh*/super/*ii*/(/*jj*/5/*kk*/);
}
}
}

View File

@@ -0,0 +1,274 @@
/*
* CTokenMarker.java - C token marker
* Copyright (C) 1998, 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import javax.swing.text.Segment;
/**
* C token marker.
*
* @author Slava Pestov
* @version $Id$
*/
public class CTokenMarker extends TokenMarker
{
public CTokenMarker()
{
this(true,getKeywords());
}
public CTokenMarker(boolean cpp, KeywordMap keywords)
{
this.cpp = cpp;
this.keywords = keywords;
}
public byte markTokensImpl(byte token, Segment line, int lineIndex)
{
char[] array = line.array;
int offset = line.offset;
lastOffset = offset;
lastKeyword = offset;
int mlength = line.count + offset;
boolean backslash = false;
loop: for(int i = offset; i < mlength; i++)
{
int i1 = (i+1);
char c = array[i];
if(c == '\\')
{
backslash = !backslash;
continue;
}
switch(token)
{
case Token.NULL:
switch(c)
{
case '#':
if(backslash)
backslash = false;
else if(cpp)
{
if(doKeyword(line,i,c))
break;
addToken(i - lastOffset,token);
addToken(mlength - i,Token.KEYWORD2);
lastOffset = lastKeyword = mlength;
break loop;
}
break;
case '"':
doKeyword(line,i,c);
if(backslash)
backslash = false;
else
{
addToken(i - lastOffset,token);
token = Token.LITERAL1;
lastOffset = lastKeyword = i;
}
break;
case '\'':
doKeyword(line,i,c);
if(backslash)
backslash = false;
else
{
addToken(i - lastOffset,token);
token = Token.LITERAL2;
lastOffset = lastKeyword = i;
}
break;
case ':':
if(lastKeyword == offset)
{
if(doKeyword(line,i,c))
break;
backslash = false;
addToken(i1 - lastOffset,Token.LABEL);
lastOffset = lastKeyword = i1;
}
else if(doKeyword(line,i,c))
break;
break;
case '/':
backslash = false;
doKeyword(line,i,c);
if(mlength - i > 1)
{
switch(array[i1])
{
case '*':
addToken(i - lastOffset,token);
lastOffset = lastKeyword = i;
if(mlength - i > 2 && array[i+2] == '*')
token = Token.COMMENT2;
else
token = Token.COMMENT1;
break;
case '/':
addToken(i - lastOffset,token);
addToken(mlength - i,Token.COMMENT1);
lastOffset = lastKeyword = mlength;
break loop;
}
}
break;
default:
backslash = false;
if(!Character.isLetterOrDigit(c)
&& c != '_')
doKeyword(line,i,c);
break;
}
break;
case Token.COMMENT1:
case Token.COMMENT2:
backslash = false;
if(c == '*' && mlength - i > 1)
{
if(array[i1] == '/')
{
i++;
addToken((i+1) - lastOffset,token);
token = Token.NULL;
lastOffset = lastKeyword = i+1;
}
}
break;
case Token.LITERAL1:
if(backslash)
backslash = false;
else if(c == '"')
{
addToken(i1 - lastOffset,token);
token = Token.NULL;
lastOffset = lastKeyword = i1;
}
break;
case Token.LITERAL2:
if(backslash)
backslash = false;
else if(c == '\'')
{
addToken(i1 - lastOffset,Token.LITERAL1);
token = Token.NULL;
lastOffset = lastKeyword = i1;
}
break;
default:
throw new InternalError("Invalid state: "
+ token);
}
}
if(token == Token.NULL)
doKeyword(line,mlength,'\0');
switch(token)
{
case Token.LITERAL1:
case Token.LITERAL2:
addToken(mlength - lastOffset,Token.INVALID);
token = Token.NULL;
break;
case Token.KEYWORD2:
addToken(mlength - lastOffset,token);
if (!backslash) token = Token.NULL;
addToken(mlength - lastOffset,token);
break;
default:
addToken(mlength - lastOffset,token);
break;
}
return token;
}
public static KeywordMap getKeywords()
{
if(cKeywords == null)
{
cKeywords = new KeywordMap(false);
cKeywords.add("char",Token.KEYWORD3);
cKeywords.add("double",Token.KEYWORD3);
cKeywords.add("enum",Token.KEYWORD3);
cKeywords.add("float",Token.KEYWORD3);
cKeywords.add("int",Token.KEYWORD3);
cKeywords.add("long",Token.KEYWORD3);
cKeywords.add("short",Token.KEYWORD3);
cKeywords.add("signed",Token.KEYWORD3);
cKeywords.add("struct",Token.KEYWORD3);
cKeywords.add("typedef",Token.KEYWORD3);
cKeywords.add("union",Token.KEYWORD3);
cKeywords.add("unsigned",Token.KEYWORD3);
cKeywords.add("void",Token.KEYWORD3);
cKeywords.add("auto",Token.KEYWORD1);
cKeywords.add("const",Token.KEYWORD1);
cKeywords.add("extern",Token.KEYWORD1);
cKeywords.add("register",Token.KEYWORD1);
cKeywords.add("static",Token.KEYWORD1);
cKeywords.add("volatile",Token.KEYWORD1);
cKeywords.add("break",Token.KEYWORD1);
cKeywords.add("case",Token.KEYWORD1);
cKeywords.add("continue",Token.KEYWORD1);
cKeywords.add("default",Token.KEYWORD1);
cKeywords.add("do",Token.KEYWORD1);
cKeywords.add("else",Token.KEYWORD1);
cKeywords.add("for",Token.KEYWORD1);
cKeywords.add("goto",Token.KEYWORD1);
cKeywords.add("if",Token.KEYWORD1);
cKeywords.add("return",Token.KEYWORD1);
cKeywords.add("sizeof",Token.KEYWORD1);
cKeywords.add("switch",Token.KEYWORD1);
cKeywords.add("while",Token.KEYWORD1);
cKeywords.add("asm",Token.KEYWORD2);
cKeywords.add("asmlinkage",Token.KEYWORD2);
cKeywords.add("far",Token.KEYWORD2);
cKeywords.add("huge",Token.KEYWORD2);
cKeywords.add("inline",Token.KEYWORD2);
cKeywords.add("near",Token.KEYWORD2);
cKeywords.add("pascal",Token.KEYWORD2);
cKeywords.add("true",Token.LITERAL2);
cKeywords.add("false",Token.LITERAL2);
cKeywords.add("NULL",Token.LITERAL2);
}
return cKeywords;
}
// private members
private static KeywordMap cKeywords;
private boolean cpp;
private KeywordMap keywords;
private int lastOffset;
private int lastKeyword;
private boolean doKeyword(Segment line, int i, char c)
{
int i1 = i+1;
int len = i - lastKeyword;
byte id = keywords.lookup(line,lastKeyword,len);
if(id != Token.NULL)
{
if(lastKeyword != lastOffset)
addToken(lastKeyword - lastOffset,Token.NULL);
addToken(len,id);
lastOffset = i;
}
lastKeyword = i1;
return false;
}
}

View File

@@ -0,0 +1,365 @@
/*
* DefaultInputHandler.java - Default implementation of an input handler
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import javax.swing.KeyStroke;
import java.awt.event.*;
import java.awt.Toolkit;
import java.util.Hashtable;
import java.util.StringTokenizer;
/**
* The default input handler. It maps sequences of keystrokes into actions
* and inserts key typed events into the text area.
* @author Slava Pestov
* @version $Id$
*/
public class DefaultInputHandler extends InputHandler
{
/**
* Creates a new input handler with no key bindings defined.
*/
public DefaultInputHandler()
{
bindings = currentBindings = new Hashtable();
}
/**
* Sets up the default key bindings.
*/
public void addDefaultKeyBindings()
{
addKeyBinding("BACK_SPACE",BACKSPACE);
addKeyBinding("C+BACK_SPACE",BACKSPACE_WORD);
addKeyBinding("DELETE",DELETE);
addKeyBinding("C+DELETE",DELETE_WORD);
addKeyBinding("ENTER",INSERT_BREAK);
addKeyBinding("TAB",INSERT_TAB);
addKeyBinding("INSERT",OVERWRITE);
addKeyBinding("C+\\",TOGGLE_RECT);
addKeyBinding("HOME",HOME);
addKeyBinding("END",END);
addKeyBinding("S+HOME",SELECT_HOME);
addKeyBinding("S+END",SELECT_END);
addKeyBinding("C+HOME",DOCUMENT_HOME);
addKeyBinding("C+END",DOCUMENT_END);
addKeyBinding("CS+HOME",SELECT_DOC_HOME);
addKeyBinding("CS+END",SELECT_DOC_END);
addKeyBinding("PAGE_UP",PREV_PAGE);
addKeyBinding("PAGE_DOWN",NEXT_PAGE);
addKeyBinding("S+PAGE_UP",SELECT_PREV_PAGE);
addKeyBinding("S+PAGE_DOWN",SELECT_NEXT_PAGE);
addKeyBinding("LEFT",PREV_CHAR);
addKeyBinding("S+LEFT",SELECT_PREV_CHAR);
addKeyBinding("C+LEFT",PREV_WORD);
addKeyBinding("CS+LEFT",SELECT_PREV_WORD);
addKeyBinding("RIGHT",NEXT_CHAR);
addKeyBinding("S+RIGHT",SELECT_NEXT_CHAR);
addKeyBinding("C+RIGHT",NEXT_WORD);
addKeyBinding("CS+RIGHT",SELECT_NEXT_WORD);
addKeyBinding("UP",PREV_LINE);
addKeyBinding("S+UP",SELECT_PREV_LINE);
addKeyBinding("DOWN",NEXT_LINE);
addKeyBinding("S+DOWN",SELECT_NEXT_LINE);
addKeyBinding("C+ENTER",REPEAT);
}
/**
* Adds a key binding to this input handler. The key binding is
* a list of white space separated key strokes of the form
* <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
* or S for Shift, and key is either a character (a-z) or a field
* name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
* @param keyBinding The key binding
* @param action The action
*/
public void addKeyBinding(String keyBinding, ActionListener action)
{
Hashtable current = bindings;
StringTokenizer st = new StringTokenizer(keyBinding);
while(st.hasMoreTokens())
{
KeyStroke keyStroke = parseKeyStroke(st.nextToken());
if(keyStroke == null)
return;
if(st.hasMoreTokens())
{
Object o = current.get(keyStroke);
if(o instanceof Hashtable)
current = (Hashtable)o;
else
{
o = new Hashtable();
current.put(keyStroke,o);
current = (Hashtable)o;
}
}
else
current.put(keyStroke,action);
}
}
/**
* Removes a key binding from this input handler. This is not yet
* implemented.
* @param keyBinding The key binding
*/
public void removeKeyBinding(String keyBinding)
{
throw new InternalError("Not yet implemented");
}
/**
* Removes all key bindings from this input handler.
*/
public void removeAllKeyBindings()
{
bindings.clear();
}
/**
* Returns a copy of this input handler that shares the same
* key bindings. Setting key bindings in the copy will also
* set them in the original.
*/
public InputHandler copy()
{
return new DefaultInputHandler(this);
}
/**
* Handle a key pressed event. This will look up the binding for
* the key stroke and execute it.
*/
public void keyPressed(KeyEvent evt)
{
int keyCode = evt.getKeyCode();
int modifiers = evt.getModifiers();
// don't get command-s or other menu key equivs on mac
if ((modifiers & KeyEvent.META_MASK) != 0) return;
/*
char keyChar = evt.getKeyChar();
System.out.println("code=" + keyCode + " char=" + keyChar +
" charint=" + ((int)keyChar));
System.out.println("other codes " + KeyEvent.VK_ALT + " " +
KeyEvent.VK_META);
*/
if(keyCode == KeyEvent.VK_CONTROL ||
keyCode == KeyEvent.VK_SHIFT ||
keyCode == KeyEvent.VK_ALT ||
keyCode == KeyEvent.VK_META)
return;
if((modifiers & ~KeyEvent.SHIFT_MASK) != 0
|| evt.isActionKey()
|| keyCode == KeyEvent.VK_BACK_SPACE
|| keyCode == KeyEvent.VK_DELETE
|| keyCode == KeyEvent.VK_ENTER
|| keyCode == KeyEvent.VK_TAB
|| keyCode == KeyEvent.VK_ESCAPE)
{
if(grabAction != null)
{
handleGrabAction(evt);
return;
}
KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode,
modifiers);
Object o = currentBindings.get(keyStroke);
if(o == null)
{
// Don't beep if the user presses some
// key we don't know about unless a
// prefix is active. Otherwise it will
// beep when caps lock is pressed, etc.
if(currentBindings != bindings)
{
Toolkit.getDefaultToolkit().beep();
// F10 should be passed on, but C+e F10
// shouldn't
repeatCount = 0;
repeat = false;
evt.consume();
}
currentBindings = bindings;
return;
}
else if(o instanceof ActionListener)
{
currentBindings = bindings;
executeAction(((ActionListener)o),
evt.getSource(),null);
evt.consume();
return;
}
else if(o instanceof Hashtable)
{
currentBindings = (Hashtable)o;
evt.consume();
return;
}
}
}
/**
* Handle a key typed event. This inserts the key into the text area.
*/
public void keyTyped(KeyEvent evt)
{
int modifiers = evt.getModifiers();
char c = evt.getKeyChar();
// this is the apple/cmd key on macosx.. so menu commands
// were being passed through as legit keys.. added this line
// in an attempt to prevent.
if ((modifiers & KeyEvent.META_MASK) != 0) return;
if (c != KeyEvent.CHAR_UNDEFINED) // &&
// (modifiers & KeyEvent.ALT_MASK) == 0)
{
if(c >= 0x20 && c != 0x7f)
{
KeyStroke keyStroke = KeyStroke.getKeyStroke(
Character.toUpperCase(c));
Object o = currentBindings.get(keyStroke);
if(o instanceof Hashtable)
{
currentBindings = (Hashtable)o;
return;
}
else if(o instanceof ActionListener)
{
currentBindings = bindings;
executeAction((ActionListener)o,
evt.getSource(),
String.valueOf(c));
return;
}
currentBindings = bindings;
if(grabAction != null)
{
handleGrabAction(evt);
return;
}
// 0-9 adds another 'digit' to the repeat number
if(repeat && Character.isDigit(c))
{
repeatCount *= 10;
repeatCount += (c - '0');
return;
}
executeAction(INSERT_CHAR,evt.getSource(),
String.valueOf(evt.getKeyChar()));
repeatCount = 0;
repeat = false;
}
}
}
/**
* Converts a string to a keystroke. The string should be of the
* form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
* is any combination of A for Alt, C for Control, S for Shift
* or M for Meta, and <i>shortcut</i> is either a single character,
* or a keycode name from the <code>KeyEvent</code> class, without
* the <code>VK_</code> prefix.
* @param keyStroke A string description of the key stroke
*/
public static KeyStroke parseKeyStroke(String keyStroke)
{
if(keyStroke == null)
return null;
int modifiers = 0;
int index = keyStroke.indexOf('+');
if(index != -1)
{
for(int i = 0; i < index; i++)
{
switch(Character.toUpperCase(keyStroke
.charAt(i)))
{
case 'A':
modifiers |= InputEvent.ALT_MASK;
break;
case 'C':
modifiers |= InputEvent.CTRL_MASK;
break;
case 'M':
modifiers |= InputEvent.META_MASK;
break;
case 'S':
modifiers |= InputEvent.SHIFT_MASK;
break;
}
}
}
String key = keyStroke.substring(index + 1);
if(key.length() == 1)
{
char ch = Character.toUpperCase(key.charAt(0));
if(modifiers == 0)
return KeyStroke.getKeyStroke(ch);
else
return KeyStroke.getKeyStroke(ch,modifiers);
}
else if(key.length() == 0)
{
System.err.println("Invalid key stroke: " + keyStroke);
return null;
}
else
{
int ch;
try
{
ch = KeyEvent.class.getField("VK_".concat(key))
.getInt(null);
}
catch(Exception e)
{
System.err.println("Invalid key stroke: "
+ keyStroke);
return null;
}
return KeyStroke.getKeyStroke(ch,modifiers);
}
}
// private members
private Hashtable bindings;
private Hashtable currentBindings;
private DefaultInputHandler(DefaultInputHandler copy)
{
bindings = currentBindings = copy.bindings;
}
}

1071
app/syntax/InputHandler.java Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

140
app/syntax/KeywordMap.java Normal file
View File

@@ -0,0 +1,140 @@
/*
* KeywordMap.java - Fast keyword->id map
* Copyright (C) 1998, 1999 Slava Pestov
* Copyright (C) 1999 Mike Dillon
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import javax.swing.text.Segment;
/**
* A <code>KeywordMap</code> is similar to a hashtable in that it maps keys
* to values. However, the `keys' are Swing segments. This allows lookups of
* text substrings without the overhead of creating a new string object.
* <p>
* This class is used by <code>CTokenMarker</code> to map keywords to ids.
*
* @author Slava Pestov, Mike Dillon
* @version $Id$
*/
public class KeywordMap
{
/**
* Creates a new <code>KeywordMap</code>.
* @param ignoreCase True if keys are case insensitive
*/
public KeywordMap(boolean ignoreCase)
{
this(ignoreCase, 52);
this.ignoreCase = ignoreCase;
}
/**
* Creates a new <code>KeywordMap</code>.
* @param ignoreCase True if the keys are case insensitive
* @param mapLength The number of `buckets' to create.
* A value of 52 will give good performance for most maps.
*/
public KeywordMap(boolean ignoreCase, int mapLength)
{
this.mapLength = mapLength;
this.ignoreCase = ignoreCase;
map = new Keyword[mapLength];
}
/**
* Looks up a key.
* @param text The text segment
* @param offset The offset of the substring within the text segment
* @param length The length of the substring
*/
public byte lookup(Segment text, int offset, int length)
{
if(length == 0)
return Token.NULL;
Keyword k = map[getSegmentMapKey(text, offset, length)];
while(k != null)
{
if(length != k.keyword.length)
{
k = k.next;
continue;
}
if(SyntaxUtilities.regionMatches(ignoreCase,text,offset,
k.keyword))
return k.id;
k = k.next;
}
return Token.NULL;
}
/**
* Adds a key-value mapping.
* @param keyword The key
* @Param id The value
*/
public void add(String keyword, byte id)
{
int key = getStringMapKey(keyword);
map[key] = new Keyword(keyword.toCharArray(),id,map[key]);
}
/**
* Returns true if the keyword map is set to be case insensitive,
* false otherwise.
*/
public boolean getIgnoreCase()
{
return ignoreCase;
}
/**
* Sets if the keyword map should be case insensitive.
* @param ignoreCase True if the keyword map should be case
* insensitive, false otherwise
*/
public void setIgnoreCase(boolean ignoreCase)
{
this.ignoreCase = ignoreCase;
}
// protected members
protected int mapLength;
protected int getStringMapKey(String s)
{
return (Character.toUpperCase(s.charAt(0)) +
Character.toUpperCase(s.charAt(s.length()-1)))
% mapLength;
}
protected int getSegmentMapKey(Segment s, int off, int len)
{
return (Character.toUpperCase(s.array[off]) +
Character.toUpperCase(s.array[off + len - 1]))
% mapLength;
}
// private members
class Keyword
{
public Keyword(char[] keyword, byte id, Keyword next)
{
this.keyword = keyword;
this.id = id;
this.next = next;
}
public char[] keyword;
public byte id;
public Keyword next;
}
private Keyword[] map;
private boolean ignoreCase;
}

View File

@@ -0,0 +1,170 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
PdeEditorListener - filter key events for tab expansion/indent/etc
Part of the Processing project - http://processing.org
Except where noted, code is written by Ben Fry and
Copyright (c) 2001-04 Massachusetts Institute of Technology
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.syntax;
import processing.app.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
public class PdeEditorListener {
PdeEditor editor;
JEditTextArea textarea;
boolean externalEditor;
boolean expandTabs;
String tabString;
boolean autoIndent;
int selectionStart, selectionEnd;
int position;
public PdeEditorListener(PdeEditor editor, JEditTextArea textarea) {
this.editor = editor;
this.textarea = textarea;
// let him know that i'm leechin'
textarea.editorListener = this;
applyPreferences();
}
public void applyPreferences() {
expandTabs = PdePreferences.getBoolean("editor.tabs.expand");
int tabSize = PdePreferences.getInteger("editor.tabs.size");
tabString = PdeEditor.EMPTY.substring(0, tabSize);
autoIndent = PdePreferences.getBoolean("editor.indent");
externalEditor = PdePreferences.getBoolean("editor.external");
}
//public void setExternalEditor(boolean externalEditor) {
//this.externalEditor = externalEditor;
//}
// called by JEditTextArea inside processKeyEvent
public boolean keyPressed(KeyEvent event) {
// don't do things if the textarea isn't editable
if (externalEditor) return false;
//deselect(); // this is for paren balancing
char c = event.getKeyChar();
int code = event.getKeyCode();
//System.out.println(c + " " + code + " " + event);
//System.out.println();
if ((event.getModifiers() & KeyEvent.META_MASK) != 0) {
//event.consume(); // does nothing
return false;
}
// TODO i don't like these accessors. clean em up later.
if (!editor.sketch.current.modified) {
if ((code == KeyEvent.VK_BACK_SPACE) || (code == KeyEvent.VK_TAB) ||
(code == KeyEvent.VK_ENTER) || ((c >= 32) && (c < 128))) {
editor.sketch.setModified();
}
}
switch ((int) c) {
case 9: // expand tabs
if (expandTabs) {
//tc.replaceSelection(tabString);
textarea.setSelectedText(tabString);
event.consume();
return true;
}
break;
case 10: // auto-indent
case 13:
if (autoIndent) {
char contents[] = textarea.getText().toCharArray();
// this is the position of the caret, if the textarea
// only used a single kind of line ending
int origIndex = textarea.getCaretPosition() - 1;
// walk through the array to the current caret position,
// and count how many weirdo windows line endings there are,
// which would be throwing off the caret position number
int offset = 0;
int realIndex = origIndex;
for (int i = 0; i < realIndex-1; i++) {
if ((contents[i] == 13) && (contents[i+1] == 10)) {
offset++;
realIndex++;
}
}
// back up until \r \r\n or \n.. @#($* cross platform
//System.out.println(origIndex + " offset = " + offset);
origIndex += offset; // ARGH!#(* WINDOWS#@($*
int index = origIndex;
int spaceCount = 0;
boolean finished = false;
while ((index != -1) && (!finished)) {
if ((contents[index] == 10) ||
(contents[index] == 13)) {
finished = true;
index++; // maybe ?
} else {
index--; // new
}
}
while ((index < contents.length) && (index >= 0) &&
(contents[index++] == ' ')) {
spaceCount++;
}
// seems that \r is being inserted anyway
// so no need to insert the platform's line separator
String insertion = "\n" + PdeEditor.EMPTY.substring(0, spaceCount);
//tc.replaceSelection(insertion);
textarea.setSelectedText(insertion);
// microsoft vm version:
//tc.setCaretPosition(oldCarrot + insertion.length() - 1);
// sun vm version:
// tc.setCaretPosition(oldCarrot + insertion.length());
event.consume();
return true;
}
break;
}
return false;
}
}

View File

@@ -2,10 +2,10 @@
/*
PdeKeywords - handles text coloring and links to html reference
Part of the Processing project - http://Proce55ing.net
Part of the Processing project - http://processing.org
Except where noted, code is written by Ben Fry and
Copyright (c) 2001-03 Massachusetts Institute of Technology
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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
@@ -22,6 +22,10 @@
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app.syntax;
import processing.app.*;
import java.io.*;
import java.util.*;

View File

@@ -0,0 +1,106 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
PdeTextAreaDefaults - grabs font/color settings for the editor
Part of the Processing project - http://Proce55ing.net
Except where noted, code is written by Ben Fry
Copyright (c) 2001-03 Massachusetts Institute of Technology
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.syntax;
import processing.app.*;
public class PdeTextAreaDefaults extends TextAreaDefaults {
public PdeTextAreaDefaults() {
inputHandler = new DefaultInputHandler();
inputHandler.addDefaultKeyBindings();
document = new SyntaxDocument();
editable = true;
electricScroll = 3;
cols = 80;
rows = 15;
// moved from SyntaxUtilities
//DEFAULTS.styles = SyntaxUtilities.getDefaultSyntaxStyles();
styles = new SyntaxStyle[Token.ID_COUNT];
// comments
styles[Token.COMMENT1] = PdePreferences.getStyle("comment1");
styles[Token.COMMENT2] = PdePreferences.getStyle("comment2");
// abstract, final, private
styles[Token.KEYWORD1] = PdePreferences.getStyle("keyword1");
// beginShape, point, line
styles[Token.KEYWORD2] = PdePreferences.getStyle("keyword2");
// byte, char, short, color
styles[Token.KEYWORD3] = PdePreferences.getStyle("keyword3");
// constants: null, true, this, RGB, TWO_PI
styles[Token.LITERAL1] = PdePreferences.getStyle("literal1");
// p5 built in variables: mouseX, width, pixels
styles[Token.LITERAL2] = PdePreferences.getStyle("literal2");
// ??
styles[Token.LABEL] = PdePreferences.getStyle("label");
// + - = /
styles[Token.OPERATOR] = PdePreferences.getStyle("operator");
// area that's not in use by the text (replaced with tildes)
styles[Token.INVALID] = PdePreferences.getStyle("invalid");
// moved from TextAreaPainter
font = PdePreferences.getFont("editor.font");
fgcolor = PdePreferences.getColor("editor.fgcolor");
bgcolor = PdePreferences.getColor("editor.bgcolor");
caretVisible = true;
caretBlinks = PdePreferences.getBoolean("editor.caret.blink");
caretColor = PdePreferences.getColor("editor.caret.color");
selectionColor = PdePreferences.getColor("editor.selection.color");
lineHighlight =
PdePreferences.getBoolean("editor.linehighlight");
lineHighlightColor =
PdePreferences.getColor("editor.linehighlight.color");
bracketHighlight =
PdePreferences.getBoolean("editor.brackethighlight");
bracketHighlightColor =
PdePreferences.getColor("editor.brackethighlight.color");
eolMarkers = PdePreferences.getBoolean("editor.eolmarkers");
eolMarkerColor = PdePreferences.getColor("editor.eolmarkers.color");
paintInvalid = PdePreferences.getBoolean("editor.invalid");
}
}

View File

@@ -0,0 +1,166 @@
/*
* SyntaxDocument.java - Document that can be tokenized
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.undo.UndoableEdit;
/**
* A document implementation that can be tokenized by the syntax highlighting
* system.
*
* @author Slava Pestov
* @version $Id$
*/
public class SyntaxDocument extends PlainDocument
{
/**
* Returns the token marker that is to be used to split lines
* of this document up into tokens. May return null if this
* document is not to be colorized.
*/
public TokenMarker getTokenMarker()
{
return tokenMarker;
}
/**
* Sets the token marker that is to be used to split lines of
* this document up into tokens. May throw an exception if
* this is not supported for this type of document.
* @param tm The new token marker
*/
public void setTokenMarker(TokenMarker tm)
{
tokenMarker = tm;
if(tm == null)
return;
tokenMarker.insertLines(0,getDefaultRootElement()
.getElementCount());
tokenizeLines();
}
/**
* Reparses the document, by passing all lines to the token
* marker. This should be called after the document is first
* loaded.
*/
public void tokenizeLines()
{
tokenizeLines(0,getDefaultRootElement().getElementCount());
}
/**
* Reparses the document, by passing the specified lines to the
* token marker. This should be called after a large quantity of
* text is first inserted.
* @param start The first line to parse
* @param len The number of lines, after the first one to parse
*/
public void tokenizeLines(int start, int len)
{
if(tokenMarker == null || !tokenMarker.supportsMultilineTokens())
return;
Segment lineSegment = new Segment();
Element map = getDefaultRootElement();
len += start;
try
{
for(int i = start; i < len; i++)
{
Element lineElement = map.getElement(i);
int lineStart = lineElement.getStartOffset();
getText(lineStart,lineElement.getEndOffset()
- lineStart - 1,lineSegment);
tokenMarker.markTokens(lineSegment,i);
}
}
catch(BadLocationException bl)
{
bl.printStackTrace();
}
}
/**
* Starts a compound edit that can be undone in one operation.
* Subclasses that implement undo should override this method;
* this class has no undo functionality so this method is
* empty.
*/
public void beginCompoundEdit() {}
/**
* Ends a compound edit that can be undone in one operation.
* Subclasses that implement undo should override this method;
* this class has no undo functionality so this method is
* empty.
*/
public void endCompoundEdit() {}
/**
* Adds an undoable edit to this document's undo list. The edit
* should be ignored if something is currently being undone.
* @param edit The undoable edit
*
* @since jEdit 2.2pre1
*/
public void addUndoableEdit(UndoableEdit edit) {}
// protected members
protected TokenMarker tokenMarker;
/**
* We overwrite this method to update the token marker
* state immediately so that any event listeners get a
* consistent token marker.
*/
protected void fireInsertUpdate(DocumentEvent evt)
{
if(tokenMarker != null)
{
DocumentEvent.ElementChange ch = evt.getChange(
getDefaultRootElement());
if(ch != null)
{
tokenMarker.insertLines(ch.getIndex() + 1,
ch.getChildrenAdded().length -
ch.getChildrenRemoved().length);
}
}
super.fireInsertUpdate(evt);
}
/**
* We overwrite this method to update the token marker
* state immediately so that any event listeners get a
* consistent token marker.
*/
protected void fireRemoveUpdate(DocumentEvent evt)
{
if(tokenMarker != null)
{
DocumentEvent.ElementChange ch = evt.getChange(
getDefaultRootElement());
if(ch != null)
{
tokenMarker.deleteLines(ch.getIndex() + 1,
ch.getChildrenRemoved().length -
ch.getChildrenAdded().length);
}
}
super.fireRemoveUpdate(evt);
}
}

137
app/syntax/SyntaxStyle.java Normal file
View File

@@ -0,0 +1,137 @@
/*
* SyntaxStyle.java - A simple text style class
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import java.awt.*;
import java.util.StringTokenizer;
/**
* A simple text style class. It can specify the color, italic flag,
* and bold flag of a run of text.
* @author Slava Pestov
* @version $Id$
*/
public class SyntaxStyle
{
/**
* Creates a new SyntaxStyle.
* @param color The text color
* @param italic True if the text should be italics
* @param bold True if the text should be bold
*/
public SyntaxStyle(Color color, boolean italic, boolean bold)
{
this.color = color;
this.italic = italic;
this.bold = bold;
}
/**
* Returns the color specified in this style.
*/
public Color getColor()
{
return color;
}
/**
* Returns true if no font styles are enabled.
*/
public boolean isPlain()
{
return !(bold || italic);
}
/**
* Returns true if italics is enabled for this style.
*/
public boolean isItalic()
{
return italic;
}
/**
* Returns true if boldface is enabled for this style.
*/
public boolean isBold()
{
return bold;
}
/**
* Returns the specified font, but with the style's bold and
* italic flags applied.
*/
public Font getStyledFont(Font font)
{
if(font == null)
throw new NullPointerException("font param must not"
+ " be null");
if(font.equals(lastFont))
return lastStyledFont;
lastFont = font;
lastStyledFont = new Font(font.getFamily(),
(bold ? Font.BOLD : 0)
| (italic ? Font.ITALIC : 0),
font.getSize());
return lastStyledFont;
}
/**
* Returns the font metrics for the styled font.
*/
public FontMetrics getFontMetrics(Font font)
{
if(font == null)
throw new NullPointerException("font param must not"
+ " be null");
if(font.equals(lastFont) && fontMetrics != null)
return fontMetrics;
lastFont = font;
lastStyledFont = new Font(font.getFamily(),
(bold ? Font.BOLD : 0)
| (italic ? Font.ITALIC : 0),
font.getSize());
fontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(
lastStyledFont);
return fontMetrics;
}
/**
* Sets the foreground color and font of the specified graphics
* context to that specified in this style.
* @param gfx The graphics context
* @param font The font to add the styles to
*/
public void setGraphicsFlags(Graphics gfx, Font font)
{
Font _font = getStyledFont(font);
gfx.setFont(_font);
gfx.setColor(color);
}
/**
* Returns a string representation of this object.
*/
public String toString()
{
return getClass().getName() + "[color=" + color +
(italic ? ",italic" : "") +
(bold ? ",bold" : "") + "]";
}
// private members
private Color color;
private boolean italic;
private boolean bold;
private Font lastFont;
private Font lastStyledFont;
private FontMetrics fontMetrics;
}

View File

@@ -0,0 +1,163 @@
/*
* SyntaxUtilities.java - Utility functions used by syntax colorizing
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import javax.swing.text.*;
import java.awt.*;
/**
* Class with several utility functions used by jEdit's syntax colorizing
* subsystem.
*
* @author Slava Pestov
* @version $Id$
*/
public class SyntaxUtilities
{
/**
* Checks if a subregion of a <code>Segment</code> is equal to a
* string.
* @param ignoreCase True if case should be ignored, false otherwise
* @param text The segment
* @param offset The offset into the segment
* @param match The string to match
*/
public static boolean regionMatches(boolean ignoreCase, Segment text,
int offset, String match)
{
int length = offset + match.length();
char[] textArray = text.array;
if(length > text.offset + text.count)
return false;
for(int i = offset, j = 0; i < length; i++, j++)
{
char c1 = textArray[i];
char c2 = match.charAt(j);
if(ignoreCase)
{
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
}
if(c1 != c2)
return false;
}
return true;
}
/**
* Checks if a subregion of a <code>Segment</code> is equal to a
* character array.
* @param ignoreCase True if case should be ignored, false otherwise
* @param text The segment
* @param offset The offset into the segment
* @param match The character array to match
*/
public static boolean regionMatches(boolean ignoreCase, Segment text,
int offset, char[] match)
{
int length = offset + match.length;
char[] textArray = text.array;
if(length > text.offset + text.count)
return false;
for(int i = offset, j = 0; i < length; i++, j++)
{
char c1 = textArray[i];
char c2 = match[j];
if(ignoreCase)
{
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
}
if(c1 != c2)
return false;
}
return true;
}
/**
* Returns the default style table. This can be passed to the
* <code>setStyles()</code> method of <code>SyntaxDocument</code>
* to use the default syntax styles.
*/
public static SyntaxStyle[] getDefaultSyntaxStyles()
{
SyntaxStyle[] styles = new SyntaxStyle[Token.ID_COUNT];
styles[Token.COMMENT1] = new SyntaxStyle(Color.black,true,false);
styles[Token.COMMENT2] = new SyntaxStyle(new Color(0x990033),true,false);
styles[Token.KEYWORD1] = new SyntaxStyle(Color.black,false,true);
styles[Token.KEYWORD2] = new SyntaxStyle(Color.magenta,false,false);
styles[Token.KEYWORD3] = new SyntaxStyle(new Color(0x009600),false,false);
styles[Token.LITERAL1] = new SyntaxStyle(new Color(0x650099),false,false);
styles[Token.LITERAL2] = new SyntaxStyle(new Color(0x650099),false,true);
styles[Token.LABEL] = new SyntaxStyle(new Color(0x990033),false,true);
styles[Token.OPERATOR] = new SyntaxStyle(Color.black,false,true);
styles[Token.INVALID] = new SyntaxStyle(Color.red,false,true);
return styles;
}
/**
* Paints the specified line onto the graphics context. Note that this
* method munges the offset and count values of the segment.
* @param line The line segment
* @param tokens The token list for the line
* @param styles The syntax style list
* @param expander The tab expander used to determine tab stops. May
* be null
* @param gfx The graphics context
* @param x The x co-ordinate
* @param y The y co-ordinate
* @return The x co-ordinate, plus the width of the painted string
*/
public static int paintSyntaxLine(Segment line, Token tokens,
SyntaxStyle[] styles,
TabExpander expander, Graphics gfx,
int x, int y)
{
Font defaultFont = gfx.getFont();
Color defaultColor = gfx.getColor();
int offset = 0;
for(;;)
{
byte id = tokens.id;
if(id == Token.END)
break;
int length = tokens.length;
if(id == Token.NULL)
{
if(!defaultColor.equals(gfx.getColor()))
gfx.setColor(defaultColor);
if(!defaultFont.equals(gfx.getFont()))
gfx.setFont(defaultFont);
}
else
styles[id].setGraphicsFlags(gfx,defaultFont);
line.count = length;
x = Utilities.drawTabbedText(line,x,y,gfx,expander,0);
line.offset += length;
offset += length;
tokens = tokens.next;
}
return x;
}
// private members
private SyntaxUtilities() {}
}

View File

@@ -0,0 +1,90 @@
/*
* TextAreaDefaults.java - Encapsulates default values for various settings
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import java.awt.*;
//import javax.swing.JPopupMenu;
/**
* Encapsulates default settings for a text area. This can be passed
* to the constructor once the necessary fields have been filled out.
* The advantage of doing this over calling lots of set() methods after
* creating the text area is that this method is faster.
*/
public class TextAreaDefaults
{
private static TextAreaDefaults DEFAULTS;
public InputHandler inputHandler;
public SyntaxDocument document;
public boolean editable;
public boolean caretVisible;
public boolean caretBlinks;
public boolean blockCaret;
public int electricScroll;
public int cols;
public int rows;
public SyntaxStyle[] styles;
public Color caretColor;
public Color selectionColor;
public Color lineHighlightColor;
public boolean lineHighlight;
public Color bracketHighlightColor;
public boolean bracketHighlight;
public Color eolMarkerColor;
public boolean eolMarkers;
public boolean paintInvalid;
// moved from TextAreaPainter [fry]
public Font font;
public Color fgcolor;
public Color bgcolor;
//public JPopupMenu popup;
/**
* Returns a new TextAreaDefaults object with the default values filled
* in.
*/
public static TextAreaDefaults getDefaults()
{
if (DEFAULTS == null) {
DEFAULTS = new TextAreaDefaults();
DEFAULTS.inputHandler = new DefaultInputHandler();
DEFAULTS.inputHandler.addDefaultKeyBindings();
DEFAULTS.document = new SyntaxDocument();
DEFAULTS.editable = true;
DEFAULTS.caretVisible = true;
DEFAULTS.caretBlinks = true;
DEFAULTS.electricScroll = 3;
DEFAULTS.cols = 80;
DEFAULTS.rows = 25;
DEFAULTS.styles = SyntaxUtilities.getDefaultSyntaxStyles();
DEFAULTS.caretColor = Color.red;
DEFAULTS.selectionColor = new Color(0xccccff);
DEFAULTS.lineHighlightColor = new Color(0xe0e0e0);
DEFAULTS.lineHighlight = true;
DEFAULTS.bracketHighlightColor = Color.black;
DEFAULTS.bracketHighlight = true;
DEFAULTS.eolMarkerColor = new Color(0x009999);
DEFAULTS.eolMarkers = true;
DEFAULTS.paintInvalid = true;
}
return DEFAULTS;
}
}

View File

@@ -0,0 +1,680 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
* TextAreaPainter.java - Paints the text area
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import processing.app.*;
import javax.swing.ToolTipManager;
import javax.swing.text.*;
import javax.swing.JComponent;
import java.awt.event.MouseEvent;
import java.awt.*;
/**
* The text area repaint manager. It performs double buffering and paints
* lines of text.
* @author Slava Pestov
* @version $Id$
*/
public class TextAreaPainter extends JComponent implements TabExpander
{
/**
* Creates a new repaint manager. This should be not be called
* directly.
*/
public TextAreaPainter(JEditTextArea textArea, TextAreaDefaults defaults)
{
this.textArea = textArea;
setAutoscrolls(true);
setDoubleBuffered(true);
setOpaque(true);
ToolTipManager.sharedInstance().registerComponent(this);
currentLine = new Segment();
currentLineIndex = -1;
setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
setFont(defaults.font);
setForeground(defaults.fgcolor);
setBackground(defaults.bgcolor);
blockCaret = defaults.blockCaret;
styles = defaults.styles;
cols = defaults.cols;
rows = defaults.rows;
caretColor = defaults.caretColor;
selectionColor = defaults.selectionColor;
lineHighlightColor = defaults.lineHighlightColor;
lineHighlight = defaults.lineHighlight;
bracketHighlightColor = defaults.bracketHighlightColor;
bracketHighlight = defaults.bracketHighlight;
paintInvalid = defaults.paintInvalid;
eolMarkerColor = defaults.eolMarkerColor;
eolMarkers = defaults.eolMarkers;
}
/**
* Returns if this component can be traversed by pressing the
* Tab key. This returns false.
*/
public final boolean isManagingFocus()
{
return false;
}
/**
* Returns the syntax styles used to paint colorized text. Entry <i>n</i>
* will be used to paint tokens with id = <i>n</i>.
* @see org.gjt.sp.jedit.syntax.Token
*/
public final SyntaxStyle[] getStyles()
{
return styles;
}
/**
* Sets the syntax styles used to paint colorized text. Entry <i>n</i>
* will be used to paint tokens with id = <i>n</i>.
* @param styles The syntax styles
* @see org.gjt.sp.jedit.syntax.Token
*/
public final void setStyles(SyntaxStyle[] styles)
{
this.styles = styles;
repaint();
}
/**
* Returns the caret color.
*/
public final Color getCaretColor()
{
return caretColor;
}
/**
* Sets the caret color.
* @param caretColor The caret color
*/
public final void setCaretColor(Color caretColor)
{
this.caretColor = caretColor;
invalidateSelectedLines();
}
/**
* Returns the selection color.
*/
public final Color getSelectionColor()
{
return selectionColor;
}
/**
* Sets the selection color.
* @param selectionColor The selection color
*/
public final void setSelectionColor(Color selectionColor)
{
this.selectionColor = selectionColor;
invalidateSelectedLines();
}
/**
* Returns the line highlight color.
*/
public final Color getLineHighlightColor()
{
return lineHighlightColor;
}
/**
* Sets the line highlight color.
* @param lineHighlightColor The line highlight color
*/
public final void setLineHighlightColor(Color lineHighlightColor)
{
this.lineHighlightColor = lineHighlightColor;
invalidateSelectedLines();
}
/**
* Returns true if line highlight is enabled, false otherwise.
*/
public final boolean isLineHighlightEnabled()
{
return lineHighlight;
}
/**
* Enables or disables current line highlighting.
* @param lineHighlight True if current line highlight
* should be enabled, false otherwise
*/
public final void setLineHighlightEnabled(boolean lineHighlight)
{
this.lineHighlight = lineHighlight;
invalidateSelectedLines();
}
/**
* Returns the bracket highlight color.
*/
public final Color getBracketHighlightColor()
{
return bracketHighlightColor;
}
/**
* Sets the bracket highlight color.
* @param bracketHighlightColor The bracket highlight color
*/
public final void setBracketHighlightColor(Color bracketHighlightColor)
{
this.bracketHighlightColor = bracketHighlightColor;
invalidateLine(textArea.getBracketLine());
}
/**
* Returns true if bracket highlighting is enabled, false otherwise.
* When bracket highlighting is enabled, the bracket matching the
* one before the caret (if any) is highlighted.
*/
public final boolean isBracketHighlightEnabled()
{
return bracketHighlight;
}
/**
* Enables or disables bracket highlighting.
* When bracket highlighting is enabled, the bracket matching the
* one before the caret (if any) is highlighted.
* @param bracketHighlight True if bracket highlighting should be
* enabled, false otherwise
*/
public final void setBracketHighlightEnabled(boolean bracketHighlight)
{
this.bracketHighlight = bracketHighlight;
invalidateLine(textArea.getBracketLine());
}
/**
* Returns true if the caret should be drawn as a block, false otherwise.
*/
public final boolean isBlockCaretEnabled()
{
return blockCaret;
}
/**
* Sets if the caret should be drawn as a block, false otherwise.
* @param blockCaret True if the caret should be drawn as a block,
* false otherwise.
*/
public final void setBlockCaretEnabled(boolean blockCaret)
{
this.blockCaret = blockCaret;
invalidateSelectedLines();
}
/**
* Returns the EOL marker color.
*/
public final Color getEOLMarkerColor()
{
return eolMarkerColor;
}
/**
* Sets the EOL marker color.
* @param eolMarkerColor The EOL marker color
*/
public final void setEOLMarkerColor(Color eolMarkerColor)
{
this.eolMarkerColor = eolMarkerColor;
repaint();
}
/**
* Returns true if EOL markers are drawn, false otherwise.
*/
public final boolean getEOLMarkersPainted()
{
return eolMarkers;
}
/**
* Sets if EOL markers are to be drawn.
* @param eolMarkers True if EOL markers should be drawn, false otherwise
*/
public final void setEOLMarkersPainted(boolean eolMarkers)
{
this.eolMarkers = eolMarkers;
repaint();
}
/**
* Returns true if invalid lines are painted as red tildes (~),
* false otherwise.
*/
public boolean getInvalidLinesPainted()
{
return paintInvalid;
}
/**
* Sets if invalid lines are to be painted as red tildes.
* @param paintInvalid True if invalid lines should be drawn, false otherwise
*/
public void setInvalidLinesPainted(boolean paintInvalid)
{
this.paintInvalid = paintInvalid;
}
/**
* Adds a custom highlight painter.
* @param highlight The highlight
*/
public void addCustomHighlight(Highlight highlight)
{
highlight.init(textArea,highlights);
highlights = highlight;
}
/**
* Highlight interface.
*/
public interface Highlight
{
/**
* Called after the highlight painter has been added.
* @param textArea The text area
* @param next The painter this one should delegate to
*/
void init(JEditTextArea textArea, Highlight next);
/**
* This should paint the highlight and delgate to the
* next highlight painter.
* @param gfx The graphics context
* @param line The line number
* @param y The y co-ordinate of the line
*/
void paintHighlight(Graphics gfx, int line, int y);
/**
* Returns the tool tip to display at the specified
* location. If this highlighter doesn't know what to
* display, it should delegate to the next highlight
* painter.
* @param evt The mouse event
*/
String getToolTipText(MouseEvent evt);
}
/**
* Returns the tool tip to display at the specified location.
* @param evt The mouse event
*/
public String getToolTipText(MouseEvent evt)
{
if(highlights != null)
return highlights.getToolTipText(evt);
else
return null;
}
/**
* Returns the font metrics used by this component.
*/
public FontMetrics getFontMetrics()
{
return fm;
}
/**
* Sets the font for this component. This is overridden to update the
* cached font metrics and to recalculate which lines are visible.
* @param font The font
*/
public void setFont(Font font)
{
super.setFont(font);
fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
textArea.recalculateVisibleLines();
}
/**
* Repaints the text.
* @param g The graphics context
*/
public void paint(Graphics gfx)
{
if (PdeBase.isMacOS()) {
Graphics2D g2 = (Graphics2D) gfx;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
}
tabSize = fm.charWidth(' ') * ((Integer)textArea.getDocument().getProperty(PlainDocument.tabSizeAttribute)).intValue();
Rectangle clipRect = gfx.getClipBounds();
gfx.setColor(getBackground());
gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
// We don't use yToLine() here because that method doesn't
// return lines past the end of the document
int height = fm.getHeight();
int firstLine = textArea.getFirstLine();
int firstInvalid = firstLine + clipRect.y / height;
// Because the clipRect's height is usually an even multiple
// of the font height, we subtract 1 from it, otherwise one
// too many lines will always be painted.
int lastInvalid = firstLine + (clipRect.y + clipRect.height - 1) / height;
try {
TokenMarker tokenMarker = textArea.getDocument().getTokenMarker();
int x = textArea.getHorizontalOffset();
for (int line = firstInvalid; line <= lastInvalid; line++) {
paintLine(gfx,tokenMarker,line,x);
}
if (tokenMarker != null && tokenMarker.isNextLineRequested()) {
int h = clipRect.y + clipRect.height;
repaint(0,h,getWidth(),getHeight() - h);
}
} catch (Exception e) {
System.err.println("Error repainting line"
+ " range {" + firstInvalid + ","
+ lastInvalid + "}:");
e.printStackTrace();
}
}
/**
* Marks a line as needing a repaint.
* @param line The line to invalidate
*/
public final void invalidateLine(int line)
{
repaint(0,textArea.lineToY(line) + fm.getMaxDescent() + fm.getLeading(),
getWidth(),fm.getHeight());
}
/**
* Marks a range of lines as needing a repaint.
* @param firstLine The first line to invalidate
* @param lastLine The last line to invalidate
*/
public final void invalidateLineRange(int firstLine, int lastLine)
{
repaint(0,textArea.lineToY(firstLine) +
fm.getMaxDescent() + fm.getLeading(),
getWidth(),(lastLine - firstLine + 1) * fm.getHeight());
}
/**
* Repaints the lines containing the selection.
*/
public final void invalidateSelectedLines()
{
invalidateLineRange(textArea.getSelectionStartLine(),
textArea.getSelectionEndLine());
}
/**
* Implementation of TabExpander interface. Returns next tab stop after
* a specified point.
* @param x The x co-ordinate
* @param tabOffset Ignored
* @return The next tab stop after <i>x</i>
*/
public float nextTabStop(float x, int tabOffset)
{
int offset = textArea.getHorizontalOffset();
int ntabs = ((int)x - offset) / tabSize;
return (ntabs + 1) * tabSize + offset;
}
/**
* Returns the painter's preferred size.
*/
public Dimension getPreferredSize()
{
Dimension dim = new Dimension();
dim.width = fm.charWidth('w') * cols;
dim.height = fm.getHeight() * rows;
return dim;
}
/**
* Returns the painter's minimum size.
*/
public Dimension getMinimumSize()
{
return getPreferredSize();
}
// package-private members
int currentLineIndex;
Token currentLineTokens;
Segment currentLine;
// protected members
protected JEditTextArea textArea;
protected SyntaxStyle[] styles;
protected Color caretColor;
protected Color selectionColor;
protected Color lineHighlightColor;
protected Color bracketHighlightColor;
protected Color eolMarkerColor;
protected boolean blockCaret;
protected boolean lineHighlight;
protected boolean bracketHighlight;
protected boolean paintInvalid;
protected boolean eolMarkers;
protected int cols;
protected int rows;
protected int tabSize;
protected FontMetrics fm;
protected Highlight highlights;
protected void paintLine(Graphics gfx, TokenMarker tokenMarker,
int line, int x)
{
Font defaultFont = getFont();
Color defaultColor = getForeground();
currentLineIndex = line;
int y = textArea.lineToY(line);
if (line < 0 || line >= textArea.getLineCount()) {
if (paintInvalid) {
paintHighlight(gfx,line,y);
styles[Token.INVALID].setGraphicsFlags(gfx,defaultFont);
gfx.drawString("~",0,y + fm.getHeight());
}
} else if(tokenMarker == null) {
paintPlainLine(gfx,line,defaultFont,defaultColor,x,y);
} else {
paintSyntaxLine(gfx,tokenMarker,line,defaultFont,
defaultColor,x,y);
}
}
protected void paintPlainLine(Graphics gfx, int line, Font defaultFont,
Color defaultColor, int x, int y)
{
paintHighlight(gfx,line,y);
textArea.getLineText(line,currentLine);
gfx.setFont(defaultFont);
gfx.setColor(defaultColor);
y += fm.getHeight();
x = Utilities.drawTabbedText(currentLine,x,y,gfx,this,0);
if (eolMarkers) {
gfx.setColor(eolMarkerColor);
gfx.drawString(".",x,y);
}
}
protected void paintSyntaxLine(Graphics gfx, TokenMarker tokenMarker,
int line, Font defaultFont,
Color defaultColor, int x, int y)
{
textArea.getLineText(currentLineIndex,currentLine);
currentLineTokens = tokenMarker.markTokens(currentLine,
currentLineIndex);
paintHighlight(gfx,line,y);
gfx.setFont(defaultFont);
gfx.setColor(defaultColor);
y += fm.getHeight();
x = SyntaxUtilities.paintSyntaxLine(currentLine,
currentLineTokens,
styles, this, gfx, x, y);
if (eolMarkers) {
gfx.setColor(eolMarkerColor);
gfx.drawString(".",x,y);
}
}
protected void paintHighlight(Graphics gfx, int line, int y)
{
if (line >= textArea.getSelectionStartLine()
&& line <= textArea.getSelectionEndLine())
paintLineHighlight(gfx,line,y);
if (highlights != null)
highlights.paintHighlight(gfx,line,y);
if (bracketHighlight && line == textArea.getBracketLine())
paintBracketHighlight(gfx,line,y);
if (line == textArea.getCaretLine())
paintCaret(gfx,line,y);
}
protected void paintLineHighlight(Graphics gfx, int line, int y)
{
int height = fm.getHeight();
y += fm.getLeading() + fm.getMaxDescent();
int selectionStart = textArea.getSelectionStart();
int selectionEnd = textArea.getSelectionEnd();
if (selectionStart == selectionEnd) {
if (lineHighlight) {
gfx.setColor(lineHighlightColor);
gfx.fillRect(0,y,getWidth(),height);
}
} else {
gfx.setColor(selectionColor);
int selectionStartLine = textArea.getSelectionStartLine();
int selectionEndLine = textArea.getSelectionEndLine();
int lineStart = textArea.getLineStartOffset(line);
int x1, x2;
if (textArea.isSelectionRectangular()) {
int lineLen = textArea.getLineLength(line);
x1 = textArea._offsetToX(line,Math.min(lineLen, selectionStart - textArea.getLineStartOffset(selectionStartLine)));
x2 = textArea._offsetToX(line,Math.min(lineLen, selectionEnd - textArea.getLineStartOffset(selectionEndLine)));
if (x1 == x2)
x2++;
} else if(selectionStartLine == selectionEndLine) {
x1 = textArea._offsetToX(line, selectionStart - lineStart);
x2 = textArea._offsetToX(line, selectionEnd - lineStart);
} else if(line == selectionStartLine) {
x1 = textArea._offsetToX(line, selectionStart - lineStart);
x2 = getWidth();
} else if(line == selectionEndLine) {
x1 = 0;
x2 = textArea._offsetToX(line, selectionEnd - lineStart);
} else {
x1 = 0;
x2 = getWidth();
}
// "inlined" min/max()
gfx.fillRect(x1 > x2 ? x2 : x1,y,x1 > x2 ?
(x1 - x2) : (x2 - x1),height);
}
}
protected void paintBracketHighlight(Graphics gfx, int line, int y)
{
int position = textArea.getBracketPosition();
if(position == -1)
return;
y += fm.getLeading() + fm.getMaxDescent();
int x = textArea._offsetToX(line,position);
gfx.setColor(bracketHighlightColor);
// Hack!!! Since there is no fast way to get the character
// from the bracket matching routine, we use ( since all
// brackets probably have the same width anyway
gfx.drawRect(x,y,fm.charWidth('(') - 1,
fm.getHeight() - 1);
}
protected void paintCaret(Graphics gfx, int line, int y)
{
//System.out.println("painting caret " + line + " " + y);
if (textArea.isCaretVisible()) {
//System.out.println("caret is visible");
int offset =
textArea.getCaretPosition() - textArea.getLineStartOffset(line);
int caretX = textArea._offsetToX(line, offset);
int caretWidth = ((blockCaret ||
textArea.isOverwriteEnabled()) ?
fm.charWidth('w') : 1);
y += fm.getLeading() + fm.getMaxDescent();
int height = fm.getHeight();
//System.out.println("caretX, width = " + caretX + " " + caretWidth);
gfx.setColor(caretColor);
if (textArea.isOverwriteEnabled()) {
gfx.fillRect(caretX,y + height - 1, caretWidth,1);
} else {
// some machines don't like the drawRect for the single
// pixel caret.. this caused a lot of hell because on that
// minority of machines, the caret wouldn't show up past
// the first column. the fix is to use drawLine() in
// those cases, as a workaround.
if (caretWidth == 1) {
gfx.drawLine(caretX, y, caretX, y + height - 1);
} else {
gfx.drawRect(caretX, y, caretWidth - 1, height - 1);
}
//gfx.drawRect(caretX, y, caretWidth, height - 1);
}
}
}
}

View File

@@ -0,0 +1,184 @@
/*
* TextUtilities.java - Utility functions used by the text area classes
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import javax.swing.text.*;
/**
* Class with several utility functions used by the text area component.
* @author Slava Pestov
* @version $Id$
*/
public class TextUtilities
{
/**
* Returns the offset of the bracket matching the one at the
* specified offset of the document, or -1 if the bracket is
* unmatched (or if the character is not a bracket).
* @param doc The document
* @param offset The offset
* @exception BadLocationException If an out-of-bounds access
* was attempted on the document text
*/
public static int findMatchingBracket(Document doc, int offset)
throws BadLocationException
{
if(doc.getLength() == 0)
return -1;
char c = doc.getText(offset,1).charAt(0);
char cprime; // c` - corresponding character
boolean direction; // true = back, false = forward
switch(c)
{
case '(': cprime = ')'; direction = false; break;
case ')': cprime = '('; direction = true; break;
case '[': cprime = ']'; direction = false; break;
case ']': cprime = '['; direction = true; break;
case '{': cprime = '}'; direction = false; break;
case '}': cprime = '{'; direction = true; break;
default: return -1;
}
int count;
// How to merge these two cases is left as an exercise
// for the reader.
// Go back or forward
if(direction)
{
// Count is 1 initially because we have already
// `found' one closing bracket
count = 1;
// Get text[0,offset-1];
String text = doc.getText(0,offset);
// Scan backwards
for(int i = offset - 1; i >= 0; i--)
{
// If text[i] == c, we have found another
// closing bracket, therefore we will need
// two opening brackets to complete the
// match.
char x = text.charAt(i);
if(x == c)
count++;
// If text[i] == cprime, we have found a
// opening bracket, so we return i if
// --count == 0
else if(x == cprime)
{
if(--count == 0)
return i;
}
}
}
else
{
// Count is 1 initially because we have already
// `found' one opening bracket
count = 1;
// So we don't have to + 1 in every loop
offset++;
// Number of characters to check
int len = doc.getLength() - offset;
// Get text[offset+1,len];
String text = doc.getText(offset,len);
// Scan forwards
for(int i = 0; i < len; i++)
{
// If text[i] == c, we have found another
// opening bracket, therefore we will need
// two closing brackets to complete the
// match.
char x = text.charAt(i);
if(x == c)
count++;
// If text[i] == cprime, we have found an
// closing bracket, so we return i if
// --count == 0
else if(x == cprime)
{
if(--count == 0)
return i + offset;
}
}
}
// Nothing found
return -1;
}
/**
* Locates the start of the word at the specified position.
* @param line The text
* @param pos The position
*/
public static int findWordStart(String line, int pos, String noWordSep)
{
char ch = line.charAt(pos - 1);
if(noWordSep == null)
noWordSep = "";
boolean selectNoLetter = (!Character.isLetterOrDigit(ch)
&& noWordSep.indexOf(ch) == -1);
int wordStart = 0;
for(int i = pos - 1; i >= 0; i--)
{
ch = line.charAt(i);
if(selectNoLetter ^ (!Character.isLetterOrDigit(ch) &&
noWordSep.indexOf(ch) == -1))
{
wordStart = i + 1;
break;
}
}
return wordStart;
}
/**
* Locates the end of the word at the specified position.
* @param line The text
* @param pos The position
*/
public static int findWordEnd(String line, int pos, String noWordSep)
{
char ch = line.charAt(pos);
if(noWordSep == null)
noWordSep = "";
boolean selectNoLetter = (!Character.isLetterOrDigit(ch)
&& noWordSep.indexOf(ch) == -1);
int wordEnd = line.length();
for(int i = pos; i < line.length(); i++)
{
ch = line.charAt(i);
if(selectNoLetter ^ (!Character.isLetterOrDigit(ch) &&
noWordSep.indexOf(ch) == -1))
{
wordEnd = i;
break;
}
}
return wordEnd;
}
}

149
app/syntax/Token.java Normal file
View File

@@ -0,0 +1,149 @@
/*
* Token.java - Generic token
* Copyright (C) 1998, 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
/**
* A linked list of tokens. Each token has three fields - a token
* identifier, which is a byte value that can be looked up in the
* array returned by <code>SyntaxDocument.getColors()</code>
* to get a color value, a length value which is the length of the
* token in the text, and a pointer to the next token in the list.
*
* @author Slava Pestov
* @version $Id$
*/
public class Token
{
/**
* Normal text token id. This should be used to mark
* normal text.
*/
public static final byte NULL = 0;
/**
* Comment 1 token id. This can be used to mark a comment.
*/
public static final byte COMMENT1 = 1;
/**
* Comment 2 token id. This can be used to mark a comment.
*/
public static final byte COMMENT2 = 2;
/**
* Literal 1 token id. This can be used to mark a string
* literal (eg, C mode uses this to mark "..." literals)
*/
public static final byte LITERAL1 = 3;
/**
* Literal 2 token id. This can be used to mark an object
* literal (eg, Java mode uses this to mark true, false, etc)
*/
public static final byte LITERAL2 = 4;
/**
* Label token id. This can be used to mark labels
* (eg, C mode uses this to mark ...: sequences)
*/
public static final byte LABEL = 5;
/**
* Keyword 1 token id. This can be used to mark a
* keyword. This should be used for general language
* constructs.
*/
public static final byte KEYWORD1 = 6;
/**
* Keyword 2 token id. This can be used to mark a
* keyword. This should be used for preprocessor
* commands, or variables.
*/
public static final byte KEYWORD2 = 7;
/**
* Keyword 3 token id. This can be used to mark a
* keyword. This should be used for data types.
*/
public static final byte KEYWORD3 = 8;
/**
* Operator token id. This can be used to mark an
* operator. (eg, SQL mode marks +, -, etc with this
* token type)
*/
public static final byte OPERATOR = 9;
/**
* Invalid token id. This can be used to mark invalid
* or incomplete tokens, so the user can easily spot
* syntax errors.
*/
public static final byte INVALID = 10;
/**
* The total number of defined token ids.
*/
public static final byte ID_COUNT = 11;
/**
* The first id that can be used for internal state
* in a token marker.
*/
public static final byte INTERNAL_FIRST = 100;
/**
* The last id that can be used for internal state
* in a token marker.
*/
public static final byte INTERNAL_LAST = 126;
/**
* The token type, that along with a length of 0
* marks the end of the token list.
*/
public static final byte END = 127;
/**
* The length of this token.
*/
public int length;
/**
* The id of this token.
*/
public byte id;
/**
* The next token in the linked list.
*/
public Token next;
/**
* Creates a new token.
* @param length The length of the token
* @param id The id of the token
*/
public Token(int length, byte id)
{
this.length = length;
this.id = id;
}
/**
* Returns a string representation of this token.
*/
public String toString()
{
return "[id=" + id + ",length=" + length + "]";
}
}

345
app/syntax/TokenMarker.java Normal file
View File

@@ -0,0 +1,345 @@
/*
* TokenMarker.java - Generic token marker
* Copyright (C) 1998, 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import javax.swing.text.Segment;
import java.util.*;
/**
* A token marker that splits lines of text into tokens. Each token carries
* a length field and an indentification tag that can be mapped to a color
* for painting that token.<p>
*
* For performance reasons, the linked list of tokens is reused after each
* line is tokenized. Therefore, the return value of <code>markTokens</code>
* should only be used for immediate painting. Notably, it cannot be
* cached.
*
* @author Slava Pestov
* @version $Id$
*
* @see org.gjt.sp.jedit.syntax.Token
*/
public abstract class TokenMarker
{
/**
* A wrapper for the lower-level <code>markTokensImpl</code> method
* that is called to split a line up into tokens.
* @param line The line
* @param lineIndex The line number
*/
public Token markTokens(Segment line, int lineIndex)
{
if(lineIndex >= length)
{
throw new IllegalArgumentException("Tokenizing invalid line: "
+ lineIndex);
}
lastToken = null;
LineInfo info = lineInfo[lineIndex];
LineInfo prev;
if(lineIndex == 0)
prev = null;
else
prev = lineInfo[lineIndex - 1];
byte oldToken = info.token;
byte token = markTokensImpl(prev == null ?
Token.NULL : prev.token,line,lineIndex);
info.token = token;
/*
* This is a foul hack. It stops nextLineRequested
* from being cleared if the same line is marked twice.
*
* Why is this necessary? It's all JEditTextArea's fault.
* When something is inserted into the text, firing a
* document event, the insertUpdate() method shifts the
* caret (if necessary) by the amount inserted.
*
* All caret movement is handled by the select() method,
* which eventually pipes the new position to scrollTo()
* and calls repaint().
*
* Note that at this point in time, the new line hasn't
* yet been painted; the caret is moved first.
*
* scrollTo() calls offsetToX(), which tokenizes the line
* unless it is being called on the last line painted
* (in which case it uses the text area's painter cached
* token list). What scrollTo() does next is irrelevant.
*
* After scrollTo() has done it's job, repaint() is
* called, and eventually we end up in paintLine(), whose
* job is to paint the changed line. It, too, calls
* markTokens().
*
* The problem was that if the line started a multiline
* token, the first markTokens() (done in offsetToX())
* would set nextLineRequested (because the line end
* token had changed) but the second would clear it
* (because the line was the same that time) and therefore
* paintLine() would never know that it needed to repaint
* subsequent lines.
*
* This bug took me ages to track down, that's why I wrote
* all the relevant info down so that others wouldn't
* duplicate it.
*/
if(!(lastLine == lineIndex && nextLineRequested))
nextLineRequested = (oldToken != token);
lastLine = lineIndex;
addToken(0,Token.END);
return firstToken;
}
/**
* An abstract method that splits a line up into tokens. It
* should parse the line, and call <code>addToken()</code> to
* add syntax tokens to the token list. Then, it should return
* the initial token type for the next line.<p>
*
* For example if the current line contains the start of a
* multiline comment that doesn't end on that line, this method
* should return the comment token type so that it continues on
* the next line.
*
* @param token The initial token type for this line
* @param line The line to be tokenized
* @param lineIndex The index of the line in the document,
* starting at 0
* @return The initial token type for the next line
*/
protected abstract byte markTokensImpl(byte token, Segment line,
int lineIndex);
/**
* Returns if the token marker supports tokens that span multiple
* lines. If this is true, the object using this token marker is
* required to pass all lines in the document to the
* <code>markTokens()</code> method (in turn).<p>
*
* The default implementation returns true; it should be overridden
* to return false on simpler token markers for increased speed.
*/
public boolean supportsMultilineTokens()
{
return true;
}
/**
* Informs the token marker that lines have been inserted into
* the document. This inserts a gap in the <code>lineInfo</code>
* array.
* @param index The first line number
* @param lines The number of lines
*/
public void insertLines(int index, int lines)
{
if(lines <= 0)
return;
length += lines;
ensureCapacity(length);
int len = index + lines;
System.arraycopy(lineInfo,index,lineInfo,len,
lineInfo.length - len);
for(int i = index + lines - 1; i >= index; i--)
{
lineInfo[i] = new LineInfo();
}
}
/**
* Informs the token marker that line have been deleted from
* the document. This removes the lines in question from the
* <code>lineInfo</code> array.
* @param index The first line number
* @param lines The number of lines
*/
public void deleteLines(int index, int lines)
{
if (lines <= 0)
return;
int len = index + lines;
length -= lines;
System.arraycopy(lineInfo,len,lineInfo,
index,lineInfo.length - len);
}
/**
* Returns the number of lines in this token marker.
*/
public int getLineCount()
{
return length;
}
/**
* Returns true if the next line should be repainted. This
* will return true after a line has been tokenized that starts
* a multiline token that continues onto the next line.
*/
public boolean isNextLineRequested()
{
return nextLineRequested;
}
// protected members
/**
* The first token in the list. This should be used as the return
* value from <code>markTokens()</code>.
*/
protected Token firstToken;
/**
* The last token in the list. New tokens are added here.
* This should be set to null before a new line is to be tokenized.
*/
protected Token lastToken;
/**
* An array for storing information about lines. It is enlarged and
* shrunk automatically by the <code>insertLines()</code> and
* <code>deleteLines()</code> methods.
*/
protected LineInfo[] lineInfo;
/**
* The number of lines in the model being tokenized. This can be
* less than the length of the <code>lineInfo</code> array.
*/
protected int length;
/**
* The last tokenized line.
*/
protected int lastLine;
/**
* True if the next line should be painted.
*/
protected boolean nextLineRequested;
/**
* Creates a new <code>TokenMarker</code>. This DOES NOT create
* a lineInfo array; an initial call to <code>insertLines()</code>
* does that.
*/
protected TokenMarker()
{
lastLine = -1;
}
/**
* Ensures that the <code>lineInfo</code> array can contain the
* specified index. This enlarges it if necessary. No action is
* taken if the array is large enough already.<p>
*
* It should be unnecessary to call this under normal
* circumstances; <code>insertLine()</code> should take care of
* enlarging the line info array automatically.
*
* @param index The array index
*/
protected void ensureCapacity(int index)
{
if(lineInfo == null)
lineInfo = new LineInfo[index + 1];
else if(lineInfo.length <= index)
{
LineInfo[] lineInfoN = new LineInfo[(index + 1) * 2];
System.arraycopy(lineInfo,0,lineInfoN,0,
lineInfo.length);
lineInfo = lineInfoN;
}
}
/**
* Adds a token to the token list.
* @param length The length of the token
* @param id The id of the token
*/
protected void addToken(int length, byte id)
{
if(id >= Token.INTERNAL_FIRST && id <= Token.INTERNAL_LAST)
throw new InternalError("Invalid id: " + id);
if(length == 0 && id != Token.END)
return;
if(firstToken == null)
{
firstToken = new Token(length,id);
lastToken = firstToken;
}
else if(lastToken == null)
{
lastToken = firstToken;
firstToken.length = length;
firstToken.id = id;
}
else if(lastToken.next == null)
{
lastToken.next = new Token(length,id);
lastToken = lastToken.next;
}
else
{
lastToken = lastToken.next;
lastToken.length = length;
lastToken.id = id;
}
}
/**
* Inner class for storing information about tokenized lines.
*/
public class LineInfo
{
/**
* Creates a new LineInfo object with token = Token.NULL
* and obj = null.
*/
public LineInfo()
{
}
/**
* Creates a new LineInfo object with the specified
* parameters.
*/
public LineInfo(byte token, Object obj)
{
this.token = token;
this.obj = obj;
}
/**
* The id of the last token of the line.
*/
public byte token;
/**
* This is for use by the token marker implementations
* themselves. It can be used to store anything that
* is an object and that needs to exist on a per-line
* basis.
*/
public Object obj;
}
}

46
app/syntax/readme.txt Normal file
View File

@@ -0,0 +1,46 @@
OLDSYNTAX PACKAGE README
I am placing the jEdit 2.2.1 syntax highlighting package in the public
domain. This means it can be integrated into commercial programs, etc.
This package requires at least Java 1.1 and Swing 1.1. Syntax
highlighting for the following file types is supported:
- C++, C
- CORBA IDL
- Eiffel
- HTML
- Java
- Java properties
- JavaScript
- MS-DOS INI
- MS-DOS batch files
- Makefile
- PHP
- Perl
- Python
- TeX
- Transact-SQL
- Unix patch/diff
- Unix shell script
- XML
This package is undocumented; read the source (start by taking a look at
JEditTextArea.java) to find out how to use it; it's really simple. Feel
free to e-mail questions, queries, etc. to me, but keep in mind that
this code is very old and I no longer maintain it. So if you find a bug,
don't bother me about it; fix it yourself.
* Copyright
The jEdit 2.2.1 syntax highlighting package contains code that is
Copyright 1998-1999 Slava Pestov, Artur Biesiadowski, Clancy Malcolm,
Jonathan Revusky, Juha Lindfors and Mike Dillon.
You may use and modify this package for any purpose. Redistribution is
permitted, in both source and binary form, provided that this notice
remains intact in all source distributions of this package.
-- Slava Pestov
25 September 2000
<sp@gjt.org>

View File

@@ -1,11 +1,11 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
PdeFontBuilder - gui interface to font creation heaven/hell
CreateFont - gui interface to font creation heaven/hell
Part of the Processing project - http://processing.org
Except where noted, code is written by Ben Fry and
Copyright (c) 2001-03 Massachusetts Institute of Technology
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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
@@ -22,6 +22,9 @@
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app.tools;
import processing.app.*;
import processing.core.*;
import java.awt.*;
@@ -34,7 +37,7 @@ import javax.swing.border.*;
import javax.swing.event.*;
public class PdeFontBuilder extends JFrame {
public class CreateFont extends JFrame {
File targetFolder;
Dimension windowSize;
@@ -62,9 +65,11 @@ public class PdeFontBuilder extends JFrame {
};
public PdeFontBuilder() {
public CreateFont(PdeEditor editor) {
super("Create Font");
targetFolder = editor.sketch.dataFolder;
Container paine = getContentPane();
paine.setLayout(new BorderLayout()); //10, 10));
@@ -347,10 +352,12 @@ public class PdeFontBuilder extends JFrame {
}
/*
public void show(File targetFolder) {
this.targetFolder = targetFolder;
show();
}
*/
public void update() {