From 971d98bba0a8acf24a9430015a7c1508e2c9d2a3 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Thu, 30 Jan 2025 22:59:16 +0100 Subject: [PATCH 1/3] Preprocessor with antlr Plugin --- gradle/libs.versions.toml | 3 + java/preprocessor/build.gradle.kts | 21 +- java/preprocessor/src/main/antlr/JavaLexer.g4 | 233 +++++ .../preprocessor/src/main/antlr/JavaParser.g4 | 813 ++++++++++++++++++ .../mode/java/preproc/Processing.g4 | 156 ++++ 5 files changed, 1212 insertions(+), 14 deletions(-) create mode 100644 java/preprocessor/src/main/antlr/JavaLexer.g4 create mode 100644 java/preprocessor/src/main/antlr/JavaParser.g4 create mode 100644 java/preprocessor/src/main/antlr/processing/mode/java/preproc/Processing.g4 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 61703c19a..b3203cbcd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,6 +2,7 @@ kotlin = "2.0.20" compose-plugin = "1.7.1" jogl = "2.5.0" +antlr = "4.13.2" [libraries] jogl = { module = "org.jogamp.jogl:jogl-all-main", version.ref = "jogl" } @@ -21,6 +22,8 @@ netbeansSwing = { module = "org.netbeans.api:org-netbeans-swing-outline", versio ant = { module = "org.apache.ant:ant", version = "1.10.14" } lsp4j = { module = "org.eclipse.lsp4j:org.eclipse.lsp4j", version = "0.22.0" } jsoup = { module = "org.jsoup:jsoup", version = "1.17.2" } +antlr4 = { module = "org.antlr:antlr4", version.ref = "antlr" } +antlr4Runtime = { module = "org.antlr:antlr4-runtime", version.ref = "antlr" } [plugins] jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } diff --git a/java/preprocessor/build.gradle.kts b/java/preprocessor/build.gradle.kts index e859a2b12..accd19d2d 100644 --- a/java/preprocessor/build.gradle.kts +++ b/java/preprocessor/build.gradle.kts @@ -1,12 +1,11 @@ import com.vanniktech.maven.publish.SonatypeHost plugins{ - id("java") + java + antlr alias(libs.plugins.mavenPublish) } -group = "org.processing" - repositories{ mavenCentral() google() @@ -16,7 +15,7 @@ repositories{ sourceSets{ main{ java{ - srcDirs("src/main/java", "../src/", "../generated/") + srcDirs("src/main/java", "../src/") include("processing/mode/java/preproc/**/*", "processing/app/**/*") } } @@ -24,10 +23,13 @@ sourceSets{ } dependencies{ + implementation(project(":core")) + implementation(libs.antlr) implementation(libs.eclipseJDT) - implementation(project(":core")) + antlr(libs.antlr4) + implementation(libs.antlr4Runtime) } mavenPublishing{ @@ -60,13 +62,4 @@ mavenPublishing{ developerConnection.set("scm:git:ssh://git@github.com/processing/processing4.git") } } -} -tasks.withType { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE -} -tasks.compileJava{ - dependsOn("ant-preproc") -} -ant.importBuild("../build.xml"){ antTaskName -> - "ant-$antTaskName" } \ No newline at end of file diff --git a/java/preprocessor/src/main/antlr/JavaLexer.g4 b/java/preprocessor/src/main/antlr/JavaLexer.g4 new file mode 100644 index 000000000..b3de61fea --- /dev/null +++ b/java/preprocessor/src/main/antlr/JavaLexer.g4 @@ -0,0 +1,233 @@ +/* + [The "BSD licence"] + Copyright (c) 2013 Terence Parr, Sam Harwell + Copyright (c) 2017 Ivan Kochurkin (upgrade to Java 8) + Copyright (c) 2021 Michał Lorek (upgrade to Java 11) + Copyright (c) 2022 Michał Lorek (upgrade to Java 17) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// $antlr-format alignTrailingComments true, columnLimit 150, maxEmptyLinesToKeep 1, reflowComments false, useTab false +// $antlr-format allowShortRulesOnASingleLine true, allowShortBlocksOnASingleLine true, minEmptyLines 0, alignSemicolons ownLine +// $antlr-format alignColons trailing, singleLineOverrulesHangingColon true, alignLexerCommands true, alignLabels true, alignTrailers true + +lexer grammar JavaLexer; + +// Keywords + +ABSTRACT : 'abstract'; +ASSERT : 'assert'; +BOOLEAN : 'boolean'; +BREAK : 'break'; +BYTE : 'byte'; +CASE : 'case'; +CATCH : 'catch'; +CHAR : 'char'; +CLASS : 'class'; +CONST : 'const'; +CONTINUE : 'continue'; +DEFAULT : 'default'; +DO : 'do'; +DOUBLE : 'double'; +ELSE : 'else'; +ENUM : 'enum'; +EXTENDS : 'extends'; +FINAL : 'final'; +FINALLY : 'finally'; +FLOAT : 'float'; +FOR : 'for'; +IF : 'if'; +GOTO : 'goto'; +IMPLEMENTS : 'implements'; +IMPORT : 'import'; +INSTANCEOF : 'instanceof'; +INT : 'int'; +INTERFACE : 'interface'; +LONG : 'long'; +NATIVE : 'native'; +NEW : 'new'; +PACKAGE : 'package'; +PRIVATE : 'private'; +PROTECTED : 'protected'; +PUBLIC : 'public'; +RETURN : 'return'; +SHORT : 'short'; +STATIC : 'static'; +STRICTFP : 'strictfp'; +SUPER : 'super'; +SWITCH : 'switch'; +SYNCHRONIZED : 'synchronized'; +THIS : 'this'; +THROW : 'throw'; +THROWS : 'throws'; +TRANSIENT : 'transient'; +TRY : 'try'; +VOID : 'void'; +VOLATILE : 'volatile'; +WHILE : 'while'; + +// Module related keywords +MODULE : 'module'; +OPEN : 'open'; +REQUIRES : 'requires'; +EXPORTS : 'exports'; +OPENS : 'opens'; +TO : 'to'; +USES : 'uses'; +PROVIDES : 'provides'; +WITH : 'with'; +TRANSITIVE : 'transitive'; + +// Local Variable Type Inference +VAR: 'var'; // reserved type name + +// Switch Expressions +YIELD: 'yield'; // reserved type name from Java 14 + +// Records +RECORD: 'record'; + +// Sealed Classes +SEALED : 'sealed'; +PERMITS : 'permits'; +NON_SEALED : 'non-sealed'; + +// Literals + +DECIMAL_LITERAL : ('0' | [1-9] (Digits? | '_'+ Digits)) [lL]?; +HEX_LITERAL : '0' [xX] [0-9a-fA-F] ([0-9a-fA-F_]* [0-9a-fA-F])? [lL]?; +OCT_LITERAL : '0' '_'* [0-7] ([0-7_]* [0-7])? [lL]?; +BINARY_LITERAL : '0' [bB] [01] ([01_]* [01])? [lL]?; + +FLOAT_LITERAL: + (Digits '.' Digits? | '.' Digits) ExponentPart? [fFdD]? + | Digits (ExponentPart [fFdD]? | [fFdD]) +; + +HEX_FLOAT_LITERAL: '0' [xX] (HexDigits '.'? | HexDigits? '.' HexDigits) [pP] [+-]? Digits [fFdD]?; + +BOOL_LITERAL: 'true' | 'false'; + +CHAR_LITERAL: '\'' (~['\\\r\n] | EscapeSequence) '\''; + +STRING_LITERAL: '"' (~["\\\r\n] | EscapeSequence)* '"'; + +TEXT_BLOCK: '"""' [ \t]* [\r\n] (. | EscapeSequence)*? '"""'; + +NULL_LITERAL: 'null'; + +// Separators + +LPAREN : '('; +RPAREN : ')'; +LBRACE : '{'; +RBRACE : '}'; +LBRACK : '['; +RBRACK : ']'; +SEMI : ';'; +COMMA : ','; +DOT : '.'; + +// Operators + +ASSIGN : '='; +GT : '>'; +LT : '<'; +BANG : '!'; +TILDE : '~'; +QUESTION : '?'; +COLON : ':'; +EQUAL : '=='; +LE : '<='; +GE : '>='; +NOTEQUAL : '!='; +AND : '&&'; +OR : '||'; +INC : '++'; +DEC : '--'; +ADD : '+'; +SUB : '-'; +MUL : '*'; +DIV : '/'; +BITAND : '&'; +BITOR : '|'; +CARET : '^'; +MOD : '%'; + +ADD_ASSIGN : '+='; +SUB_ASSIGN : '-='; +MUL_ASSIGN : '*='; +DIV_ASSIGN : '/='; +AND_ASSIGN : '&='; +OR_ASSIGN : '|='; +XOR_ASSIGN : '^='; +MOD_ASSIGN : '%='; +LSHIFT_ASSIGN : '<<='; +RSHIFT_ASSIGN : '>>='; +URSHIFT_ASSIGN : '>>>='; + +// Java 8 tokens + +ARROW : '->'; +COLONCOLON : '::'; + +// Additional symbols not defined in the lexical specification + +AT : '@'; +ELLIPSIS : '...'; + +// Whitespace and comments + +WS : [ \t\r\n\u000C]+ -> channel(HIDDEN); +COMMENT : '/*' .*? '*/' -> channel(HIDDEN); +LINE_COMMENT : '//' ~[\r\n]* -> channel(HIDDEN); + +// Identifiers + +IDENTIFIER: Letter LetterOrDigit*; + +// Fragment rules + +fragment ExponentPart: [eE] [+-]? Digits; + +fragment EscapeSequence: + '\\' 'u005c'? [btnfr"'\\] + | '\\' 'u005c'? ([0-3]? [0-7])? [0-7] + | '\\' 'u'+ HexDigit HexDigit HexDigit HexDigit +; + +fragment HexDigits: HexDigit ((HexDigit | '_')* HexDigit)?; + +fragment HexDigit: [0-9a-fA-F]; + +fragment Digits: [0-9] ([0-9_]* [0-9])?; + +fragment LetterOrDigit: Letter | [0-9]; + +fragment Letter: + [a-zA-Z$_] // these are the "java letters" below 0x7F + | ~[\u0000-\u007F\uD800-\uDBFF] // covers all characters above 0x7F which are not a surrogate + | [\uD800-\uDBFF] [\uDC00-\uDFFF] // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF +; \ No newline at end of file diff --git a/java/preprocessor/src/main/antlr/JavaParser.g4 b/java/preprocessor/src/main/antlr/JavaParser.g4 new file mode 100644 index 000000000..1fa8ced28 --- /dev/null +++ b/java/preprocessor/src/main/antlr/JavaParser.g4 @@ -0,0 +1,813 @@ +/* + [The "BSD licence"] + Copyright (c) 2013 Terence Parr, Sam Harwell + Copyright (c) 2017 Ivan Kochurkin (upgrade to Java 8) + Copyright (c) 2021 Michał Lorek (upgrade to Java 11) + Copyright (c) 2022 Michał Lorek (upgrade to Java 17) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// $antlr-format alignTrailingComments true, columnLimit 150, minEmptyLines 1, maxEmptyLinesToKeep 1, reflowComments false, useTab false +// $antlr-format allowShortRulesOnASingleLine false, allowShortBlocksOnASingleLine true, alignSemicolons hanging, alignColons hanging + +parser grammar JavaParser; + +options { + tokenVocab = JavaLexer; +} + +compilationUnit + : packageDeclaration? (importDeclaration | ';')* (typeDeclaration | ';')* EOF + | moduleDeclaration EOF + ; + +packageDeclaration + : annotation* PACKAGE qualifiedName ';' + ; + +importDeclaration + : IMPORT STATIC? qualifiedName ('.' '*')? ';' + ; + +typeDeclaration + : classOrInterfaceModifier* ( + classDeclaration + | enumDeclaration + | interfaceDeclaration + | annotationTypeDeclaration + | recordDeclaration + ) + ; + +modifier + : classOrInterfaceModifier + | NATIVE + | SYNCHRONIZED + | TRANSIENT + | VOLATILE + ; + +classOrInterfaceModifier + : annotation + | PUBLIC + | PROTECTED + | PRIVATE + | STATIC + | ABSTRACT + | FINAL // FINAL for class only -- does not apply to interfaces + | STRICTFP + | SEALED // Java17 + | NON_SEALED // Java17 + ; + +variableModifier + : FINAL + | annotation + ; + +classDeclaration + : CLASS identifier typeParameters? (EXTENDS typeType)? (IMPLEMENTS typeList)? ( + PERMITS typeList + )? // Java17 + classBody + ; + +typeParameters + : '<' typeParameter (',' typeParameter)* '>' + ; + +typeParameter + : annotation* identifier (EXTENDS annotation* typeBound)? + ; + +typeBound + : typeType ('&' typeType)* + ; + +enumDeclaration + : ENUM identifier (IMPLEMENTS typeList)? '{' enumConstants? ','? enumBodyDeclarations? '}' + ; + +enumConstants + : enumConstant (',' enumConstant)* + ; + +enumConstant + : annotation* identifier arguments? classBody? + ; + +enumBodyDeclarations + : ';' classBodyDeclaration* + ; + +interfaceDeclaration + : INTERFACE identifier typeParameters? (EXTENDS typeList)? (PERMITS typeList)? interfaceBody + ; + +classBody + : '{' classBodyDeclaration* '}' + ; + +interfaceBody + : '{' interfaceBodyDeclaration* '}' + ; + +classBodyDeclaration + : ';' + | STATIC? block + | modifier* memberDeclaration + ; + +memberDeclaration + : recordDeclaration //Java17 + | methodDeclaration + | genericMethodDeclaration + | fieldDeclaration + | constructorDeclaration + | genericConstructorDeclaration + | interfaceDeclaration + | annotationTypeDeclaration + | classDeclaration + | enumDeclaration + ; + +/* We use rule this even for void methods which cannot have [] after parameters. + This simplifies grammar and we can consider void to be a type, which + renders the [] matching as a context-sensitive issue or a semantic check + for invalid return type after parsing. + */ +methodDeclaration + : typeTypeOrVoid identifier formalParameters ('[' ']')* (THROWS qualifiedNameList)? methodBody + ; + +methodBody + : block + | ';' + ; + +typeTypeOrVoid + : typeType + | VOID + ; + +genericMethodDeclaration + : typeParameters methodDeclaration + ; + +genericConstructorDeclaration + : typeParameters constructorDeclaration + ; + +constructorDeclaration + : identifier formalParameters (THROWS qualifiedNameList)? constructorBody = block + ; + +compactConstructorDeclaration + : modifier* identifier constructorBody = block + ; + +fieldDeclaration + : typeType variableDeclarators ';' + ; + +interfaceBodyDeclaration + : modifier* interfaceMemberDeclaration + | ';' + ; + +interfaceMemberDeclaration + : recordDeclaration // Java17 + | constDeclaration + | interfaceMethodDeclaration + | genericInterfaceMethodDeclaration + | interfaceDeclaration + | annotationTypeDeclaration + | classDeclaration + | enumDeclaration + ; + +constDeclaration + : typeType constantDeclarator (',' constantDeclarator)* ';' + ; + +constantDeclarator + : identifier ('[' ']')* '=' variableInitializer + ; + +// Early versions of Java allows brackets after the method name, eg. +// public int[] return2DArray() [] { ... } +// is the same as +// public int[][] return2DArray() { ... } +interfaceMethodDeclaration + : interfaceMethodModifier* interfaceCommonBodyDeclaration + ; + +// Java8 +interfaceMethodModifier + : annotation + | PUBLIC + | ABSTRACT + | DEFAULT + | STATIC + | STRICTFP + ; + +genericInterfaceMethodDeclaration + : interfaceMethodModifier* typeParameters interfaceCommonBodyDeclaration + ; + +interfaceCommonBodyDeclaration + : annotation* typeTypeOrVoid identifier formalParameters ('[' ']')* (THROWS qualifiedNameList)? methodBody + ; + +variableDeclarators + : variableDeclarator (',' variableDeclarator)* + ; + +variableDeclarator + : variableDeclaratorId ('=' variableInitializer)? + ; + +variableDeclaratorId + : identifier ('[' ']')* + ; + +variableInitializer + : arrayInitializer + | expression + ; + +arrayInitializer + : '{' (variableInitializer (',' variableInitializer)* ','?)? '}' + ; + +classOrInterfaceType + : (identifier typeArguments? '.')* typeIdentifier typeArguments? + ; + +typeArgument + : typeType + | annotation* '?' ((EXTENDS | SUPER) typeType)? + ; + +qualifiedNameList + : qualifiedName (',' qualifiedName)* + ; + +formalParameters + : '(' ( + receiverParameter? + | receiverParameter (',' formalParameterList)? + | formalParameterList? + ) ')' + ; + +receiverParameter + : typeType (identifier '.')* THIS + ; + +formalParameterList + : formalParameter (',' formalParameter)* (',' lastFormalParameter)? + | lastFormalParameter + ; + +formalParameter + : variableModifier* typeType variableDeclaratorId + ; + +lastFormalParameter + : variableModifier* typeType annotation* '...' variableDeclaratorId + ; + +// local variable type inference +lambdaLVTIList + : lambdaLVTIParameter (',' lambdaLVTIParameter)* + ; + +lambdaLVTIParameter + : variableModifier* VAR identifier + ; + +qualifiedName + : identifier ('.' identifier)* + ; + +literal + : integerLiteral + | floatLiteral + | CHAR_LITERAL + | STRING_LITERAL + | BOOL_LITERAL + | NULL_LITERAL + | TEXT_BLOCK // Java17 + ; + +integerLiteral + : DECIMAL_LITERAL + | HEX_LITERAL + | OCT_LITERAL + | BINARY_LITERAL + ; + +floatLiteral + : FLOAT_LITERAL + | HEX_FLOAT_LITERAL + ; + +// ANNOTATIONS +altAnnotationQualifiedName + : (identifier DOT)* '@' identifier + ; + +annotation + : ('@' qualifiedName | altAnnotationQualifiedName) ( + '(' ( elementValuePairs | elementValue)? ')' + )? + ; + +elementValuePairs + : elementValuePair (',' elementValuePair)* + ; + +elementValuePair + : identifier '=' elementValue + ; + +elementValue + : expression + | annotation + | elementValueArrayInitializer + ; + +elementValueArrayInitializer + : '{' (elementValue (',' elementValue)*)? ','? '}' + ; + +annotationTypeDeclaration + : '@' INTERFACE identifier annotationTypeBody + ; + +annotationTypeBody + : '{' annotationTypeElementDeclaration* '}' + ; + +annotationTypeElementDeclaration + : modifier* annotationTypeElementRest + | ';' // this is not allowed by the grammar, but apparently allowed by the actual compiler + ; + +annotationTypeElementRest + : typeType annotationMethodOrConstantRest ';' + | classDeclaration ';'? + | interfaceDeclaration ';'? + | enumDeclaration ';'? + | annotationTypeDeclaration ';'? + | recordDeclaration ';'? // Java17 + ; + +annotationMethodOrConstantRest + : annotationMethodRest + | annotationConstantRest + ; + +annotationMethodRest + : identifier '(' ')' defaultValue? + ; + +annotationConstantRest + : variableDeclarators + ; + +defaultValue + : DEFAULT elementValue + ; + +// MODULES - Java9 + +moduleDeclaration + : OPEN? MODULE qualifiedName moduleBody + ; + +moduleBody + : '{' moduleDirective* '}' + ; + +moduleDirective + : REQUIRES requiresModifier* qualifiedName ';' + | EXPORTS qualifiedName (TO qualifiedName)? ';' + | OPENS qualifiedName (TO qualifiedName)? ';' + | USES qualifiedName ';' + | PROVIDES qualifiedName WITH qualifiedName ';' + ; + +requiresModifier + : TRANSITIVE + | STATIC + ; + +// RECORDS - Java 17 + +recordDeclaration + : RECORD identifier typeParameters? recordHeader (IMPLEMENTS typeList)? recordBody + ; + +recordHeader + : '(' recordComponentList? ')' + ; + +recordComponentList + : recordComponent (',' recordComponent)* + ; + +recordComponent + : typeType identifier + ; + +recordBody + : '{' (classBodyDeclaration | compactConstructorDeclaration)* '}' + ; + +// STATEMENTS / BLOCKS + +block + : '{' blockStatement* '}' + ; + +blockStatement + : localVariableDeclaration ';' + | localTypeDeclaration + | statement + ; + +localVariableDeclaration + : variableModifier* (VAR identifier '=' expression | typeType variableDeclarators) + ; + +identifier + : IDENTIFIER + | MODULE + | OPEN + | REQUIRES + | EXPORTS + | OPENS + | TO + | USES + | PROVIDES + | WITH + | TRANSITIVE + | YIELD + | SEALED + | PERMITS + | RECORD + | VAR + ; + +typeIdentifier // Identifiers that are not restricted for type declarations + : IDENTIFIER + | MODULE + | OPEN + | REQUIRES + | EXPORTS + | OPENS + | TO + | USES + | PROVIDES + | WITH + | TRANSITIVE + | SEALED + | PERMITS + | RECORD + ; + +localTypeDeclaration + : classOrInterfaceModifier* (classDeclaration | interfaceDeclaration | recordDeclaration) + ; + +statement + : blockLabel = block + | ASSERT expression (':' expression)? ';' + | IF parExpression statement (ELSE statement)? + | FOR '(' forControl ')' statement + | WHILE parExpression statement + | DO statement WHILE parExpression ';' + | TRY block (catchClause+ finallyBlock? | finallyBlock) + | TRY resourceSpecification block catchClause* finallyBlock? + | SWITCH parExpression '{' switchBlockStatementGroup* switchLabel* '}' + | SYNCHRONIZED parExpression block + | RETURN expression? ';' + | THROW expression ';' + | BREAK identifier? ';' + | CONTINUE identifier? ';' + | YIELD expression ';' // Java17 + | SEMI + | statementExpression = expression ';' + | switchExpression ';'? // Java17 + | identifierLabel = identifier ':' statement + ; + +catchClause + : CATCH '(' variableModifier* catchType identifier ')' block + ; + +catchType + : qualifiedName ('|' qualifiedName)* + ; + +finallyBlock + : FINALLY block + ; + +resourceSpecification + : '(' resources ';'? ')' + ; + +resources + : resource (';' resource)* + ; + +resource + : variableModifier* (classOrInterfaceType variableDeclaratorId | VAR identifier) '=' expression + | qualifiedName + ; + +/** Matches cases then statements, both of which are mandatory. + * To handle empty cases at the end, we add switchLabel* to statement. + */ +switchBlockStatementGroup + : switchLabel+ blockStatement+ + ; + +switchLabel + : CASE ( + constantExpression = expression + | enumConstantName = IDENTIFIER + | typeType varName = identifier + ) ':' + | DEFAULT ':' + ; + +forControl + : enhancedForControl + | forInit? ';' expression? ';' forUpdate = expressionList? + ; + +forInit + : localVariableDeclaration + | expressionList + ; + +enhancedForControl + : variableModifier* (typeType | VAR) variableDeclaratorId ':' expression + ; + +// EXPRESSIONS + +parExpression + : '(' expression ')' + ; + +expressionList + : expression (',' expression)* + ; + +methodCall + : (identifier | THIS | SUPER) arguments + ; + +expression + // Expression order in accordance with https://introcs.cs.princeton.edu/java/11precedence/ + // Level 16, Primary, array and member access + : primary #PrimaryExpression + | expression '[' expression ']' #SquareBracketExpression + | expression bop = '.' ( + identifier + | methodCall + | THIS + | NEW nonWildcardTypeArguments? innerCreator + | SUPER superSuffix + | explicitGenericInvocation + ) #MemberReferenceExpression + // Method calls and method references are part of primary, and hence level 16 precedence + | methodCall #MethodCallExpression + | expression '::' typeArguments? identifier #MethodReferenceExpression + | typeType '::' (typeArguments? identifier | NEW) #MethodReferenceExpression + | classType '::' typeArguments? NEW #MethodReferenceExpression + + // Java17 + | switchExpression #ExpressionSwitch + + // Level 15 Post-increment/decrement operators + | expression postfix = ('++' | '--') #PostIncrementDecrementOperatorExpression + + // Level 14, Unary operators + | prefix = ('+' | '-' | '++' | '--' | '~' | '!') expression #UnaryOperatorExpression + + // Level 13 Cast and object creation + | '(' annotation* typeType ('&' typeType)* ')' expression #CastExpression + | NEW creator #ObjectCreationExpression + + // Level 12 to 1, Remaining operators + // Level 12, Multiplicative operators + | expression bop = ('*' | '/' | '%') expression #BinaryOperatorExpression + // Level 11, Additive operators + | expression bop = ('+' | '-') expression #BinaryOperatorExpression + // Level 10, Shift operators + | expression ('<' '<' | '>' '>' '>' | '>' '>') expression #BinaryOperatorExpression + // Level 9, Relational operators + | expression bop = ('<=' | '>=' | '>' | '<') expression #BinaryOperatorExpression + | expression bop = INSTANCEOF (typeType | pattern) #InstanceOfOperatorExpression + // Level 8, Equality Operators + | expression bop = ('==' | '!=') expression #BinaryOperatorExpression + // Level 7, Bitwise AND + | expression bop = '&' expression #BinaryOperatorExpression + // Level 6, Bitwise XOR + | expression bop = '^' expression #BinaryOperatorExpression + // Level 5, Bitwise OR + | expression bop = '|' expression #BinaryOperatorExpression + // Level 4, Logic AND + | expression bop = '&&' expression #BinaryOperatorExpression + // Level 3, Logic OR + | expression bop = '||' expression #BinaryOperatorExpression + // Level 2, Ternary + | expression bop = '?' expression ':' expression #TernaryExpression + // Level 1, Assignment + | expression bop = ( + '=' + | '+=' + | '-=' + | '*=' + | '/=' + | '&=' + | '|=' + | '^=' + | '>>=' + | '>>>=' + | '<<=' + | '%=' + ) expression #BinaryOperatorExpression + + // Level 0, Lambda Expression // Java8 + | lambdaExpression #ExpressionLambda + ; + +// Java17 +pattern + : variableModifier* typeType annotation* identifier + ; + +// Java8 +lambdaExpression + : lambdaParameters '->' lambdaBody + ; + +// Java8 +lambdaParameters + : identifier + | '(' formalParameterList? ')' + | '(' identifier (',' identifier)* ')' + | '(' lambdaLVTIList? ')' + ; + +// Java8 +lambdaBody + : expression + | block + ; + +primary + : '(' expression ')' + | THIS + | SUPER + | literal + | identifier + | typeTypeOrVoid '.' CLASS + | nonWildcardTypeArguments (explicitGenericInvocationSuffix | THIS arguments) + ; + +// Java17 +switchExpression + : SWITCH parExpression '{' switchLabeledRule* '}' + ; + +// Java17 +switchLabeledRule + : CASE (expressionList | NULL_LITERAL | guardedPattern) (ARROW | COLON) switchRuleOutcome + | DEFAULT (ARROW | COLON) switchRuleOutcome + ; + +// Java17 +guardedPattern + : '(' guardedPattern ')' + | variableModifier* typeType annotation* identifier ('&&' expression)* + | guardedPattern '&&' expression + ; + +// Java17 +switchRuleOutcome + : block + | blockStatement* + ; + +classType + : (classOrInterfaceType '.')? annotation* identifier typeArguments? + ; + +creator + : nonWildcardTypeArguments? createdName classCreatorRest + | createdName arrayCreatorRest + ; + +createdName + : identifier typeArgumentsOrDiamond? ('.' identifier typeArgumentsOrDiamond?)* + | primitiveType + ; + +innerCreator + : identifier nonWildcardTypeArgumentsOrDiamond? classCreatorRest + ; + +arrayCreatorRest + : ('[' ']')+ arrayInitializer + | ('[' expression ']')+ ('[' ']')* + ; + +classCreatorRest + : arguments classBody? + ; + +explicitGenericInvocation + : nonWildcardTypeArguments explicitGenericInvocationSuffix + ; + +typeArgumentsOrDiamond + : '<' '>' + | typeArguments + ; + +nonWildcardTypeArgumentsOrDiamond + : '<' '>' + | nonWildcardTypeArguments + ; + +nonWildcardTypeArguments + : '<' typeList '>' + ; + +typeList + : typeType (',' typeType)* + ; + +typeType + : annotation* (classOrInterfaceType | primitiveType) (annotation* '[' ']')* + ; + +primitiveType + : BOOLEAN + | CHAR + | BYTE + | SHORT + | INT + | LONG + | FLOAT + | DOUBLE + ; + +typeArguments + : '<' typeArgument (',' typeArgument)* '>' + ; + +superSuffix + : arguments + | '.' typeArguments? identifier arguments? + ; + +explicitGenericInvocationSuffix + : SUPER superSuffix + | identifier arguments + ; + +arguments + : '(' expressionList? ')' + ; \ No newline at end of file diff --git a/java/preprocessor/src/main/antlr/processing/mode/java/preproc/Processing.g4 b/java/preprocessor/src/main/antlr/processing/mode/java/preproc/Processing.g4 new file mode 100644 index 000000000..f16f5ba31 --- /dev/null +++ b/java/preprocessor/src/main/antlr/processing/mode/java/preproc/Processing.g4 @@ -0,0 +1,156 @@ +/** + * Based on Java 1.7 grammar for ANTLR 4, see Java.g4 + * + * - changes main entry point to reflect sketch types 'static' | 'active' + * - adds support for type converter functions like "int()" + * - adds pseudo primitive type "color" + * - adds HTML hex notation with hash symbol: #ff5522 + * - allow color to appear as part of qualified names (like in imports) + */ + +grammar Processing; + +@lexer::members { + public static final int WHITESPACE = 1; + public static final int COMMENTS = 2; +} + +@header { + package processing.mode.java.preproc; +} + +// import Java grammar +import JavaParser, JavaLexer; + +// main entry point, select sketch type +processingSketch + : staticProcessingSketch + | javaProcessingSketch + | activeProcessingSketch + | warnMixedModes + ; + +// java mode, is a compilation unit +javaProcessingSketch + : packageDeclaration? importDeclaration* typeDeclaration+ EOF + ; + +// No method declarations, just statements +staticProcessingSketch + : (importDeclaration | blockStatement | typeDeclaration)* EOF + ; + +// active mode, has function definitions +activeProcessingSketch + : (importDeclaration | classBodyDeclaration)* EOF + ; + +// User incorrectly mixing modes. Included to allow for kind error message. +warnMixedModes + : (importDeclaration | classBodyDeclaration | blockStatement)* blockStatement classBodyDeclaration (importDeclaration | classBodyDeclaration | blockStatement)* + | (importDeclaration | classBodyDeclaration | blockStatement)* classBodyDeclaration blockStatement (importDeclaration | classBodyDeclaration | blockStatement)* + ; + +variableDeclaratorId + : warnTypeAsVariableName + | IDENTIFIER ('[' ']')* + ; + +// bug #93 +// https://github.com/processing/processing/issues/93 +// prevent from types being used as variable names +warnTypeAsVariableName + : primitiveType ('[' ']')* { + notifyErrorListeners("Type names are not allowed as variable names: "+$primitiveType.text); + } + ; + +// catch special API function calls that we are interested in +methodCall + : functionWithPrimitiveTypeName + | IDENTIFIER '(' expressionList? ')' + | THIS '(' expressionList? ')' + | SUPER '(' expressionList? ')' + ; + +// these are primitive type names plus "()" +// "color" is a special Processing primitive (== int) +functionWithPrimitiveTypeName + : ( 'boolean' + | 'byte' + | 'char' + | 'float' + | 'int' + | 'color' + ) '(' expressionList? ')' + ; + +// adding support for "color" primitive +primitiveType + : BOOLEAN + | CHAR + | BYTE + | SHORT + | INT + | LONG + | FLOAT + | DOUBLE + | colorPrimitiveType + ; + +colorPrimitiveType + : 'color' + ; + +qualifiedName + : (IDENTIFIER | colorPrimitiveType) ('.' (IDENTIFIER | colorPrimitiveType))* + ; + +// added HexColorLiteral +literal + : integerLiteral + | floatLiteral + | CHAR_LITERAL + | STRING_LITERAL + | BOOL_LITERAL + | NULL_LITERAL + | hexColorLiteral + ; + +// As parser rule so this produces a separate listener +// for us to alter its value. +hexColorLiteral + : HexColorLiteral + ; + +// add color literal notations for +// #ff5522 +HexColorLiteral + : '#' (HexDigit HexDigit)? HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit + ; + +// hide but do not remove whitespace and comments + +WS : [ \t\r\n\u000C]+ -> channel(1) + ; + +COMMENT + : '/*' .*? '*/' -> channel(2) + ; + +LINE_COMMENT + : '//' ~[\r\n]* -> channel(2) + ; + +CHAR_LITERAL + : '\'' (~['\\\r\n] | EscapeSequence)* '\'' // A bit nasty but let JDT tackle invalid chars + ; + +// Parser Rules +multilineStringLiteral + : MULTILINE_STRING_START .*? MULTILINE_STRING_END + ; + +// Lexer Rules +MULTILINE_STRING_START: '"""' '\r'? '\n'; +MULTILINE_STRING_END: '"""'; \ No newline at end of file From f016cc474caa637f982c8dd21078801974aabb69 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Wed, 5 Feb 2025 12:01:47 +0100 Subject: [PATCH 2/3] Removed core dependency from the pre-processor --- java/preprocessor/build.gradle.kts | 2 -- .../mode/java/preproc/PdeParseTreeListener.java | 9 ++++----- .../processing/mode/java/preproc/TextTransform.java | 10 ++++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/java/preprocessor/build.gradle.kts b/java/preprocessor/build.gradle.kts index accd19d2d..713e1d5c0 100644 --- a/java/preprocessor/build.gradle.kts +++ b/java/preprocessor/build.gradle.kts @@ -23,8 +23,6 @@ sourceSets{ } dependencies{ - implementation(project(":core")) - implementation(libs.antlr) implementation(libs.eclipseJDT) diff --git a/java/src/processing/mode/java/preproc/PdeParseTreeListener.java b/java/src/processing/mode/java/preproc/PdeParseTreeListener.java index cb4fd0001..2f9580f78 100644 --- a/java/src/processing/mode/java/preproc/PdeParseTreeListener.java +++ b/java/src/processing/mode/java/preproc/PdeParseTreeListener.java @@ -32,7 +32,6 @@ import processing.app.Base; import processing.app.Preferences; -import processing.core.PApplet; import processing.mode.java.preproc.PdePreprocessor.Mode; /** @@ -1237,16 +1236,16 @@ protected void writeMain(PrintWriterWithEditGen footerWriter, boolean shouldFullScreen = Preferences.getBoolean("export.application.present"); shouldFullScreen = shouldFullScreen || Preferences.getBoolean("export.application.fullscreen"); if (shouldFullScreen) { - argsJoiner.add("\"" + PApplet.ARGS_FULL_SCREEN + "\""); + argsJoiner.add("\"--full-screen\""); String bgColor = Preferences.get("run.present.bgcolor"); - argsJoiner.add("\"" + PApplet.ARGS_BGCOLOR + "=" + bgColor + "\""); + argsJoiner.add("\"--bgcolor=" + bgColor + "\""); if (Preferences.getBoolean("export.application.stop")) { String stopColor = Preferences.get("run.present.stop.color"); - argsJoiner.add("\"" + PApplet.ARGS_STOP_COLOR + "=" + stopColor + "\""); + argsJoiner.add("\"--stop-color=" + stopColor + "\""); } else { - argsJoiner.add("\"" + PApplet.ARGS_HIDE_STOP + "\""); + argsJoiner.add("\"--hide-stop\""); } } diff --git a/java/src/processing/mode/java/preproc/TextTransform.java b/java/src/processing/mode/java/preproc/TextTransform.java index 77ae022f1..19ba8f3e5 100644 --- a/java/src/processing/mode/java/preproc/TextTransform.java +++ b/java/src/processing/mode/java/preproc/TextTransform.java @@ -8,8 +8,6 @@ import java.util.ListIterator; import java.util.stream.Collectors; -import processing.core.PApplet; - public class TextTransform { @@ -256,7 +254,7 @@ public int getInputOffset(int outputOffset) { i = -(i + 1); i -= 1; } - i = PApplet.constrain(i, 0, outMap.size()-1); + i = constrain(i, 0, outMap.size()-1); Edit edit = outMap.get(i); int diff = outputOffset - edit.toOffset; return edit.fromOffset + Math.min(diff, Math.max(0, edit.fromLength - 1)); @@ -271,7 +269,7 @@ public int getOutputOffset(int inputOffset) { i = -(i + 1); i -= 1; } - i = PApplet.constrain(i, 0, inMap.size()-1); + i = constrain(i, 0, inMap.size()-1); Edit edit = inMap.get(i); int diff = inputOffset - edit.fromOffset; return edit.toOffset + Math.min(diff, Math.max(0, edit.toLength - 1)); @@ -283,6 +281,10 @@ public OffsetMapper thenMapping(OffsetMapper mapper) { } } + static public final int constrain(int amt, int low, int high) { + return (amt < low) ? low : ((amt > high) ? high : amt); + } + private static class CompositeOffsetMapper implements OffsetMapper { private List mappers = new ArrayList<>(); From ad5c27ec42dc6f479c1c4cee2b119568ba8fa252 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Mon, 17 Mar 2025 15:11:24 +0100 Subject: [PATCH 3/3] Apply same parser & lexer fixes from before --- .gitignore | 1 + java/preprocessor/src/main/antlr/JavaLexer.g4 | 2 ++ java/preprocessor/src/main/antlr/JavaParser.g4 | 15 ++++++++++++++- .../processing/mode/java/preproc/Processing.g4 | 15 +++------------ .../src/main/java/processing/app/Preferences.java | 2 +- java/test/resources/bug1532.pde | 6 +++--- 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 7faa26d70..0e321ec1a 100644 --- a/.gitignore +++ b/.gitignore @@ -108,6 +108,7 @@ java/build/ /java/libraries/svg/bin /java/preprocessor/build /java/lsp/build +/java/gradle/build /.kotlin/sessions /core/examples/build diff --git a/java/preprocessor/src/main/antlr/JavaLexer.g4 b/java/preprocessor/src/main/antlr/JavaLexer.g4 index b3de61fea..b924864ea 100644 --- a/java/preprocessor/src/main/antlr/JavaLexer.g4 +++ b/java/preprocessor/src/main/antlr/JavaLexer.g4 @@ -134,6 +134,8 @@ CHAR_LITERAL: '\'' (~['\\\r\n] | EscapeSequence) '\''; STRING_LITERAL: '"' (~["\\\r\n] | EscapeSequence)* '"'; +MULTI_STRING_LIT: '"""' (~[\\] | EscapeSequence)*? '"""'; + TEXT_BLOCK: '"""' [ \t]* [\r\n] (. | EscapeSequence)*? '"""'; NULL_LITERAL: 'null'; diff --git a/java/preprocessor/src/main/antlr/JavaParser.g4 b/java/preprocessor/src/main/antlr/JavaParser.g4 index 1fa8ced28..d273fa888 100644 --- a/java/preprocessor/src/main/antlr/JavaParser.g4 +++ b/java/preprocessor/src/main/antlr/JavaParser.g4 @@ -314,11 +314,24 @@ qualifiedName : identifier ('.' identifier)* ; +baseStringLiteral + : STRING_LITERAL + ; + +multilineStringLiteral + : MULTI_STRING_LIT + ; + +stringLiteral + : baseStringLiteral + | multilineStringLiteral + ; + literal : integerLiteral | floatLiteral | CHAR_LITERAL - | STRING_LITERAL + | stringLiteral | BOOL_LITERAL | NULL_LITERAL | TEXT_BLOCK // Java17 diff --git a/java/preprocessor/src/main/antlr/processing/mode/java/preproc/Processing.g4 b/java/preprocessor/src/main/antlr/processing/mode/java/preproc/Processing.g4 index f16f5ba31..2d4edc041 100644 --- a/java/preprocessor/src/main/antlr/processing/mode/java/preproc/Processing.g4 +++ b/java/preprocessor/src/main/antlr/processing/mode/java/preproc/Processing.g4 @@ -27,7 +27,7 @@ processingSketch : staticProcessingSketch | javaProcessingSketch | activeProcessingSketch - | warnMixedModes +// | warnMixedModes ; // java mode, is a compilation unit @@ -111,7 +111,7 @@ literal : integerLiteral | floatLiteral | CHAR_LITERAL - | STRING_LITERAL + | stringLiteral | BOOL_LITERAL | NULL_LITERAL | hexColorLiteral @@ -144,13 +144,4 @@ LINE_COMMENT CHAR_LITERAL : '\'' (~['\\\r\n] | EscapeSequence)* '\'' // A bit nasty but let JDT tackle invalid chars - ; - -// Parser Rules -multilineStringLiteral - : MULTILINE_STRING_START .*? MULTILINE_STRING_END - ; - -// Lexer Rules -MULTILINE_STRING_START: '"""' '\r'? '\n'; -MULTILINE_STRING_END: '"""'; \ No newline at end of file + ; \ No newline at end of file diff --git a/java/preprocessor/src/main/java/processing/app/Preferences.java b/java/preprocessor/src/main/java/processing/app/Preferences.java index 7ce476fde..eab3a2397 100644 --- a/java/preprocessor/src/main/java/processing/app/Preferences.java +++ b/java/preprocessor/src/main/java/processing/app/Preferences.java @@ -58,7 +58,7 @@ static public String get(String attribute /*, String defaultValue */) { } } static public boolean getBoolean(String attribute) { - String value = get(attribute); //, null); + String value = get(attribute); return Boolean.parseBoolean(value); } static public int getInteger(String attribute /*, int defaultValue*/) { diff --git a/java/test/resources/bug1532.pde b/java/test/resources/bug1532.pde index 66b24b777..ae8ecdbf8 100644 --- a/java/test/resources/bug1532.pde +++ b/java/test/resources/bug1532.pde @@ -20,9 +20,9 @@ flatCube[][] grid; void setup() { try { - quicktime.QTSession.open(); - } - catch (quicktime.QTException qte) { + // quicktime.QTSession.open(); + } + catch (quicktime.QTException qte) { qte.printStackTrace(); }