diff --git a/.gitignore b/.gitignore index 7be691a9fdda6f1bd2c0b8d60d66f7f481e44ddf..017dc80de5bc43f8d3ff4db2e8a40fc311a532a4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,3 @@ src/test/jastadd/*/*.ast src/test/jastadd/*/*.jadd src/test/jastadd/*/*ResolverStubs.jrag !src/test/jastadd/*/MyRefResolver.jadd -/gradle.properties diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e5a30e5a7bba8003594b853fc1eda86531001e43..8a37876249b513e66993553cde15e5e4a7023972 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,47 +1,30 @@ stages: - build - test -- jar - deploy +before_script: + - export GRADLE_USER_HOME=`pwd`/.gradle + +cache: + paths: + - .gradle/wrapper + - .gradle/caches + build: image: openjdk:8 stage: build script: - - ./gradlew --console=plain --build-cache assemble - cache: - key: "$CI_COMMIT_REF_NAME" - policy: push + - ./gradlew --console=plain --no-daemon assemble fatJar + artifacts: paths: - - build - - .gradle + - "/builds/jastadd/relational-rags/build/libs/relast-*.jar" test: image: openjdk:8 stage: test script: - - ./gradlew --continue --console=plain --info check - cache: - key: "$CI_COMMIT_REF_NAME" - policy: pull - paths: - - build - - .gradle - -jar: - image: openjdk:8 - stage: jar - script: - - ./gradlew --continue --console=plain --info jar - cache: - key: "$CI_COMMIT_REF_NAME" - policy: pull - paths: - - build - - .gradle - artifacts: - paths: - - "/builds/jastadd/*/build/libs/*relast*.jar" + - ./gradlew --console=plain --no-daemon --info test pages: image: python:3.7-alpine @@ -53,4 +36,28 @@ pages: paths: - public only: - - master + - master + - main + +publish: + image: openjdk:8 + stage: deploy + needs: + - test + script: + - "./gradlew publish" + only: + - master + - main + +publish_dev: + image: openjdk:8 + stage: deploy + needs: + - test + script: + - "./gradlew setDevVersionForCI" + - "./gradlew publish" + except: + - master + - main diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 46997637b262d0b2c2696a43154fc008f4afa3b5..8a44f97d5c135c9262f166256036043ad49748be 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,33 +23,27 @@ This location can be [configured](#build-configuration). ## Build configuration -Currently, the test setup is configured within `build.gradle` using a specialized Gradle task `RelastTest`. +Currently, the test setup is configured within `build.gradle` using a specialized Gradle task `RelastTest` provided by the [preprocessor testing Gradle plugin][preprocessor-testing]. An example configuration might look like: ```groovy task compileMultipleTest(type: RelastTest) { - relastFiles 'src/test/jastadd/multiple/Part1.relast', - 'src/test/jastadd/multiple/Part2.relast', - 'src/test/jastadd/multiple/Part3.relast' - grammarName = 'src/test/jastadd/multiple/Multiple' - useJastAddNames = true - packageName = 'multiple.ast' - moreInputFiles 'src/test/jastadd/Utils.jadd' + relast { + inputFiles = [file('src/test/jastadd/multiple/Part1.relast'), + file('src/test/jastadd/multiple/Part2.relast'), + file('src/test/jastadd/multiple/Part3.relast')] + grammarName = 'src/test/jastadd/multiple/Multiple' + useJastAddNames = true + noResolverHelper = true + } + jastadd { + packageName = 'multiple.ast' + inputFiles = [file('src/test/jastadd/Utils.jadd')] + } } ``` -The following options are supported, similar to the [command-line options](/../#supported-command-line-options): - -| Name | Description | Required | Default | -|-------------------|--------------------------------------------------------------------------------------------------------|--------------------|---------------------| -| `relastFiles` | Input grammar(s). Either one or multiple files separated by comma. | Yes | _none_ | -| `grammarName` | Directory and file prefix for the generated grammar and jrag created by RelAST. | Yes | _none_ | -| `useJastAddNames` | Set to `true` to use accessor names for relations matching JastAdd naming convention. | No | `false` | -| `packageName` | Name of the package for the Java files generated by JastAdd. | Yes (not enforced) | The empty package | -| `moreInputFiles` | Additional files as input for JastAdd. | No | No additional files | -| `resolverHelper` | Set to `true` to generate means for lazy resolving. | No | `false` | -| `jastAddList` | Alternative name for `List` nodes. Will be passed to both RelAST and JastAdd. | No | `List` | -| `serializer` | Name of supported serializer. One of {`jackson`, `jackson-json-pointer`, `jackson-manual-references`}. | No | No serializer | +Please see the [documentation of the plugin][preprocessor-testing] for all options. ## Test files @@ -106,3 +100,4 @@ The workflow: [semantic-versioning]: https://semver.org/ [create-release]: /../-/tags/new [create-issue]: https://git-st.inf.tu-dresden.de/jastadd/relational-rags/issues/new +[preprocessor-testing]: https://jastadd.pages.st.inf.tu-dresden.de/testing/ diff --git a/README.md b/README.md index b49c377f9290f661896b992cc1d363d9334d0053..f034b04e8263eeb6065b4e30fb1da197a903219b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@  -See [releases page](/../../releases) for the latest version. +See [releases page](/../../releases) for the latest version and <https://jastadd.pages.st.inf.tu-dresden.de/relational-rags/> for more documentation. The RelAST preprocessor takes a `.relast` file as input comprising AST rules and relations. It produces files that afterwards are processed by JastAdd to generated Java code. To use it in your project, build the JAR file running @@ -52,21 +52,23 @@ Note that you may have to change ## Supported relations - // Directed relations - A.b -> B; - A.b? -> B; - A.bs* -> B; - B <- A.b ; - B <- A.b? ; - B <- A.bs*; +``` +// Directed relations +A.b -> B; +A.b? -> B; +A.bs* -> B; +B <- A.b ; +B <- A.b? ; +B <- A.bs*; - // Bidirectional relations - A.b <-> B.a; - A.b <-> B.a?; - A.b <-> B.as*; - A.b? <-> B.a; - A.b? <-> B.a?; - A.b? <-> B.as*; - A.bs* <-> B.a; - A.bs* <-> B.a?; - A.bs* <-> B.as*; +// Bidirectional relations +A.b <-> B.a; +A.b <-> B.a?; +A.b <-> B.as*; +A.b? <-> B.a; +A.b? <-> B.a?; +A.b? <-> B.as*; +A.bs* <-> B.a; +A.bs* <-> B.a?; +A.bs* <-> B.as*; +``` diff --git a/build.gradle b/build.gradle index 068c1f63ec3bc3e4f09a87e14198f696307c1bf9..1fafdb14e6010af678a0ab9e8d42c0d564e63428 100644 --- a/build.gradle +++ b/build.gradle @@ -1,59 +1,74 @@ -import org.jastadd.relast.plugin.RelastTest +buildscript { + repositories { + maven { + name "gitlab-maven" + url "https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven" + } + } + dependencies { + classpath 'org.jastadd.preprocessor:testing:0.2.14-32' + } +} + +import org.jastadd.preprocessor.testing.plugin.PreprocessorPlugin +import org.jastadd.preprocessor.testing.plugin.RelastTest + +//file:noinspection HardCodedStringLiteral +plugins { + id 'java' + id 'java-library' + id 'application' + id 'idea' + id 'maven-publish' + id 'org.jastadd' version '1.14.5' +} -apply plugin: 'java' -apply plugin: 'jastadd' -apply plugin: 'application' -apply plugin: "idea" -apply plugin: "org.jastadd.relast.plugin" +apply plugin: PreprocessorPlugin -sourceCompatibility = 1.8 +java.toolchain.languageVersion = JavaLanguageVersion.of(8) -mainClassName = 'org.jastadd.relast.compiler.Compiler' +ext { + mainClassName = 'org.jastadd.relast.compiler.Compiler' +} repositories { - jcenter() + mavenCentral() + maven { + name 'gitlab-maven' + url 'https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven' + } } group = 'org.jastadd' -apply plugin: 'maven-publish' -buildscript { - repositories.jcenter() - dependencies { - classpath 'org.jastadd:jastaddgradle:1.13.3' - } -} +// set the main class name for `gradle run` +application.mainClass = "${mainClassName}" dependencies { - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.4.0' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.4.0' - testCompile 'org.assertj:assertj-core:3.12.1' - compile 'com.fasterxml.jackson.core:jackson-core:2.9.8' - compile 'com.fasterxml.jackson.core:jackson-databind:2.9.8' - compile 'org.jastadd:jastadd:2.3.4' - runtime 'org.jastadd:jastadd:2.3.4' - compile group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' - compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.10.0' - compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.10.0' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' + testImplementation 'org.assertj:assertj-core:3.22.0' + testImplementation 'com.fasterxml.jackson.core:jackson-core:2.13.1' + testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.13.1' + api 'org.jastadd:jastadd:2.3.5' + api 'net.sf.beaver:beaver-rt:0.9.11' } -sourceSets { - main { - java.srcDir "src/gen/java" - java.srcDir "buildSrc/gen/java" - } - test { - java.srcDir "src/test/java-gen" - } -} +File genSrc = file('src/gen/java') +File testGenSrc = file('src/test/java-gen') +sourceSets.main.java.srcDir genSrc +sourceSets.test.java.srcDir testGenSrc + +idea.module.generatedSourceDirs += genSrc +idea.module.generatedSourceDirs += testGenSrc def versionFile = 'src/main/resources/RelASTVersion.properties' -def oldProps = new Properties() try { + def oldProps = new Properties() file(versionFile).withInputStream { stream -> oldProps.load(stream) } version = oldProps['version'] -} catch (e) { +} catch (ignored) { // this happens, if either the properties file is not present, or cannot be read from throw new GradleException("File ${versionFile} not found or unreadable. Aborting.") } @@ -66,13 +81,33 @@ task newVersion() { } } -jar { - manifest { - attributes "Main-Class": 'org.jastadd.relast.compiler.Compiler' +task printVersion() { + doLast { + println(version) + } +} + +task setDevVersionForCI() { + doFirst { + def props = new Properties() + props['version'] = version + "-$System.env.CI_PIPELINE_IID" + props.store(file(versionFile).newWriter(), null) } +} + +jar.archiveBaseName = 'relast' +task fatJar(type: Jar) { + dependsOn jar + group = "build" + archiveBaseName = 'relast' + archiveAppendix = "fatjar" + from sourceSets.main.output from { - configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } + manifest { + attributes "Main-Class": "${mainClassName}" } } @@ -80,94 +115,95 @@ jastadd { configureModuleBuild() modules { //noinspection GroovyAssignabilityCheck - module("RelAst") { - - java { - basedir "." - include "src/main/**/*.java" - include "src/gen/**/*.java" - } + module('RelAst') { jastadd { - basedir "src/main/jastadd/" - include "**/*.ast" - include "**/*.jadd" - include "**/*.jrag" + basedir 'src/main/jastadd/' + include '**/*.ast' + include '**/*.jadd' + include '**/*.jrag' } scanner { - include "src/main/jastadd/RelAst.flex" + include 'src/main/jastadd/RelAst.flex' } parser { - include "src/main/jastadd/Preamble.parser" - include "src/main/jastadd/RelAst.parser" + include 'src/main/jastadd/Preamble.parser' + include 'src/main/jastadd/RelAst.parser' } } } cleanGen.doFirst { - delete "src/gen/java/org" - delete "src/gen-res/BuildInfo.properties" + delete 'src/gen/' + delete 'src/gen-res/' } preprocessParser.doFirst { - args += ["--no-beaver-symbol"] - } - module = "RelAst" - + module = 'RelAst' astPackage = 'org.jastadd.relast.ast' - parser.name = 'RelAstParser' - genDir = 'src/gen/java' - buildInfoDir = 'src/gen-res' + scanner.genDir = 'src/gen/java/org/jastadd/relast/scanner' + parser.genDir = 'src/gen/java/org/jastadd/relast/parser' - scanner.genDir = "src/gen/java/org/jastadd/relast/scanner" - parser.genDir = "src/gen/java/org/jastadd/relast/parser" - - jastaddOptions = ["--lineColumnNumbers", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"] + // default options are: ['--rewrite=cnta', '--safeLazy', '--visitCheck=false', '--cacheCycle=false'] + extraJastAddOptions = ['--lineColumnNumbers', '--visitCheck=true'] } +// publish gitlab project publishing { publications { maven(MavenPublication) { - artifact("build/libs/relast-${version}.jar") { - extension 'jar' - } + artifactId = 'relast' + from components.java } } repositories { maven { - name 'nexus' - url "http://172.22.1.152:8081/repository/" + (project.hasProperty('asSnapshot') ? "maven-snapshots" : "maven-releases/") - credentials { - username project.ext.properties.repoUser - password project.ext.properties.repoPassword + url "https://git-st.inf.tu-dresden.de/api/v4/projects/$System.env.CI_PROJECT_ID/packages/maven" + credentials(HttpHeaderCredentials) { + name = 'Job-Token' + value = System.getenv('CI_JOB_TOKEN') + } + authentication { + header(HttpHeaderAuthentication) } } + } } -publish.dependsOn jar - task firstRelationsRun(type: RelastTest) { - relastFiles 'src/test/jastadd/relations/Relations.relast' - grammarName = 'src/test/jastadd/relations/Relations' - useJastAddNames = true - packageName = 'relations.ast' - moreInputFiles 'src/test/jastadd/Utils.jadd', 'src/test/jastadd/relations/Relations.jrag' + relast { + inputFiles = [file('src/test/jastadd/relations/Relations.relast')] + grammarName = 'src/test/jastadd/relations/Relations' + useJastAddNames = true + noResolverHelper = true + } + jastadd { + packageName = 'relations.ast' + inputFiles = [file('src/test/jastadd/Utils.jadd'), + file('src/test/jastadd/relations/Relations.jrag')] + } } task secondRelationsRun(type: RelastTest, dependsOn: firstRelationsRun) { - relastFiles 'src/test/jastadd/relations/Relations.ast' - grammarName = 'src/test/jastadd/relations/Relations2' - useJastAddNames = true - packageName = 'ignored' + relast { + inputFiles = [file('src/test/jastadd/relations/Relations.ast')] + grammarName = 'src/test/jastadd/relations/Relations2' + useJastAddNames = true + noResolverHelper = true + } + jastadd { + skipRun = true + packageName = 'ignored' + } doLast { delete fileTree('src/test/java-gen/ignored') @@ -176,131 +212,198 @@ task secondRelationsRun(type: RelastTest, dependsOn: firstRelationsRun) { } task compileConstructorTest(type: RelastTest) { - relastFiles 'src/test/jastadd/constructors/Constructors.relast' - grammarName = 'src/test/jastadd/constructors/Constructors' - packageName = 'constructors.ast' - moreInputFiles 'src/test/jastadd/constructors/Constructors.jrag' + relast { + inputFiles = [file('src/test/jastadd/constructors/Constructors.relast')] + grammarName = 'src/test/jastadd/constructors/Constructors' + noResolverHelper = true + } + jastadd { + packageName = 'constructors.ast' + inputFiles = [file('src/test/jastadd/constructors/Constructors.jrag')] + } } task compileDefaultNamesTest(type: RelastTest) { - relastFiles 'src/test/jastadd/relations/Relations.relast' - grammarName = 'src/test/jastadd/relations/Relations3' - packageName = 'defaultnames.ast' - moreInputFiles 'src/test/jastadd/Utils.jadd', 'src/test/jastadd/relations/Relations.jrag' + relast { + inputFiles = [file('src/test/jastadd/relations/Relations.relast')] + grammarName = 'src/test/jastadd/relations/Relations3' + useJastAddNames = false + noResolverHelper = true + } + jastadd { + packageName = 'defaultnames.ast' + inputFiles = [file('src/test/jastadd/Utils.jadd'), + file('src/test/jastadd/relations/Relations.jrag')] + } } task compileDefaultNamesResolverTest(type: RelastTest) { - relastFiles 'src/test/jastadd/resolver/Resolver.relast' - grammarName = 'src/test/jastadd/resolver/Resolver2' - resolverHelper = true - packageName = 'defaultnames.resolver.ast' - moreInputFiles 'src/test/jastadd/Utils.jadd', - 'src/test/jastadd/resolver/ResolverUtils.jadd', - 'src/test/jastadd/resolver/MyRefResolver.jadd' + relast { + inputFiles = [file('src/test/jastadd/resolver/Resolver.relast')] + grammarName = 'src/test/jastadd/resolver/Resolver2' + useJastAddNames = false + } + jastadd { + packageName = 'defaultnames.resolver.ast' + inputFiles = [file('src/test/jastadd/Utils.jadd'), + file('src/test/jastadd/resolver/ResolverUtils.jadd'), + file('src/test/jastadd/resolver/MyRefResolver.jadd')] + } } task compileLowerBoundsTest(type: RelastTest) { - relastFiles 'src/test/jastadd/lowerbounds/LowerBounds.relast' - grammarName = 'src/test/jastadd/lowerbounds/LowerBounds' - useJastAddNames = true - packageName = 'lowerbounds.ast' - moreInputFiles 'src/test/jastadd/Utils.jadd' + relast { + inputFiles = [file('src/test/jastadd/lowerbounds/LowerBounds.relast')] + grammarName = 'src/test/jastadd/lowerbounds/LowerBounds' + useJastAddNames = true + noResolverHelper = true + } + jastadd { + packageName = 'lowerbounds.ast' + inputFiles = [file('src/test/jastadd/Utils.jadd')] + } } task compileMultipleTest(type: RelastTest) { - relastFiles 'src/test/jastadd/multiple/Part1.relast', - 'src/test/jastadd/multiple/Part2.relast', - 'src/test/jastadd/multiple/Part3.relast' - grammarName = 'src/test/jastadd/multiple/Multiple' - useJastAddNames = true - packageName = 'multiple.ast' - moreInputFiles 'src/test/jastadd/Utils.jadd' + relast { + inputFiles = [file('src/test/jastadd/multiple/Part1.relast'), + file('src/test/jastadd/multiple/Part2.relast'), + file('src/test/jastadd/multiple/Part3.relast')] + grammarName = 'src/test/jastadd/multiple/Multiple' + useJastAddNames = true + noResolverHelper = true + } + jastadd { + packageName = 'multiple.ast' + inputFiles = [file('src/test/jastadd/Utils.jadd')] + } } task compileMultipleMixedTest(type: RelastTest) { - relastFiles 'src/test/jastadd/multiple-mixed/Part1.relast', - 'src/test/jastadd/multiple-mixed/Part2.relast', - 'src/test/jastadd/multiple-mixed/Part3.relast' - grammarName = 'src/test/jastadd/multiple/Multiple' - useJastAddNames = true - packageName = 'mixed.multiple.ast' - moreInputFiles 'src/test/jastadd/Utils.jadd' + relast { + inputFiles = [file('src/test/jastadd/multiple-mixed/Part1.relast'), + file('src/test/jastadd/multiple-mixed/Part2.relast'), + file('src/test/jastadd/multiple-mixed/Part3.relast')] + grammarName = 'src/test/jastadd/multiple/Multiple' + useJastAddNames = true + noResolverHelper = true + } + jastadd { + packageName = 'mixed.multiple.ast' + inputFiles = [file('src/test/jastadd/Utils.jadd')] + } } task compileResolverTest(type: RelastTest) { - relastFiles 'src/test/jastadd/resolver/Resolver.relast' - grammarName = 'src/test/jastadd/resolver/Resolver' - useJastAddNames = true - resolverHelper = true - packageName = 'resolver.ast' - moreInputFiles 'src/test/jastadd/Utils.jadd', - 'src/test/jastadd/resolver/ResolverUtils.jadd', - 'src/test/jastadd/resolver/MyRefResolver.jadd' + relast { + inputFiles = [file('src/test/jastadd/resolver/Resolver.relast')] + grammarName = 'src/test/jastadd/resolver/Resolver' + useJastAddNames = true + } + jastadd { + packageName = 'resolver.ast' + inputFiles = [file('src/test/jastadd/Utils.jadd'), + file('src/test/jastadd/resolver/ResolverUtils.jadd'), + file('src/test/jastadd/resolver/MyRefResolver.jadd')] + } } task compileResolver2Test(type: RelastTest) { - relastFiles 'src/test/jastadd/resolver2/Resolver.relast' - grammarName = 'src/test/jastadd/resolver2/Resolver' - useJastAddNames = true - resolverHelper = true - packageName = 'resolver2.ast' - moreInputFiles 'src/test/jastadd/Utils.jadd', - 'src/test/jastadd/resolver2/ResolverUtils.jadd', - 'src/test/jastadd/resolver2/MyRefResolver.jadd' + relast { + inputFiles = [file('src/test/jastadd/resolver2/Resolver.relast')] + grammarName = 'src/test/jastadd/resolver2/Resolver' + useJastAddNames = true + } + jastadd { + packageName = 'resolver2.ast' + inputFiles = [file('src/test/jastadd/Utils.jadd'), + file('src/test/jastadd/resolver2/ResolverUtils.jadd'), + file('src/test/jastadd/resolver2/MyRefResolver.jadd')] + } } task compileListNamesTest(type: RelastTest) { - relastFiles 'src/test/jastadd/listnames/ListNames.relast' - grammarName = 'src/test/jastadd/listnames/ListNames' - useJastAddNames = true - jastAddList = 'ListyMcListface' - listClass = 'java.util.LinkedList' - packageName = 'listnames.ast' - moreInputFiles 'src/test/jastadd/Utils.jadd' + relast { + inputFiles = [file('src/test/jastadd/listnames/ListNames.relast')] + grammarName = 'src/test/jastadd/listnames/ListNames' + useJastAddNames = true + noResolverHelper = true + listClass = 'java.util.LinkedList' + } + jastadd { + jastAddList = 'ListyMcListface' + packageName = 'listnames.ast' + inputFiles = [file('src/test/jastadd/Utils.jadd')] + } } task compileSerializerTest(type: RelastTest) { - relastFiles 'src/test/jastadd/serializer/Serializer.relast' - grammarName = 'src/test/jastadd/serializer/Serializer' - useJastAddNames = true - serializer = 'jackson' - packageName = 'serializer.ast' - moreInputFiles 'src/test/jastadd/Utils.jadd' + relast { + inputFiles = [file('src/test/jastadd/serializer/Serializer.relast')] + grammarName = 'src/test/jastadd/serializer/Serializer' + useJastAddNames = true + serializer = 'jackson' + noResolverHelper = true + } + jastadd { + packageName = 'serializer.ast' + inputFiles = [file('src/test/jastadd/Utils.jadd')] + } } task compileSerializerDefaultNamesTest(type: RelastTest) { - relastFiles 'src/test/jastadd/serializer-names/Serializer.relast' - grammarName = 'src/test/jastadd/serializer-names/Serializer' - serializer = 'jackson' - packageName = 'defaultnames.serializer.ast' - moreInputFiles 'src/test/jastadd/Utils.jadd' + relast { + inputFiles = [file('src/test/jastadd/serializer-names/Serializer.relast')] + grammarName = 'src/test/jastadd/serializer-names/Serializer' + serializer = 'jackson' + useJastAddNames = false + noResolverHelper = true + } + jastadd { + packageName = 'defaultnames.serializer.ast' + inputFiles = [file('src/test/jastadd/Utils.jadd')] + } } task compileSerializerPointerTest(type: RelastTest) { - resolverHelper = true - relastFiles 'src/test/jastadd/serializer-pointer/Serializer.relast' - grammarName = 'src/test/jastadd/serializer-pointer/Serializer' - serializer = 'jackson-json-pointer' - packageName = 'pointer.serializer.ast' - moreInputFiles 'src/test/jastadd/Utils.jadd' + relast { + inputFiles = [file('src/test/jastadd/serializer-pointer/Serializer.relast')] + grammarName = 'src/test/jastadd/serializer-pointer/Serializer' + serializer = 'jackson-json-pointer' + useJastAddNames = false + } + jastadd { + packageName = 'pointer.serializer.ast' + inputFiles = [file('src/test/jastadd/Utils.jadd')] + } } task compileSerializerManualTest(type: RelastTest) { - resolverHelper = true - relastFiles 'src/test/jastadd/serializer-manual/Serializer.relast' - grammarName = 'src/test/jastadd/serializer-manual/Serializer' - serializer = 'jackson-manual-references' - packageName = 'manual.serializer.ast' - moreInputFiles 'src/test/jastadd/Utils.jadd', 'src/test/jastadd/serializer-manual/JsonPointer.jrag' + relast { + inputFiles = [file('src/test/jastadd/serializer-manual/Serializer.relast')] + grammarName = 'src/test/jastadd/serializer-manual/Serializer' + serializer = 'jackson-manual-references' + useJastAddNames = false + } + jastadd { + packageName = 'manual.serializer.ast' + inputFiles = [file('src/test/jastadd/Utils.jadd'), + file('src/test/jastadd/serializer-manual/JsonPointer.jrag')] + } } task compileSerializerManualRelativeTest(type: RelastTest) { - resolverHelper = true - relastFiles 'src/test/jastadd/serializer-manual-relative/Serializer.relast' - grammarName = 'src/test/jastadd/serializer-manual-relative/Serializer' - serializer = 'jackson-manual-references' - packageName = 'manual.relative.serializer.ast' - moreInputFiles 'src/test/jastadd/Utils.jadd', 'src/test/jastadd/serializer-manual-relative/JsonPointer.jrag' + relast { + inputFiles = [file('src/test/jastadd/serializer-manual-relative/Serializer.relast')] + grammarName = 'src/test/jastadd/serializer-manual-relative/Serializer' + serializer = 'jackson-manual-references' + useJastAddNames = false + } + jastadd { + packageName = 'manual.relative.serializer.ast' + inputFiles = [file('src/test/jastadd/Utils.jadd'), + file('src/test/jastadd/serializer-manual-relative/JsonPointer.jrag')] + } } task cleanTestGen(type: Delete) { diff --git a/buildSrc/src/main/java/org/jastadd/relast/plugin/RelastPlugin.java b/buildSrc/src/main/java/org/jastadd/relast/plugin/RelastPlugin.java deleted file mode 100644 index 392c2e03c57c3d5235770b4dadc402504ef9cb5f..0000000000000000000000000000000000000000 --- a/buildSrc/src/main/java/org/jastadd/relast/plugin/RelastPlugin.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.jastadd.relast.plugin; - -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.Task; -import org.gradle.api.tasks.TaskCollection; - -import java.util.Set; - -/** - * Plugin for RelAst-Test. - * - * @author rschoene - Initial contribution - */ -public class RelastPlugin implements Plugin<Project> { - - private Task testTask; - - @Override - public void apply(Project project) { - Set<Task> tasks = project.getTasksByName("test", false); - // there should be only one task "test" - testTask = tasks.iterator().next(); - TaskCollection<RelastTest> relastTests = project.getTasks().withType(RelastTest.class); - relastTests.forEach(this::setupRelastTest); - relastTests.whenTaskAdded(this::setupRelastTest); - } - - private void setupRelastTest(RelastTest relastTest) { - testTask.dependsOn(relastTest); - relastTest.setGroup("verification"); - } -} diff --git a/buildSrc/src/main/java/org/jastadd/relast/plugin/RelastTest.java b/buildSrc/src/main/java/org/jastadd/relast/plugin/RelastTest.java deleted file mode 100644 index 5098a85916ee63c0af3c5c0045a221cb0f83939c..0000000000000000000000000000000000000000 --- a/buildSrc/src/main/java/org/jastadd/relast/plugin/RelastTest.java +++ /dev/null @@ -1,252 +0,0 @@ -package org.jastadd.relast.plugin; - -import org.gradle.api.DefaultTask; -import org.gradle.api.Project; -import org.gradle.api.file.FileCollection; -import org.gradle.api.plugins.JavaPlugin; -import org.gradle.api.tasks.SourceSet; -import org.gradle.api.tasks.SourceSetContainer; -import org.gradle.api.tasks.TaskAction; - -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * RelAst Test Task - * - * @author rschoene - Initial contribution - */ -@SuppressWarnings({"unused", "WeakerAccess"}) -public class RelastTest extends DefaultTask { - // general options - private boolean verbose = false; - // pre-process options - private List<String> relastFiles = new ArrayList<>(); - private boolean useJastAddNames; - private boolean resolverHelper; - private boolean writeToFile = true; - private String grammarName; - private String listClass; - private String jastAddList; - private String serializer; - - // compile options - private boolean runJastAdd = true; - private String outputDir = "src/test/java-gen/"; - private String packageName; - private List<String> moreInputFiles = new ArrayList<>(); - - public boolean isVerbose() { - return verbose; - } - - public void setVerbose(boolean verbose) { - this.verbose = verbose; - } - - // pre-process options - public List<String> getRelastFiles() { - return relastFiles; - } - - public void relastFiles(String relastFile) { - this.relastFiles.add(relastFile); - } - - public void relastFiles(String[] relastFilesArray) { - this.relastFiles = Arrays.asList(relastFilesArray); - } - - public boolean isUseJastAddNames() { - return useJastAddNames; - } - - public void setUseJastAddNames(boolean useJastAddNames) { - this.useJastAddNames = useJastAddNames; - } - - public boolean isResolverHelper() { - return resolverHelper; - } - - public void setResolverHelper(boolean resolverHelper) { - this.resolverHelper = resolverHelper; - } - - public boolean isWriteToFile() { - return writeToFile; - } - - public void setWriteToFile(boolean writeToFile) { - this.writeToFile = writeToFile; - } - - public String getGrammarName() { - return grammarName; - } - - public void setGrammarName(String grammarName) { - this.grammarName = grammarName; - } - - public String getListClass() { - return listClass; - } - - public void setListClass(String listClass) { - this.listClass = listClass; - } - - public String getJastAddList() { - return jastAddList; - } - - public void setJastAddList(String jastAddList) { - this.jastAddList = jastAddList; - } - - public String getSerializer() { - return serializer; - } - - public void setSerializer(String serializer) { - this.serializer = serializer; - } - - // compile options - public boolean isRunJastAdd() { - return runJastAdd; - } - - public void setRunJastAdd(boolean runJastAdd) { - this.runJastAdd = runJastAdd; - } - - public String getOutputDir() { - return outputDir; - } - - public void setOutputDir(String outputDir) { - this.outputDir = outputDir; - } - - public String getPackageName() { - return packageName; - } - - public void setPackageName(String packageName) { - this.packageName = packageName; - } - - public List<String> getMoreInputFiles() { - return moreInputFiles; - } - - public void moreInputFiles(String f) { - this.moreInputFiles.add(f); - } - - public void moreInputFiles(String[] fileArray) { - this.moreInputFiles.addAll(Arrays.asList(fileArray)); - } - - private boolean isSet(String option) { - return option != null && !option.isEmpty(); - } - - private String[] genSuffixes = {".ast", ".jadd", "RefResolver.jadd", "ResolverStubs.jrag", "Serializer.jadd"}; - - @TaskAction - void runTest() { - setGroup("verification"); - setDescription("Runs a relast test"); - Project project = getProject(); - if (isVerbose()) { - System.out.println("Running relast test"); - System.out.println("relast files: " + getRelastFiles()); - System.out.println("Deleting files"); - } - // first, delete generated files - List<String> genFiles = new ArrayList<>(); - for (String suffix : genSuffixes) { - genFiles.add(getGrammarName() + suffix); - } - if (isVerbose()) { - System.out.println("gen files: " + genFiles); - } - project.delete(deleteSpec -> { - deleteSpec.delete(genFiles); - if (isSet(getPackageName())) { - deleteSpec.delete(Paths.get(getOutputDir(), getPackageName())); - } - }); - if (isVerbose()) { - System.out.println("Pre processing, running relast"); - } - // then, run relast pre processing - project.getPlugins().withType(JavaPlugin.class, javaPlugin -> { - SourceSetContainer sourceSets = (SourceSetContainer) project.getProperties().get("sourceSets"); - FileCollection runtimeClasspath = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath(); - project.javaexec(javaExecSpec -> { - javaExecSpec.setClasspath(runtimeClasspath); - javaExecSpec.setMain("org.jastadd.relast.compiler.Compiler"); - List<Object> args = new ArrayList<>(getRelastFiles()); - args.add("--quiet"); - if (isWriteToFile()) { - args.add("--file"); - } - if (isUseJastAddNames()) { - args.add("--useJastAddNames"); - } - if (isResolverHelper()) { - args.add("--resolverHelper"); - } - if (isSet(getJastAddList())) { - args.add("--jastAddList=" + getJastAddList()); - } - if (isSet(getListClass())) { - args.add("--listClass=" + getListClass()); - } - if (isSet(getSerializer())) { - args.add("--serializer=" + getSerializer()); - } - args.add("--grammarName=" + getGrammarName()); - if (isVerbose()) { - System.out.println("Start relast with args: " + args); - } - javaExecSpec.args(args); - }); - }); - if (isRunJastAdd()) { - if (isVerbose()) { - System.out.println("Compile with JastAdd"); - } - // check which files were actually generated - genFiles.removeIf(s -> !Paths.get(s).toFile().exists()); - // finally, compile generated files - project.getPlugins().withType(JavaPlugin.class, javaPlugin -> { - SourceSetContainer sourceSets = (SourceSetContainer) project.getProperties().get("sourceSets"); - FileCollection runtimeClasspath = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath(); - project.javaexec(javaExecSpec -> { - javaExecSpec.setClasspath(runtimeClasspath); - javaExecSpec.setMain("org.jastadd.JastAdd"); - List<Object> args = new ArrayList<>(); - args.add("--o=" + getOutputDir()); - args.add("--package=" + getPackageName()); - if (isSet(getJastAddList())) { - args.add("--List=" + getJastAddList()); - } - args.addAll(genFiles); - args.addAll(getMoreInputFiles()); - if (isVerbose()) { - System.out.println("Start JastAdd with args: " + args); - } - javaExecSpec.args(args); - }); - }); - } - } - -} diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/org.jastadd.relast.plugin.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/org.jastadd.relast.plugin.properties deleted file mode 100644 index 20222b8953522469ec2c0340ea3aeb5ed6294d13..0000000000000000000000000000000000000000 --- a/buildSrc/src/main/resources/META-INF/gradle-plugins/org.jastadd.relast.plugin.properties +++ /dev/null @@ -1 +0,0 @@ -implementation-class=org.jastadd.relast.plugin.RelastPlugin diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 94336fcae912db8a11d55634156fa011f4686124..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5028f28f8e4755a78a5bf0fef4644577b199c8c4..41dfb87909a877d96c3af4adccce4c7a301b55a2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d517fc5249beaefa600691cf150f2fa3e6..4f906e0c811fc9e230eb44819f509cd0627f2600 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -66,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -109,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -138,19 +156,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index e95643d6a2ca62258464e83c72f5156dc941c609..ac1b06f93825db68fb0c0b5150917f340eaa5d02 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000000000000000000000000000000000000..0d12b34fc0f5973338efb7ba84cf7e4e6e970b4e --- /dev/null +++ b/settings.gradle @@ -0,0 +1,3 @@ +rootProject.name = 'RelAST' + + diff --git a/src/main/jastadd/Analysis.jrag b/src/main/jastadd/Analysis.jrag index 21456331346ac897ee8f597c52e93c1ac8edb498..e5a65ebf0e03dea7f0be60aa9a04bd11ce8ee8af 100644 --- a/src/main/jastadd/Analysis.jrag +++ b/src/main/jastadd/Analysis.jrag @@ -97,19 +97,20 @@ aspect ComponentAnalysis { // find a matching component on the RHS of the (current) super type Component c = findComponent(td, getID()); - if (c != null && !this.isEqual(c)) return c; + if (c != null && !this.usesSameTypeAs(c)) return c; } return null; } - //--- isEqual --- - syn boolean Component.isEqual(Component c) = this.getClass() == c.getClass() && getTypeUse().isEqual(c.getTypeUse()); + //--- usesSameTypeAs --- + syn boolean Component.usesSameTypeAs(Component c) = this.getClass() == c.getClass() && getTypeUse().isEqual(c.getTypeUse()); /** * TokenComponents may be specialized by NTATokenComponents and vice versa */ - eq TokenComponent.isEqual(Component c) = (c instanceof TokenComponent) && getTypeUse().isEqual(c.getTypeUse()); + eq TokenComponent.usesSameTypeAs(Component c) = (c instanceof TokenComponent) && getTypeUse().isEqual(c.getTypeUse()); + // --- isEqual --- syn boolean TypeUse.isEqual(TypeUse u); eq SimpleTypeUse.isEqual(TypeUse u) = u instanceof SimpleTypeUse && getID().equals(u.getID()); eq ParameterizedTypeUse.isEqual(TypeUse u) { @@ -123,6 +124,8 @@ aspect ComponentAnalysis { return true; } + syn boolean Component.isEqual(Component other) = this.usesSameTypeAs(other) && this.getID().equals(other.getID()); + //--- matches --- /** * @return true, if the component has both type and role, its type matches the given typeDecl and its name matches the given name @@ -160,11 +163,39 @@ aspect ComponentAnalysis { return set; } - //--- needUnresolvedClass --- - syn boolean TypeDecl.needUnresolvedClass() { - // a TypeDecl needs an unresolved class, if it can appear in a relation - // TODO - return true; + /** + * @return a set of all types that refer to this type using a non-containment relation + */ + coll Set<TypeDecl> TypeDecl.referencingTypes() [new HashSet<TypeDecl>()]; + RelationComponent contributes opposite().getTypeUse().decl() + when opposite().isNavigable() + to TypeDecl.referencingTypes() + for getTypeUse().decl(); + + /** + * @return true, if the type can be the target of a non-containment relation + */ + syn boolean TypeDecl.isReferenceTarget() { + return !referencingTypes().isEmpty(); + } + + /** + * @return true, if the type or one of its abstract supertypes can be the target of a non-containment relation + */ + syn boolean TypeDecl.requiresUresolvedClass() { + if (referencingTypes().isEmpty()) { + // if the type is not referenced itself, it may still be required by an abstract supertype that is referenced + TypeDecl decl = this; + while (decl.hasSuper()) { + decl = decl.getSuper().decl(); + if (decl.getAbstract() && !decl.referencingTypes().isEmpty()) { + return true; + } + } + return false; + } else { + return true; + } } //--- isList --- @@ -201,6 +232,8 @@ aspect InstanceSupplier { return subDecls; } + syn boolean TypeDecl.instantiable() = instantiableSubType() != null; + //--- instantiableSubType --- syn TypeDecl TypeDecl.instantiableSubType() { if (getAbstract() == false) { @@ -225,14 +258,20 @@ aspect Constructors { //--- componentsTransitive --- syn Collection<Component> TypeDecl.componentsTransitive() { ArrayList<Component> list = new ArrayList<>(); - if (hasSuper() && getSuper().decl() != null) { - list.addAll(getSuper().decl().componentsTransitive()); - } for (Component c: getComponents()) { if (c.inConstructor()) { list.add(c); } } + int insertionIndex = 0; + if (hasSuper() && getSuper().decl() != null) { + for (Component c : getSuper().decl().componentsTransitive()) { + if (!list.stream().anyMatch(c::isEqual)) { + list.add(insertionIndex, c); + insertionIndex++; + } + } + } return list; } diff --git a/src/main/jastadd/RelAst.parser b/src/main/jastadd/RelAst.parser index 12a7044e722f07ebce04c83fb4f37dc0a3f15c13..f8db594ad85daa98b6b891a6a41c8898efaf36fd 100644 --- a/src/main/jastadd/RelAst.parser +++ b/src/main/jastadd/RelAst.parser @@ -79,8 +79,8 @@ Component component = // NTA | SLASH ID COL s_type_use.u SLASH {: return new NTAComponent(ID, u); :} | SLASH s_type_use.u SLASH {: return new NTAComponent(u.getID(), u); :} - // NTA Token (same as NTA) - | SLASH LT ID COL s_type_use.u GT SLASH {: return new NTATokenComponent(ID, u); :} + // NTA Token + | SLASH LT ID COL type_use.u GT SLASH {: return new NTATokenComponent(ID, u); :} | SLASH LT ID GT SLASH {: return new NTATokenComponent(ID, new SimpleTypeUse("String")); :} // Token | LT ID COL type_use.u GT {: return new TokenComponent(ID, u); :} diff --git a/src/main/jastadd/backend/NameResolution.jadd b/src/main/jastadd/backend/NameResolution.jadd index 9b47dc1cee3a14f4ad114c8028b0f446392ad064..8080fa0450cc08da31eca31222144a78199a25b7 100644 --- a/src/main/jastadd/backend/NameResolution.jadd +++ b/src/main/jastadd/backend/NameResolution.jadd @@ -149,7 +149,9 @@ aspect NameResolutionHelper { sb.append("aspect ReferenceCreation {\n\n"); for (TypeDecl decl : getTypeDeclList()) { - decl.createReferenceCreator(sb); + if (decl.isReferenceTarget()) { + decl.createReferenceCreator(sb); + } } sb.append("}\n\n"); @@ -172,7 +174,7 @@ aspect NameResolutionHelper { sb.append(ind(1) + "}\n\n"); for (TypeDecl td: getTypeDecls()) { - if (td.needUnresolvedClass()) { + if (td.requiresUresolvedClass()) { td.generateUnresolvedClass(sb); } } @@ -182,24 +184,24 @@ aspect NameResolutionHelper { public void TypeDecl.createReferenceCreator(StringBuilder sb) { - TypeDecl instantiableSubType = instantiableSubType(); - if (instantiableSubType == null) { - throw new RuntimeException("unable to find instantiable subtype for " + getID()); - } - - sb.append(ind(1) + "public static " + getID() + " " + getID() + "." + createRefMethod + "(String ref) {\n"); - sb.append(ind(2) + unresolvedPrefix + instantiableSubType.getID() + " unresolvedNode = new " + unresolvedPrefix + instantiableSubType.getID() + "();\n"); - sb.append(ind(2) + "unresolvedNode.set" + unresolvedPrefix + "Token(ref);\n"); - sb.append(ind(2) + "unresolvedNode.set" + unresolvedPrefix + "ResolveOpposite(true);\n"); - sb.append(ind(2) + "return unresolvedNode;\n"); - sb.append(ind(1) + "}\n"); + if (!instantiable()) { + System.out.println("WARNING: unable to find instantiable subtype for " + getID() + "! Skipping the creation of reference creator methods."); + } else { + TypeDecl instantiableSubType = instantiableSubType(); + sb.append(ind(1) + "public static " + getID() + " " + getID() + "." + createRefMethod + "(String ref) {\n"); + sb.append(ind(2) + unresolvedPrefix + instantiableSubType.getID() + " unresolvedNode = new " + unresolvedPrefix + instantiableSubType.getID() + "();\n"); + sb.append(ind(2) + "unresolvedNode.set" + unresolvedPrefix + "Token(ref);\n"); + sb.append(ind(2) + "unresolvedNode.set" + unresolvedPrefix + "ResolveOpposite(true);\n"); + sb.append(ind(2) + "return unresolvedNode;\n"); + sb.append(ind(1) + "}\n"); - sb.append(ind(1) + "public static " + getID() + " " + getID() + "." + createRefDirectionMethod + "(String ref) {\n"); - sb.append(ind(2) + unresolvedPrefix + instantiableSubType.getID() + " unresolvedNode = new " + unresolvedPrefix + instantiableSubType.getID() + "();\n"); - sb.append(ind(2) + "unresolvedNode.set" + unresolvedPrefix + "Token(ref);\n"); - sb.append(ind(2) + "unresolvedNode.set" + unresolvedPrefix + "ResolveOpposite(false);\n"); - sb.append(ind(2) + "return unresolvedNode;\n"); - sb.append(ind(1) + "}\n"); + sb.append(ind(1) + "public static " + getID() + " " + getID() + "." + createRefDirectionMethod + "(String ref) {\n"); + sb.append(ind(2) + unresolvedPrefix + instantiableSubType.getID() + " unresolvedNode = new " + unresolvedPrefix + instantiableSubType.getID() + "();\n"); + sb.append(ind(2) + "unresolvedNode.set" + unresolvedPrefix + "Token(ref);\n"); + sb.append(ind(2) + "unresolvedNode.set" + unresolvedPrefix + "ResolveOpposite(false);\n"); + sb.append(ind(2) + "return unresolvedNode;\n"); + sb.append(ind(1) + "}\n"); + } } public void TypeDecl.generateContextIndependentNameResolution(StringBuilder sb) { diff --git a/src/main/jastadd/backend/Serializer.jadd b/src/main/jastadd/backend/Serializer.jadd index 21db37f69f93f2c852528ab334245ce5f3e3d9e0..6973c630367428d01c42410cbb74dc19b040f938 100644 --- a/src/main/jastadd/backend/Serializer.jadd +++ b/src/main/jastadd/backend/Serializer.jadd @@ -268,67 +268,73 @@ aspect Serializer { public void TypeDecl.deserialize(StringBuilder sb) { sb.append(ind(1) + "public static " + getID() + " " + getID() + ".deserialize(java.io.File file) throws DeserializationException {\n"); - sb.append(ind(2) + "try {\n"); - sb.append(ind(3) + "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n"); - sb.append(ind(3) + "com.fasterxml.jackson.core.JsonFactory factory = mapper.getFactory();\n"); - sb.append(ind(3) + "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(file);\n"); - sb.append(ind(3) + getID() + " result = deserialize((com.fasterxml.jackson.databind.JsonNode)mapper.readTree(parser));\n"); - sb.append(ind(3) + "parser.close();\n"); - sb.append(ind(3) + "return result;\n"); - sb.append(ind(2) + "} catch (java.io.IOException e) {\n"); - sb.append(ind(3) + "throw new DeserializationException(\"unable to deserialize \" + file.getAbsolutePath(), e);\n"); - sb.append(ind(2) + "}\n"); - sb.append(ind(1) + "}\n"); - sb.append(ind(1) + "public static " + getID() + " " + getID() + ".deserialize(" + jsonNodeType + " node) throws DeserializationException {\n"); - sb.append(ind(2) + getID() + " element;\n"); - if (getAbstract()) { - // switch case between all implementations of the abstract class - sb.append(ind(2) + "switch (node" + jsonNodeTypeAccessor + ") {\n"); - for (TypeDecl subType : subTypeDecls()) { - sb.append(ind(3) + "case \"" + subType.getID() + "\":\n"); - sb.append(ind(4) + "element = " + subType.getID() + ".deserialize(node);\n"); - sb.append(ind(4) + "break;\n"); - } - sb.append(ind(3) + "default:\n"); - sb.append(ind(4) + "throw new DeserializationException(\"Unable to deserialize child of unexpected type \" + node" + jsonNodeTypeAccessor + " + \"(" + getID() + " expected)\");\n"); + if (instantiable()) { + + sb.append(ind(2) + "try {\n"); + sb.append(ind(3) + "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n"); + sb.append(ind(3) + "com.fasterxml.jackson.core.JsonFactory factory = mapper.getFactory();\n"); + sb.append(ind(3) + "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(file);\n"); + sb.append(ind(3) + getID() + " result = deserialize((com.fasterxml.jackson.databind.JsonNode)mapper.readTree(parser));\n"); + sb.append(ind(3) + "parser.close();\n"); + sb.append(ind(3) + "return result;\n"); + sb.append(ind(2) + "} catch (java.io.IOException e) {\n"); + sb.append(ind(3) + "throw new DeserializationException(\"unable to deserialize \" + file.getAbsolutePath(), e);\n"); sb.append(ind(2) + "}\n"); - } else { - sb.append(ind(2) + "element = new " + getID() + "();\n"); - } + sb.append(ind(1) + "}\n"); + + sb.append(ind(1) + "public static " + getID() + " " + getID() + ".deserialize(" + jsonNodeType + " node) throws DeserializationException {\n"); + sb.append(ind(2) + getID() + " element;\n"); + if (getAbstract()) { + // switch case between all implementations of the abstract class + sb.append(ind(2) + "switch (node" + jsonNodeTypeAccessor + ") {\n"); + for (TypeDecl subType : subTypeDecls()) { + sb.append(ind(3) + "case \"" + subType.getID() + "\":\n"); + sb.append(ind(4) + "element = " + subType.getID() + ".deserialize(node);\n"); + sb.append(ind(4) + "break;\n"); + } + sb.append(ind(3) + "default:\n"); + sb.append(ind(4) + "throw new DeserializationException(\"Unable to deserialize child of unexpected type \" + node" + jsonNodeTypeAccessor + " + \"(" + getID() + " expected)\");\n"); + sb.append(ind(2) + "}\n"); + } else { + sb.append(ind(2) + "element = new " + getID() + "();\n"); + } - if (!jsonPointer && !manualReferences) { - // deserialize id - sb.append(ind(2) + "if (node.has(\"id\")) {\n"); - sb.append(ind(3) + "element.unique$Id = node.get(\"id\").asText();\n"); - sb.append(ind(2) + "}\n"); - } + if (!jsonPointer && !manualReferences) { + // deserialize id + sb.append(ind(2) + "if (node.has(\"id\")) {\n"); + sb.append(ind(3) + "element.unique$Id = node.get(\"id\").asText();\n"); + sb.append(ind(2) + "}\n"); + } - // deserialize containment children - if (componentsTransitive().size() > 0) { - sb.append(ind(2) + "if (node.has(\"children\")) {\n"); - sb.append(ind(3) + jsonNodeType + " children = node.get(\"children\");\n"); - for (Component component : componentsTransitive()) { - sb.append(ind(3) + "if (children.has(\"" + component.getID() + "\")) {\n"); - component.deserialize(sb, 4); - sb.append(ind(3) + "}\n"); + // deserialize containment children + if (componentsTransitive().size() > 0) { + sb.append(ind(2) + "if (node.has(\"children\")) {\n"); + sb.append(ind(3) + jsonNodeType + " children = node.get(\"children\");\n"); + for (Component component : componentsTransitive()) { + sb.append(ind(3) + "if (children.has(\"" + component.getID() + "\")) {\n"); + component.deserialize(sb, 4); + sb.append(ind(3) + "}\n"); + } + sb.append(ind(2) + "}\n"); } - sb.append(ind(2) + "}\n"); - } - // deserialize non-containment children - Set<RelationComponent> relationComponents = relationComponents(); - if (relationComponents.size() > 0) { - sb.append(ind(2) + "if (node.has(\"relations\")) {\n"); - sb.append(ind(3) + jsonNodeType + " relations = node.get(\"relations\");\n"); - for (RelationComponent component : relationComponents) { - sb.append(ind(3) + "if (relations.has(\"" + component.getID() + "\")) {\n"); - component.deserialize(sb, 4); - sb.append(ind(3) + "}\n"); + // deserialize non-containment children + Set<RelationComponent> relationComponents = relationComponents(); + if (relationComponents.size() > 0) { + sb.append(ind(2) + "if (node.has(\"relations\")) {\n"); + sb.append(ind(3) + jsonNodeType + " relations = node.get(\"relations\");\n"); + for (RelationComponent component : relationComponents) { + sb.append(ind(3) + "if (relations.has(\"" + component.getID() + "\")) {\n"); + component.deserialize(sb, 4); + sb.append(ind(3) + "}\n"); + } + sb.append(ind(2) + "}\n"); } - sb.append(ind(2) + "}\n"); - } - sb.append(ind(2) + "return element;\n"); + sb.append(ind(2) + "return element;\n"); + } else { + sb.append(ind(2) + "throw new DeserializationException(\"Unable to deserialize type \\\"" + getID() + "\\\" because it is not instantiable.\");\n"); + } sb.append(ind(1) + "}\n"); } @@ -586,7 +592,7 @@ aspect Serializer { sb.append(ind(3) + "if (map.keySet().contains(unique$Id())) {\n"); sb.append(ind(4) + "throw new RuntimeException(new SerializationException(\"UID \" + this.unique$Id() + \" is assigned to both \" + this.getClass().getSimpleName() + \":\" + this.hashCode() + \" and \" + map.get(unique$Id()).getClass().getSimpleName() + \":\" + map.get(unique$Id()).hashCode()));\n"); sb.append(ind(3) + "} else {\n"); - sb.append(ind(4) + "map.put(this.unique$Id, new java.lang.ref.WeakReference(this));\n"); + sb.append(ind(4) + "map.put(this.unique$Id(), new java.lang.ref.WeakReference(this));\n"); sb.append(ind(3) + "}\n"); sb.append(ind(2) + "}\n"); sb.append(ind(2) + "for (ASTNode child : astChildren()) {\n"); diff --git a/src/main/resources/RelASTVersion.properties b/src/main/resources/RelASTVersion.properties index 909fca73c343468a25475a043d5e03ca356550cf..1c4dba180c7eb18acb7c959cb64271b53da6458b 100644 --- a/src/main/resources/RelASTVersion.properties +++ b/src/main/resources/RelASTVersion.properties @@ -1,2 +1,2 @@ -#Thu Apr 16 11:22:48 CEST 2020 -version=0.3.0 +#Fri Mar 11 17:24:51 CET 2022 +version=0.4.0 diff --git a/src/test/jastadd/constructors/Constructors.relast b/src/test/jastadd/constructors/Constructors.relast index 5b3d5a9eda5af27ecaef5382c3d83fb65ee3a7cf..23f473bb76ef111781f76f544555884b76cd6629 100644 --- a/src/test/jastadd/constructors/Constructors.relast +++ b/src/test/jastadd/constructors/Constructors.relast @@ -3,6 +3,7 @@ S ::= S1:A S2:A* [S3:A] /S4:A/ /S5:A*/ /[S6:A]/ /<S7>/ /<S8:long>/; B : S ::= B1:A B2:A* [B3:A] /B4:A/ /B5:A*/ /[B6:A]/ /<B7>/ /<B8:long>/; X; Y; +C : B ::= /<B8:long>/ /<B7>/ /[B6:A]/ /B5:A*/ /B4:A/ [B3:A] B2:A* B1:A; rel A.r1 -> X; rel S.r2* <-> Y.r2; diff --git a/src/test/jastadd/relations/Relations.relast b/src/test/jastadd/relations/Relations.relast index b519a442e963f8fbdd184cec3b006a67e8228f68..cccea4744ba0eb7904e04e1803cfb64c064797d2 100644 --- a/src/test/jastadd/relations/Relations.relast +++ b/src/test/jastadd/relations/Relations.relast @@ -63,3 +63,5 @@ G : C ::= [D] ; // line comment with special symbols like |, *, ->, <-, <->, [A], B ::= C, :, \n, \r, ~, #, /A/ /* block comment with special symbols like |, *, ->, <-, <->, [A], B ::= C, :, \n, \r, ~, #, /A/ */ + +abstract Uninstantiable:A; diff --git a/src/test/jastadd/resolver/Resolver.relast b/src/test/jastadd/resolver/Resolver.relast index 5cbaecfeda6d8445cd880245733847721ef4b8b9..b5c34e0716682dcb212c706aee6db82b919dca57 100644 --- a/src/test/jastadd/resolver/Resolver.relast +++ b/src/test/jastadd/resolver/Resolver.relast @@ -21,3 +21,5 @@ rel A.Bi6? <-> B.Bi6*; rel A.Bi7* <-> B.Bi7; rel A.Bi8* <-> B.Bi8?; rel A.Bi9* <-> B.Bi9*; + +abstract Uninstantiable:A; diff --git a/src/test/jastadd/resolver2/Resolver.relast b/src/test/jastadd/resolver2/Resolver.relast index 93a52019c56ee00c4a7af797a3d806f9d80da4ee..c51e717e56e0e0ac0f6eeabbd1646b492c254c73 100644 --- a/src/test/jastadd/resolver2/Resolver.relast +++ b/src/test/jastadd/resolver2/Resolver.relast @@ -21,3 +21,5 @@ rel A.Bi6l? <-> B.Bi6*; rel A.Bi7l* <-> B.Bi7; rel A.Bi8l* <-> B.Bi8?; rel A.Bi9l* <-> B.Bi9*; + +abstract Uninstantiable:A; diff --git a/src/test/jastadd/serializer-manual-relative/Serializer.relast b/src/test/jastadd/serializer-manual-relative/Serializer.relast index 0e850a74f81791809b4a80e40be4e4a329d69abb..f6928d1293b2299f1f09914b330163ecc9152e56 100644 --- a/src/test/jastadd/serializer-manual-relative/Serializer.relast +++ b/src/test/jastadd/serializer-manual-relative/Serializer.relast @@ -21,3 +21,5 @@ rel A.Bi6? <-> B.Bi6*; rel A.Bi9* <-> B.Bi9*; rel Root.D <-> D.Root?; + +abstract Uninstantiable:A; diff --git a/src/test/jastadd/serializer-manual/Serializer.relast b/src/test/jastadd/serializer-manual/Serializer.relast index 0e850a74f81791809b4a80e40be4e4a329d69abb..f6928d1293b2299f1f09914b330163ecc9152e56 100644 --- a/src/test/jastadd/serializer-manual/Serializer.relast +++ b/src/test/jastadd/serializer-manual/Serializer.relast @@ -21,3 +21,5 @@ rel A.Bi6? <-> B.Bi6*; rel A.Bi9* <-> B.Bi9*; rel Root.D <-> D.Root?; + +abstract Uninstantiable:A; diff --git a/src/test/jastadd/serializer-names/Serializer.relast b/src/test/jastadd/serializer-names/Serializer.relast index 9106765c6ee6b8016de898c75efa2f3c38903050..aa7e099caa1aeaf7756854995cf9544a51e91b89 100644 --- a/src/test/jastadd/serializer-names/Serializer.relast +++ b/src/test/jastadd/serializer-names/Serializer.relast @@ -32,3 +32,5 @@ rel A.Bi5? <-> B.Bi5?; rel A.Bi6? <-> B.Bi6*; rel A.Bi9* <-> B.Bi9*; + +abstract Uninstantiable:A; diff --git a/src/test/jastadd/serializer-pointer/Serializer.relast b/src/test/jastadd/serializer-pointer/Serializer.relast index 0e850a74f81791809b4a80e40be4e4a329d69abb..f6928d1293b2299f1f09914b330163ecc9152e56 100644 --- a/src/test/jastadd/serializer-pointer/Serializer.relast +++ b/src/test/jastadd/serializer-pointer/Serializer.relast @@ -21,3 +21,5 @@ rel A.Bi6? <-> B.Bi6*; rel A.Bi9* <-> B.Bi9*; rel Root.D <-> D.Root?; + +abstract Uninstantiable:A; diff --git a/src/test/jastadd/serializer/Serializer.relast b/src/test/jastadd/serializer/Serializer.relast index 9106765c6ee6b8016de898c75efa2f3c38903050..aa7e099caa1aeaf7756854995cf9544a51e91b89 100644 --- a/src/test/jastadd/serializer/Serializer.relast +++ b/src/test/jastadd/serializer/Serializer.relast @@ -32,3 +32,5 @@ rel A.Bi5? <-> B.Bi5?; rel A.Bi6? <-> B.Bi6*; rel A.Bi9* <-> B.Bi9*; + +abstract Uninstantiable:A; diff --git a/src/test/java/org/jastadd/relast/tests/ConstructorsTest.java b/src/test/java/org/jastadd/relast/tests/ConstructorsTest.java index 689e991ea44e38f6cd96c6211a797b34c9041905..ba22042fa6bd4e043a523f2e0991ba6859c5a533 100644 --- a/src/test/java/org/jastadd/relast/tests/ConstructorsTest.java +++ b/src/test/java/org/jastadd/relast/tests/ConstructorsTest.java @@ -39,6 +39,9 @@ public class ConstructorsTest { A a = new A(); S s0 = new S(); S s1 = new S(a, new List<>(), new Opt<>()); + + // S1:A S2:A* [S3:A] S.r2* + // \------ S ------/ \- rel of S -/ S s2 = new S(a, new List<>(), new Opt<>(), new ArrayList<>()); Assertions.assertEquals(3, numberOfConstructors(S.class)); } @@ -48,10 +51,25 @@ public class ConstructorsTest { A a = new A(); B b0 = new B(); B b1 = new B(a, new List<>(), new Opt<>(), a, new List<>(), new Opt<>()); + + // S1:A S2:A* [S3:A] S.r2* B1:A B2:A* [B3:A] B.r3? + // \------ S ------/ \- rel of S -/ \------ B ------/ \- rel of B -/ B b2 = new B(a, new List<>(), new Opt<>(), new ArrayList<>(), a, new List<>(), new Opt<>(), null); Assertions.assertEquals(3, numberOfConstructors(B.class)); } + @Test + public void testC() { + A a = new A(); + C c0 = new C(); + C c1 = new C(a, new List<>(), new Opt<>(), new Opt<>(), new List<>(), a); + + // S1:A S2:A* [S3:A] S.r2* B.r3? [B3:A] B2:A* B1:A + // \------ S ------/ \- rel of S -/ \- rel of B -/ \------ C ------/ + C c2 = new C(a, new List<>(), new Opt<>(), new ArrayList<>(), null, new Opt<>(), new List<>(), a); + Assertions.assertEquals(3, numberOfConstructors(B.class)); + } + private int numberOfConstructors(Class<?> clazz) { return clazz.getConstructors().length; } diff --git a/src/test/java/org/jastadd/relast/tests/Errors.java b/src/test/java/org/jastadd/relast/tests/Errors.java index 5134381a825854479ba6c7b5b179c4310e3a491b..4221dc642fcc6e4fd2ce9e9dfc03dc2c3ab53435 100644 --- a/src/test/java/org/jastadd/relast/tests/Errors.java +++ b/src/test/java/org/jastadd/relast/tests/Errors.java @@ -1,7 +1,5 @@ package org.jastadd.relast.tests; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.jastadd.relast.compiler.Compiler; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -20,7 +18,6 @@ import static org.jastadd.relast.tests.TestHelpers.readFile; class Errors { - private static final Logger logger = LogManager.getLogger(Errors.class); private static final String FILENAME_PATTERN = "$FILENAME"; @Test @@ -61,7 +58,7 @@ class Errors { String expectedFile = "./src/test/jastadd/errors/" + name + ".expected"; try { - logger.debug("user.dir: {}", System.getProperty("user.dir")); + System.out.println("user.dir: " + System.getProperty("user.dir")); int returnValue = exec(Compiler.class, inFiles.toArray(new String[0]), new File(outFile)); Assertions.assertEquals(1, returnValue, "Relast did not return with value 1"); } catch (IOException | InterruptedException e) { @@ -86,7 +83,7 @@ class Errors { Assertions.assertLinesMatch(expectedList, outList); - logger.info("relast for " + name + " returned \n{}", out); + System.out.println("relast for " + name + " returned \n" + out); }