diff --git a/.gitignore b/.gitignore
index 26b5b97563..3c486a4fc5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -98,7 +98,9 @@ bin-test
processing-examples
# Maven ignores
+/.kotlin/sessions
.gradle
+.build/
core/build/
build/publish/
app/build
@@ -108,8 +110,11 @@ java/build/
/java/libraries/svg/bin
/java/preprocessor/build
/java/lsp/build
+/java/gradle/build
/.kotlin/sessions
/core/examples/build
+/java/gradle/build
+/java/gradle/example/.processing
.build/
/app/windows/obj
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index e9be690395..0608b9af03 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -26,5 +26,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java
index 9f3486a10d..3d991a4802 100644
--- a/core/src/processing/core/PApplet.java
+++ b/core/src/processing/core/PApplet.java
@@ -9942,8 +9942,8 @@ static public void runSketch(final String[] args,
boolean hideStop = false;
int displayNum = -1; // use default
- boolean present = false;
- boolean fullScreen = false;
+ boolean present = System.getProperty("processing.present", "false").equals("true");
+ boolean fullScreen = System.getProperty("processing.fullscreen", "false").equals("true");
float uiScale = 0;
String param, value;
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index a9fe0b6e52..f2dc0305ec 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"
jupiter = "5.12.0"
[libraries]
@@ -25,6 +26,11 @@ 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" }
+composeGradlePlugin = { module = "org.jetbrains.compose:compose-gradle-plugin", version.ref = "compose-plugin" }
+kotlinGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
+kotlinComposePlugin = { module = "org.jetbrains.kotlin.plugin.compose:org.jetbrains.kotlin.plugin.compose.gradle.plugin", version.ref = "kotlin" }
markdown = { module = "com.mikepenz:multiplatform-markdown-renderer-m2", version = "0.31.0" }
markdownJVM = { module = "com.mikepenz:multiplatform-markdown-renderer-jvm", version = "0.31.0" }
@@ -34,4 +40,5 @@ kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref =
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
download = { id = "de.undercouch.download", version = "5.6.0" }
-mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.30.0" }
\ No newline at end of file
+mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.30.0" }
+gradlePublish = { id = "com.gradle.plugin-publish", version = "1.2.1" }
\ No newline at end of file
diff --git a/java/gradle/build.gradle.kts b/java/gradle/build.gradle.kts
new file mode 100644
index 0000000000..dcfc8bac45
--- /dev/null
+++ b/java/gradle/build.gradle.kts
@@ -0,0 +1,34 @@
+plugins{
+ `java-gradle-plugin`
+ alias(libs.plugins.gradlePublish)
+
+ kotlin("jvm") version libs.versions.kotlin
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies{
+ implementation(project(":java:preprocessor"))
+
+ implementation(libs.composeGradlePlugin)
+ implementation(libs.kotlinGradlePlugin)
+ implementation(libs.kotlinComposePlugin)
+
+ testImplementation(libs.junit)
+}
+
+gradlePlugin{
+ plugins{
+ create("processing"){
+ id = "processing.java.gradle"
+ implementationClass = "org.processing.java.gradle.ProcessingPlugin"
+ }
+ }
+}
+publishing{
+ repositories{
+ mavenLocal()
+ }
+}
\ No newline at end of file
diff --git a/java/gradle/example/brightness.pde b/java/gradle/example/brightness.pde
new file mode 100644
index 0000000000..46ea60820a
--- /dev/null
+++ b/java/gradle/example/brightness.pde
@@ -0,0 +1,32 @@
+/**
+ * Brightness
+ * by Rusty Robison.
+ *
+ * Brightness is the relative lightness or darkness of a color.
+ * Move the cursor vertically over each bar to alter its brightness.
+ */
+
+int barWidth = 20;
+int lastBar = -1;
+
+import controlP5.*;
+
+ControlP5 cp5;
+
+
+void setup() {
+ size(640, 360);
+ colorMode(HSB, width, 100, height);
+ noStroke();
+ background(0);
+}
+
+void draw() {
+ int whichBar = mouseX / barWidth;
+ if (whichBar != lastBar) {
+ int barX = whichBar * barWidth;
+ fill(barX, 100, mouseY);
+ rect(barX, 0, barWidth, height);
+ lastBar = whichBar;
+ }
+}
\ No newline at end of file
diff --git a/java/gradle/example/build.gradle.kts b/java/gradle/example/build.gradle.kts
new file mode 100644
index 0000000000..d0cb1919e9
--- /dev/null
+++ b/java/gradle/example/build.gradle.kts
@@ -0,0 +1,3 @@
+plugins{
+ id("processing.java.gradle")
+}
\ No newline at end of file
diff --git a/java/gradle/example/settings.gradle.kts b/java/gradle/example/settings.gradle.kts
new file mode 100644
index 0000000000..ee9c97e155
--- /dev/null
+++ b/java/gradle/example/settings.gradle.kts
@@ -0,0 +1,5 @@
+rootProject.name = "processing-gradle-plugin-demo"
+
+pluginManagement {
+ includeBuild("../../../")
+}
\ No newline at end of file
diff --git a/java/gradle/src/main/kotlin/ProcessingPlugin.kt b/java/gradle/src/main/kotlin/ProcessingPlugin.kt
new file mode 100644
index 0000000000..158af74f4b
--- /dev/null
+++ b/java/gradle/src/main/kotlin/ProcessingPlugin.kt
@@ -0,0 +1,159 @@
+package org.processing.java.gradle
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.file.SourceDirectorySet
+import org.gradle.api.internal.file.DefaultSourceDirectorySet
+import org.gradle.api.internal.tasks.TaskDependencyFactory
+import org.gradle.api.model.ObjectFactory
+import org.gradle.api.plugins.JavaPlugin
+import org.gradle.api.plugins.JavaPluginExtension
+import org.gradle.api.tasks.JavaExec
+import org.jetbrains.compose.ComposeExtension
+import org.jetbrains.compose.desktop.DesktopExtension
+import java.io.File
+import java.util.*
+import javax.inject.Inject
+
+class ProcessingPlugin @Inject constructor(private val objectFactory: ObjectFactory) : Plugin {
+ override fun apply(project: Project) {
+ project.plugins.apply(JavaPlugin::class.java)
+
+ // TODO: Only set the build directory when run from the Processing plugin
+ project.layout.buildDirectory.set(project.layout.projectDirectory.dir(".processing"))
+
+ project.plugins.apply("org.jetbrains.compose")
+ project.plugins.apply("org.jetbrains.kotlin.jvm")
+ project.plugins.apply("org.jetbrains.kotlin.plugin.compose")
+
+ // TODO: Add to tests
+ project.dependencies.add("implementation", "org.processing:core:4.4.0")
+ // TODO: Add tests
+ project.dependencies.add("implementation", project.fileTree("src").apply { include("**/code/*.jar") })
+
+ // Base JOGL and Gluegen dependencies
+ // TODO: Add only if user is compiling for P2D or P3D
+ project.dependencies.add("runtimeOnly", "org.jogamp.jogl:jogl-all-main:2.5.0")
+ project.dependencies.add("runtimeOnly", "org.jogamp.gluegen:gluegen-rt-main:2.5.0")
+
+ // TODO: Only add the native dependencies for the platform the user is building for
+
+ // MacOS specific native dependencies
+ project.dependencies.add("runtimeOnly", "org.jogamp.jogl:jogl-all:2.5.0:natives-macosx-universal")
+ project.dependencies.add("runtimeOnly", "org.jogamp.gluegen:gluegen-rt:2.5.0:natives-macosx-universal")
+
+ // Windows specific native dependencies
+ project.dependencies.add("runtimeOnly", "org.jogamp.jogl:jogl-all:2.5.0:natives-windows-amd64")
+ project.dependencies.add("runtimeOnly", "org.jogamp.gluegen:gluegen-rt:2.5.0:natives-windows-amd64")
+
+ // Linux specific native dependencies
+ project.dependencies.add("runtimeOnly", "org.jogamp.jogl:jogl-all:2.5.0:natives-linux-amd64")
+ project.dependencies.add("runtimeOnly", "org.jogamp.gluegen:gluegen-rt:2.5.0:natives-linux-amd64")
+
+ // NativeWindow dependencies for all platforms
+ project.dependencies.add("implementation", "org.jogamp.jogl:nativewindow:2.5.0")
+ project.dependencies.add("runtimeOnly", "org.jogamp.jogl:nativewindow:2.5.0:natives-macosx-universal")
+ project.dependencies.add("runtimeOnly", "org.jogamp.jogl:nativewindow:2.5.0:natives-windows-amd64")
+ project.dependencies.add("runtimeOnly", "org.jogamp.jogl:nativewindow:2.5.0:natives-linux-amd64")
+
+ project.repositories.add(project.repositories.maven { it.setUrl("https://jogamp.org/deployment/maven") })
+ project.repositories.add(project.repositories.mavenCentral())
+ project.repositories.add(project.repositories.mavenLocal())
+
+ project.extensions.configure(ComposeExtension::class.java) { extension ->
+ extension.extensions.getByType(DesktopExtension::class.java).application { application ->
+ application.mainClass = project.layout.projectDirectory.asFile.name.replace(Regex("[^a-zA-Z0-9_]"), "_")
+ application.nativeDistributions.modules("java.management")
+ }
+ }
+ // TODO: Also only do within Processing
+ project.tasks.findByName("wrapper")?.enabled = false
+
+ project.tasks.create("sketch").apply {
+ group = "processing"
+ description = "Runs the Processing sketch"
+ dependsOn("run")
+ }
+ project.tasks.create("present").apply {
+ group = "processing"
+ description = "Presents the Processing sketch"
+ doFirst{
+ project.tasks.withType(JavaExec::class.java).configureEach{ task ->
+ task.systemProperty("processing.fullscreen", "true")
+ }
+ }
+ finalizedBy("run")
+ }
+ project.tasks.create("export").apply {
+ group = "processing"
+ description = "Creates a distributable version of the Processing sketch"
+
+ dependsOn("createDistributable")
+ doLast{
+ project.copy {
+ it.from(project.tasks.named("createDistributable").get().outputs.files)
+ it.into(project.layout.projectDirectory)
+ }
+ }
+ }
+
+ project.extensions.getByType(JavaPluginExtension::class.java).sourceSets.all { sourceSet ->
+ // TODO: also supporting normal gradle setup
+ val pdeSourceSet = objectFactory.newInstance(
+ DefaultPDESourceDirectorySet::class.java,
+ objectFactory.sourceDirectorySet("${sourceSet.name}.pde", "${sourceSet.name} Processing Source")
+ ).apply {
+ filter.include("**/*.pde")
+ filter.exclude("${project.layout.buildDirectory.asFile.get().name}/**")
+
+ srcDir("./")
+ }
+ sourceSet.allSource.source(pdeSourceSet)
+
+ val outputDirectory = project.layout.buildDirectory.file( "generated/pde/" + sourceSet.name).get().asFile
+ sourceSet.java.srcDir(outputDirectory)
+
+ // TODO: Support multiple sketches?
+
+ val taskName = sourceSet.getTaskName("preprocess", "PDE")
+ project.tasks.register(taskName, ProcessingTask::class.java) { task ->
+ task.description = "Processes the ${sourceSet.name} PDE"
+ task.source = pdeSourceSet
+ task.outputDirectory = outputDirectory
+ }
+
+ project.tasks.named(
+ sourceSet.compileJavaTaskName
+ ) { task -> task.dependsOn(taskName) }
+ }
+
+ var settingsFolder = File(System.getProperty("user.home"),".processing")
+ val osName = System.getProperty("os.name").lowercase()
+ if (osName.contains("win")) {
+ settingsFolder = File(System.getenv("APPDATA"), "Processing")
+ } else if (osName.contains("mac")) {
+ settingsFolder = File(System.getProperty("user.home"), "Library/Processing")
+ }else if (osName.contains("nix") || osName.contains("nux")) {
+ settingsFolder = File(System.getProperty("user.home"), ".processing")
+ }
+
+ val preferences = File(settingsFolder, "preferences.txt")
+ val prefs = Properties()
+ prefs.load(preferences.inputStream())
+ prefs.setProperty("export.application.fullscreen", "false")
+ prefs.setProperty("export.application.present", "false")
+ prefs.setProperty("export.application.stop", "false")
+ prefs.store(preferences.outputStream(), null)
+
+ val sketchbook = prefs.getProperty("sketchbook.path.four")
+
+ File(sketchbook, "libraries").listFiles { file -> file.isDirectory }?.forEach{
+ project.dependencies.add("implementation", project.fileTree(it).apply { include("**/*.jar") })
+ }
+ }
+ abstract class DefaultPDESourceDirectorySet @Inject constructor(
+ sourceDirectorySet: SourceDirectorySet,
+ taskDependencyFactory: TaskDependencyFactory
+ ) : DefaultSourceDirectorySet(sourceDirectorySet, taskDependencyFactory), SourceDirectorySet
+}
+
diff --git a/java/gradle/src/main/kotlin/ProcessingTask.kt b/java/gradle/src/main/kotlin/ProcessingTask.kt
new file mode 100644
index 0000000000..88041ee1c4
--- /dev/null
+++ b/java/gradle/src/main/kotlin/ProcessingTask.kt
@@ -0,0 +1,77 @@
+package org.processing.java.gradle
+
+import org.gradle.api.file.*
+import org.gradle.api.tasks.*
+import org.gradle.internal.file.Deleter
+import org.gradle.work.ChangeType
+import org.gradle.work.FileChange
+import org.gradle.work.InputChanges
+import processing.mode.java.preproc.PdePreprocessor
+import java.io.File
+import java.io.IOException
+import java.io.UncheckedIOException
+import java.util.concurrent.Callable
+import javax.inject.Inject
+
+abstract class ProcessingTask : SourceTask() {
+ @get:OutputDirectory
+ var outputDirectory: File? = null
+
+ @get:InputFiles
+ @get:PathSensitive(PathSensitivity.RELATIVE)
+ @get:IgnoreEmptyDirectories
+ @get:SkipWhenEmpty
+ open val stableSources: FileCollection = project.files(Callable { this.source })
+
+ @TaskAction
+ fun execute(inputChanges: InputChanges) {
+ val files: MutableSet = HashSet()
+ if (inputChanges.isIncremental) {
+ var rebuildRequired = true
+ for (fileChange: FileChange in inputChanges.getFileChanges(stableSources)) {
+ if (fileChange.fileType == FileType.FILE) {
+ if (fileChange.changeType == ChangeType.REMOVED) {
+ rebuildRequired = true
+ break
+ }
+ files.add(fileChange.file)
+ }
+ }
+ if (rebuildRequired) {
+ try {
+ outputDirectory?.let { deleter.ensureEmptyDirectory(it) }
+ } catch (ex: IOException) {
+ throw UncheckedIOException(ex)
+ }
+ files.addAll(stableSources.files)
+ }
+ } else {
+ files.addAll(stableSources.files)
+ }
+
+ val name = project.layout.projectDirectory.asFile.name.replace(Regex("[^a-zA-Z0-9_]"), "_")
+ val combined = files.joinToString("\n") { it.readText() }
+ File(outputDirectory, "$name.java")
+ .bufferedWriter()
+ .use { out ->
+ val meta = PdePreprocessor
+ .builderFor(name)
+ .build()
+ .write(out, combined)
+
+
+ // TODO: Only import the libraries that are actually used
+ val importStatement = meta.importStatements
+
+// for (import in importStatement) {
+// project.dependencies.add("implementation", import)
+// }
+ }
+ }
+
+ @get:Inject
+ open val deleter: Deleter
+ get() {
+ throw UnsupportedOperationException("Decorator takes care of injection")
+ }
+}
\ No newline at end of file
diff --git a/java/gradle/src/test/kotlin/ProcessingPluginTest.kt b/java/gradle/src/test/kotlin/ProcessingPluginTest.kt
new file mode 100644
index 0000000000..a98bc16d95
--- /dev/null
+++ b/java/gradle/src/test/kotlin/ProcessingPluginTest.kt
@@ -0,0 +1,48 @@
+import org.gradle.api.Task
+import org.gradle.testfixtures.ProjectBuilder
+import org.gradle.testkit.runner.GradleRunner
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+
+class ProcessingPluginTest{
+ @Test
+ fun testPluginAddsSketchTask(){
+ val project = ProjectBuilder.builder().build()
+ project.pluginManager.apply("processing.java.gradle")
+
+ assert(project.tasks.getByName("sketch") is Task)
+ }
+ @JvmField
+ @Rule
+ val folder: TemporaryFolder = TemporaryFolder()
+
+ @Test
+ fun testPluginOutcome() {
+
+ val buildFile = folder.newFile("build.gradle.kts")
+ buildFile.writeText("""
+ plugins{
+ id("processing.java.gradle")
+ }
+ """.trimIndent())
+
+ val sketchFile = folder.newFile("sketch.pde")
+ sketchFile.writeText("""
+ void setup(){
+ size(100, 100);
+ }
+ void draw(){
+ background(0);
+ }
+ """.trimIndent())
+
+ val result = GradleRunner.create()
+ .withProjectDir(folder.root)
+ .withArguments("build")
+ .withPluginClasspath()
+ .build()
+
+ assert(folder.root.resolve(".processing/generated/pde/main").exists())
+ }
+}
diff --git a/java/preprocessor/build.gradle.kts b/java/preprocessor/build.gradle.kts
index c6855df06d..4e7a2a1a1a 100644
--- a/java/preprocessor/build.gradle.kts
+++ b/java/preprocessor/build.gradle.kts
@@ -1,7 +1,8 @@
import com.vanniktech.maven.publish.SonatypeHost
plugins{
- id("java")
+ java
+ antlr
alias(libs.plugins.mavenPublish)
}
@@ -14,18 +15,24 @@ repositories{
sourceSets{
main{
java{
- srcDirs("src/main/java", "../src/", "../generated/")
+ srcDirs("src/main/java", "../src/")
include("processing/mode/java/preproc/**/*", "processing/app/**/*")
}
}
-
+}
+afterEvaluate{
+ tasks.withType(Jar::class.java){
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+ dependsOn(tasks.generateGrammarSource)
+ }
}
dependencies{
implementation(libs.antlr)
implementation(libs.eclipseJDT)
- implementation(project(":core"))
+ antlr(libs.antlr4)
+ implementation(libs.antlr4Runtime)
}
mavenPublishing{
@@ -58,13 +65,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 0000000000..b924864ea2
--- /dev/null
+++ b/java/preprocessor/src/main/antlr/JavaLexer.g4
@@ -0,0 +1,235 @@
+/*
+ [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)* '"';
+
+MULTI_STRING_LIT: '"""' (~[\\] | 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 0000000000..d273fa8885
--- /dev/null
+++ b/java/preprocessor/src/main/antlr/JavaParser.g4
@@ -0,0 +1,826 @@
+/*
+ [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)*
+ ;
+
+baseStringLiteral
+ : STRING_LITERAL
+ ;
+
+multilineStringLiteral
+ : MULTI_STRING_LIT
+ ;
+
+stringLiteral
+ : baseStringLiteral
+ | multilineStringLiteral
+ ;
+
+literal
+ : integerLiteral
+ | floatLiteral
+ | CHAR_LITERAL
+ | stringLiteral
+ | 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 0000000000..2d4edc041a
--- /dev/null
+++ b/java/preprocessor/src/main/antlr/processing/mode/java/preproc/Processing.g4
@@ -0,0 +1,147 @@
+/**
+ * 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
+ | stringLiteral
+ | 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
+ ;
\ 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 7ce476fdea..eab3a23974 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/src/processing/mode/java/preproc/PdeParseTreeListener.java b/java/src/processing/mode/java/preproc/PdeParseTreeListener.java
index cb4fd00010..2f9580f787 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 77ae022f19..19ba8f3e53 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<>();
diff --git a/java/test/resources/bug1532.pde b/java/test/resources/bug1532.pde
index 66b24b7779..ae8ecdbf8b 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();
}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 4bdcd880e8..5809665d7c 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -5,6 +5,7 @@ include(
"app",
"java",
"java:preprocessor",
+ "java:gradle",
"java:libraries:dxf",
"java:libraries:io",
"java:libraries:net",