mirror of
https://github.com/processing/processing4.git
synced 2026-02-14 10:55:38 +01:00
Fixes #1936. Tokens can be null when Recognizer uses "guess" mode, which was not accounted for.
395 lines
13 KiB
Plaintext
395 lines
13 KiB
Plaintext
/* -*- mode: antlr; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
header {
|
|
package processing.mode.java.preproc;
|
|
}
|
|
|
|
class PdeRecognizer extends JavaRecognizer;
|
|
|
|
options {
|
|
importVocab = Java;
|
|
exportVocab = PdePartial;
|
|
|
|
//codeGenMakeSwitchThreshold=10; // this is set high for debugging
|
|
//codeGenBitsetTestThreshold=10; // this is set high for debugging
|
|
|
|
// developers may to want to set this to true for better
|
|
// debugging messages, however, doing so disables highlighting errors
|
|
// in the editor.
|
|
defaultErrorHandler = false; //true;
|
|
}
|
|
|
|
tokens {
|
|
CONSTRUCTOR_CAST; EMPTY_FIELD;
|
|
}
|
|
|
|
{
|
|
// this clause copied from java15.g! ANTLR does not copy this
|
|
// section from the super grammar.
|
|
/**
|
|
* Counts the number of LT seen in the typeArguments production.
|
|
* It is used in semantic predicates to ensure we have seen
|
|
* enough closing '>' characters; which actually may have been
|
|
* either GT, SR or BSR tokens.
|
|
*/
|
|
private int ltCounter = 0;
|
|
|
|
private PdePreprocessor pp;
|
|
public PdeRecognizer(final PdePreprocessor pp, final TokenStream ts) {
|
|
this(ts);
|
|
this.pp = pp;
|
|
}
|
|
|
|
private void mixed() throws RecognitionException, TokenStreamException {
|
|
throw new RecognitionException("It looks like you're mixing \"active\" and \"static\" modes.",
|
|
getFilename(), LT(1).getLine(), LT(1).getColumn());
|
|
}
|
|
}
|
|
|
|
pdeProgram
|
|
:
|
|
// Some programs can be equally well interpreted as STATIC or ACTIVE;
|
|
// this forces the parser to prefer the STATIC interpretation.
|
|
(staticProgram) => staticProgram
|
|
{ pp.setMode(PdePreprocessor.Mode.STATIC); }
|
|
|
|
| (activeProgram) => activeProgram
|
|
{ pp.setMode(PdePreprocessor.Mode.ACTIVE); }
|
|
|
|
| staticProgram
|
|
{ pp.setMode(PdePreprocessor.Mode.STATIC); }
|
|
;
|
|
|
|
// advanced mode is really just a normal java file
|
|
javaProgram
|
|
: compilationUnit
|
|
;
|
|
|
|
activeProgram
|
|
: (
|
|
(IDENT LPAREN) => IDENT LPAREN { mixed(); }
|
|
| possiblyEmptyField
|
|
)+ EOF!
|
|
;
|
|
|
|
staticProgram
|
|
: (
|
|
statement
|
|
)* EOF!
|
|
;
|
|
|
|
// copy of the java.g rule with WEBCOLOR_LITERAL added
|
|
constant
|
|
: NUM_INT
|
|
| CHAR_LITERAL
|
|
| STRING_LITERAL
|
|
| NUM_FLOAT
|
|
| NUM_LONG
|
|
| NUM_DOUBLE
|
|
| webcolor_literal
|
|
;
|
|
|
|
// fix bug http://dev.processing.org/bugs/show_bug.cgi?id=1519
|
|
// by altering a syntactic predicate whose sole purpose is to
|
|
// emit a useless error with no line numbers.
|
|
// These are from Java15.g, with a few lines edited to make nice errors.
|
|
|
|
// Type arguments to a class or interface type
|
|
typeArguments
|
|
{int currentLtLevel = 0;}
|
|
:
|
|
{currentLtLevel = ltCounter;}
|
|
LT! {ltCounter++;}
|
|
typeArgument
|
|
(options{greedy=true;}: // match as many as possible
|
|
{if (! (inputState.guessing !=0 || ltCounter == currentLtLevel + 1)) {
|
|
throw new RecognitionException("Maybe too many > characters?",
|
|
getFilename(), LT(1).getLine(), LT(1).getColumn());
|
|
}}
|
|
COMMA! typeArgument
|
|
)*
|
|
|
|
( // turn warning off since Antlr generates the right code,
|
|
// plus we have our semantic predicate below
|
|
options{generateAmbigWarnings=false;}:
|
|
typeArgumentsOrParametersEnd
|
|
)?
|
|
|
|
// make sure we have gobbled up enough '>' characters
|
|
// if we are at the "top level" of nested typeArgument productions
|
|
{if (! ((currentLtLevel != 0) || ltCounter == currentLtLevel)) {
|
|
throw new RecognitionException("Maybe too many > characters?",
|
|
getFilename(), LT(1).getLine(), LT(1).getColumn());
|
|
}}
|
|
|
|
{#typeArguments = #(#[TYPE_ARGUMENTS, "TYPE_ARGUMENTS"], #typeArguments);}
|
|
;
|
|
|
|
typeParameters
|
|
{int currentLtLevel = 0;}
|
|
:
|
|
{currentLtLevel = ltCounter;}
|
|
LT! {ltCounter++;}
|
|
typeParameter (COMMA! typeParameter)*
|
|
(typeArgumentsOrParametersEnd)?
|
|
|
|
// make sure we have gobbled up enough '>' characters
|
|
// if we are at the "top level" of nested typeArgument productions
|
|
{if (! ((currentLtLevel != 0) || ltCounter == currentLtLevel)) {
|
|
throw new RecognitionException("Maybe too many > characters?",
|
|
getFilename(), LT(1).getLine(), LT(1).getColumn());
|
|
}}
|
|
|
|
{#typeParameters = #(#[TYPE_PARAMETERS, "TYPE_PARAMETERS"], #typeParameters);}
|
|
;
|
|
|
|
|
|
// this gobbles up *some* amount of '>' characters, and counts how many
|
|
// it gobbled.
|
|
protected typeArgumentsOrParametersEnd
|
|
: GT! {ltCounter-=1;}
|
|
| SR! {ltCounter-=2;}
|
|
| BSR! {ltCounter-=3;}
|
|
;
|
|
|
|
// of the form #cc008f in PDE
|
|
webcolor_literal
|
|
: w:WEBCOLOR_LITERAL
|
|
{ if (! (processing.app.Preferences.getBoolean("preproc.web_colors")
|
|
&&
|
|
w.getText().length() == 6)) {
|
|
throw new RecognitionException("Web colors must be exactly 6 hex digits. This looks like " + w.getText().length() + ".",
|
|
getFilename(), LT(1).getLine(), LT(1).getColumn());
|
|
}} // must be exactly 6 hex digits
|
|
;
|
|
|
|
// copy of the java.g builtInType rule
|
|
builtInConsCastType
|
|
: "void"
|
|
| "boolean"
|
|
| "byte"
|
|
| "char"
|
|
| "short"
|
|
| "int"
|
|
| "float"
|
|
| "long"
|
|
| "double"
|
|
;
|
|
|
|
// our types include the java types and "color". this is separated into two
|
|
// rules so that constructor casts can just use the original typelist, since
|
|
// we don't want to support the color type as a constructor cast.
|
|
//
|
|
builtInType
|
|
: builtInConsCastType
|
|
| "color" // aliased to an int in PDE
|
|
{ processing.app.Preferences.getBoolean("preproc.color_datatype") }?
|
|
;
|
|
|
|
// constructor style casts.
|
|
constructorCast!
|
|
: t:consCastTypeSpec[true]
|
|
LPAREN!
|
|
e:expression
|
|
RPAREN!
|
|
// if this is a string literal, make sure the type we're trying to cast
|
|
// to is one of the supported ones
|
|
//
|
|
{ (#e == null) ||
|
|
( (#e.getType() != STRING_LITERAL) ||
|
|
( #t.getType() == LITERAL_boolean ||
|
|
#t.getType() == LITERAL_double ||
|
|
#t.getType() == LITERAL_float ||
|
|
#t.getType() == LITERAL_int ||
|
|
#t.getType() == LITERAL_long ||
|
|
#t.getType() == LITERAL_short )) }?
|
|
// create the node
|
|
//
|
|
{#constructorCast = #(#[CONSTRUCTOR_CAST,"CONSTRUCTOR_CAST"], t, e);}
|
|
;
|
|
|
|
// A list of types that be used as the destination type in a constructor-style
|
|
// cast. Ideally, this would include all class types, not just "String".
|
|
// Unfortunately, it's not possible to tell whether Foo(5) is supposed to be
|
|
// a method call or a constructor cast without have a table of all valid
|
|
// types or methods, which requires semantic analysis (eg processing of import
|
|
// statements). So we accept the set of built-in types plus "String".
|
|
//
|
|
consCastTypeSpec[boolean addImagNode]
|
|
// : stringTypeSpec[addImagNode]
|
|
// | builtInConsCastTypeSpec[addImagNode]
|
|
: builtInConsCastTypeSpec[addImagNode]
|
|
// trying to remove String() cast [fry]
|
|
;
|
|
|
|
//stringTypeSpec[boolean addImagNode]
|
|
// : id:IDENT { #id.getText().equals("String") }?
|
|
// {
|
|
// if ( addImagNode ) {
|
|
// #stringTypeSpec = #(#[TYPE,"TYPE"],
|
|
// #stringTypeSpec);
|
|
// }
|
|
// }
|
|
// ;
|
|
|
|
builtInConsCastTypeSpec[boolean addImagNode]
|
|
: builtInConsCastType
|
|
{
|
|
if ( addImagNode ) {
|
|
#builtInConsCastTypeSpec = #(#[TYPE,"TYPE"],
|
|
#builtInConsCastTypeSpec);
|
|
}
|
|
}
|
|
;
|
|
|
|
// Since "color" tokens are lexed as LITERAL_color now, we need to have a rule
|
|
// that can generate a method call from an expression that starts with this
|
|
// token
|
|
//
|
|
colorMethodCall
|
|
: c:"color" {#c.setType(IDENT);} // this would default to LITERAL_color
|
|
lp:LPAREN^ {#lp.setType(METHOD_CALL);}
|
|
argList
|
|
RPAREN!
|
|
;
|
|
|
|
// copy of the java.g rule with added constructorCast and colorMethodCall
|
|
// alternatives
|
|
primaryExpression
|
|
: (consCastTypeSpec[false] LPAREN) => constructorCast
|
|
{ processing.app.Preferences.getBoolean("preproc.enhanced_casting") }?
|
|
| identPrimary ( options {greedy=true;} : DOT^ "class" )?
|
|
| constant
|
|
| "true"
|
|
| "false"
|
|
| "null"
|
|
| newExpression
|
|
| "this"
|
|
| "super"
|
|
| LPAREN! assignmentExpression RPAREN!
|
|
| colorMethodCall
|
|
// look for int.class and int[].class
|
|
| builtInType
|
|
( lbt:LBRACK^ {#lbt.setType(ARRAY_DECLARATOR);} RBRACK! )*
|
|
DOT^ "class"
|
|
;
|
|
|
|
// the below variable rule hacks are needed so that it's possible for the
|
|
// emitter to correctly output variable declarations of the form "float a, b"
|
|
// from the AST. This means that our AST has a somewhat different form in
|
|
// these rules than the java one does, and this new form may have its own
|
|
// semantic issues. But it seems to fix the comma declaration issues.
|
|
//
|
|
variableDefinitions![AST mods, AST t]
|
|
: vd:variableDeclarator[getASTFactory().dupTree(mods),
|
|
getASTFactory().dupTree(t)]
|
|
{#variableDefinitions = #(#[VARIABLE_DEF,"VARIABLE_DEF"], mods,
|
|
t, vd);}
|
|
;
|
|
variableDeclarator[AST mods, AST t]
|
|
: ( id:IDENT (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)*
|
|
v:varInitializer (COMMA!)? )+
|
|
;
|
|
|
|
// java.g builds syntax trees with an inconsistent structure. override one of
|
|
// the rules there to fix this.
|
|
//
|
|
explicitConstructorInvocation!
|
|
: (typeArguments)?
|
|
t:"this" LPAREN a1:argList RPAREN SEMI
|
|
{#explicitConstructorInvocation = #(#[CTOR_CALL, "CTOR_CALL"],
|
|
#t, #a1);}
|
|
| s:"super" LPAREN a2:argList RPAREN SEMI
|
|
{#explicitConstructorInvocation = #(#[SUPER_CTOR_CALL,
|
|
"SUPER_CTOR_CALL"],
|
|
#s, #a2);}
|
|
;
|
|
|
|
// quick-n-dirty hack to the get the advanced class name. we should
|
|
// really be getting it from the AST and not forking this rule from
|
|
// the java.g copy at all. Since this is a recursive descent parser, we get
|
|
// the last class name in the file so that we don't end up with the classname
|
|
// of an inner class. If there is more than one "outer" class in a file,
|
|
// this heuristic will fail.
|
|
//
|
|
classDefinition![AST modifiers]
|
|
: "class" i:IDENT
|
|
// it _might_ have type paramaters
|
|
(tp:typeParameters)?
|
|
// it _might_ have a superclass...
|
|
sc:superClassClause
|
|
// it might implement some interfaces...
|
|
ic:implementsClause
|
|
// now parse the body of the class
|
|
cb:classBlock
|
|
{#classDefinition = #(#[CLASS_DEF,"CLASS_DEF"],
|
|
modifiers,i,tp,sc,ic,cb);
|
|
pp.setAdvClassName(i.getText());}
|
|
;
|
|
|
|
possiblyEmptyField
|
|
: classField
|
|
| s:SEMI {#s.setType(EMPTY_FIELD);}
|
|
;
|
|
|
|
class PdeLexer extends JavaLexer;
|
|
|
|
options {
|
|
importVocab=PdePartial;
|
|
exportVocab=Pde;
|
|
}
|
|
|
|
// We need to preserve whitespace and commentary instead of ignoring
|
|
// like the supergrammar does. Otherwise Jikes won't be able to give
|
|
// us error messages that point to the equivalent PDE code.
|
|
|
|
// WS, SL_COMMENT, ML_COMMENT are copies of the original productions,
|
|
// but with the SKIP assigment removed.
|
|
|
|
WS : ( ' '
|
|
| '\t'
|
|
| '\f'
|
|
// handle newlines
|
|
| ( options {generateAmbigWarnings=false;}
|
|
: "\r\n" // Evil DOS
|
|
| '\r' // Macintosh
|
|
| '\n' // Unix (the right way)
|
|
)
|
|
{ newline(); }
|
|
)+
|
|
;
|
|
|
|
// Single-line comments
|
|
SL_COMMENT
|
|
: "//"
|
|
(~('\n'|'\r'))* ('\n'|'\r'('\n')?)
|
|
{newline();}
|
|
;
|
|
|
|
// multiple-line comments
|
|
ML_COMMENT
|
|
: "/*"
|
|
( /* '\r' '\n' can be matched in one alternative or by matching
|
|
'\r' in one iteration and '\n' in another. I am trying to
|
|
handle any flavor of newline that comes in, but the language
|
|
that allows both "\r\n" and "\r" and "\n" to all be valid
|
|
newline is ambiguous. Consequently, the resulting grammar
|
|
must be ambiguous. I'm shutting this warning off.
|
|
*/
|
|
options {
|
|
generateAmbigWarnings=false;
|
|
}
|
|
:
|
|
{ LA(2)!='/' }? '*'
|
|
| '\r' '\n' {newline();}
|
|
| '\r' {newline();}
|
|
| '\n' {newline();}
|
|
| ~('*'|'\n'|'\r')
|
|
)*
|
|
"*/"
|
|
;
|
|
|
|
WEBCOLOR_LITERAL
|
|
: '#'! (HEX_DIGIT)+
|
|
;
|
|
|